node.js - En enda källa till sanning för API-förfrågnings-/svarsstrukturer, inklusive JSON-schema, TypeScript-typer och körningsvaliderare?


Jag skriver en typisk Node.js REST-tjänst med slutpunkter som tar emot JSON-input (som POST-kroppar) och returnerar JSON-svar.

Jag vill ha de här tre sakerna:

  1. JSON-scheman som definierar formen på förfrågningskroppar och responskroppar för mina endpoints. (Eller Swagger-filer, eller något som är lämpligt för att definiera kontraktet för klienter.)
  2. TypeScript-typer/gränssnitt som perfekt speglar JSON-scheman.
  3. Körningstidsvalideringsfunktioner som jag kan anropa från mina hanterare för att säkerställa att ett objekt har rätt form. (Och när ett inmatningsobjekt har validerats bör min TypeScript-kod känna till dess gränssnitt.)

Men jag vill ha en enda källa till sanning för att undvika att dessa tre komponenter hamnar ur synk. Så jag vill bara underhålla en av dem för hand (antingen JSON-scheman eller TypeScript-typningar). De andra två komponenterna bör genereras från den handskrivna, och de bör inte checkas in i versionskontrollen.

Jag känner att detta måste vara en ganska vanlig uppsättning krav nuförtiden, men jag kan inte hitta så mycket om det online. Kanske använder jag fel söktermer för att undersöka det. Finns det någon ramverk eller konventionellt mönster som tar itu med detta mål, det vill säga målet att definiera ett JSON-API med strikt validering av input, standardiserad schemadokumentation och all bekvämlighet med fullt typade objekt i TypeScript?

Svar #1

Jag antar att du använder TypeScript i frontend och backend.

Återanvändning av anpassad typ

Låt oss säga att vi har ett projektarkiv som innehåller tre huvudmappar: client, server och shared.

I shared har du en Types.ts-fil som innehåller alla dina egna typdefinitioner. Till exempel:

export interface Person {
  name: string;
  age: number;
}

Nu kan du importera dessa typer från vilken undermapp som helst som .\client\src\components\SomeComponent.ts eller server .\server\src\api\SomeRoute.ts med:

import { Person } from `..\..\..\..\shared\Types.ts`;

Så på detta sätt har du en plats för typdefinitioner som kan användas projektomfattande på servern och klienten.

JSON-modell och dokumentationsgenerering

Du kan använda ts-doc-kommentarer för att dokumentera ditt API och ts-docs-gen för att generera ett markdown-dokument.

Ett alternativ skulle vara typedoc. TypeDoc omvandlar kommentarer i TypeScript-källkod till renderad HTML-dokumentation eller en JSON-modell. Du kan konfigurera det på ett sätt som gör att endast de relevanta API-filerna tas i beaktande.

Körtidsvalidering

När det gäller körningstidvalidering av indata och utdata kan du antingen skriva din egen valideringsfunktion för varje typ, vilket förklaras av stereobooster i den här artikeln: pragmatic-types-io-validation-or-how-to-handle-json-based-apis-in-statically-typed-language. Eller så använder du ett bibliotek som io-ts.

Det finns förmodligen ingen universell lösning, eftersom det i hög grad beror på de exakta kraven vad du vill uppnå. Så att komponera din verktygssats av olika verktyg bör leda dig i rätt riktning. Känn dig fri att själv posta ett svar, om du har hittat en uppsättning som fungerar bra för dig.

Svar #2

Så jag gillar idén med ett mönster för projekt som har all JSON-data definierad genom JSD-scheman. JSD-schemat ÄR kontraktet. Du kan koppla JSD:s till Kafka-ämnen (istället för deras Avro-format), du kan koppla JSD:s till Mongo, eller så kan du bara använda dem i vilket JSON-valideringsbibliotek som helst som ditt språk använder (som Newtonsoft i .NET-världen). JSD är det heliga gralet av språkneutrala kontrakt. Jag medger här att binär data med gRPC och Protobuf har hastighetsfördelar och bör övervägas. Jag vill också påpeka att jag ser 99 procent av data som JSON-kropps-affärsdata och kan göras med detta mönster. Sammanfattning: Bygg ditt JSD, lägg det på en plats (webbplats) som kan refereras av din kod för validering på vilken nivå som helst i ditt mjukvarusystem.

Relaterade artiklar