Перейти к основному содержимому

6. Автоматическая кодогенерация

Общие принципы кодогенерации

Понятие кодогенерации

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

В контексте GraphQL и React, кодогенерация играет особенно важную роль. GraphQL как язык запросов предоставляет строго типизированную схему, которая описывает все доступные типы данных, запросы и мутации. На основе этой схемы инструменты кодогенерации могут автоматически создавать:

  • TypeScript-типы и интерфейсы — точно соответствующие структуре данных в GraphQL-схеме
  • Типизированные хуки React — для выполнения запросов и мутаций с проверкой типов
  • React-компоненты — для отображения, создания и редактирования данных
  • Валидаторы форм — учитывающие ограничения полей из схемы
  • Фрагменты GraphQL-запросов — для эффективной выборки взаимосвязанных данных

Такой подход создает бесшовную интеграцию между серверным GraphQL API и клиентским React-приложением, обеспечивая полную согласованность типов на всех уровнях. При изменении схемы GraphQL инструменты кодогенерации автоматически обновляют все сгенерированные артефакты, что позволяет быстро адаптироваться к изменениям API и избежать ошибок несоответствия типов.

Современные инструменты кодогенерации для GraphQL и React, такие как GraphQL Code Generator (используемый в проекте медицинской клиники), Apollo Codegen или Relay Compiler, поддерживают широкие возможности настройки выходного кода, позволяя интегрироваться с различными библиотеками и фреймворками, а также адаптировать генерируемый код под специфические требования проекта.

Ключевые преимущества кодогенерации:

  • Снижение рутинной работы — автоматическое создание повторяющегося кода
  • Типизация — генерация типов TypeScript из GraphQL-схемы
  • Согласованность — поддержка единого стиля кода во всем приложении
  • Скорость разработки — ускорение процесса создания типовых компонентов и запросов

В проекте данного курса используется подход, основанный на следующих этапах:

  1. Определение схемы GraphQL
  2. Генерация типов TypeScript из схемы
  3. Генерация GraphQL-запросов и мутаций
  4. Создание React-компонентов на основе типов и запросов

Проект использует библиотеку @graphql-codegen/cli для настройки процесса кодогенерации и определяет несколько конфигурационных файлов:

  • codegen.ts — для генерации основных типов из GraphQL-схемы
  • gqlgen.ts — для генерации GraphQL-запросов
  • formgen.ts — для генерации React-компонентов форм

Автоматическая кодогенерация React-приложения на основе GraphQL-схемы

  1. Утилита автоматической кодогенерации находится в папке simple-ds-gql-generator DataSpace CE и включает следующие ключевые директории:
  • src/ - исходный код React-приложения
  • tsgen/ - исходный код TypeScript-генератора
  • template/ - шаблоны для генерации компонентов
  1. Выполните сборку кодогенератора:
cd tsgen
npm install
npm run build

На этом этапе происходит:

  • Установка зависимостей для TypeScript-генератора
  • Компиляция TypeScript-кода генератора в JavaScript
  • Подготовка генератора к работе с шаблонами и утилитами

Сборка генератора создает JavaScript-файлы в директории tsgen/build/, которые затем используются в процессе кодогенерации. Эти файлы содержат функции для анализа GraphQL-схемы, формирования типов и генерации компонентов.

  1. Выполните кодогенерацию React-приложения:

Перейдите в корневую директорию кодогенератора (simple-ds-gql-generator) и выполните команды:

npm install
npm run get-schema
npm run allgen

Для предотвращения возможных ошибок при запуске также выполните:

export NODE_OPTIONS=--openssl-legacy-provider

И, наконец, запустите приложение:

npm start

На этом этапе происходит последовательное выполнение следующих действий:

  • npm install - установка зависимостей для React-приложения, включая:

    • Apollo Client для взаимодействия с GraphQL API
    • React и React Router для построения пользовательского интерфейса
    • Ant Design как библиотеку компонентов
    • TypeScript для типизации
    • GraphQL CodeGen для генерации типов и хуков
  • npm run get-schema - получение актуальной GraphQL-схемы с сервера:

    • Команда выполняет запрос к GraphQL API на http://localhost:8080/graphql
    • Сохраняет полученную схему в файл src/graphql/schema.graphql
    • Схема содержит описание всех типов, запросов и мутаций, доступных в API
  • npm run allgen - запуск всех процессов кодогенерации, который последовательно выполняет:

    1. graphql-codegen --config gqlgen.ts - генерация GraphQL-запросов:

      • Анализ GraphQL-схемы из src/graphql/schema.graphql
      • Генерация запросов для каждой сущности в файлы .graphql
      • Создание файлов в директории src/graphql/__generate/
      • Результат: набор типизированных GraphQL-запросов и мутаций для всех сущностей
    2. graphql-codegen --config codegen.ts - генерация TypeScript-типов:

      • Анализ GraphQL-схемы и сгенерированных запросов
      • Преобразование GraphQL-типов в TypeScript-интерфейсы и типы
      • Генерация React-хуков для запросов и мутаций
      • Создание файла src/__generate/graphql-frontend.ts с типами и хуками
      • Результат: полный набор TypeScript-типов для всех сущностей и операций
    3. graphql-codegen --config formgen.ts - генерация React-компонентов форм:

      • Анализ GraphQL-схемы и TypeScript-типов
      • Создание компонентов для списков сущностей
      • Генерация форм создания, обновления и удаления
      • Формирование навигационной структуры приложения
      • Создание компонентов в директории src/components/__generate/
      • Результат: полный набор React-компонентов для работы с данными
  • npm start - запуск локального сервера разработки:

    • Использует webpack-dev-server для обслуживания приложения
    • Запускает приложение на http://localhost:3000
    • Обеспечивает горячую перезагрузку при изменении кода
  1. Протестируйте сгенерированное React-приложение

После запуска приложения можно открыть его в браузере по адресу http://localhost:3000/. Сгенерированное приложение представляет собой полноценную информационную систему с возможностями:

  • Навигации по сущностям через главное меню
  • Просмотра списков сущностей в табличном виде
  • Создания новых записей через формы
  • Редактирования существующих записей
  • Удаления записей

Все компоненты взаимодействуют с GraphQL API через сгенерированные запросы и хуки, обеспечивая типизированный доступ к данным.

Структура и ключевые файлы проекта

Обзор структуры директорий

Структура сгенерированного приложения имеет следующий вид:

src/
├── __generate/ # Сгенерированные файлы GraphQL-операций и типов
│ └── graphql-frontend.ts # Основной файл с типами и хуками для GraphQL-операций
├── components/
│ ├── __generate/ # Сгенерированные React-компоненты
│ │ ├── Clinic/ # Компоненты для сущности Clinic
│ │ │ ├── ClinicAgg.tsx # Компонент-агрегатор
│ │ │ ├── Clinic/ # Директория с компонентами для Clinic
│ │ │ │ ├── ClinicBase.tsx # Базовый компонент
│ │ │ │ ├── ClinicList.tsx # Компонент списка
│ │ │ │ ├── ClinicCreate.tsx # Компонент создания
│ │ │ │ ├── ClinicCreateForm.tsx # Форма создания
│ │ │ │ ├── ClinicUpdate.tsx # Компонент обновления
│ │ │ │ ├── ClinicUpdateForm.tsx # Форма обновления
│ │ │ │ └── ClinicDelete.tsx # Компонент удаления
│ │ ├── Customer/ # Компоненты для сущности Customer
│ │ │ └── ... (аналогичная структура)
│ │ ├── ... # Другие сущности с аналогичной структурой
│ │ └── MainMenu.tsx # Главное меню приложения
│ ├── basic/ # Базовые компоненты
│ │ ├── ErrorBoundary.tsx # Компонент для обработки ошибок
│ │ ├── FormUtils.tsx # Утилиты для работы с формами
│ │ └── ... # Другие базовые компоненты
│ └── Home.tsx # Домашняя страница
├── graphql/
│ ├── __generate/ # Сгенерированные GraphQL-запросы
│ │ ├── Clinic/ # Запросы для сущности Clinic
│ │ │ ├── Clinic.graphql # Запросы для работы с Clinic
│ │ │ └── ... # Другие запросы
│ │ └── ... # Запросы для других сущностей
│ └── schema.graphql # Схема GraphQL, полученная с сервера
├── permgen.ts # Конфигурация для генерации файла разрешений
├── perm-plugin.js # Плагин для генерации JSON разрешений из GraphQL операций
├── permissions.json # Сгенерированный файл с разрешениями для всех операций
├── index.tsx # Точка входа в приложение
└── App.tsx # Корневой компонент приложения

Ключевые директории и файлы

1. Директория src/__generate/

Эта директория содержит автоматически сгенерированные TypeScript-типы и хуки для взаимодействия с GraphQL API:

  • graphql-frontend.ts - централизованный файл, содержащий:
    • Типы данных для всех сущностей из GraphQL-схемы
    • Типизированные входные параметры для запросов и мутаций
    • React-хуки для выполнения запросов (например, useGetClinicQuery)
    • React-хуки для выполнения мутаций (например, useCreateClinicMutation)

Пример фрагмента сгенерированного кода:

// Типы данных сущностей
export type Clinic = {
__typename?: 'Clinic';
id: string;
name?: Maybe<string>;
address?: Maybe<Address>;
};

// Входные параметры для запросов
export type GetClinicQueryVariables = Exact<{
id: Scalars['ID'];
}>;

// Результаты запросов
export type GetClinicQuery = {
__typename?: 'Query';
getClinic?: Maybe<{ __typename?: 'Clinic'; id: string; name?: Maybe<string> }>;
};

// Хуки для запросов
export function useGetClinicQuery(options: QueryHookOptions<GetClinicQuery, GetClinicQueryVariables>) {
return useQuery<GetClinicQuery, GetClinicQueryVariables>(GetClinicDocument, options);
}

2. Директория src/graphql/

Содержит GraphQL-схему и сгенерированные GraphQL-запросы:

  • schema.graphql - полная GraphQL-схема, полученная с сервера, которая описывает все типы, запросы и мутации.

  • __generate/ - директория с сгенерированными GraphQL-запросами для каждой сущности:

    • Clinic/Clinic.graphql - запросы и мутации для сущности Clinic
    • Аналогичные файлы для других сущностей

Пример сгенерированного GraphQL-запроса:

query GetClinicList {
getClinicList {
id
name
address {
city
street
flatNo
}
}
}

mutation CreateClinic($input: _CreateClinicInput!) {
createClinic(input: $input) {
id
name
}
}

3. Директория src/components/__generate/

Содержит сгенерированные React-компоненты, сгруппированные по агрегатам и сущностям:

  • MainMenu.tsx - главное навигационное меню приложения, объединяющее все агрегаты:

    • Использует React Router для маршрутизации
    • Содержит ссылки на все агрегаты сущностей
    • Определяет структуру маршрутов приложения
  • [AggregateName]/[AggregateName]Agg.tsx - компонент-агрегатор для конкретной сущности (например, Clinic/ClinicAgg.tsx):

    • Содержит подменю для навигации между компонентами агрегата
    • Определяет маршруты для дочерних компонентов
  • [AggregateName]/[EntityName]/[EntityName]List.tsx - компонент для отображения списка сущностей:

    • Использует хук запроса для получения данных (например, useGetClinicListQuery)
    • Отображает данные в табличном виде с использованием Ant Design Table
    • Содержит кнопки для навигации к компонентам создания, редактирования и удаления
  • [EntityName]CreateForm.tsx / [EntityName]UpdateForm.tsx - формы для создания и обновления сущностей:

    • Используют Ant Design Form для создания форм
    • Автоматически генерируют поля на основе типов из GraphQL-схемы
    • Добавляют валидацию полей на основе обязательности в схеме
  • [EntityName]Create.tsx / [EntityName]Update.tsx / [EntityName]Delete.tsx - компоненты для операций создания, обновления и удаления:

    • Инкапсулируют логику работы с соответствующими формами
    • Обрабатывают обратные вызовы успешного/неуспешного завершения операций
    • Реализуют навигацию после завершения операций

4. Файлы генерации разрешений

Для управления разрешениями доступа к GraphQL операциям в проекте используются следующие файлы:

  • permgen.ts - конфигурационный файл для генерации разрешений:
    • Определяет схему GraphQL как источник типов
    • Указывает на сгенерированные GraphQL-документы в качестве источника операций
    • Настраивает использование пользовательского плагина perm-plugin.js для генерации файла permissions.json
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
overwrite: true,
schema: 'src/graphql/schema.graphql',
documents: 'src/graphql/__generate/**/*.graphql',
generates: {
'./permissions.json': {
plugins: ['./perm-plugin.js'],
},
},
};
  • perm-plugin.js - пользовательский плагин для GraphQL Code Generator:

    • Анализирует GraphQL-операции (запросы и мутации) из сгенерированных документов
    • Извлекает фрагменты GraphQL для повторного использования в операциях
    • Создает JSON-структуру с разрешениями для каждой операции, включающую:
      • Название операции
      • Тело GraphQL-запроса с встроенными фрагментами
      • Настройки безопасности (по умолчанию JWT-верификация включена, пустые проверки запрещены)
  • permissions.json - сгенерированный файл с разрешениями:

    • Содержит массив объектов разрешений для всех GraphQL-операций в системе
    • Каждое разрешение включает полное тело запроса с встроенными фрагментами
    • Используется системой безопасности для контроля доступа к операциям
    • Автоматически обновляется при изменении схемы или операций GraphQL

Пример структуры разрешения в permissions.json:

{
"name": "searchClinic",
"body": "query searchClinic($cond: String) { ... }",
"allowEmptyChecks": false,
"disableJwtVerification": false
}

5. Ключевые файлы приложения

  • index.tsx - точка входа в приложение:
    • Инициализирует Apollo Client для работы с GraphQL
    • Рендерит корневой компонент App
    • Подключает провайдеры контекста
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import { client } from './cache';
import App from './App';

ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
  • App.tsx - корневой компонент приложения:
    • Определяет общую структуру приложения
    • Подключает главное меню
    • Реализует основной контейнер для контента
import React from 'react';
import { Layout } from 'antd';
import { MainMenu } from './components/__generate/MainMenu';

const App: React.FC = () => {
return (
<Layout style={{ minHeight: '100vh' }}>
<MainMenu />
</Layout>
);
};

export default App;

Взаимодействие компонентов

Сгенерированные компоненты следуют единой архитектуре и взаимодействуют следующим образом:

  1. Навигационная иерархия:

    • MainMenu.tsx[Entity]Agg.tsx[Entity]List.tsx[Entity]Create.tsx/[Entity]Update.tsx/[Entity]Delete.tsx
  2. Поток данных:

    • Компоненты списка ([Entity]List.tsx) получают данные через GraphQL-запросы с помощью сгенерированных хуков
    • Формы создания и обновления ([Entity]CreateForm.tsx, [Entity]UpdateForm.tsx) отправляют данные через GraphQL-мутации
    • Компоненты обновления предварительно загружают данные сущности через запрос для предзаполнения формы
  3. Компонентная композиция:

    • Каждый компонент имеет четкую ответственность и фокусируется на конкретной задаче
    • Формы инкапсулированы в отдельные компоненты, которые можно переиспользовать
    • Компоненты списка предоставляют навигацию к компонентам операций (создание, обновление, удаление)

Эта структура обеспечивает четкое разделение ответственности, повторное использование кода и поддержку типизации на всех уровнях приложения.

Заключение

В данном разделе мы рассмотрели инструменты для автоматической генерации кода на основе GraphQL-схемы. Мы узнали, как использовать GraphQL Code Generator для генерации типов TypeScript и компонентов React. Мы также рассмотрели структуру и ключевые файлы сгенерированного приложения, а также взаимодействие компонентов. В следующем разделе мы познакомимся с TypeScript на примере сгенерированного в этом разделе приложения.

Ссылки

  1. GraphQL Code Generator - инструмент для генерации типов TypeScript и компонентов React из GraphQL-схемы
  2. Plop.js - генератор микрокода для создания файлов и компонентов по шаблонам
  3. OPENAPI Generator - генерация кода клиентов и серверов из спецификаций OpenAPI
  4. Type-GraphQL - создание GraphQL-схемы с использованием TypeScript и декораторов
  5. Prisma - ORM с генерацией TypeScript-типов из схемы базы данных