@napp/dti-server
v6.1.2
Published
`napp-dti` Express REST API server adapter.
Readme
napp-dti
DTI нь Data Transfer Interface гэсэн товчлол. napp-dti нь REST API-ийн request, response болон runtime validation contract-ийг client/server хооронд нэг эх сурвалжаас ашиглах TypeScript library багц юм.
DTI stands for Data Transfer Interface. napp-dti is a TypeScript library set for sharing one REST API request, response, and runtime-validation contract between clients and servers.
Одоогийн version: 6.1.1.
Current version: 6.1.1.
6.xнь breaking release.@napp/dti-core,@napp/dti-client,@napp/dti-serverpackage-уудыг ижил version-оор ашиглана.
6.xis a breaking release. Keep@napp/dti-core,@napp/dti-client, and@napp/dti-serveron the same version.
Ямар асуудлыг шийдэх вэ? / What Does It Solve?
- REST route, method, params, query, body болон result schema-г нэг contract дээр тодорхойлно. / Define REST route, method, params, query, body, and result schemas in one contract.
- Client болон server талд type inference болон Zod runtime validation хамт ашиглана. / Share TypeScript inference and Zod runtime validation across client and server.
- DTI client ашиглаагүй
curl, Postman, browserfetchзэрэг энгийн REST client-ийг дэмжинэ. / Support standard REST clients such ascurl, Postman, and browserfetchwithout requiring the DTI client. - JSON, text болон file response-ийг нэг server adapter-аар ажиллуулна. / Serve JSON, text, and file responses through one server adapter.
- Router-level auth, request metadata, error mapping болон optional HMAC signing дэмжинэ. / Support router-level auth, request metadata, error mapping, and optional HMAC signing.
Package-ууд / Packages
| Package | Үүрэг / Role |
| --- | --- |
| @napp/dti-core | Shared action contract, DTIError, signing helper болон нийтлэг type-ууд.Shared action contracts, DTIError, signing helpers, and common types. |
| @napp/dti-client | Contract-aware REST client.Contract-aware REST client. |
| @napp/dti-server | Express router adapter.Express router adapter. |
flowchart LR
Contract["@napp/dti-core<br/>Shared action contract"]
Client["@napp/dti-client"]
Server["@napp/dti-server"]
Raw["curl / fetch / Postman"]
Contract --> Client
Contract --> Server
Client -->|"HTTP REST"| Server
Raw -->|"HTTP REST"| ServerСуулгах / Installation
Client application-д:
For a client application:
npm install @napp/[email protected] @napp/[email protected] zodExpress server-д:
For an Express server:
npm install @napp/[email protected] @napp/[email protected] zod express
npm install -D @types/expressНэг repository дотор client/server хамт байвал гурван package-ийг бүгдийг ижил version-оор install хийнэ.
If the client and server share one repository, install all three packages at the same version.
Түргэн эхлэх / Quick Start
1. Shared contract тодорхойлох / Define a Shared Contract
// contracts/user.ts
import { z } from "zod";
import { createAction } from "@napp/dti-core";
export const userCreate = createAction("userCreate", {
body: z.object({
name: z.string().min(1),
age: z.number().int().nonnegative(),
}),
result: z.object({
id: z.string(),
name: z.string(),
}),
}, {
path: "/users",
method: "POST",
contentType: "json",
});path заавал /-ээр эхэлнэ. action.name нь logging/debug identifier бөгөөд URL fallback биш.
path must start with /. action.name is a logging/debug identifier and is never used as a URL fallback.
2. Express server тохируулах / Configure an Express Server
// server.ts
import express from "express";
import { randomUUID } from "node:crypto";
import { createDTIExpressRouter } from "@napp/dti-server";
import { userCreate } from "./contracts/user";
const app = express();
const dti = createDTIExpressRouter();
dti.action(userCreate, async ({ body }) => {
return {
id: randomUUID(),
name: body.name,
};
});
app.use("/api", dti.router());
app.listen(3000);DTI router нь action-ийн content type-д тохирох body parser-ийг өөрөө холбоно. Ижил route дээр тусдаа express.json() заавал нэмэх шаардлагагүй.
The DTI router attaches the body parser required by the action content type. You do not need to add a separate express.json() parser to the same route.
3. Typed client ашиглах / Use the Typed Client
// client.ts
import { DTIClient } from "@napp/dti-client";
import { userCreate } from "./contracts/user";
const client = new DTIClient("/api");
const user = await client.call(userCreate, {
body: {
name: "Bat",
age: 25,
},
});
console.log(user.id, user.name);Request нь POST /api/users рүү явна. Server JSON success envelope буцаана.
The request is sent to POST /api/users. The server returns a JSON success envelope.
{
"success": true,
"data": {
"id": "user-001",
"name": "Bat"
}
}Action contract-ийн дүрэм / Action Contract Rules
Path болон method / Path and Method
const tenantList = createAction("tenantList", {
result: z.array(z.object({ id: z.string() })),
}, {
path: "/tenants",
});method omitted үед GET болно. Client, server, route uniqueness болон signing бүгд ижил resolved method ашиглана.
When method is omitted, it resolves to GET. The client, server, route uniqueness checks, and signing all use the same resolved method.
| Contract | Үр дүн / Result |
| --- | --- |
| path: "/tenants" | GET /tenants |
| method: "GET" + body schema | Contract construction error |
| method: "POST", "PUT", "PATCH" + body | Дэмжинэ. / Supported. |
| method: "DELETE" + body | Дэмжинэ; infrastructure compatibility-г application шалгана.Supported; the application must verify infrastructure compatibility. |
| path: "tenants" эсвэл empty path | DTI_ACTION_PATH_ERROR |
GET input-д params эсвэл query ашиглана. Body шаардлагатай operation дээр method-ийг explicit тодорхойлно.
Use params or query for GET input. Explicitly declare a body-capable method for operations that require a request body.
Query parameter ашиглах / Query Parameters
Query нь нэг түвшний flat object байна. Field value нь string, finite number, boolean, bigint, эсвэл optional undefined байж болно.
A query is a one-level flat object. Each field may contain a string, finite number, boolean, bigint, or optional undefined value.
export const userList = createAction("userList", {
query: z.object({
q: z.string().optional(),
page: z.coerce.number().int().positive().optional(),
}),
result: z.object({
items: z.array(z.object({ id: z.string() })),
total: z.number(),
}),
}, {
path: "/users",
});
const result = await client.call(userList, {
query: {
q: "bat",
page: 2,
},
});Wire request:
GET /api/users?q=bat&page=2HTTP query value server дээр string байдлаар ирдэг. Number, boolean эсвэл bigint output хэрэгтэй бол z.coerce/z.preprocess ашиглана.
HTTP query values arrive at the server as strings. Use z.coerce or z.preprocess when the parsed output must be a number, boolean, or bigint.
Дэмжихгүй shape / Unsupported shapes:
{ tags: ["a", "b"] } // array / repeated-key convention
{ filter: { active: true } } // nested object
{ value: null } // null
{ page: Number.POSITIVE_INFINITY }DTI array болон nested query-д bracket notation, repeated key, comma-separated эсвэл JSON convention таамаглахгүй. Complex filter шаардлагатай бол body-тэй POST action эсвэл application-specific route ашиглана.
DTI does not assume bracket notation, repeated keys, comma-separated values, or JSON conventions for array and nested queries. Use a body-based POST action or an application-specific route for complex filters.
Typed path params ашиглах / Typed Path Parameters
export const tenantUserRead = createAction("tenantUserRead", {
params: z.object({
tenantId: z.string(),
userId: z.coerce.number().int().positive(),
}),
result: z.object({
id: z.number(),
tenantId: z.string(),
}),
}, {
path: "/tenants/:tenantId/users/:userId",
});
const user = await client.call(tenantUserRead, {
params: {
tenantId: "acme corp",
userId: 42,
},
});Client placeholder value бүрийг URL encode хийнэ. Дээрх path /tenants/acme%20corp/users/42 болно.
The client URL-encodes every placeholder value. The path above becomes /tenants/acme%20corp/users/42.
Route placeholder болон params schema field-үүд яг таарна. Optional, duplicate, wildcard болон custom regex placeholder дэмжихгүй.
Route placeholders and params schema fields must match exactly. Optional, duplicate, wildcard, and custom-regex placeholders are not supported.
Body болон content type / Body and Content Type
Дэмжих request content type / Supported request content types:
| contentType | Client serialization | Server parser |
| --- | --- | --- |
| json | JSON.stringify | express.json() |
| form | Object/URLSearchParams → URL-encoded body | express.urlencoded() |
| text | Raw string | express.text() |
contentType omitted үед json ашиглана.
When contentType is omitted, it defaults to json.
const login = createAction("login", {
body: z.object({
username: z.string(),
password: z.string(),
}),
result: z.object({ token: z.string() }),
}, {
path: "/login",
method: "POST",
contentType: "form",
});JSON response envelope / JSON Response Envelope
Body-тэй success response / Success response with a body:
{
"success": true,
"data": {}
}Error response:
{
"success": false,
"code": "DTI_BODY_VALIDATE_ERROR",
"message": "Invalid action body",
"details": {}
}details optional. Request contract/format validation ихэвчлэн 400, auth 401, permission 403, not found 404, conflict 409, domain validation 422, result/internal error 500 status ашиглана.
details is optional. Request contract/format validation generally uses 400, auth uses 401, permission uses 403, not found uses 404, conflict uses 409, domain validation uses 422, and result/internal errors use 500.
Application-specific error status-ийг error parser-аар тодорхойлно.
Use the error parser to define application-specific error statuses.
Client хэрэглээ / Client Usage
call болон callDetailed
call() parsed result буцаана. Response status/header хэрэгтэй бол callDetailed() ашиглана.
call() returns the parsed result. Use callDetailed() when response status or headers are required.
const { result, response } = await client.callDetailed(userCreate, {
body: {
name: "Bat",
age: 25,
},
});
console.log(response.status);
console.log(response.headers.get("x-trace-id"));
console.log(result.id);Fetch options, headers болон auth / Fetch Options, Headers, and Auth
DTIClientOptions болон per-call options нь RequestInit option-уудыг дэмжинэ.
DTIClientOptions and per-call options support standard RequestInit options.
const client = new DTIClient("/api", {
credentials: "include",
headers: {
"x-client": "web",
},
auth: async () => ({
authorization: `Bearer ${accessToken}`,
}),
});
await client.call(userCreate, param, {
signal: abortController.signal,
headers: {
"x-trace-id": "trace-001",
},
});Header merge дараалал / Header merge order:
- global
headers - global
auth - per-call
headers - per-call
auth - library-owned signing headers
- байхгүй үед
Content-Typefallback /Content-Typefallback when missing
Сүүлд орсон ижил нэртэй header өмнөх утгыг override хийнэ. Header name comparison case-insensitive.
The last source wins when header names collide. Header name comparison is case-insensitive.
Алдаа боловсруулах / Error Handling
Client network, HTTP envelope болон validation алдааг DTIError хэлбэрээр шиднэ.
The client throws network, HTTP-envelope, and validation failures as DTIError instances.
import { DTIError } from "@napp/dti-core";
try {
await client.call(userCreate, param);
} catch (error) {
if (error instanceof DTIError) {
console.error(error.code, error.status, error.details);
}
throw error;
}Нийтлэг code / Common codes:
| Code | Тайлбар / Description |
| --- | --- |
| DTI_ACTION_PATH_ERROR | Invalid action path config |
| DTI_PATH_PARAMS_VALIDATE_ERROR | Path parameter schema validation failed |
| DTI_QUERY_VALIDATE_ERROR | Query schema or flat-scalar rule failed |
| DTI_BODY_VALIDATE_ERROR | Body schema validation failed |
| DTI_RESULT_PARSE_ERROR | Server result violated the result contract |
| DTI_SIGNATURE_REQUIRED | Signing callback or required header is missing |
| DTI_SIGNATURE_INVALID | HMAC signature mismatch |
| DTI_REPLAY_DETECTED | Nonce was reused |
Server хэрэглээ / Server Usage
Auth болон meta context / Auth and Meta Context
Router дээр auth тохируулбал тухайн router-ийн бүх action protected болно. Public endpoint-д auth-гүй тусдаа router үүсгэнэ.
When auth is configured, every action on that router is protected. Create a separate router without auth for public endpoints.
import { randomUUID } from "node:crypto";
import { DTIError } from "@napp/dti-core";
import { createDTIExpressRouter } from "@napp/dti-server";
type AuthContext = {
userId: string;
roles: string[];
};
type MetaContext = {
requestId: string;
locale: string;
};
const protectedDti = createDTIExpressRouter<AuthContext, MetaContext>({
auth: async ({ action, req }) => {
const authorization = req.header("authorization");
if (!authorization) {
throw new DTIError("Authentication required", {
code: "AUTH_REQUIRED",
status: 401,
});
}
console.info("auth-attempt", action.name);
return {
userId: "user-001",
roles: ["admin"],
};
},
meta: async ({ req, auth }) => ({
requestId: req.header("x-request-id") || randomUUID(),
locale: req.header("x-locale") || "mn-MN",
}),
});
protectedDti.action(userCreate, async ({ body, auth, meta }) => ({
id: auth.userId,
name: `${body.name}:${meta.locale}`,
}));auth callback registered action contract-ийг action param-аар авна. Contract object-ийг request lifecycle дотор mutate хийхгүй.
The auth callback receives the registered action contract through action. Do not mutate the contract object during the request lifecycle.
Domain алдаа map хийх / Domain Error Mapping
Application error-ийг public REST error болгон map хийхдээ router-level parser ашиглана.
Use the router-level parser to map application errors into public REST errors.
const dti = createDTIExpressRouter({
error: {
parse: async ({ error }) => {
if (error instanceof TenantNotFoundError) {
return new DTIError("Tenant not found", {
code: "TENANT_NOT_FOUND",
status: 404,
details: {
tenantId: error.tenantId,
},
});
}
return undefined;
},
},
});Parser undefined буцаавал client-д 500 / DTI_INTERNAL_ERROR очно. Error details дотор secret, token, stack trace зэрэг sensitive мэдээлэл оруулахгүй.
If the parser returns undefined, the client receives 500 / DTI_INTERNAL_ERROR. Never expose secrets, tokens, stack traces, or other sensitive data in error details.
Custom success status болон headers / Custom Success Status and Headers
Library method-оос 201, 204, Location зэрэг утга автоматаар infer хийхгүй.
The library does not infer values such as 201, 204, or Location from the HTTP method.
dti.action(userCreate, async ({ body, res }) => {
const user = await createUser(body);
res.status(201);
res.setHeader("Location", `/users/${user.id}`);
return user;
});204 эсвэл 205 status дээр response body буцаахгүй. Action result contract bodyless semantics-тэй нийцсэн байх үүргийг application хариуцна.
Responses with status 204 or 205 have no body. The application is responsible for keeping the action result contract consistent with bodyless semantics.
Text response / Text Response
import { dtiText } from "@napp/dti-server";
const exportCsv = createAction("exportCsv", {}, {
path: "/exports/users.csv",
responseType: "text",
});
dti.action(exportCsv, async () => {
return dtiText("id,name\n1,Bat", {
contentType: "text/csv; charset=utf-8",
headers: {
"x-export-version": "1",
},
});
});Default content type нь text/plain; charset=utf-8. Custom contentType өгвөл library charset нэмэх эсвэл солихгүй.
The default content type is text/plain; charset=utf-8. When a custom contentType is supplied, the library does not add or replace its charset.
File response / File Response
import { dtiFile } from "@napp/dti-server";
const reportDownload = createAction("reportDownload", {
params: z.object({ reportId: z.string() }),
}, {
path: "/reports/:reportId/download",
responseType: "file",
});
dti.action(reportDownload, async ({ params }) => {
const report = await loadReport(params.reportId);
return dtiFile({
body: report.bytes,
filename: report.filename,
contentType: report.contentType,
});
});
const { result: blob, response } = await client.callDetailed(reportDownload, {
params: { reportId: "report-001" },
});
console.log(blob.type);
console.log(response.headers.get("content-disposition"));File response-ийн дүрэм / File response rules:
contentTypeomitted болapplication/octet-stream. / OmittedcontentTypedefaults toapplication/octet-stream.- Blob type эсвэл filename extension-оос media type таахгүй. / Media type is not inferred from Blob metadata or filename extensions.
filenameөгвөл Unicodefilename*болон safe ASCIIfilenamefallback үүсгэнэ. / A suppliedfilenameproduces a Unicodefilename*and a safe ASCIIfilenamefallback./,\\, control character,.,..filename reject хийнэ. / Filenames containing/,\\, control characters,., or..are rejected.- Raw
Content-Typeболон filename-тэй үед rawContent-Disposition-ийг resolved metadata override хийнэ. / Resolved metadata overrides rawContent-Typeand, when a filename exists, rawContent-Disposition. filenameөгөөгүй бол application rawContent-Dispositionөөрөө тохируулж болно. / Without a filename, the application may set rawContent-Dispositionitself.
Content-Disposition header-ийг application гараар string concatenate хийх шаардлагагүй.
The application does not need to build Content-Disposition headers through manual string concatenation.
Request signing / Request Signing
Signing нь optional router-level policy. Signed болон public endpoint-ийг тусдаа router-аар салгах нь зөв.
Signing is an optional router-level policy. Keep signed and public endpoints on separate routers.
Signing contract / Signing Contract
export const paymentCreate = createAction("paymentCreate", {
body: z.object({
invoiceId: z.string(),
amount: z.number(),
}),
result: z.object({ id: z.string() }),
}, {
path: "/payments",
method: "POST",
signature: ({ body }) => `${body.invoiceId}:${body.amount}`,
});signature(param) нь application-ийн sign хийх field-үүдээс deterministic string үүсгэнэ. Library raw request body-г бүхэлд нь sign хийхгүй.
signature(param) builds a deterministic string from application-selected fields. The library does not sign the entire raw request body.
Client болон server config / Client and Server Configuration
const client = new DTIClient("/api", {
sign: {
keyId: "client-a",
secret: "secret-a",
},
});
const signedDti = createDTIExpressRouter({
sign: {
nonceStore,
toleranceMs: 5 * 60 * 1000,
getSecret: async ({ keyId }) => {
return await secretStore.get(keyId);
},
},
});nonceStore.consume(key, ttl) нь atomic check-and-store operation байна. Production distributed deployment дээр process-local Map ашиглахгүй; Redis SET key value NX PX ttl зэрэг shared atomic storage ашиглана.
nonceStore.consume(key, ttl) must be an atomic check-and-store operation. In distributed production deployments, use shared atomic storage such as Redis SET key value NX PX ttl, not a process-local Map.
Client global signing-г зөвхөн public router руу хийх call дээр disable хийж болно.
Client global signing can be disabled for an individual call intended for a public router.
await client.call(publicAction, param, {
sign: false,
});sign: false нь server policy-г өөрчлөхгүй. Signed router ийм request-ийг 401 / DTI_SIGNATURE_REQUIRED гэж reject хийнэ.
sign: false does not change server policy. A signed router rejects such a request with 401 / DTI_SIGNATURE_REQUIRED.
Custom signing header names / Custom Signing Header Names
import type { DTISignHeaderNames } from "@napp/dti-core";
const headerNames = {
keyId: "x-app-key-id",
timestamp: "x-app-timestamp",
nonce: "x-app-nonce",
signature: "x-app-signature",
} satisfies DTISignHeaderNames;
const client = new DTIClient("/api", {
sign: {
keyId: "client-a",
secret: "secret-a",
headerNames,
},
});
const dti = createDTIExpressRouter({
sign: {
nonceStore,
getSecret,
headerNames,
},
});headerNames partial байж болно. Client/server resolved mapping яг ижил байх ёстой. Mapping зөрвөл DTI_SIGNATURE_REQUIRED гарна.
headerNames may be partial. The resolved client and server mappings must match exactly. A mismatch produces DTI_SIGNATURE_REQUIRED.
Per-call sign: { ... } нь global sign config-ийг бүхэлд нь override хийнэ. Global custom headerNames автоматаар inherit хийхгүй.
A per-call sign: { ... } object replaces the complete global signing configuration. It does not automatically inherit global custom headerNames.
Signing compatibility / Signing Compatibility
- Timestamp exact
YYYY-MM-DDTHH:mm:ss.sssZformat-тай байна. / Timestamps must use the exactYYYY-MM-DDTHH:mm:ss.sssZformat. - Canonical payload нь resolved uppercase method, exact encoded path/query, timestamp, nonce болон action signature-аас бүрдэнэ. / The canonical payload contains the resolved uppercase method, exact encoded path/query, timestamp, nonce, and action signature.
- Query order/encoding өөрчлөгдвөл signature таарахгүй. / Changing query order or encoding changes the signature.
6.xsigning protocol хуучин5.xcanonical payload-тай нийцэхгүй. / The6.xsigning protocol is not compatible with the old5.xcanonical payload.- Client/server-ийг coordinated байдлаар ижил version руу deploy хийнэ. / Deploy client and server changes together on the same version.
Raw REST client ашиглах / Using a Raw REST Client
Unsigned endpoint нь DTI-specific header шаардахгүй.
Unsigned endpoints do not require DTI-specific headers.
curl "http://localhost:3000/api/users?q=bat&page=2"curl -X POST "http://localhost:3000/api/users" \
-H "Content-Type: application/json" \
-d '{"name":"Bat","age":25}'Router дээр auth/sign enabled бол raw client тухайн security protocol-ийг өөрөө хэрэгжүүлнэ. Signed request дээр exact method болон encoded path/query-г sign хийх шаардлагатай.
When auth or signing is enabled on a router, a raw client must implement that security protocol. Signed requests must sign the exact method and encoded path/query sent on the wire.
Production шалгах жагсаалт / Production Checklist
- Core, client, server package version яг ижил эсэхийг шалгана. / Verify that core, client, and server package versions match exactly.
- Write action бүр method-ээ explicit тодорхойлсон байна. / Explicitly declare the method for every write action.
- Public, authenticated болон signed endpoint-үүдийг policy бүрээр тусдаа router-д mount хийнэ. / Mount public, authenticated, and signed endpoints on separate routers by policy.
- Distributed deployment дээр atomic shared
nonceStoreашиглана. / Use an atomic sharednonceStorein distributed deployments. - Signing header mapping болон secret rotation config client/server дээр coordinated байна. / Coordinate signing-header mappings and secret rotation across client and server.
- Error
details, log болон response header-д secret/token оруулахгүй. / Never expose secrets or tokens in errordetails, logs, or response headers. - File metadata-г
dtiFile()-аар өгч,Content-Dispositionstring гараар үүсгэхгүй. / Provide file metadata throughdtiFile()instead of manually buildingContent-Dispositionstrings. - Status/header хэрэгтэй client flow дээр
callDetailed()ашиглана. / UsecallDetailed()when client logic needs status or headers. - Deploy хийхээс өмнө raw REST client болон DTI client хоёулангаар integration test хийнэ. / Run integration tests with both a raw REST client and the DTI client before deployment.
v5-аас v6.1.1 рүү шилжих / Migrating from v5 to v6.1.1
- Бүх action-д explicit
pathнэмнэ. / Add an explicitpathto every action. - Method omitted action бүрийг шалгана. Write operation бол
POST,PUT,PATCHэсвэлDELETE-ийг explicit тодорхойлно. / Review every action with an omitted method. Explicitly setPOST,PUT,PATCH, orDELETEfor write operations. GETbody schema-гparams/queryруу шилжүүлэх эсвэл operation-ийг body зөвшөөрдөг method болгоно. / MoveGETbody schemas toparams/query, or change the operation to a body-capable method.- Query schema дотор array, nested object,
nullболон non-finite number output байхгүйг шалгана. / Ensure query schemas do not produce arrays, nested objects,null, or non-finite numbers. - Client/server package-уудыг хамтад нь
6.1.1болгоно. / Upgrade client and server packages to6.1.1together. - Signing ашигладаг бол client/server-ийг coordinated deployment хийнэ; canonical payload өөрчлөгдсөн. / Coordinate client/server deployment when signing is enabled because the canonical payload changed.
- Custom timestamp callback strict UTC ISO format буцааж байгааг шалгана. / Verify that custom timestamp callbacks return strict UTC ISO timestamps.
- File download дээр filename/content type-ийг
dtiFile()metadata-р дамжуулна. / Pass file download filename and content type throughdtiFile()metadata. npm run typecheckболон integration test-ээ ажиллуулна. / Runnpm run typecheckand application integration tests.
Library хөгжүүлэлт / Library Development
npm run typecheck
npm test
npm run buildНэг дор шалгах / Run all verification:
npm run verifyTest нь TypeScript дээр node:test, node:assert/strict болон tsx ашиглана.
Tests are written in TypeScript and use node:test, node:assert/strict, and tsx.
Дэлгэрэнгүй documentation / More Documentation
Гол ADR / Main ADRs:
- ADR-0001: Standalone REST API
- ADR-0002: Response envelope болон status
- ADR-0003: Response types
- ADR-0006: Request signing болон nonce store
- ADR-0007: Typed path params
- ADR-0008: Configurable signing header names
- ADR-0009: Explicit action path
- ADR-0010: Default GET болон GET body rule
- ADR-0011: Flat scalar query parameters
