Галоўная / Артыкулы / Спакоўванне генеравання Prisma CRUD з адказным карантинам маршрутаў

Спакоўванне генеравання Prisma CRUD з адказным карантинам маршрутаў

Дазвольце дазнацься, як генераванне роутэраў Prisma CRUD на адной засадзе схемы можа пазбавіць ад павтаральнага базовага коду, заўсёды залічываючы рашэнні пра довер’е, межы і доступ у кодзе аплікацыі.

2767 слоў

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

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

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

prisma-generator-express зьменшыць вам кантрольную роботу і адчыніць яе ў крок prisma generate. Ён можа ствараць рутэры для Express, Fastify чыст Hono. Супутні пакет prisma-guard генеруе метаданы паўнай пераверкі данных і сферы дасягнення, якія узгадваюць Prisma, а форматы операцый чытальна паказваюць, якія аргументы можа надасці кожны тип вызывальніка.

У выніку мы не маем прыкладнення без коду. Это прыкладненне з набліжна меншай кантроллю над роботай сэрвісаў і з набліжна яснейшым распадам адпаведальнасцяў за застаўшыяся рашэнні.

Гэтае разлікаванне ёсць важлівым аспектам. Генераванне должна брасці на сябе все, што сама схема можа полным чынам апісаць. Аутентыкацыя, тые операцыі, якія ўпускаюцца, хто є вызывальнікамі, а таксама будь-яя політыка, якой патрэбны ведамасці, якія выходзяць за межы схемы, все ўсё застаюцца часткай коду прыкладнення.

Перакладзіце парадоксальную работу ў адны крок генеравання

Гэнераваны API запускаецца з трох сувязанных выходных элементаў: Prisma Client, метаданыя захавання і самі HTTP-маршрутызаторы.

generator client {
  provider = "prisma-client-js"
}
generator guard {
  provider          = "prisma-guard"
  output            = "../generated/guard"
  enforceProjection = "true"
}generator express {
  provider = "prisma-generator-express"
  target   = "express"
}

Выкананне npx prisma generate аднойчы занова генеруе всі тры элементы кожны раз, калі змянюецца схема або настройкі генератора.

Схема застаецца ўнікальным источнікам правды для вашай модэлі дадзеных. Гэнераваныя файлы маршрутызатораў ёсць толькі рэзультатам будовы, нічога больш. Наладкі маршрутаў вялікія, якіе операцыі насправды будуць запускацца, а форматы захавання вялікія, якіе аргументы Prisma можа выкарыстоўваць паказаны вызывальнік.

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

Якщо якае-небудзь правілле нельга верна выразіць за дапамогою гэнератора чыя-небудзь захоўчыцкай структуры, не прымушвайце яго ўключыцца ў настройкі. Спецыяльны працоўнік чы правілле, якое выкарыстоўваецца ў базе дадзенаў, являюцься чыстэйшай межой, чым декларатывныя настройкі, якія таямна брэшу пра тое, што яны робяць.

Генераванне таксама змінюе тое, што на самай працэ ўскладнення коду. Рукапісныя CRUD-функцыі спакушаюць адпаведальных за перагляд да практычнага перагляду кожнай лініі коду, якая займаецца павтаральным парсінгам і логікай делегавання. Генераваныя CRUD-функцыі пераключаюць такій аналіз на значна меншыя элементы: схему Prisma, параметры генератора, апісаванні маршрутаў, ўзоры дадзеных і тое, што як рэшалер стварае надзяйны контекст.

Гэта не значыць, што генераваны выхід няма значэння — проста мова пра тое, што безпосереднія правкі ў яму — глыбока памылка. Якщо маршруту трэба выправіць, трэба змяніць конфігурацыю, якая яго стварае, і занова генераваць. Рукамі адредагаваны фікс, дадзены ў файл маршрута, можа зникнуць пасля наступных змян схемы, і не залишае жадных следоў таго, які самэй насправдзе быў задуманы контракт.

Для апдэйтаў версій таксама неабходна тая ж дисцыпліна. Закрепіце Prisma, пакет захавання і генератор маршрутаў для адпаведных версій, занова створыце проект на чыстай базе дадзеных і запрацаваце тэсты на адпаведнасць выходным дадзенням. Створаны код застаецца часткай вашага спісу залежнасцей, нават якщо ваш репазітарый не спрыявае кожнай выданай лініі коду як да дадзення, напісанага рукой.

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

Дазвольце адной модэлі служыць калькам разных контрактаў

Адна модэлі Prisma можа адразу падтрымваць калькі разных інтерфейсаў, надатковых да продукту.

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

Іменаваныя форматы дазволяюць адной створанай операцыі выконваць калькуляцыі па колькох разных контрактах адразу:

const roomRoutes = {
  findMany: {
    shape: {
      storefront: {
        where: {
          isPublished: { equals: force(true) },
          name: { contains: true },
        },
        select: { id: true, name: true, nightlyRate: true },
        take: { max: 40, default: 20 },
      },
      backoffice: {
        where: {
          name: { contains: true },
          floor: { equals: true },
        },
        select: {
          id: true,
          name: true,
          nightlyRate: true,
          floor: true,
          internalNote: true,
        },
        take: { max: 200, default: 50 },
      },
    },
  },
}

Кожны іменаваны ключ визначае цэлысны, самостатны контракт API. Публічны аб’ект не можа расшырыць свой результат, ўключыўшы internalNote, таму што гэтае поле проста не існуе ў публічным формате. Персонал можа отрымаць набаго богачы результат, не прымушваючы кожнага іншаго кліента викорыстоўваць свой саставны механізм обробкі дадзеных.

Выберачы shape, калі толькі контракт на рэвэле Prisma павінен разлічвацца межаў аб’ектамі. Выберачы variants, калі аб’ект, які падходзіць, таксама павінен выкарыстоўваць свае спецыяльныя хукі.

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

Для будзь-якіх прывілейаваных задач краща вырашваць пытанне абонента за дапамою resolveVariant, выкарыстоўваючы аутентыфікованы стан сервера. Або ж спачатку падбіраюцца точныя ключы абонента, а потым параметрызаваныя. Ключ default прыемлівае випадкі, калі ключ абонента не існуе, ёсць порожнім або не падходзіць, таму задаюць яго толькі тады, калі вы впэўненыя, што всі тры такія випадкі будуць пераказаны на гэты альтернатыўны варіант.

Інодзе проста не включаць контракт усё ж краща, чым дадаць ўсё больш апэвтазійных пераканаў. Якщо партнеры ніколі не должны магчымаць выдалення камер, проста не надавайце гэнераванай операцыі выдалення жадных ключоў партнера.

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

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

Параметрызаваныя клучы вызываючых элементаў даўаюць вам ўсё большую прычыну павергацца на вбудованы механізм разв’язвання проблем у замест на стварэнне сопрацоўніцтва з выборам вызываючага элемента ўнутрь хука. Маршрутазёр зберагае первісную значэння вызываючага элемента окрэмна ад заявленага клуча, з якім ён быў паўнасообразны, і абмовляеся ад неодназначных шаблонаў параметраў. Для таго, каб строгае падобнасць строк, было можна стварыць тыя ж гарантіі, патрэбна была б воспрадаць точную падобнасць, прыорітэты параметраў, правілы адначасовага выкарыстоўвання значэння і поведзенне у разы невялікіх проблем.

Іспользуйце хукі для прыняття рашэнняў па жыцёвым циклу, а не для стварэння схованых запытоў

Сгенераваныя маршруты не пазбавляюць неабходнасці прыняття рашэнняў на рэвэле аплікацыі. Яны проста даюць гэтым рашэнням прыемлівую структуру.

Для запита, які падае на адзінаковы варіянт, выконанне праходзіць через before-hooks на рэвэлюйнам узле, пасля чаго — through before-hooks на рэвэлюйнам варіянта, далей — сам адгэнераваны хендлар, пасля чаго — after-hooks на рэвэлюйнам варіянта, і нарэшце — after-hooks на рэвэлюйнам узле.

Hooks на рэвэлюйнах — гэта правыя месцы для правілаў, якія прыменяюцца да кожнага вызывальніка таго рэвэлюйна, незалежна ад таго, які варіянт ён падпаў. Hooks на варіянтах належаць да логікі, якая є спецыфічная для адзінаго заявленага формата вызывальніка.

const transferRoutes = {
  update: {
    before: [authenticateOperator],
    variants: {
      warehouse: {
        before: [authorizeTransferLocation],
        shape: warehouseTransferShape,
      },
      supervisor: {
        before: [requireSupervisorApproval],
        shape: supervisorTransferShape,
      },
    },
  },
}

Before-hook можа свабодна пераглядаць точны ідэнтыфікатор, які будзе выкарыстоўваць адгэнераваны хендлар, і ён можа абмовіцца прыйняць запит, якщо гэты ідэнтыфікатор не праходзіць пераканальне. Ён ніколі не должен разрэшыць адзін ідэнтыфікатор, тым часам таямна заменяючы яго на іншы ў фактычны запит. Такая таямная замена паспелае разгародзіць мэту наявнасці хендлара, які можна пераглядаць.

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

Існуюць случаі, калі сервер практычна вынужаны ствараць запыт, які няма можлівасці выразіць жадной формай. Самэ тады і стае неабходныя спецыяльна створаны працоўнік — насуперак усему, калі патрэбна справжняя дыс’юнкцыя, якая належыць самаму серверу. Варта памятаць, што прымусовыя умовы, якія знаходзяцца ўнутрь булевых комбінатораў, становяцца обавязковымі абмежэннямі для запыту, а не гнучкім механізамам для выражэння дазволаеўных правіл. Адносаванне іх да універсальнага логічнага двыжка — это распашчасты спосаб прыведзця да правіл, якія на самай працэ не рэалізуюць тое, што вы думаеце, што яны рэалізуюць.

Післяхуки запускаюцца пасля обробніка, але яны не ўтвараюць фазы чысткі, на якую можна безумовна паслацца. Адпаведны адказ, які завершаецца раней, чым планавалося, або кэшчэнняя прыбутка пад час запиту можа не дазволіць пазнейшым фазам — уключаючы післяхуки — выконвацца. Якщо рэсурс прынцыпова павінен быть зваліваўся незалежна ад таго, што выйдзе, яму патрэбна саўсем свая жыцёвая цягласць з явным блокам finally, які знаходзіцца за межами створанага ланца хукав, а не ўсередзіні яго.

Конкрэтныя деталі таксама залежыць ад вашай цэльовай платформы. Express, Fastify і Hono кожны іншача рэалізуюць схемы хукав і механізмы перерыву ў выконанні. Абщая прынцыпа — дзе павінна быць вядомая рашэнне — застаецца адной і той жа ў всіх трох, але сам код прыкладнага програму павінен адпавядаць умовам той платформы, да якой ён ствараецца.

Зберагачыце довяральны контэкст за межамі аргументаў Prisma

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

У замен неабходна прызначыць маркер @scope-root для модэлі арендніка, запрацаваць процес гэнеравання, каб стварыцца адпаведны картэй сферы, і прыўязаць рашатлера контэксту да Prisma Client через механізм расшырэння:

const prisma = new PrismaClient().$extends(
  guard.extension(() => ({
    Nursery: requestStore.getStore()?.nurseryId,
  }))
)

Сама значенне выходзіць з автапраўдзанага, локальнага да запыту стану — а не з чаго-небудзь, што надае кліент. Расшырэнне пасля чаго вводзіць яго ў падтрымваныя операцыі верхньага рэвэля над модэлямі, якія картаваныя як дзеці таго корневага сегмента сферы.

Это рэальная, чыткая функцыя, а не загальныя гарантыі таго, што кожныя зв’язкі будуць аўтаматычна захаваны. Рэалізацыя прынцыпа дыялянкавасці не распрастраняецца на вкладзеныя запыты чытання або запісу. Сама модэль делегата-корана таксама не фільтруецца праз свой сябе-маркер дыялянкавасці. А кожная модэль, якай не мае створанага картоўнага адпаведніка, все рава патрэбуе свайго явнага захавання — контекст дыялянкавасці за замовчанням яго не пакрывае.

Шыранне на высокай скорасці застойна самэўжо таму, што гэтыя межы ёсць відкрытымі, а не схованымі. Вы можете безпасебна пераглядаць карта дыялянкавасці. Вкладзеныя проекцыі могу маты своія незалежныя фільтры і ліміты. А кожная нестандартная правіла прыналежнасці, якая не пасходзіць пад стандартны шаблон, можа быць перанесена ў код прыемліка або обрабатвацца на рэвэрсе базы дадзеных.

Стан спецыязначанага прыемніка — ўсё, што выходзіць за межы дыялогу аб’екта-арендніка — павінен знаходзіцца ў контэксте запиту, а не самаў у параметрах Prisma. Пры включэнні ідэнтыфікатора вызывача чы ўсіх метадаў автарызацыі у тэле запиту Prisma створаны контракт дадзеных стае набліжэнай да розумелага, а таксама гэта можа спрачынаць строгую пераверку, якая чакае чыстага формата параметраў.

Спрыяйце адкрыццю маршрутаў як часткі дизайну продукту

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

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

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

Для чытання дадзеных трэбуецца той жа рыхласць увагі, як і для запісу. Пры захаванам чытанні, select або include, заявленыя на рэвэле формы, выступаюць як список дозволеных элементоў, так і як стандартны варыянт, калі запит кліента не містіць саўства свайго проекцыі. Для мутацый праектавання дзеяўні стандартныя правілы, і якщо не падазроўваць, што пры ўсуненні проекцыі магчыма расшырzyć адпаведны адказ, неабходна явна увімкнуць enforceProjection.

Для масовых запускаў кожны раз трэба прымкнуць адзіннае рашэнне. Метод масовых запускаў следзь імкнуцца вважаць чыстым у створанай суперфісе толькі тады, калі яго форма вказвае наявнасць правильнага словніка фільтраў, а прыходзячый запит у час выканання все ўсё ж праставляе значымую умову. Увімкненне deleteMany проста таму, што відмова ад выдалення адной записі вялікі ўжо дазволена, ігнаруе гэты другі, адзінаковы рызык. Верненне варіянтав масовых операцый прыносіта сваю сабе залежнасць ад прадастальніка і падтрымкі Prisma, таму канфігурацыя вашага маршруту должна адбіваць тое, што рэальна можа выканаць размешчаная база дадзеных, а не тое, што праглаў бы план развіцця продукту.

Выкарыстоўваны OpenAPI-дакументацыйны выхід можа описваць шляхі запуску і структуру запыткаў, якія вылучаюцца на адпаведных етапах. Ён не можа прыглядзецца ў вольныя функціі-хукі, таму не можа описваць правіла, якія знаходзяцца ў іхнім вучыне. Якщо хук блакуе перадачы, якія выходзяць за межы складу, назначанага аператару, гэтае ўмова павінна быть задокументаваная разам з настройкамі шляху і пераканана за дапамою тэста, які адбіваецца на поведзенні прыемліванага застосоўвання — выкарыстоўваная дакументацыйная інфармацыя ніколі не павінна спрыймалася як доказ логіки, яку ён не можа пераглядзець.

Версіі GET і POST чытальнага канцэнтраў павінны выкорыстоўваць аднойчы той самы контракт запыткаў. GET паслугоўваецца кодаванымі параметрамі запытка; POST прыймае натыўны JSON, які є болей практычным для большых структураў аргументаў. Хук, які чыніць вплыв толькі на тэла запытка, стварае такое поведзенне, якое таямна залежыць ад методу перадачы, і самэ гэта є прычыной таго, што стабільныя обмежэння не павінны быць ставленыя там.

Адаптавацыя генеравання без абыяковага здавальніка

Практычны спосаб працэвыкання такога падходу складаецца з кароткага рэжыму:

  1. Створыце рутэр для адного толькі для чытання моделю.
  2. Адкрыйце толькі тыя операцыі, якія на самай працэўыканні неабходны.
  3. Дадзіце адну прымітную форму з явным праекцыяй і межай розмеру сторанкі.
  4. Практыкуйце аргументы Prisma, якія рутэр на самай працэўыканні выдае.
  5. Дадзіце доверлівы контэкст дыяпазону, якщо модель прысвоена для аренды.
  6. Раздзеліце операцыю на аднальныя контракты вызывача толькі тады, калі аудытары справжнья па-разным.
  7. Дадзіце хукі толькі для рашэнняў, якія формы, дыяпазоны і варіанты не можаць прыняць самастоятельна.
  8. Уведзіце можлівасць запісу толькі пасля таго, як будуць створеныя цэлесапраўленасці, масовая фільтрацыя і власнасць адносаў будуць практыкуваныя з явнымі тэстамі.

Храніце тэсты контракта real-guard нават у сетапах, дзе тэсты браузера ад канца да канца выконваліся на конфігурацыі, якая ўсё цэла прахоўвае верыфікацыю захавання. Тэсты браузера добра падходzą для перакрыцья механізмаў направлення трафіку і поведэнкі UI, але яны не можу паказаць, чы рэальная версія продукту адхіляе недазволеныя поля, калі шар захавання на самай працэ не існуе.

Генераторы прадаюць свою цэннасць таму, што памагаюць командзе сфокусавацца на рашэннях, якія дзейсна маюць значэнне. Prisma апісвае даныя; генераторы ведаюцься павтаральной, механічнаяй роботай. Форматы вялікія, якіе вызовы дазволены. Код прыемленаея застаёцца для забезпечэння доверлівасці, правілаў, спецыфічных для продукту, і выключэнняў, якія нельга чыста і адкрыта выказаць іншым спосабам.

Супаўзвязаная літэратура

  • Raw SQL, Prisma, чырад Drizzle: Как насправдзе выбраць шар базы дадзеных — З’ясавайце, якія разніцы ў кантролі, безпецы типаў і досвядзе разработчика між raw SQL, Prisma і Drizzle, а таксама дазнаецеся практычны спосаб выбору наіболей падходягучага рашэння для проекту.
  • MovieVault Walkthrough: API для списку перагляду з Express 5, Prisma 7 і JWT — Указаны спецыфікат завершэння задання на всю структуру праекту з викорыстоўваннем Express, Prisma і JWT, а таксама прымечанні ўжо пасля завершэння ўсьмо галоўнае: перакананне ў правах на доступ, каскадныя дзеянні і обробка памылак.
  • Анаіз прымаса-prisma-guard Shapes: права на власнасць, проекцыя і контракты з запісу — Навучыцеся аналізаваць прымасы prisma-guard Shapes як контракты API, запытваючы, хто володзіць кожным значэнням, якія даныя можа выйсці ў адпаведзе, і якія запісы можа выконваць створаны канцэнтры.
  • З’явленне адхылэнняў у контрактах API ў час складання за дапамогою інкрементальнага розгортання tRPC — Как тRPC ператварае перэназваны поле бэкенду на адпаведную памятку пра адхыленне ў час складання, як адаптаваць яго канцэнтры паштоўку за паштою разам з REST, і калі ён не падходзіць.