How does GraphQL Code Generator Work?
In order to generate code and types, GraphQL Code Generator relies on 2 main core concepts of GraphQL:
- GraphQL Introspection allows fetching the types defined in the target GraphQL API
- GraphQL AST allows to navigate through both client-side operations and remote schema types
Once all GraphQL types (schema types and operations) are identified, GraphQL Code Generator relies on a set of plugins to generate specific code snippets and types.
This process, applied to the @graphql-codegen/typescript
plugin, is illustrated below:
Operations and fragments must have unique names
GraphQL Code Generator checks for this automatically and will let you know if you have any conflicts.
Example with @graphql-codegen/typescript
Given the following GraphQL schema:
scalar Date
schema {
query: Query
}
type Query {
me: User!
user(id: ID!): User
allUsers: [User]
search(term: String!): [SearchResult!]!
myChats: [Chat!]!
}
enum Role {
USER
ADMIN
}
interface Node {
id: ID!
}
union SearchResult = User | Chat | ChatMessage
type User implements Node {
id: ID!
username: String!
email: String!
role: Role!
}
type Chat implements Node {
id: ID!
users: [User!]!
messages: [ChatMessage!]!
}
type ChatMessage implements Node {
id: ID!
content: String!
time: Date!
user: User!
}
And the following operation (Query) on client-side:
query findUser($userId: ID!) {
user(id: $userId) {
...UserFields
}
}
fragment UserFields on User {
id
username
role
}
And with the following codegen.ts
configuration file:
import { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'http://localhost:3333',
generates: {
'types-and-hooks.tsx': { plugins: ['typescript', 'typescript-operations', 'typescript-react-query'] },
},
};
export default config;
@graphql-codegen/typescript
plugin can generate the following TypeScript typings and React hooks files based on defined operations:
// …
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
Date: any;
};
export type Query = {
__typename?: 'Query';
me: User;
user?: Maybe<User>;
allUsers?: Maybe<Array<Maybe<User>>>;
search: Array<SearchResult>;
myChats: Array<Chat>;
};
export type QueryUserArgs = {
id: Scalars['ID'];
};
export type QuerySearchArgs = {
term: Scalars['String'];
};
export enum Role {
User = 'USER',
Admin = 'ADMIN'
}
export type Node = {
id: Scalars['ID'];
};
export type SearchResult = User | Chat | ChatMessage;
export type User = Node & {
__typename?: 'User';
id: Scalars['ID'];
username: Scalars['String'];
email: Scalars['String'];
role: Role;
};
export type Chat = Node & {
__typename?: 'Chat';
id: Scalars['ID'];
users: Array<User>;
messages: Array<ChatMessage>;
};
export type ChatMessage = Node & {
__typename?: 'ChatMessage';
id: Scalars['ID'];
content: Scalars['String'];
time: Scalars['Date'];
user: User;
};
export type FindUserQueryVariables = Exact<{
userId: Scalars['ID'];
}>;
export type FindUserQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, username: string, role: Role } | null | undefined };
export type UserFieldsFragment = { __typename?: 'User', id: string, username: string, role: Role };
export const UserFieldsFragmentDoc = `
fragment UserFields on User {
id
username
role
}
`;
export const FindUserDocument = `
query findUser($userId: ID!) {
user(id: $userId) {
...UserFields
}
}
${UserFieldsFragmentDoc}`;
export const useFindUserQuery = <
TData = FindUserQuery,
TError = unknown
>(
dataSource: { endpoint: string, fetchParams?: RequestInit },
variables: FindUserQueryVariables,
options?: UseQueryOptions<FindUserQuery, TError, TData>
) =>
useQuery<FindUserQuery, TError, TData>(
['findUser', variables],
fetcher<FindUserQuery, FindUserQueryVariables>(dataSource.endpoint, dataSource.fetchParams || {}, FindUserDocument, variables),
options
);
Now our React components can use the type-safe useFindUserQuery()
hook to query the GraphQL API.