Mearie GraphQL Client

website·Indexed 31 days ago

https://mearie.dev

A framework-agnostic GraphQL client featuring end-to-end type safety and a stream-based architecture. Mearie provides automatic type generation from GraphQL schemas and official integrations for React, Vue, Svelte, and Solid. It includes a composable exchange system for networking, normalized caching, deduplication, and real-time subscriptions via SSE or WebSockets.

Tokens
26.1K
Snippets
134
Records
153
Agent score
53%

What's inside Mearie

  1. Overview of Mearie GraphQL Client

    Mearie is a full-featured, framework-agnostic GraphQL client designed for scalable applications. It brings advanced architectural patterns—such as fragment colocation, normalized caching, and end-to-end type safety—to a broader audience by reducing the complexity and rigid requirements typically associated with similar tools like Relay.

    Key features include:

    • End-to-end type safety: Generated types flow from the GraphQL schema to UI components.
    • Ahead-of-time (AOT) compilation: Operations are parsed and optimized during build to remove runtime overhead.
    • Fragment colocation: Data requirements are defined alongside the components that consume them.
    • Normalized caching: Ensures data consistency across the application automatically.
    • Framework support: Dedicated integrations for React, Vue, Svelte, Solid, and vanilla JavaScript.
    • Extensible architecture: A stream-based exchange system for handling authentication, retries, and logging.
  2. Overview of Mearie Client Directives

    Mearie uses client-side directives (annotations added to GraphQL operations and fragments) to customize how data is processed and typed on the client. Unlike standard GraphQL directives, these are processed during code generation and are stripped out before the query is sent to the server, meaning they do not affect the server-side execution.
  3. Overview of Mearie Exchanges

    Exchanges are composable middleware used to customize how GraphQL operations are executed. They process operations as a stream, allowing developers to modify requests, handle responses, or implement cross-cutting concerns like authentication, caching, and logging. The architecture is based on the urql exchange system.
  4. Architecture of a modern GraphQL client stack

    A modern GraphQL client is composed of several layers that separate concerns from network transport to UI integration:

    1. Transport Layer: Manages network communication (HTTP for queries/mutations, WebSockets or SSE for subscriptions).
    2. Cache Layer: Handles entity normalization and cache invalidation based on the schema.
    3. Middleware Layer: Provides composable functions to transform requests and responses (e.g., for authentication, logging, or retries).
    4. Framework Integration: Provides UI-specific bindings (e.g., React hooks, Vue composables) to trigger re-renders when data changes.
    5. Type Generation: A build-time process that produces TypeScript types from GraphQL operations to ensure end-to-end type safety.
  5. Understand the capabilities of modern GraphQL clients

    Modern GraphQL clients provide infrastructure to solve common data-fetching challenges that basic fetch implementations cannot handle efficiently. Key capabilities include:

    • Request Management: Handles serialization, request cancellation on component unmount, automatic retries, and request deduplication.
    • State Management: Manages loading states, error recovery, and data availability during revalidation.
    • Normalized Caching: Stores data at the entity level (by ID) rather than by query. This ensures that if a mutation updates a specific entity, all queries displaying that entity update automatically across the UI.
    • Type Safety: Generates TypeScript types from the GraphQL schema to provide autocomplete and compile-time validation for queries, mutations, and responses.
    • Fragment Colocation: Allows individual components to declare their own specific data requirements.
  6. Implement End-to-End Type Safety with Mearie

    Mearie provides end-to-end type safety by generating types directly from your GraphQL schema. These types flow through the entire application stack down to the UI components, allowing developers to catch type mismatches and errors at compile time instead of during production runtime.
  7. Implement full-stack type safety with GraphQL and TypeScript

    Type safety in this environment converts runtime bugs into compile-time errors by validating GraphQL operations against the schema. This creates a type flow across three main stages:

    1. Schema → Operations: Build tools validate graphql() calls against the schema. Requesting a non-existent field triggers a build-time error.
    2. Operations → Variables: Interfaces are generated for query variables. Passing an incorrect type (e.g., a number instead of a string) is caught by TypeScript.
    3. Variables → Components: Generated types flow into components, preventing access to properties not requested in the query.
    // Example of compile-time validation for a query
    const { data } = useQuery(
      graphql(`
        query GetUserQuery($id: ID!) {
          user(id: $id) {
            name
            email
          }
        }
      `),
      { id: userId },
    );
    
    // TypeScript will error here if 'nmae' is a typo for 'name'
    console.log(data.user.nmae);
  8. Understand Normalized Caching and Automatic Updates

    Normalized caching stores entities separately from queries by indexing data by entity type and ID (e.g., User:1). Queries store references to these entities rather than copies of the data. This ensures that when a mutation updates an entity, every query referencing that entity automatically reflects the change and triggers a re-render without requiring manual cache invalidation.
  9. Understand Mearie's stream-based architecture

    Mearie uses a push-based, reactive stream architecture to handle GraphQL operations. Operations flow through a pipeline of 'exchanges' from source to destination. This architecture provides four primary benefits:

    • Reactive: Operations flow automatically through the pipeline upon execution.
    • Composable: Exchanges are pure functions that can be chained together independently.
    • Cancellable: Resources are cleaned up automatically when a subscription is cancelled (e.g., when a component unmounts).
    • Lazy: The pipeline only executes when a consumer subscribes to the results.
  10. Initialize a basic Mearie GraphQL client

    Create a GraphQL client using createClient. At a minimum, the client requires a schema and at least one terminating exchange, such as httpExchange, to handle network requests.
    // src/lib/graphql-client.ts
    import { createClient, httpExchange } from '@mearie/react'; // or @mearie/vue, @mearie/svelte, @mearie/solid
    import { schema } from '$mearie';
    
    export const client = createClient({
      schema,
      exchanges: [
        httpExchange({
          url: 'https://api.example.com/graphql',
        }),
      ],
    });
  11. Install Mearie for SolidJS

    Install the core Mearie package as a development dependency and the Solid integration package as a production dependency.
    # npm
    npm install -D mearie
    npm install @mearie/solid
    
    # yarn
    yarn add -D mearie
    yarn add @mearie/solid
    
    # pnpm
    pnpm add -D mearie
    pnpm add @mearie/solid
    
    # bun
    bun add -D mearie
    bun add @mearie/solid
    
    # deno
    deno add --dev npm:mearie
    deno add npm:@mearie/solid
  12. Compose fragments and handle nested data

    Fragments can be composed by including other fragments within their definition. This allows for modular data requirements across nested component hierarchies.
    // UserAvatar.tsx
    export const UserAvatar = ({ user }: { user: UserAvatar_user$key }) => {
      const fragment = useFragment(
        graphql(`
          fragment UserAvatar_user on User {
            id
            avatar
            name
          }
        `),
        user,
      );
      return <img src={fragment.data.avatar} alt={fragment.data.name} />;
    };
    
    // UserCard.tsx - Reuses UserAvatar_user
    export const UserCard = ({ user }: { user: UserCard_user$key }) => {
      const fragment = useFragment(
        graphql(`
          fragment UserCard_user on User {
            id
            ...UserAvatar_user
            name
            email
          }
        `),
        user,
      );
    
      return (
        <div>
          <UserAvatar user={fragment.data} />
          <h3>{fragment.data.name}</h3>
          <p>{fragment.data.email}</p>
        </div>
      );
    };