Вступление
В этом посте я покажу вам, как автоматически генерировать типы для ваших API GraphQL, написанных в TypeScript, используя Graphql codegen .
Предварительные условия
Ожидается некоторые знания GraphQL и TypeScript. Nodejs и TypeScript должны быть установлены на вашей машине.
Контекст
Машинопись
TypeScript это статический шахер для JavaScript. Он используется в качестве инструмента разработки и помогает писать лучший код и улавливать потенциальные ошибки при разработке вместо этого во время выполнения.
Graphql
Graphql это язык запросов для написания HTTP API. Он очень гибкий и может помочь оптимизировать нагрузку сети, а также количество конечных точек, которые вам понадобятся в типичном API REST.
Аполлон График
Аполлон График это структура/набор инструментов для создания API GraphQL. Он предоставляет решения как для сервера, так и для клиента.
Генератор кода GraphQL (GraphQL-Codegen)
graphql-codegen является инструментом, который автоматически генерирует типы типографий из типов GraphQL и определения резолюров.
Что мы собираемся построить
Мы построим простой API GraphQL, который будет управлять художниками и его картинами. Мы будем использовать Apollo Server и GraphQL-Codegen для автоматического генерации типов типов, которые будут доступны для использования в кодовой базе.
Если вы чувствуете себя потерянным в любой момент или просто хотите ускорить вещи, здесь вы можете найти окончательный код: https://github.com/xcanchal/apollo-server-typescript
Руки вверх
Прежде всего, создайте новую папку для проекта и инициализируйте проект NPM:
$ mkdir {project-name}
$ cd {project-name}
$ npm init --yes
Установите следующие зависимости и DevDependencies:
$ npm install --save apollo-server graphql $ npm install --save-dev typescript @tsconfig/recommended graphql-codegen @graphql-codegen/cli @graphql-codegen/typescript nodemon ts-node
Создать tsconfig.json , файл конфигурации для TypeScript. Мы будем использовать рекомендуемый пример, но мы добавим дополнительное свойство Outdir , потому что мы хотим, чтобы сгенерированные файлы помещали все внутри папки «Dist/», а не рядом с каждой оригиналом .ts файл:
{
"compilerOptions": {
"outDir": "dist",
"target": "ES2015",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Recommended"
}
Чтобы закончить с основной начальной настройкой, добавьте следующее Dev командование под Package.json сценарии. Эта команда будет использоваться для запуска сервера в режиме разработки (создание файлов JS и перезапуск его при каждом изменении):
"scripts": {
"dev": "nodemon --exec ts-node ./server.ts --watch"
}
Теперь давайте напишем код для нашего сервера GraphQL. Создайте новый Server.ts Файл и игнорировать ошибки редактора, если таковые имеются, на данный момент:
import { ApolloServer } from 'apollo-server';
import typeDefs from './type-defs';
import resolvers from './resolvers';
(async () => {
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await server.listen();
console.log(`🚀 Server ready at ${url}`);
})();
Мы будем использовать пару массивов для работы в качестве базы данных. Создайте новый файл с именем Database.ts и вставьте следующий контент. Временно мы будем использовать любой Для типов организаций (не судите меня, мы исправим это позже!)
export const painters: any[] = []; export const paintings: any[] = [];
Большой! Так что теперь мы можем начать определять схему для нашего API. Создайте новый файл с именем type-defs.ts и добавьте типы для Художник и Живопись сущности:
import { gql } from 'apollo-server';
export default gql`
type Painter {
name: String!
country: String!
techniques: [String]!
}
type Painting {
author: String!
title: String!
technique: String!
date: String!
}
`
Нам нужен способ вставить новые художники и картины в нашу базу данных. Давайте определим нашу первую мутацию в type-defs.ts Файл, который позволит нам создавать художников:
# [...]
input PainterInput {
name: String!
country: String!
techniques: [String]!
}
type Mutation {
createPainter(input: PainterInput!): Painter!
}
После этого давайте добавим аналогичную мутация для создания картин:
# [...]
input PaintingInput {
author: String!
title: String!
technique: String!
date: String!
}
type Mutation {
# [...]
createPainting(input: PaintingInput!): Painting!
}
Следующим шагом будет создание резолюров, которые сообщат GraphQL, как запросить или мутировать данные, связанные с ранее определенными типами.
Создайте файл с именем Resolvers.ts С помощью следующего контента (опять же, мы будем использовать любое , пока мы не генерируем типы типографии):
import { painters, paintings } from './database';
const resolvers = {
Mutation: {
createPainter(_: any, { input: painter }: any): any {
painters.push(painter);
return painter;
},
createPainting(_: any, { input: painting }: any): any {
paintings.push(painting);
return painting;
}
}
};
export default resolvers;
До этого момента мы можем вставить художников и картин. Следующим шагом является реализация нескольких запросов для извлечения данных из базы данных. Добавьте следующие запросы в type-defs.ts файл.
# [...]
type Query {
painters: [Painter]! # get all painters
paintings: [Painting]! # get all paintings
painter(name: String): Painter # get a painter by name
painting(title: String): Painting # get a painting by title
}
А также добавьте соответствующие резолюры в Resolvers.ts файл.
// [...]
const resolvers = {
// [...]
Query: {
painters: (): any => painters,
paintings: (): any => paintings,
painter(_: any, { name }: any): any {
console.log(name);
return painters.find((painter) => painter.name === name);
},
painting(_: any, { title }: any): any {
return paintings.find((painting) => painting.title === title);
},
},
// [...]
};
Ваш type-defs.ts Файл должен выглядеть так:
import { gql } from 'apollo-server';
export default gql`
type Painter {
name: String!
country: String!
techniques: [String]!
}
type Painting {
author: String!
title: String!
technique: String!
date: String!
}
input PainterInput {
name: String!
country: String!
techniques: [String]!
}
input PaintingInput {
author: String!
title: String!
technique: String!
date: String!
}
type Query {
painters: [Painter]!
paintings: [Painting]!
painter(name: String): Painter
painting(title: String): Painting
}
type Mutation {
createPainter(input: PainterInput!): Painter!
createPainting(input: PaintingInput!): Painting!
}
`
И Resolvers.ts Файл должен выглядеть так:
import { painters, paintings } from './database';
const resolvers = {
Query: {
painters: (): any => painters,
paintings: (): any => paintings,
painter(_: any, { name }: any): any {
console.log(name);
return painters.find((painter) => painter.name === name);
},
painting(_: any, { title }: any): any {
return paintings.find((painting) => painting.title === title);
},
},
},
Mutation: {
createPainter(_: any, { input: painter }: any): any {
painters.push(painter);
return painter;
},
createPainting(_: any, { input: painting }: any): any {
paintings.push(painting);
return painting;
}
}
};
export default resolvers;
Теперь, когда мы определили определение типов и резолюров для нашего API, давайте запустим сервер в режиме разработки и посмотрим, как он смотрит в Apollo Studio, которая является игровой площадкой для его тестирования.
Выполнить npm запустить dev Откройте новый браузер, перейдите к нему:
$ npm run dev // -> 🚀 Server ready at http://localhost:4000/
После нажатия кнопки «Запросить ваш сервер» вы приземлитесь в студии Apollo, где вы сможете изучить определение схемы, а также попытаться выполнить мутации и запросы, которые мы реализовали.
Последнее, что нужно сделать, и вишня на вершине этой статьи – это генерировать типы, которые будут использоваться в наших файлах TypeScript, которые соответствуют нашей схеме GraphQL.
Вернемся в кодовую базу для настройки graphql-codegen Анкет Создайте новый файл с именем codegen.yaml и вставьте следующую базовую конфигурацию (см. Полный список доступных параметров здесь ):
schema: "./type-defs.ts" # GraphQL types (input file)
generates:
./gql-types.d.ts: # Typescript types (output generated file)
plugins: # List of needed plugins (installed as devDeps)
- typescript
Наконец, добавьте новый сценарий в Package.json для удобства:
"scripts": {
"generate-gql-types": "graphql-codegen"
}
Выполните его ( npm запустить генерировать GQL-тип ) и посмотрите, как новый файл с именем, которое мы определили в codegen.yaml ( gql-types.d.ts ) генерируется. Этот файл содержит все типы TypeScript:
export type Maybe= T | null; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: string; String: string; Boolean: boolean; Int: number; Float: number; }; export type Mutation = { __typename?: 'Mutation'; createPainter: Painter; createPainting: Painting; }; export type MutationCreatePainterArgs = { input: PainterInput; }; export type MutationCreatePaintingArgs = { input: PaintingInput; }; export type Painter = { __typename?: 'Painter'; country: Scalars['String']; name: Scalars['String']; techniques: Array >; }; export type PainterInput = { country: Scalars['String']; name: Scalars['String']; techniques: Array >; }; export type Painting = { __typename?: 'Painting'; author: Scalars['String']; date: Scalars['String']; technique: Scalars['String']; title: Scalars['String']; }; export type PaintingInput = { author: Scalars['String']; date: Scalars['String']; technique: Scalars['String']; title: Scalars['String']; }; export type Query = { __typename?: 'Query'; painter?: Maybe ; painters: Array >; painting?: Maybe ; paintings: Array >; }; export type QueryPainterArgs = { name?: Maybe ; }; export type QueryPaintingArgs = { title?: Maybe ; };
Довольно круто, верно? Тогда вам понравится, когда вы увидите, как они выглядят, когда мы действительно используем их в коде, и мы действительно получаем выгоду от проверки типа:
Прежде всего, давайте обновим Database.ts файл:
import { Painter, Painting } from './gql-types';
export const painters: Painter[] = [];
export const paintings: Painting[] =[];
После этого сделайте то же самое в Resolvers.ts файл:
import { painters, paintings } from './database';
import {
Painter,
Painting,
MutationCreatePainterArgs,
MutationCreatePaintingArgs,
QueryPainterArgs,
QueryPaintingArgs,
} from './gql-types';
const resolvers = {
Query: {
painters: (): Painter[] => painters,
paintings: (): Painting[] => paintings,
painter(_: any, { name }: QueryPainterArgs): Painter | undefined {
console.log(name);
return painters.find((painter) => painter.name === name);
},
painting(_: any, { title }: QueryPaintingArgs): Painting | undefined {
return paintings.find((painting) => painting.title === title);
},
},
Mutation: {
createPainter(_: any, { input: painter }: MutationCreatePainterArgs): Painter {
painters.push(painter);
return painter;
},
createPainting(_: any, { input: painting }: MutationCreatePaintingArgs): Painting {
paintings.push(painting);
return painting;
}
}
};
export default resolvers;
Вывод
Потрясающий! Вы завершили этот урок. Следуя этому подходу, нет необходимости дважды определять одни и те же сущности (одна для GraphQL и один для TypeScript), и мы можем сосредоточиться на том, что действительно важно при разработке, реализации и поддержании API GraphQL: его схемы, запросы и мутации.
С graphql-codegen мы получаем автоматически сгенерированные типы типов, и наш код согласуется с схемой GraphQL без особых усилий, кроме любых настроек конфигурации, которые могут потребоваться по мере развития приложения.
Это один из многих способов работы с TypeScript и GraphQL, поэтому, если вы следуете по другому подходу, не сомневайтесь, чтобы поделиться им, чтобы мы могли узнать что -то новое!
Оригинал: “https://dev.to/xaviercanchal/automatically-generate-typescript-types-for-your-graphql-api-1fah”