Типизация общения фронта и бэка через GraphQL SDK

Типизация общения фронта и бэка через GraphQL SDK

Tema



В данной статье я покажу как можно на этапе написания кода типизировать общение фронта и бэка, без необходимости поднимать какие-либо сервисы. Этот метод хорош тем, что код, в котором есть ошибки на уровне запросов с фронта до сервера, больше не будет попадать в Git. При настроенном воркфлоу, разработчик не сможет запушить нерабочий код, а у фронта всегда будет свежая SDK схема GraphQL

Этот метод работает только в монорепе, когда фронт и бэк лежат в одном репозитории

В оффициальной доке NestJS в разделе Generating SDL можно найти код, в котором генерируется схема, на основе переданных туда резолверов

async function generateSchema() {
  const app = await NestFactory.create(GraphQLSchemaBuilderModule);
  await app.init();

  const gqlSchemaFactory = app.get(GraphQLSchemaFactory);
  const schema = await gqlSchemaFactory.create([RecipesResolver]);
  console.log(printSchema(schema));
}

Но тут есть проблема: нужно вручную передавать каждый резолвер. На практике, я забывал это делать почти всегда. Как-то я рылся в исходниках неста и нашел код, который позволяет выцепить все резолверы, которые подключены в проекте на лету. За это отвечает ResolversExplorerService. С помощью него можно выцепить все резолверы и передавать в фабрику для генерации схемы

// generate-graphql.ts

import { NestFactory } from '@nestjs/core'
import { GraphQLSchemaFactory } from '@nestjs/graphql'
import { ResolversExplorerService } from '@nestjs/graphql/dist/services'
import { writeFileSync } from 'fs'
import { printSchema } from 'graphql'
import { join } from 'lodash'
import { exit } from 'process'

import { AppModule } from './app/app.module'

export async function generateSchema() {
    const app = await NestFactory.create(AppModule)

    const graphQLFactory = app.get(GraphQLSchemaFactory)
    const explorer = app.get(ResolversExplorerService)

    const schema = await graphQLFactory.create(explorer.getAllCtors())

    writeFileSync(join(process.cwd(), '/schema.graphql'), printSchema(schema))

    console.log('Graphql schema was generated successfuly!')

    exit(0)
}

generateSchema()


Кайф, теперь у нас генерируется схема и сохраняется локально файл. Осталось добавить скрипт для фронта. Я использую genql т.к он лучше всего генерит схему и работает с ApolloClient.

genql --schema ./schema.graphql --output {projectRoot}/src/sdk


Интеграция с Nx

Имея две папки: server и client мы можем добавить nx команды для автоматизации этого дела.

Здесь мы добавляем две команды для билда и запуска скрипта, который будет создавать GraphQL схему

{
  "name": "server",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "packages/server/src",
  "projectType": "application",
  "tags": [],
  "targets": {
    "graphql:prepare": {
      "executor": "@nx/webpack:webpack",
      "dependsOn": [],
      "outputs": ["{options.outputPath}"],
      "options": {
        "target": "node",
        "compiler": "tsc",
        "outputPath": "dist/packages/graphql-generate",
        "main": "packages/server/src/lib/generate-graphql.ts",
        "tsConfig": "packages/server/tsconfig.app.json",
        "webpackConfig": "packages/server/webpack.config.js"
      }
    },
    "graphql:generate": {
      "executor": "@nx/js:node",
      "options": {
        "watch": false,
        "buildTarget": "server:graphql:prepare"
      }
    }
  }
}

А на уровне клиента мы добавим команду, которая генерирует из схемы SDK. В зависимостях команды проставим запуск генерации GraphQL схемы.

{
  "name": "client",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "packages/client",
  "projectType": "application",
  "tags": [],
  "targets": {
    "sdk:generate": {
      "executor": "nx:run-commands",
      "dependsOn": ["server:graphql:generate"],
      "outputs": ["{projectRoot}/src/sdk"],
      "options": {
        "command": "genql --schema ./schema.graphql --output {projectRoot}/src/sdk"
      }
    }
  }
}

Дальше достаточно запустить

nx run client:sdk:generate
Если открыть Nx Graph, то получим такую зависимость


Report Page