Zum Inhalt springen
Dokumentation auf Deutsch

Übersetzung der englischen Originalseite. Bei Abweichungen gilt die englische Fassung.

Typisierte Clients

DaloyJS bietet zwei Wege, Ihre API vollständig typsicher aufzurufen. Nehmen Sie den, der zu Ihrem Consumer passt.

1. Typisierter In-Process-Client (ohne Codegenerierung)

Für TypeScript-Consumer im selben Monorepo (Tests, interne Werkzeuge, Server Actions in Next.js):

ts
import { createInProcessClient } from "@daloyjs/core/client";
import { app } from "./app.js";

const client = createInProcessClient(app);

const r = await client.getBookById({ params: { id: "1" } });
//    ^? { status: 200; body: { id: string; title: string } }
//      | { status: 404; body: ProblemJson }

if (r.status === 200) {
  console.log(r.body.title); // string, vollständig typisiert
}

Der Client ist nach operationId geordnet, gibt eine diskriminierte Union aus {status, body, headers} zurück und leitet alles aus der Routendefinition selbst ab. Kein Build-Schritt.

Pfadparameter

Pfadparameter werden segmentweise ersetzt, daher passt :id nie innerhalb von :idx, und jeder Wert wird prozentkodiert in sein eigenes Segment eingesetzt. Seit 1.3.7 wirft eine generierte Methode einen TypeError, bevor überhaupt ein Request gesendet wird, wenn ein Parameter fehlt, leer ist oder . bzw. .. lautet. Das URL-Parsing würde ein Punktsegment auflösen (auch als %2E%2E) und den Aufruf stillschweigend umlenken, zum Beispiel DELETE /orgs/:org/members/.. in DELETE /orgs/:org verwandeln. Wildcard-Routen (*path oder ein bloßes * mit dem Namen wildcard) akzeptieren einen durch / getrennten Wert; jedes Segment wird auf dieselbe Weise kodiert und geprüft.

ts
// app.get("/files/*path", { operationId: "getFile", ... })
await client.getFile({ params: { path: "docs/guide.md" } }); // GET /files/docs/guide.md

await client.getBookById({ params: { id: ".." } });
// TypeError: Invalid path parameter "id" for /books/:id: empty and "."/".." segments are not allowed

2. Hey-API-SDK (sprachübergreifend, repo-übergreifend, zur Build-Zeit)

Für Consumer außerhalb des Monorepos oder in anderen Sprachen generieren Sie mit @hey-api/openapi-ts ein vollständig typisiertes fetch-SDK.

Pipeline der Codegenerierung
  1. 01Routenapp.route(...)
  2. 02generateOpenAPI@daloyjs/core/openapi
  3. 03openapi.jsonOpenAPI-3.1-Spezifikation auf der Festplatte
  4. 04openapi-tsGenerator von Hey API
  5. 05Typisiertes SDKsdk.gen.ts · types.gen.ts
  6. 06Consumervollständig typisierte fetch-Aufrufe
pnpm gen führt die ganze Kette aus: die Spezifikation aus Ihren Routen schreiben, dann von Hey API in ein typisiertes fetch-SDK verwandeln lassen. Führen Sie es nach jeder Routenänderung erneut aus, und der Client bleibt im Gleichschritt mit dem Vertrag.
bash
pnpm add -D @hey-api/openapi-ts prettier
ts
// openapi-ts.config.ts
import { defineConfig } from "@hey-api/openapi-ts";

export default defineConfig({
  input: "./generated/openapi.json",
  output: { path: "./generated/client", postProcess: ["prettier"] },
  plugins: ["@hey-api/client-fetch", "@hey-api/typescript", "@hey-api/sdk"],
});
json
// package.json
"scripts": {
  "gen:openapi": "node scripts/dump-openapi.ts",
  "gen:client":  "openapi-ts",
  "gen":         "pnpm gen:openapi && pnpm gen:client"
}
bash
pnpm gen
# schreibt:
#   generated/openapi.json
#   generated/client/{client.gen.ts, sdk.gen.ts, types.gen.ts, index.ts}

Das generierte SDK verwenden

ts
import { client } from "./generated/client/client.gen.js";
import { getBookById } from "./generated/client/sdk.gen.js";

client.setConfig({ baseUrl: "https://api.example.com" });

const { data, error } = await getBookById({ path: { id: "1" } });
if (error) console.error(error);
else if (data) console.log(data.title);

Welchen soll ich verwenden?

AnwendungsfallWahl
TypeScript-Aufrufer im selben Repo (Tests, interne Werkzeuge)createInProcessClient
Web-App / React-Native-Bundle in einem separaten RepoHey-API-SDK
Consumer ohne TypeScript (Python, Swift, Kotlin)OpenAPI-Dokument + ihr bevorzugter Generator
Öffentliches SDK für DritteHey-API-SDK, als eigenes Paket veröffentlicht

Sie kommen von ts-rest?

ts-rest ist eine beliebte Contract-first-Bibliothek, die Ihnen durchgängige TypeScript-Typen ohne Codegenerierung liefert, indem ein Vertrag (initContract) zwischen einem adapterbasierten Server (Express, Fastify, NestJS, Next.js) und einem fetch-Client (initClient) geteilt wird. Gefällt Ihnen dieses Modell, wird sich DaloyJS vertraut anfühlen, mit zwei Unterschieden.

Erstens ist in DaloyJS die Routendefinition der Vertrag; es gibt kein separates Vertragsobjekt, das synchron gehalten werden muss. Der oben gezeigte In-Process-createClient bietet TypeScript-Aufrufern im selben Repo dieselbe Erfahrung ohne Codegenerierung und mit geteilten Typen.

Zweitens ist die Typsicherheit von ts-rest auf TypeScript beschränkt und verlangt, dass der Client den Vertrag importiert. DaloyJS erzeugt aus denselben Routen eine vollwertige OpenAPI-3.1-Spezifikation und ein Hey-API-SDK, sodass auch Consumer abgedeckt sind, die Ihre Typen nicht importieren können (andere Repos, andere Sprachen, öffentliche SDKs). In ts-rest ist OpenAPI eine optionale Erweiterung (@ts-rest/open-api). DaloyJS ist außerdem selbst Server und Laufzeitschicht, portabel über Node, Bun, Deno, Cloudflare und Vercel, statt einer Typisierungsschicht auf einem separaten Framework.

ts-restDaloyJS
Quelle des VertragsSeparates initContract-ObjektDie Routendefinition selbst
Typisierter Client ohne CodegenerierungJa (initClient, nur TypeScript)Ja (createClient, nur TypeScript)
Sprach- und repo-übergreifende ClientsOpenAPI-Erweiterung (@ts-rest/open-api)OpenAPI 3.1 + Hey-API-SDK, vollwertig
ServerAdapter auf Express / Fastify / NestJS / Next.jsEingebaut, portabel über Laufzeiten
Validierung zur LaufzeitStandard Schema (Zod / Valibot / ArkType)Standard Schema (Zod / Valibot / ArkType / TypeBox)
SicherheitsstandardsSelbst mitbringenHeader und Body-Limits standardmäßig aktiv; CSRF und Rate Limits eine Zeile entfernt

Sie brauchen einen größeren Vertrag, um die Ausgabe Ihres Generators zu prüfen? Nehmen Sie die große Fake-REST-Demo (auf Englisch) als Belastungsfall statt einer minimalen Tutorial-App.

Zuletzt aktualisiert: