Кодогенерація та типізація GraphQL
GraphQL-схема—контракт між сервером та клієнтом. GraphQL Code Generator автоматично створює TypeScript-типи зі схеми та операцій, виключаючи ручне синхронізацію типів та runtime-помилки невідповідності даних.
Установка GraphQL Code Generator
npm install -D @graphql-codegen/cli @graphql-codegen/typescript \
@graphql-codegen/typescript-resolvers \
@graphql-codegen/typescript-operations \
@graphql-codegen/typescript-react-apollo \
@graphql-codegen/introspection
Конфігурація (codegen.yml)
# codegen.yml
overwrite: true
schema: "http://localhost:4000/graphql" # або шлях до SDL файлу
documents: "src/**/*.graphql" # клієнтські операції
generates:
# Серверні типи (резолверы)
src/generated/graphql-server.ts:
plugins:
- typescript
- typescript-resolvers
config:
contextType: "../context#GraphQLContext"
mappers:
# Маппінг GraphQL-типів на реальні моделі БД
User: "../models/User#UserModel"
Post: "../models/Post#PostModel"
useIndexSignature: true
enumsAsTypes: true
avoidOptionals:
field: true
# Клієнтські типи (операції + хуки)
src/generated/graphql-client.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
config:
withHooks: true
withComponent: false
withHOC: false
dedupeFragments: true
# Introspection для IDE
src/generated/introspection.json:
plugins:
- introspection
Серверні типи резолверів
Після генерації типи Resolvers точно відповідають схемі:
// src/generated/graphql-server.ts (фрагмент)
export type QueryResolvers<ContextType = GraphQLContext> = {
user?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType, RequireFields<QueryUserArgs, 'id'>>;
posts?: Resolver<ResolversTypes['PostConnection'], ParentType, ContextType, Partial<QueryPostsArgs>>;
}
export type UserResolvers<ContextType = GraphQLContext> = {
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
posts?: Resolver<Array<ResolversTypes['Post']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}
// src/resolvers/user.resolver.ts
import { QueryResolvers, UserResolvers } from '../generated/graphql-server'
import { GraphQLContext } from '../context'
export const userQueryResolvers: QueryResolvers = {
user: async (parent, { id }, ctx: GraphQLContext) => {
// TypeScript знає: id: string, повернення: UserModel | null
return ctx.db.users.findById(id)
}
}
export const userTypeResolvers: UserResolvers = {
posts: async (parent, args, ctx) => {
// parent типізований як UserModel (з mappers)
return ctx.loaders.postsByUserId.load(parent.id)
}
}
// Зібрані резолверы з повною типізацією
export const resolvers = {
Query: userQueryResolvers,
User: userTypeResolvers,
}
Клієнтські типи та хуки
// src/generated/graphql-client.ts (фрагмент)
export type GetUserQueryVariables = Exact<{
id: Scalars['ID']['input'];
}>;
export type GetUserQuery = {
__typename?: 'Query';
user?: {
__typename?: 'User';
id: string;
name: string;
posts: Array<{
__typename?: 'Post';
id: string;
title: string;
}>;
} | null;
};
export const useGetUserQuery = (baseOptions?: Apollo.QueryHookOptions<GetUserQuery, GetUserQueryVariables>) => {
const options = { ...defaultOptions, ...baseOptions }
return Apollo.useQuery<GetUserQuery, GetUserQueryVariables>(GetUserDocument, options)
}
Запуск кодогенерації
# Одноразова генерація
npx graphql-code-generator
# Режим спостереження під час розробки
npx graphql-code-generator --watch
Переваги
- Типобезпечність: типи точно відповідають схемі
- IDE автодоповнення: повний IntelliSense для операцій
- Виявлення breaking changes: кодогенерація не вдається, якщо схема несумісна
- Без ручної синхронізації: оновлює при змінах схеми
Терміни
Налаштування кодогенерації з резолверами та клієнтськими хуками: 1–2 дні. Повна інтеграція з CI/CD, спеціальні плагіни: 2–3 дні.







