Рубрики
Без рубрики

Автоматически генерировать типы TypeScript для вашей схемы GraphQL

Введение в этом посте я покажу вам, как автоматически генерировать типы для вашего … Tagged с помощью TypeScript, GraphQL, JavaScript, Node.

Вступление

В этом посте я покажу вам, как автоматически генерировать типы для ваших 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”