skip to Main Content

I am learning graphql, and want to find out how to use the type of object which we are getting in result.

as we are requesting like

const GET_CHARACTERS = gql`
    query {
        characters{
                results{
                id
                name
                image
                gender
            }
        }
    }
`

so in the result we got the object same as it is
but can we get the type defination of the so we don’t have to make own type

or we ignore using any cause it is annoying to mention the thing which we have mentioned back in this query also

How can we get the Type of the mention object by graphql
So we can use it as typescript type

2

Answers


  1. To automatically generate TypeScript types for the result of a GraphQL query, you can use tools like Apollo Client’s codegen feature or the graphql-codegen library. These tools can analyze your GraphQL queries and generate TypeScript types based on the schema of your GraphQL server.

    Login or Signup to reply.
  2. I struggled with the same problem and didn’t find anything else but graphql-zeus library. I almost got it working when I ended up having this issue: https://github.com/graphql-editor/graphql-zeus/issues/322

    So I wrote my own similar typescript types because I like to have strong typings. It wasn’t an easy or quick task so I hope someone finds this useful 🙂 It’s used something like this:

    // Specify what fields you want to get from the api
    // Projection's type argument is the full graphql-codegen generated type in my project
    const charactersProjection = {
      results: [
        {
          id: true,
          name: true,
          image: true,
          gender: true,
          items: [
            id: true,
            name: true,
          ],
        },
      ],
      totalResults: true,
    } satisfies Projection<CharactersResult>;
    
    // Write the gql as usual but use the 'toGqlString'
    // to convert the projection object to gql compatible format
    const query = gql`
      query characters() {
        characters() {
          ${toGqlString(charactersProjection)}
        }
      }
    `;
    
    // Use a high-order function to wrap the (Apollo's) refetch function
    // to get types correctly in the response.
    // The last type argument, "characters", is there to reflect
    // the query name which is included in the Apollo's response.
    const refetchFn = gqlRefetch<CharactersResult, typeof charactersProjection, "characters">(refetch);
    
    const { data } = await refetchFn();
    const firstCharacterName = data.characters.results[0].name;
    

    It’s a somewhat verbose but it’s worth it.

    The implementation can be used with only using the Projection and Intersection types but I included a few helper/wrapper functions that I’m using. It’s a bit of a monster but here it is:

    // Maybe comes from generated graphql-codegen types
    import { Maybe } from '../graphql';
    
    /**
     * Used to select a subset of the fields for graphql queries.
     * This allows user to select fields from type T by assigning true to the desired fields. For example:
     * const userProjection = {
     *   id: true
     *   name: true
     *   address: {
     *     street: true
     *   }
     * } satisfies Projection<User>.
     */
    export type Projection<T> = T extends Array<infer U>
      ? U extends object
        ? Array<Projection<U>>
        : true
      : T extends object
      ? {
          [K in keyof T]?: T[K] extends object
            ? Projection<T[K]>
            : T[K] extends Maybe<infer V>
            ? true | Projection<V>
            : true;
        }
      : T;
    
    /**
     * Create a new type by intersecting the fields of type T and U.
     * Resulting type will have fields present in both T and U but field types are taken from T.
     * Usually T is the graphql-codegen generated type and U is the Projection type. For example:
     * type UserProjection = Intersection<User, typeof userProjection>.
     */
    export type Intersection<T, U> = T extends Date
      ? Date
      : T extends (infer I)[]
      ? U extends (infer J)[]
        ? Intersection<I, J>[]
        : T
      : T extends object
      ? { [K in keyof T & keyof U]: Intersection<T[K], U[K]> }
      : T;
    
    /**
     * Helper type to get apollo client query response under given property key.
     * For example, if the gql query is { user { id name } }, then the response type will be constructed as follows:
     * type UserResponse = ResultType<User, typeof userProjection, 'user'>.
     */
    type ResultType<T, U, K extends PropertyKey> = {
      [P in K]: Intersection<T, U>;
    };
    
    /**
     * Higher order function to create a function that returns the response of a Apollo GraphQL client query.
     * The function returned by gqlResponse will have the same signature as the original query
     * function but with the response type that actually matches with the desired projection.
     * Usage:
     * const { data, refetch } = gqlResponse<User, typeof userProjection, "user">(useSuspenseQuery)(getUserGql, opts);
     */
    export const gqlResponse = <T, U, K extends PropertyKey>(
      queryFn: (gql: any, opts: any) => { data: Intersection<T, U>; refetch: () => Promise<{ data: Intersection<T, U> }> },
    ) => {
      return (gql: unknown, opts: unknown) => {
        const result = queryFn(gql, opts) as {
          data: ResultType<T, U, K>;
          refetch: () => Promise<{ data: ResultType<T, U, K> }>;
        };
        return result;
      };
    };
    /**
     * Create an async function that returns the response of a Apollo GraphQL client's refetch with the desired projection as the response type.
     * The parameter should be the refetch function of the original Apollo client query. For example:
     * const refetchUser = gqlRefetch<User, typeof userProjection, "user">(refetch);
     * const { data } = await refetchUser(opts);
     */
    export const gqlRefetch = <T, U, K extends PropertyKey>(
      queryFn: (opts: any) => Promise<{ data: Intersection<T, U> }>,
    ) => {
      return async (opts: unknown) => {
        return (await queryFn(opts)) as { data: ResultType<T, U, K> };
      };
    };
    
    /**
     * Generate a gql query string from the given object.
     */
    export const toGqlString = (obj: any, indent = ''): string => {
      let gql = '';
      for (const key in obj) {
        if (Array.isArray(obj[key])) {
          gql += `${indent}${key} {n${toGqlQuery(obj[key][0], indent + '  ')}${indent}}n`;
        } else if (typeof obj[key] === 'object') {
          gql += `${indent}${key} {n${toGqlQuery(obj[key], indent + '  ')}${indent}}n`;
        } else {
          gql += `${indent}${key}n`;
        }
      }
      return gql;
    };
    

    Like said, I’m using Apollo so there might be some Apollo specific stuff there but I guess it can be adjusted pretty easily. Hope this helps.

    Edit: I probably need to note that I’m generating and publishing the graphql-codegen types in the backend so that’s why I only get the types for my objects but not types for the gql response objects. So my solution might not be optimal/required for OP but maybe someone else.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search