
Устанавливаем pnpm, создаем воркспейсы для фронта и бека, импортируем что угодно из одного в другое, типизируем запросы и избавляемся от бойлерплейта.
pnpm. Что, зачем, почему, впечатление
-
Менеджер пакетов вместо npm
-
Создает единую глобальную папку node_modules на компьютере и используем символические ссылки на нее в проектах
-
Поддерживает workspaces
Заменили им npm без проблем. Увеличение скорости по сравнению с npm на уровне «вроде быстрее», производительность не измеряли. Краем уха слышал что коллеги столкнулись с трудностями при создании докер образа из-за симссылок, но раз сейчас докер крутится — делаю вывод, что трудности преодолели.
Использование pnpm вызвало только положительное впечатление.
Установка и организация проекта
Инструкция по установке от разработчиков на всякий случай.
Для большинства подойдет установка через npm:
npm install -g pnpm
В корне проекта необходимо создать файл pnpm-workspace.yaml. В нем указываются все части приложения. В соответствии с этим списком необходимо создать папки.
packages: - frontend - backend
Важно, что в корне проекта и в каждом воркспейсе должны быть свои файлы package.json.
-
В общем package.json может не быть зависимостей вообще.
-
Флаг —parallel запускает скрипты одновременно во всех воркспейсах (если в них есть скрипт с таким названием).
-
С помощью флага —filter можно запустить скрипт в конкретном воркспейсе.
-
Ключевое слово exec — аналог npx
Чтобы иметь возможность подключаться к другому воркспейсу, его нужно вписать в зависимости:
"backend": "workspace:*"
На этом с организацией проекта мы закончили. Пара напутствий:
-
Не забывайте писать в терминале pnpm i вместо npm i.
-
Чтобы установить пакет в конкретный воркспейс, в него нужно сначала перейти — cd frontend, pnpm i something.
Типизация запросов из бекенда
Импорт из одного воркспейса в другой работает также, как если бы они были одним проектом.
Если на этом этапе не работает импорт — проверьте все ли шаги из этой статьи учтены, если все в порядке — вот неплохая статья по организации фулстек монорепозитория с pnpm.
Вот таким образом я реализовал типизацию запросов
-
На бекенде экспортирую функции эндпоинтов (обычно они обращаются к бд и отправляют массив данных).
-
На фронтенде в промежуточном файле types.ts импортирую функции и с помощью TS дженериков описываю типы.
-
Использую эти типы в проекте, в том числе для аргументов и возвращаемых значений асинхронных экшнов redux-toolkit
// Бэкенд. Функция эндпоинта export async function getGateConfigAllBack() { return await prisma.gate__config.findMany({ orderBy: { index: "asc" }, }) }
// Фронтенд. Описание типов с помощью дженериков import { getGateConfigAllBack, upsertGateConfigBack } from "backend/src/directory/GATE__CONFIG/service" import { ArrayToElement, GetArgs } from "../../../shared/UTILS/typeGenerics" export type IGateConfigUpsertArgs = GetArgs<typeof upsertGateConfigBack> export type IGateConfigResponse = ReturnType<typeof getGateConfigAllBack> export type IGateConfig = ArrayToElement<Awaited<IGateConfigResponse>> & {}
// Фронтенд. AsyncThunk для обращения к эндпоинтам и записи данных в стор import axiosClient from "../../../axios-client" import { createAsyncThunk } from "@reduxjs/toolkit" import { IGateConfigResponse, IGateConfigUpsertArgs } from "../types" export const getGateConfigAll = createAsyncThunk("DIRECTORY/GATE_CONFIG/getAll", async (): IGateConfigResponse => { const res = await axiosClient.get("directory/gate-config/get-all") return res.data }) export const upsertGateConfig = createAsyncThunk("DIRECTORY/GATE_CONFIG/upsert", async (data: IGateConfigUpsertArgs): IGateConfigResponse => { const res = await axiosClient.post("directory/gate-config/upsert", data) return res.data })
Вот все используемые дженерики
export type ArrayToElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[] ? ElementType : never export type GetReturn<T extends (...args: any) => any> = ReturnType<T> export type GetReturnAwaited<T extends (...args: any) => any> = Awaited<ReturnType<T>> export type GetArgs<T extends (...args: any) => any> = Parameters<T> extends [infer a] ? a : Parameters<T>
Заключение
До перехода на монорепозиторий, организация фронта и бэкенда на этом проекте была точно такой же. За исключением файла types.ts, типы в нем описывались вручную — Копировали схему таблицы базы данных, вставляли в файл, корректировали. При каждом изменении схемы таблицы в базе данных, файл типов необходимо было править, причем корректно. Это очень сильно тормозило процесс, потому что баги были на каждом шагу, а ошибок TS не было.
Монорепа помогла избавиться от огромного количества кода и головной боли. Рекомендую.
Жду советов / критики / предложений
ссылка на оригинал статьи https://habr.com/ru/articles/798995/
Добавить комментарий