Перейти к содержимому

18. GraphQL Codegen

GraphQL Codegen

GraphQL Code Generator автоматически генерирует TypeScript типы из твоей схемы. Больше никакого ручного написания интерфейсов!

Без Codegen — ручная типизация, которая расходится со схемой:

// ❌ Вручную — легко пропустить изменения
interface User {
id: string;
name: string;
email: string;
}
// Эти типы могут не совпадать с реальной схемой GraphQL!

С Codegen — всё генерируется из схемы:

// ✅ Автоматически из схемы
export type User = {
__typename?: 'User';
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
email: Scalars['String']['output'];
posts: Array<Post>;
};
Окно терминала
npm install -D @graphql-codegen/cli
npx graphql-code-generator init

Или вручную:

Окно терминала
npm install -D \
@graphql-codegen/cli \
@graphql-codegen/typescript \
@graphql-codegen/typescript-operations \
@graphql-codegen/typescript-react-apollo
codegen.yml
schema: "http://localhost:4000/graphql"
# Или из файла схемы:
# schema: "./schema.graphql"
documents:
- "src/**/*.{ts,tsx}"
- "src/**/*.graphql"
generates:
# Типы схемы (для клиента и сервера)
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
config:
withHooks: true
withComponent: false
withHOC: false
# Resolver типы (только для сервера)
server/generated/resolvers.ts:
schema: "./server/schema.graphql"
plugins:
- typescript
- typescript-resolvers
config:
contextType: "../src/types#Context"
mappers:
User: "../models#UserModel"
Post: "../models#PostModel"

Или в JSON:

codegen.json
{
"schema": "http://localhost:4000/graphql",
"documents": ["src/**/*.{ts,tsx,graphql}"],
"generates": {
"src/generated/graphql.ts": {
"plugins": [
"typescript",
"typescript-operations",
"typescript-react-apollo"
],
"config": {
"withHooks": true
}
}
}
}
package.json
{
"scripts": {
"codegen": "graphql-codegen --config codegen.yml",
"codegen:watch": "graphql-codegen --config codegen.yml --watch"
}
}
Окно терминала
npm run codegen
# Генерирует src/generated/graphql.ts
// src/generated/graphql.ts (автоматически)
export type Scalars = {
ID: { input: string; output: string; };
String: { input: string; output: string; };
Int: { input: number; output: number; };
Float: { input: number; output: number; };
Boolean: { input: boolean; output: boolean; };
DateTime: { input: string; output: string; };
};
export type User = {
__typename?: 'User';
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
email: Scalars['String']['output'];
role: UserRole;
posts: Array<Post>;
createdAt: Scalars['DateTime']['output'];
};
export enum UserRole {
Admin = 'ADMIN',
Editor = 'EDITOR',
Reader = 'READER'
}
export type CreatePostInput = {
title: Scalars['String']['input'];
body: Scalars['String']['input'];
tags?: InputMaybe<Array<Scalars['String']['input']>>;
};

После запуска codegen с typescript-react-apollo, вместо:

// ❌ Без codegen — вручную
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
// Нет типизации! data.user имеет тип any
const { data } = useQuery(GET_USER, { variables: { id } });

Используешь:

// ✅ С codegen — полная типизация
import {
useGetUserQuery,
GetUserDocument,
} from '../generated/graphql';
// Полная типизация! data.user — типизированный объект
const { data, loading, error } = useGetUserQuery({
variables: { id: userId },
});
// Автодополнение работает!
console.log(data?.user.name); // string
console.log(data?.user.email); // string
# src/queries/user.graphql (или прямо в tsx)
query GetUserProfile($id: ID!) {
user(id: $id) {
id
name
email
avatar
bio
posts(limit: 5) {
id
title
createdAt
}
}
}
mutation UpdateProfile($name: String, $bio: String) {
updateProfile(name: $name, bio: $bio) {
id
name
bio
}
}

После npm run codegen:

// Автоматически сгенерировано:
export function useGetUserProfileQuery(
baseOptions: Apollo.QueryHookOptions<GetUserProfileQuery, GetUserProfileQueryVariables>
) {
return Apollo.useQuery<GetUserProfileQuery, GetUserProfileQueryVariables>(
GetUserProfileDocument,
baseOptions
);
}
export function useUpdateProfileMutation(
baseOptions?: Apollo.MutationHookOptions<UpdateProfileMutation, UpdateProfileMutationVariables>
) {
return Apollo.useMutation<UpdateProfileMutation, UpdateProfileMutationVariables>(
UpdateProfileDocument,
baseOptions
);
}

Используешь в компоненте:

import {
useGetUserProfileQuery,
useUpdateProfileMutation,
} from '../generated/graphql';
function UserProfilePage({ userId }: { userId: string }) {
// Полная типизация!
const { data, loading } = useGetUserProfileQuery({
variables: { id: userId },
});
const [updateProfile, { loading: saving }] = useUpdateProfileMutation();
if (loading) return <Spinner />;
return (
<div>
<h1>{data?.user.name}</h1>
<p>{data?.user.bio}</p>
{/* TypeScript знает, что posts — массив объектов с id, title, createdAt */}
<ul>
{data?.user.posts.map(post => (
<li key={post.id}>
{post.title} — {new Date(post.createdAt).toLocaleDateString()}
</li>
))}
</ul>
</div>
);
}
Окно терминала
npm install -D @graphql-codegen/typescript-resolvers
// server/generated/resolvers.ts (автоматически)
export type Resolvers = {
Query: QueryResolvers;
Mutation: MutationResolvers;
User: UserResolvers;
Post: PostResolvers;
};
export type QueryResolvers = {
user?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType, RequireFields<QueryUserArgs, 'id'>>;
posts?: Resolver<Array<ResolversTypes['Post']>, ParentType, ContextType, Partial<QueryPostsArgs>>;
me?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType>;
};
// resolvers/user.ts — типизированные resolvers
import { Resolvers } from '../generated/resolvers';
export const userResolvers: Resolvers = {
Query: {
user: (_, { id }, { db }) => db.users.findById(id), // Типизировано!
me: (_, __, { user }) => user,
},
User: {
posts: (parent, { limit }, { db }) => // parent типизирован как User
db.posts.findByAuthor(parent.id, { limit }),
},
};
codegen.yml
generates:
src/generated/graphql.ts:
plugins:
- typescript
config:
scalars:
DateTime: string
URL: string
JSON: Record<string, unknown>
Upload: File
codegen.yml
generates:
src/:
preset: near-operation-file
presetConfig:
extension: .generated.ts
baseTypesPath: generated/graphql.ts
plugins:
- typescript-operations
- typescript-react-apollo

Каждый файл получает свои типы рядом:

src/
├── components/
│ ├── PostCard.tsx
│ └── PostCard.generated.ts # Автоматически!
└── pages/
├── BlogPage.tsx
└── BlogPage.generated.ts # Автоматически!
  1. Установи graphql-codegen в проект с Apollo Client
  2. Настрой codegen.yml для генерации типов и хуков
  3. Перепиши 2-3 компонента с useQuery на сгенерированные хуки
  4. Добавь resolver типы на сервере
  5. Настрой watch режим для автогенерации при изменении файлов
  • Codegen генерирует TypeScript типы из GraphQL схемы
  • Больше нет ручных интерфейсов, которые расходятся со схемой
  • Генерируются хуки: useGetUserQuery, useCreatePostMutation, etc.
  • Resolver типы — типизация на сервере
  • --watch режим — автообновление при изменениях

Следующий урокGraphQL vs REST →