Мой хороший друг порекомендовал курс Уэса Боса Advance React and GraphQL. Курс преподавался на JavaScript, но, поскольку тогда я только изучал TypeScript, я хотел сделать это на TypeScript для практики.

Хотя я мог добавлять типы почти ко всему, меня очень беспокоил тот факт, что мои преобразователи не были типизированы. После долгих поисков в Google я наконец нашел способ печатать мои серверные распознаватели. В этой статье будет рассказано обо всем, что я делал, от начала до конца, так что расслабьтесь, расслабьтесь и давайте начнем печатать!

Настраивать

В этом проекте используются Prisma и Генератор кода GraphQL.

Сначала создайте каталог для вашего сервера:

mkdir server; cd server; yarn init -y

Во-вторых, установите эти пакеты:

yarn add graphql-cli prisma-binding
yarn add -D typescript

Инициализация Prisma

Затем запустите prisma init и следуйте инструкциям. Вы можете использовать существующую базу данных, создать новую или использовать их демонстрационную базу данных.

Для этого урока я собираюсь использовать демонстрационную базу данных:

На следующих нескольких экранах просто следуйте инструкциям. Когда вы перейдете на этот экран, выберите Don't generate, так как мы будем вручную создавать .graphql схему.

После этого вы увидите 2 новых файла: prisma.yml и datamodel.prisma. Давайте откроем upprisma.yml.

Генерация схемы Prisma и Local GraphQL

Добавьте в свой prisma.yml следующие строки кода:

# this is what's generated
endpoint: ${env:PRISMA_ENDPOINT}
datamodel: datamodel.prisma
# add these
hooks:
  post-deploy:
    - graphql get-schema -p prisma

Это означает, что всякий раз, когда вы запускаете prisma deploy, эти обработчики после развертывания всегда будут запускаться после. Но прежде чем мы запустим это, давайте создадим .graphqlconfig.yml в корневом каталоге:

### /.graphqlschema.yml
projects:
  app:
    # path to YOUR gql schema
    schemaPath: 'src/schema.graphql'
    extensions:
      endpoints:
        # your graphql endpoint (not playground url!)
        default: 'http://localhost:5000'
  prisma:
    # where you want to generate the PRISMA graphql schema
    schemaPath: 'src/generated/prisma.graphql'
    extensions:
      # path to the prisma.yml
      prisma: 'src/prisma/prisma.yml'

Я упростил приведенный выше файл с помощью комментариев, чтобы вы могли создавать файлы, подходящие для вашего каталога, сколько захотите. Обратите внимание, что проекты app и prisma имеют отдельные схемы GraphQL. Проект app - это то, что ваш сервер будет использовать. В проекте prisma используется автоматически созданная схема GraphQL Prisma.

Генерация схемы Prisma

После этого запустите yarn graphql get-schema -p prisma. Ваша схема Prisma GraphQL теперь создается там, где вы ее указали! Откройте его и осмотритесь.

Создайте схему GraphQL для своего сервера

Теперь создайте схему GraphQL вашего сервера. Чтобы использовать схему Prisma, создайте schema.graphql файл и импортируйте схему Prisma следующим образом:

### src/schema.graphql
# import * from './generated/prisma.graphql
type Query {
  # Since the Prisma schema is imported,
  # we don't need to specify User type again!
  # Just call it here and it will automagically do the rest!
  fetchUser(id: ID!): User
}

Файлы GraphQL используют комментарии для импорта.

Теперь вы можете запустить prisma deploy в своем терминале, и он автоматически сгенерирует для вас схему Prisma. Вы можете внести изменения в свой datamodel.prisma, добавив дополнительные типы:

type User {
  id: ID! @id
  name: String! @unique
  email: String! @unique
  password: String!
}
type Item {
  id: ID! @id
  name: String!
  description: String!
  price: Int!
}

Запустите prisma deploy, и вы увидите соответствующие изменения в prisma.graphql файле.

Создание схемы Prisma TypeScript

Пришло время сгенерировать нашу схему Prisma в TypeScript. Откройте prisma.yml и добавьте новый хук после развертывания:

endpoint: ${env:PRISMA_ENDPOINT}
datamodel: datamodel.prisma
hooks:
  post-deploy:
    - graphql get-schema -p prisma
    - graphql codegen # add this line

В .graphqlconfig.yml добавьте расширение codegen к проекту prisma:

projects:
  app:
    schemaPath: 'src/schema.graphql'
    extensions:
      endpoints:
        default: 'http://localhost:5000'
  prisma:
    schemaPath: 'src/generated/prisma.graphql'
    extensions:
      prisma: 'src/prisma/prisma.yml'
      # add these below
      codegen:
        generator: prisma-binding
        language: typescript
        # path to the Prisma schema
        input: 'src/generated/prisma.graphql'
        output:
          # where to generate the TypeScript Prisma schema
          binding: 'src/generated/prisma.ts'

Теперь запустите yarn graphql codegen или prisma deploy и вуаля! Создан prisma.ts. Откройте его и осмотритесь. Это похоже на огромный беспорядок кода, но вы всегда можете запустить Prettier после этого, чтобы все очистить. Обратите внимание на указанную переменную экспорта Prisma.

Создание типа контекста преобразователя

Помните именованную переменную экспорта Prisma из сгенерированного prisma.ts? Здесь мы добавляем его в контекст наших преобразователей! Создайте context.ts:

// ./src/resolvers/context.ts
import { Prisma } from '../generated/prisma'
export type Context = { db: Prisma }

Настройка генератора кода GraphQL

Вот где действительно проявляется генератор кода GraphQL! Установите следующие пакеты:

yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers

Выполните следующую команду:

graphql-codegen init

Ответьте как таковые на следующие вопросы:

Последние 4 вопроса действительно касаются того, как вы хотите структурировать свой сервер. Мои текущие ответы просты. Это сгенерирует codegen.yml (в зависимости от того, какое имя файла вы указали для вывода).

Указание типа контекста для генератора кода GraphQL

Откройте codegen.yml и добавьте еще строки кода:

### /codegen.yml
overwrite: true
schema: './src/schema.graphql'
documents: null
generates:
  src/resolvers/types.ts:
    plugins:
      - 'typescript'
      - 'typescript-resolvers'
# add these below
config:
  skipTypename: true
  # This path is RELATIVE to where you will generate your output!!
  # In this case, it looks for the context.ts in src/resolvers
  # './context#Context' means it will look for a Type/Interface with the name 'Context' in context.s
  contextType: './context#Context'
hooks:
  afterAllFileWrite:
    - prettier --write # optional

Обратите внимание на contextType в разделе config:

  • contextType ищет файл, относительный к тому месту, где типы преобразователя генерируются (выводятся).
  • В этом случае, поскольку наш types.ts создается в src/resolvers, он будет искать context.ts в том же каталоге.
  • ./context#Context ищет type или interface с именем Context в context.ts файле.
  • В таком случае помните наш export type Context ранее? Он будет искать это.

Кроме того, skipTypename - это полностью ваш звонок. Теперь запустите yarn codegen (или имя скрипта, которое вы дали ему во время установки). Откройте сгенерированный types.ts и внимательно осмотритесь.

Настройка наших резолверов

Наконец-то наступил момент, которого мы все ждали! Давайте настроим наши запросы и изменения. Кроме того, перед продолжением добавьте больше запросов / мутаций в свой schema.graphql.

Настройка запросов

Создайте query.ts в src/resolvers:

### src/schema.graphql
type Query {
  fetchUser(id: ID!): User
  fetchItems(where: ItemWhereUniqueInput): [Item]!
}

// ./src/resolvers/query.ts
import { QueryResolvers } from './types'
const Query: QueryResolvers = {
  fetchItems: {
    fragment: '',
    resolve: async (parent, { where }, ctx, info) => {
      return await ctx.db.query.items({where: where}, info)
    }
  },
  fetchUser: {
    fragment: '',
    resolve: async (parent, { id }, ctx, info) => {
      return await ctx.db.query.user({ where: { id } }, info)
    }
  }
}
export default Query

Как вы увидите, редактор теперь может определять тип args и ctx каждого запроса:

Тип QueryResolvers также знает, какие запросы у меня есть в моем schema.graphql:

На мой взгляд, лучшая часть этого заключается в том, что аргумент ctx теперь полностью типизирован:

Настройка мутаций

Давайте также быстро наберем мутации:

### src/schema.graphql
input createItemInput {
  name: String!
  description: String!
  price: Int!
}
type Mutation {
  createItem(data: createItemInput!): Item!

// ./src/resolvers/mutation.ts
import { MutationResolvers } from './types'
const Mutation: MutationResolvers = {
  createItem: {
    fragment: '',
    resolve: async (parent, { data }, ctx, info) => {
      const { name, description, price } = data
      
      return await ctx.db.mutation.createItem({
        data: { name, description, price }
      }, info)
    }
  }
}
export default Mutation

Как видите, при деструктуризации объекта data он также знает, какие аргументы мы ему передаем!

Заключение

Как видите, распознаватели с типобезопасностью - вещь прекрасная. Он помогает определить, какие у вас есть запросы / мутации, какие аргументы они принимают, и, что наиболее важно, дает вам полностью типизированный объект ctx. Всякий раз, когда вы вносите изменения в свой schema.graphql, вы можете просто запустить сгенерированный скрипт codegen, чтобы обновить все ваши преобразователи!