Галоўная / Артыкулы

Раздзеленыця коду між NestJS, Next.js і Expo (React Native) у пакете pnpm Turborepo

Створыце монарепо падаць Pnpm Turborepo для API NestJS, веб-сайта Next.js і дапліка Expo React Native, а таксама выберыце, якія типы і схемы будуць належаць да спяльных пакетаў.

Выдавец: REACTAPP.TOP5 хв чытання1135 слоў

Крыніцы: Building a Modern Full-Stack Monorepo with Turborepo, NestJS, Next.js, and React Native

Продукт з адміністрацыяйным слоем на NestJS, веб-дзеяннем на Next.js і мобільным дзеяннем на Expo (React Native) зазвычай пачынаецца як тры репазітарыі, і выкліканыя цім затраты стаюць відчутнымі быстра. Зменены тип павінен копіювацца вручную, правілы верыфікацыі існуюць у трох версіях, а кліенты API кожнага дзеяння поступова адступаюць ад іншых. Monorepo на базе Turborepo і працовыя прасторы pnpm дазволяюць трох дзеянням разам запускатыся, адночасна даючы ўсім іх коду адну базу, якая мае быць спакою. Ёсць паказаныя падэлкі працовага прастора для такой структуры і правіла для выбору таго, што павінна быць у спакетах, якія выкорыстоўваюцца спольна; чы рэальна ўсё-такі польза ад дадавання Turborepo раскрываецца ў стацыі «Дзе месца Turborepo ў NestJS monorepo і калі яго можна праігнораваць».

З трох репазітарыяў да аднаго працовага прастора

Выхідны пункт зазвычай — трох проектаў-братоў, якія нічым не з’яўлены між сабою:

backend/
frontend/
mobile/

У манорепае яны стаюць прасторамі працы ў аднам репазітарыі, калі яны керуецца Turborepo і з’яўляюцца ў зв’язку за дапамогою pnpm:

my-project/
├── apps/
├── packages/
├── turbo.json
└── pnpm-workspace.yaml

Кожныя даплікія ў настоўчы час дзеліцца аднай установкай залежнасцяў, аднай сэтам налаштаванняў TypeScript і lint, а таксама аднам месцам для коду, які можна викорыстоўваць знову. У маленькім проекте гэта можа здавацца зайвай процедурой. У середнім чы роўным большым проекте усуненне неабходнасці копіювання і синхронізаціі ўскладненняў ўважаецца аднам з найбольшых плюсаў з точкі зору продуктывнасці.

Структура, у якой кожны папака відпавядае аднаму завадзе

Структура, якая добра падходзіць для такога набора інструментаў:

my-project/
apps/
├── api/
├── web/
└── mobile/
packages/
├── api-client/
├── config/
├── types/
├── utils/
├── validation/
└── ui/
turbo.json
package.json
pnpm-workspace.yaml

Высшыя рэгістры раздзеляюць элементы, якія можна запускаць, ад элементаў, якія можна перадаць знову, і кожны папака мае адну-едзиную задачу.

apps: програмы, якія можна запускаць незалежна

  • api — это бэкэнд NestJS.
  • web — это прыкладнэе програма Next.js.
  • mobile — это прыкладнэе програма Expo (React Native).

Кожны з іх мае свой процес складання, настройкі і развяроць; наявнасць аднаго рэпазітарыя не значыць, што ўсе запускаецца разам.

packages: код, які адмаўляюць усі прыкладнэяя

Сюды і праглядаецца перавага манорэпазітарыя. Спакушаная логіка пішыцца тут адной раз і імпортуецца там, дзе яя неабходна. Просты прыклад — тип домэна:

//packages/types

export interface User {
  id: string;
  name: string;
  email: string;
}

У файле package.json задайце пакету назва, напрыклад @repo/types, дадзіце яго як залежнасць типу workspace:* у кожным прыкладзе, і кожны прыклад запоўніце тым самым адзначэннем:

import { User } from "@repo/types";

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

Адна схема верыфікацыі для сервера і кліентаў

Верыфікацыя ўсё таксама ўсё чащэ стае прычыной дуплікацыі. Тыя ж правілы рэгістрацыі часта з’яўляюцца як DTO для NestJS, верыфікатор формы для Next.js і верыфікатор формы для React Native, і раней чы пазней адны з іх перестае працаваць правільна.

Спяльная схема Zod усуне гэту проблему:

import { z } from "zod";

export const RegisterSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

API перакантролеўвае прыходзячыя запыткі за дапамою RegisterSchema, і як кліенты, так і серверы выкарыстоўваюць яго для перакантролеўвання форм, таму ўсе яны прыменяюць ідэнтычныя правілы. Якщо змяніцца мінімальная дужына пароля, павінен змяніцца адны файл. Вы таксама можете вылучыць тип у TypeScript за дапамою z.infer<typeof RegisterSchema> замест таго, каб заявляць яго окрема. Заўважыце, што Zod 4 адкрыў дапамогі для форматавання на верхнім роўні, такія як z.email(); ланцоўкавы формат z.string().email() яшчэ працюе, але там адзначаны як застарэлы, таму паспрабуйце прыстаць стыль да вашай версіі Zod.

Одна пакет-кліент API

Замест таго, каб кожны прыклад выстраўляў свой сабскі экземпляр Axios, разместіце яго ў спецыяльным пакете:

//packages/api-client

import axios from "axios";

export const api = axios.create({
  baseURL: process.env.API_URL,
});

Кожны кліентскі прыкладнік імпортуе яго з таго ж месца:

import { api } from "@repo/api-client";

Загаловкі автанацыі, прыемнікі, механізмы павторных спроб і обробкі адзінакоў визначаюцца аднойчы і працуюць аднакова як у веб-версіі, так і ў мобільнай. Є адна умова: process.env.API_URL чытаецца разнае ў кожнай фрэймворку. Next.js выкладзець у код браузера толькі зменныя, прадзвоненыя NEXT_PUBLIC_, а Expo уключае толькі зменныя EXPO_PUBLIC_ у пакет аплікацыі. На практыцы прыемнае будзе выэкспортацыя фабрыкі, якая прыймае базовую URL як аргумент, і кожная аплікацыя можа перадаць сваее настаўленыя значэнні.

Чаму компаненты UI рэдка перадаюць даны

Команды часта спытваюцца, чы можна дзеліцца компанентамі React між Next.js і React Native. Не безпосередна. Веб-аплікацыя атрыбутуе элементы DOM, такія як:

<div>

хоча React Native атрыбутуее натывныя прымітывы, такія як:

<View>

Обе платформы выкарыстоўваюць React, але ў яных разныя слоўнікі компанентаў, таму компанент, напісаны для адной платформы, не будзе адрасавацца на іншай. Якщо справжнім цілям є крос-платформовы інтерфейс, для гэтага ствараны бібліятэкі такія як Tamagui, Solito чыя React Native Web, і яны варыяцыйнае розгляду. У іншым случае трэба дзеліцца бізнес-логікай, процедурамі верыфікацыі, хукамі, типамі і інструментамі, а інтерфейс кожной платформы залишаць у сваёй сабее аплікацыі.

Запуск рабочай прасторы

Turborepo спростоўвае ўседзённыя каманды. За дапамогою корневага dev скрыпта, які вызывае turbo dev, адна каманда запускае кожную аплікацыю:

pnpm dev

Ёнколі трэба працаваць над аднам даплам, выкорыстаце фільтр pnpm з назвай цялевага пакета:

pnpm --filter api dev
pnpm --filter web dev
pnpm --filter mobile start

За гэтымі камандамі Turborepo чытае граф залежнасцяў, выканае задачы у правым порядку і зберагае рэзультаты, тады пакеты, якія выкорыстоўваюцца спольна, будуць створаны раней за аплікацыі, якія ад яных залежнаць, а незменныя элементы будуць праўялены.

Практычная правіла ўжоўвання пакетаў

Дзеліце логіку; не прымушваеце да спольнага інтерфейсу. Гэтыя элементы лёгка пераносяцца між аплікацыямі:

  • Тыпы та перыяды TypeScript
  • Шэмы Zod
  • Функцыі-інструменты та сталярныя значэнні
  • Кліент API та адпаведныя функцыі аутантыкацыі
  • Бізнес-логіка
  • Спольная настройка (TypeScript, ESLint, форматаванне)
  • Внутрашнія бібліятэкі

Компаненты UI належаць у спяльны пакет толькі тады, калі проект быў спецыяльна створаны на мультіплатформенным рашэнні для UI. Таксама трэба выкарыстоваць код, прызначаны толькі для сервера, такі як доступ да базы дадзеных чыста карыстоўвацца секрэтнымі данымі, не ў пакетах, якія імпортуюць кліентскія прыкладненні, інакш гэты код можа опыніцца ў браузеры чыста мобільным пакете.

Чаму саме граніцы ёсць важлівымі

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

Супаўзяныя матэрыялы

  • Стварэнне манорепа з pnpm і Turborepo для прыкладоў Node.js па кроках: Сконфігуруйце манореп на базе TypeScript у порожнім папаке з выкарыстоўваннем працовых прастораў pnpm і Turborepo, а пасля запустіце, сконструюйце і фільтруйце задачі для веб-прыкладоў, API і спяльных пакетаў.