GraphQL Zeus Documentation

repository·master·Indexed 24 days ago

https://github.com/graphql-editor/graphql-zeus

A TypeScript-native code generator that transforms GraphQL schemas into strongly typed TypeScript clients. It enables developers to interact with GraphQL APIs using type-safe object syntax instead of raw strings. Key features include the Zeus CLI for type generation, the Chain client for queries and mutations, support for custom scalar encoding/decoding, and utilities for reusable selection sets via Selectors.

Tokens
52.6K
Snippets
150
Records
232
Agent score
81%

What's inside GraphQL Zeus

  1. Overview of completed GraphQL Zeus documentation

    master

    The GraphQL Zeus documentation is currently ~50% complete and covers several key areas of the library.

    Completed Core Areas

    • Getting Started: Installation, quickstart, CLI usage, and first query tutorials.
    • Core Concepts: Chain client, selectors (including FromSelector), type inference, thunder (custom fetch implementation), and understanding generated types.
    • Queries & Mutations: Basic queries (pagination, filtering, sorting), mutations (create, update, delete, file uploads), and type-safe variables.
    • Advanced: SSE (Server-Sent Events) subscriptions including API methods (on, error, open, off, close) and React integration.
    • Integrations: TypedDocumentNode guide for Apollo Client, React Query, and urql.
    • Examples: Node.js + TypeScript project structure and selector patterns.

    Key Technical Features Documented

    • Type Inference: Selection-based inference for nested objects, arrays, unions, interfaces, and enums.
    • Thunder Client: Custom fetch implementation supporting authentication (Bearer, dynamic, refresh), retry logic with exponential backoff, and file uploads.
    • Generated Types: Distinction between ValueTypes and ModelTypes, as well as GraphQLTypes, InputTypes, Enums, and Scalars.
  2. What is Thunder and when to use it

    master

    Thunder is Zeus's custom fetch client designed for developers who need full control over the HTTP request lifecycle while maintaining complete type safety.

    While the standard Chain client is simpler and more convenient for most use cases, you should choose Thunder when you need to:

    • Customize the underlying fetch implementation (e.g., using node-fetch or a custom authenticated fetch).
    • Add request interceptors.
    • Implement custom retry logic, timeouts, or exponential backoff.
    • Implement custom caching mechanisms.
    • Control request/response transformations.
    • Handle complex scenarios like file uploads via multipart/form-data.
    • Add custom headers per request via a context object.
  3. Handle real-time updates with Subscriptions

    master

    Zeus provides built-in support for real-time data via WebSockets and Server-Sent Events (SSE).

    • WebSocket Subscriptions: Use the Subscription class to connect to a WebSocket endpoint.
    • SSE Subscriptions: Use the SSESubscription class for Server-Sent Events.

    Both methods allow you to define a subscription query and use an .on(callback) listener to handle incoming data.

    import { Subscription, SSESubscription } from './zeus';
    
    // WebSocket subscriptions
    const sub = Subscription('wss://api.com/graphql');
    sub('subscription')({
      messageAdded: {
        id: true,
        content: true,
        author: { name: true },
      },
    }).on((data) => {
      console.log('New message:', data.messageAdded);
    });
    
    // SSE subscriptions
    const sse = SSESubscription('https://api.com/graphql');
    const stream = sse('subscription')({
      liveMetrics: {
        timestamp: true,
        activeUsers: true,
      },
    });
    
    stream.on((data) => {
      console.log('Metrics:', data.liveMetrics);
    });
  4. Mutation Syntax in Zeus

    master

    Mutations in Zeus follow a specific array-based syntax to ensure type safety for both input arguments and return fields. The structure is:

    await chain('mutation')({
      mutationName: [
        { /* input arguments */ },
        { /* return fields */ },
      ],
    });

    This pattern allows you to define exactly what data you are sending to the server and exactly what fields you want to receive back in the response.

    await chain('mutation')({
      mutationName: [
        {
          /* input arguments */
        },
        {
          /* return fields */
        },
      ],
    });
  5. How GraphQL Zeus works

    master

    Zeus provides a type-safe way to interact with GraphQL endpoints by using your schema to generate TypeScript types and strongly typed clients.

    Instead of writing raw GraphQL strings (which are not type-safe), you use a Zeus syntax that allows you to define queries and selectors using objects. This enables features like:

    • Type-safe selectors: Defining which fields to fetch using object keys.
    • Variable handling: Using the $(name, type) syntax for variables.
    • Reusable selection sets: Using fields("TypeName") to fetch all primitive fields of a type, similar to fragments.
    // Zeus syntax (type-safe)
    {
      usersQuery: {
        admin: {
          sequenceById: [
            { id: $("id", "String!") },
            {
              _id: true,
              name: true,
              analytics: { ...fields("SequenceAnalytics") },
              replies: {
                ...fields("SequenceTrackReply"),
              },
              messages: {
                ...fields("Message"),
              },
              tracks: {
                ...fields("SequenceTrack"),
                contact: {
                  linkedInId: true,
                },
              },
            },
          ],
        },
      },
    }
  6. How Zeus queries are structured

    master

    A Zeus query is built using a chain function and follows a specific three-part structure for each field:

    1. Field name: The GraphQL field you want to query.
    2. Arguments (optional): An object containing the arguments for that field. If arguments are present, they must be the first element in an array.
    3. Selection set: An object defining the sub-fields to be returned.

    When arguments are used, the field is represented as an array where the first element is the arguments object and the second is the selection set object.

    const result = await chain('query')({
      // 1. Field name
      user: [
        // 2. Arguments (optional)
        { id: '123' },
        // 3. Selection set
        {
          id: true,
          name: true,
          email: true,
        },
      ],
    });
  7. Inference for Union and Interface types

    master

    Zeus handles GraphQL unions and interfaces using discriminated union types. To access type-specific fields, you must select the __typename field and use it to narrow the type within your code.

    // Union Type Inference with __typename
    const result = await chain('query')({
      search: [
        { query: 'Zeus' },
        {
          __typename: true,
          '...on User': {
            name: true,
            email: true,
          },
          '...on Post': {
            title: true,
            content: true,
          },
        },
      ],
    });
    
    result.search.forEach((item) => {
      if (item.__typename === 'User') {
        console.log(item.name); // ✅ TypeScript knows this is a User
      } else if (item.__typename === 'Post') {
        console.log(item.title); // ✅ TypeScript knows this is a Post
      }
    });
  8. Use the Chain client to execute GraphQL operations

    master

    The Chain client is the primary interface for executing GraphQL queries and mutations with full type safety. You initialize it with the GraphQL endpoint URL. Once created, you call the chain with the operation name and an object representing the selection set and variables.

    import { Chain } from './zeus';
    
    const chain = Chain('https://your-api.com/graphql');
    
    // Executing a query
    const result = await chain('query')({
      user: [{ id: '123' }, { name: true }],
    });
    
    console.log(result.user.name);
  9. What are Selectors and why use them?

    master

    Selectors are reusable selection sets in Zeus that promote DRY (Don't Repeat Yourself) code and type safety. They allow you to define a set of GraphQL fields once and reuse them across multiple queries, ensuring that your data fetching logic remains consistent and maintainable.

    Key benefits include:

    • Reusability: Define once, use everywhere.
    • Type Safety: Fully typed with IntelliSense.
    • Composition: Combine selectors together to build complex queries.
    • Maintainability: Update field requirements in one place.
  10. Use SSE Subscriptions for real-time streaming

    master
    Server-Sent Events (SSE) provide a simpler alternative to WebSockets for server-to-client streaming. Use SSE when you need to implement features like live notifications, analytics dashboards, real-time monitoring, event streaming, or server-push updates where full-duplex communication (client-to-server) is not required.
  11. Select fields and handle nested objects

    master

    Scalar Fields

    Select scalar fields (String, Number, Boolean, etc.) by setting them to true in the selection object.

    No Arguments

    If a field requires no arguments, omit the array syntax and pass the selection object directly.

    Nested Objects

    For nested objects, use a nested object structure within the selection. This can be done deeply for complex hierarchies.

    Arrays and Lists

    To query a list, pass the selection object. If the list requires arguments (like pagination or filtering), use the array syntax [ { arguments }, { selections } ].

    // Nested objects
    const result = await chain('query')({
      user: [
        { id: '123' },
        {
          name: true,
          profile: {
            avatar: true,
            location: {
              city: true,
            },
          },
        },
      ],
    });
    
    // Lists with arguments
    const result = await chain('query')({
      users: [
        { first: 10, orderBy: 'CREATED_AT' },
        { id: true, name: true },
      ],
    });