Tipando APIs GraphQL com código gerado
1. Por que gerar tipos para GraphQL?
Um dos maiores desafios ao trabalhar com GraphQL em TypeScript é a natureza dinâmica do schema. Diferente de APIs REST, onde as respostas são geralmente estáticas e previsíveis, o GraphQL permite que o cliente especifique exatamente quais campos deseja. Isso significa que o tipo de retorno de uma query pode variar drasticamente dependendo da seleção de campos.
Tipar manualmente essas variações é tedioso, propenso a erros e dificulta a manutenção. Imagine ter que definir manualmente interfaces para cada combinação possível de campos em uma query com 20 campos opcionais. A geração automática de tipos resolve esse problema, oferecendo:
- Segurança em tempo de compilação: erros de tipo são capturados antes de chegar à produção
- Autocomplete inteligente: seu editor sabe exatamente quais campos estão disponíveis
- Refatoração segura: mudanças no schema são propagadas automaticamente para todos os consumidores
2. Ferramentas de geração de código: GraphQL Code Generator
A ferramenta mais madura para esse fim é o GraphQL Code Generator (graphql-codegen). Para começar, instale os pacotes necessários:
npm install graphql
npm install -D @graphql/codegen-cli @graphql-codegen/typescript
Crie um arquivo de configuração codegen.ts na raiz do projeto:
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'https://api.example.com/graphql',
documents: ['src/**/*.graphql', 'src/**/*.tsx'],
generates: {
'./src/generated/graphql.ts': {
plugins: ['typescript', 'typescript-operations']
}
}
};
export default config;
Execute o gerador:
npx graphql-codegen
Isso gerará automaticamente tipos TypeScript baseados no schema GraphQL remoto e nos documentos (queries, mutations) que você definiu.
3. Gerando tipos para operações
Suponha que você tenha o seguinte schema GraphQL:
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Query {
getUser(id: ID!): User
}
E uma query definida em src/queries/getUser.graphql:
query getUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
Após executar o codegen, você terá tipos como:
export type GetUserQueryVariables = {
id: Scalars['ID'];
};
export type GetUserQuery = {
getUser?: {
id: string;
name: string;
email: string;
} | null;
};
Para usar com inferência automática, utilize TypedDocumentNode:
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
import { GetUserQuery, GetUserQueryVariables } from './generated/graphql';
const getUserDocument: TypedDocumentNode<GetUserQuery, GetUserQueryVariables> = gql`
query getUser($id: ID!) {
getUser(id: $id) {
id
name
email
}
}
`;
4. Plugins específicos para React e hooks
Para projetos React, plugins especializados geram hooks tipados automaticamente. Instale:
npm install -D @graphql-codegen/typescript-react-query
Atualize a configuração:
const config: CodegenConfig = {
schema: 'https://api.example.com/graphql',
documents: ['src/**/*.graphql'],
generates: {
'./src/generated/graphql.ts': {
plugins: [
'typescript',
'typescript-operations',
'typescript-react-query'
],
config: {
fetcher: 'fetch'
}
}
}
};
Agora você pode usar hooks tipados:
import { useGetUserQuery } from './generated/graphql';
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading, error } = useGetUserQuery({
variables: { id: userId }
});
if (isLoading) return <div>Carregando...</div>;
if (error) return <div>Erro: {error.message}</div>;
return <h1>{data?.getUser?.name}</h1>;
}
O hook useGetUserQuery já sabe que data.getUser tem os campos id, name e email, com tipos corretos.
5. Tipando fragmentos e composição de queries
Fragmentos são essenciais para reutilização em GraphQL. Com codegen, eles geram tipos específicos:
fragment UserFields on User {
id
name
email
}
query getUserWithPosts($id: ID!) {
getUser(id: $id) {
...UserFields
posts {
title
}
}
}
O código gerado incluirá:
export type UserFieldsFragment = {
id: string;
name: string;
email: string;
};
export type GetUserWithPostsQuery = {
getUser?: UserFieldsFragment & {
posts: Array<{ title: string }>;
} | null;
};
Isso permite composição segura: se você atualizar o fragmento UserFields, todas as queries que o utilizam serão atualizadas automaticamente.
6. Lidando com schemas remotos e escalares customizados
GraphQL permite escalares customizados como Date, JSON ou UUID. Configure o mapeamento no codegen.ts:
const config: CodegenConfig = {
schema: 'https://api.example.com/graphql',
generates: {
'./src/generated/graphql.ts': {
plugins: ['typescript', 'typescript-operations'],
config: {
scalars: {
Date: 'string',
JSON: 'Record<string, unknown>',
UUID: 'string'
}
}
}
}
};
Para enums e unions, o codegen gera tipos discriminados automaticamente:
export type UserRole = 'ADMIN' | 'USER' | 'MODERATOR';
export type SearchResult =
| { __typename: 'User'; id: string; name: string }
| { __typename: 'Post'; id: string; title: string };
7. Estratégias avançadas: watchers e pipelines de build
Ative o modo watch para regenerar tipos automaticamente durante o desenvolvimento:
npx graphql-codegen --watch
Integre a geração no pipeline de CI/CD:
# .github/workflows/ci.yml
- name: Generate GraphQL types
run: npx graphql-codegen
- name: Type check
run: npx tsc --noEmit
Existem duas estratégias para versionamento:
- Gerar sob demanda: não versionar os arquivos gerados, executar codegen no build
- Versionar código gerado: commit dos arquivos para garantir consistência entre desenvolvedores
A escolha depende do seu fluxo de trabalho, mas para times grandes, recomenda-se versionar o código gerado para evitar surpresas.
8. Boas práticas e armadilhas comuns
Evite tipos genéricos demais: configure exactOptionalPropertyTypes no tsconfig.json para evitar que campos opcionais aceitem undefined indevidamente.
{
"compilerOptions": {
"exactOptionalPropertyTypes": true
}
}
Gerencie __typename: por padrão, o codegen inclui __typename nos tipos. Se não precisar, configure:
config: {
nonOptionalTypename: false,
skipTypename: true
}
Cuidado com variáveis $: o codegen gera tipos para variáveis com prefixo $. Certifique-se de que suas queries usam a sintaxe correta.
Quando não gerar código: para projetos muito pequenos ou schemas extremamente voláteis (que mudam várias vezes ao dia), a geração automática pode ser mais custosa que benéfica. Nesses casos, considere tipagem manual ou ferramentas mais leves como graphql-zeus.
A geração de tipos para GraphQL com TypeScript não é apenas uma conveniência — é uma prática fundamental para manter a sanidade mental em projetos de médio e grande porte. Com ferramentas como o GraphQL Code Generator, você elimina uma classe inteira de bugs e acelera significativamente o desenvolvimento.
Referências
- GraphQL Code Generator Official Documentation — Documentação completa com exemplos de configuração e plugins
- TypedDocumentNode no GitHub — Biblioteca para inferência automática de tipos em documentos GraphQL
- How to Use GraphQL Code Generator with React Query — Tutorial oficial de integração com React Query
- TypeScript and GraphQL: The Ultimate Guide — Guia completo da Apollo sobre TypeScript com GraphQL
- GraphQL Code Generator Plugins Catalog — Catálogo oficial de plugins para React, Apollo, React Query e mais
- Scalars Configuration in GraphQL Code Generator — Documentação sobre mapeamento de escalares customizados