@napp/dti-core
v5.1.2
Published
`napp-dti` core action contract болон signing helper.
Downloads
712
Readme
napp-dti
DTI нь Data Transfer Interface гэсэн товчлол юм.DTI stands for Data Transfer Interface.
napp-dti нь server болон client хооронд REST API standard-аар мэдээлэл дамжуулах contract, client хэрэглээ, server хэрэгжилтийг нэг дүрэмтэй болгох library багц юм.napp-dti is a library set for defining one shared REST API contract between server and client, keeping client usage and server implementation under the same rules.
v5 хөгжүүлэлтийн чиглэл / v5 Development Direction
napp-dti library нь v5 хүртэл хөгжих явцдаа бодит хэрэглээн дээр олон удаа туршигдаж, сайжруулагдсан. Томоохон system-үүдийн хэрэглээнд шалгагдсан туршлага дээр тулгуурлан v5 хувилбараас эхлэн core concept, pattern-ийг тогтворжуулахыг үндсэн чиглэл болгоно.
The napp-dti library has evolved through multiple real-world trials before reaching v5. Starting from v5, the main direction is to stabilize the core concept and patterns based on experience validated in large systems.
v5-ийн гол зарчим / Main principles for v5:
- Core contract болон API pattern-ийг аль болох түгжиж, backward compatibility-ийг эрхэмлэх. / Keep the core contract and API patterns stable, and prioritize backward compatibility.
- Туршилтын шинжтэй өөрчлөлтийг core behavior-д шууд оруулахгүй, тогтвортой ажиллагаанд нөлөөлөх эрсдэлийг багасгах. / Avoid adding experimental changes directly into core behavior, and reduce the risk of affecting stable operation.
- Томоохон system дээр батлагдсан хэрэглээний загварыг хадгалж, client/server contract-ийн найдвартай байдлыг чухалчлах. / Preserve usage patterns proven in large systems, and prioritize reliable client/server contracts.
Зорилго / Purpose
- REST API request, response, validation rule-ийг нэг contract дээр тодорхойлох. / Define REST API request, response, and validation rules in one contract.
- Client болон server талд ижил contract ашиглаж type зөрөх эрсдэлийг багасгах. / Use the same contract on both client and server to reduce type mismatch risk.
- Ердийн REST API client-аар хандахад server хэвийн ажилладаг байх. / Keep the server compatible with regular REST API clients.
- JSON, text, file response зэрэг нийтлэг API use case-ийг нэг загвараар шийдэх. / Support common API use cases such as JSON, text, and file responses with one model.
Package бүтэц / Package Structure
| Package | Үүрэг / Role |
| --- | --- |
| @napp/dti-core | Action contract тодорхойлно. query, body, result, signature, validateParam, validateResult зэрэг үндсэн дүрэм энд байна.Defines the action contract. Core rules such as query, body, result, signature, validateParam, and validateResult live here. |
| @napp/dti-client | Тодорхойлсон action contract-ийн дагуу client талаас REST API дуудна.Calls REST APIs from the client side using the defined action contract. |
| @napp/dti-server | Тодорхойлсон action contract-ийн дагуу Express router дээр REST API endpoint үүсгэнэ.Creates REST API endpoints on an Express router using the defined action contract. |
Ерөнхий ажиллагаа / General Flow
@napp/dti-core дээр action contract тодорхойлж, ижил contract-ийг client болон server талд хамт ашиглана.
Define an action contract in @napp/dti-core, then share the same contract across both client and server.
@napp/dti-core
|-> @napp/dti-client -> REST API -> @napp/dti-server
`-> @napp/dti-serverҮндсэн хэрэглээ / Basic Usage
1. Action contract тодорхойлох / Define an Action Contract
import { z } from "zod";
import { createAction } from "@napp/dti-core";
export const userCreate = createAction("userCreate", {
body: z.object({
name: z.string(),
age: z.number(),
}),
result: z.object({
id: z.string(),
}),
}, {
path: "/users",
method: "POST",
contentType: "json",
signature: ({ body }) => `${body.name}:${body.age}`,
});2. Server дээр Express router ашиглах / Use an Express Router on the Server
import express from "express";
import { createDTIExpressRouter } from "@napp/dti-server";
import { userCreate } from "./contract";
const app = express();
const dti = createDTIExpressRouter();
dti.action(userCreate, async ({ body }) => {
return {
id: `user:${body.name}`,
};
});
app.use("/api", dti.router());3. Client дээр API дуудах / Call the API from the Client
import { DTIClient } from "@napp/dti-client";
import { userCreate } from "./contract";
const client = new DTIClient("/api");
const result = await client.call(userCreate, {
body: {
name: "Bat",
age: 25,
},
});
result.id;Response хэлбэр / Response Format
Default response нь JSON envelope байна.
The default response is a JSON envelope.
{
"success": true,
"data": {}
}Error response нь REST status code дагаж JSON envelope буцаана.
Error responses follow REST status codes and return a JSON envelope.
{
"success": false,
"code": "DTI_BODY_VALIDATE_ERROR",
"message": "Invalid action body"
}responseType дараах утгатай / responseType supports the following values:
json: default,dataenvelope ашиглана. / default, uses thedataenvelope.text: raw text response буцаана. / returns a raw text response.file: raw file response буцаана, client талдBlobавна. / returns a raw file response, and the client receives aBlob.
Header, auth, sign / Headers, Auth, and Signing
DTIClient дээр global болон per-call header дамжуулж болно.DTIClient supports both global and per-call headers.
const client = new DTIClient("/api", {
credentials: "include",
headers: {
"x-client": "web",
},
});
await client.call(userCreate, param, {
headers: {
"x-trace-id": "trace-001",
},
});Router түвшинд auth өгвөл бүх action verify хийнэ.
When auth is configured at router level, every action is verified.
auth callback нь тухайн request-д тохирох @napp/dti-core-ийн DTIAction contract-ийг action param-аар авна.
The auth callback receives the matched @napp/dti-core DTIAction contract through the action param.
const dti = createDTIExpressRouter({
auth: async ({ action, req }) => {
if (action.name === "userCreate") {
// action-specific auth logic
}
return {
userId: req.header("x-user-id") || "anonymous",
};
},
});Router түвшинд meta өгвөл handler param дээр typed meta context орж ирнэ. meta нь static object эсвэл request бүр дээр ажиллах async resolver байж болно.
When meta is configured at router level, the action handler receives a typed meta context. meta can be a static object or a per-request async resolver.
const staticDti = createDTIExpressRouter({
meta: {
aa: "bb",
},
});type AuthContext = {
userId: string;
};
type MetaContext = {
requestId: string;
locale: string;
};
const dti = createDTIExpressRouter<AuthContext, MetaContext>({
auth: async ({ req }) => {
return {
userId: req.header("x-user-id") || "anonymous",
};
},
meta: async ({ req, auth }) => {
return {
requestId: req.header("x-request-id") || crypto.randomUUID(),
locale: req.header("x-locale") || "mn-MN",
};
},
});
dti.action(userCreate, async ({ body, auth, meta }) => {
return {
id: auth.userId,
name: body.name,
requestId: meta.requestId,
};
});meta resolver нь auth verify хийгдсэний дараа ажиллана. Тиймээс auth option тохируулсан үед meta resolver дотор verified auth context ашиглаж болно.
The meta resolver runs after auth verification, so it can use the verified auth context when the router has an auth option.
Request signing хэрэгтэй үед client/server хоёр тал shared secret болон nonceStore тохируулна.
When request signing is required, configure a shared secret on both client/server sides and provide a nonceStore.
nonceStore нь INonceStore.consume(nonce, ttl) contract-тай бөгөөд replay request-ийг atomic байдлаар хаана.nonceStore follows the INonceStore.consume(nonce, ttl) contract and must block replay requests atomically.
const client = new DTIClient("/api", {
sign: {
keyId: "client-a",
secret: "secret-a",
},
});
const dti = createDTIExpressRouter({
sign: {
nonceStore,
getSecret: async ({ keyId }) => "secret-a",
},
});Дэлгэрэнгүй docs / More Docs
Development command / Development Commands
npm run typecheck
npm run test
npm run buildOpen Questions
- Release болон versioning policy одоогоор тусдаа тодорхойлоогүй байна. / The release and versioning policy is not separately defined yet.
