From a368a925457470d3e34751846298202f6750f1c5 Mon Sep 17 00:00:00 2001 From: Jaehyun Yoon Date: Thu, 25 Jun 2026 23:07:19 +0900 Subject: [PATCH 1/3] docs: fill empty page and unify missing-value decorators --- apps/docs/docs/advanced/default.md | 19 ----- apps/docs/docs/advanced/inherited-binding.md | 24 +++++- apps/docs/docs/decorators/missing-fields.md | 80 +++++++++++++++++ apps/docs/docs/decorators/overview.md | 37 +++++++- apps/docs/docs/decorators/validators.md | 4 - apps/docs/docs/examples/basic-usage.md | 76 ++++++++++++++++- apps/docs/docs/faq.md | 2 +- .../current/advanced/default.md | 19 ----- .../current/advanced/inherited-binding.md | 25 ++++-- .../current/decorators/missing-fields.md | 80 +++++++++++++++++ .../current/decorators/overview.md | 37 +++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 76 ++++++++++++++++- .../current/faq.md | 2 +- .../current/advanced/default.md | 19 ----- .../current/advanced/inherited-binding.md | 24 +++++- .../current/decorators/missing-fields.md | 80 +++++++++++++++++ .../current/decorators/overview.md | 37 +++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 76 ++++++++++++++++- .../current/faq.md | 2 +- .../current/advanced/default.md | 19 ----- .../current/advanced/inherited-binding.md | 26 ++++-- .../current/decorators/missing-fields.md | 80 +++++++++++++++++ .../current/decorators/overview.md | 37 +++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 76 ++++++++++++++++- .../current/faq.md | 2 +- .../current/advanced/default.md | 19 ----- .../current/advanced/inherited-binding.md | 28 ++++-- .../current/decorators/missing-fields.md | 80 +++++++++++++++++ .../current/decorators/overview.md | 37 +++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 76 ++++++++++++++++- .../current/faq.md | 2 +- .../current/advanced/default.md | 24 ------ .../current/advanced/inherited-binding.md | 29 +++++-- .../current/decorators/missing-fields.md | 85 +++++++++++++++++++ .../current/decorators/overview.md | 38 ++++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 78 ++++++++++++++++- .../current/faq.md | 2 +- .../current/advanced/default.md | 19 ----- .../current/advanced/inherited-binding.md | 26 ++++-- .../current/decorators/missing-fields.md | 80 +++++++++++++++++ .../current/decorators/overview.md | 37 +++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 76 ++++++++++++++++- .../current/faq.md | 2 +- apps/docs/sidebars.ts | 2 +- 50 files changed, 1499 insertions(+), 224 deletions(-) delete mode 100644 apps/docs/docs/advanced/default.md create mode 100644 apps/docs/docs/decorators/missing-fields.md delete mode 100644 apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/missing-fields.md delete mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/missing-fields.md delete mode 100644 apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/missing-fields.md delete mode 100644 apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/missing-fields.md delete mode 100644 apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md delete mode 100644 apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/missing-fields.md diff --git a/apps/docs/docs/advanced/default.md b/apps/docs/docs/advanced/default.md deleted file mode 100644 index e330ca16..00000000 --- a/apps/docs/docs/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# Default Field Decorator - -Express-Cargo provides decorators to define default values for request fields. The decorator automatically assigns a value when the incoming request does not provide one (undefined or null). - -## Built-in Default Decorator - -### `@Default(value: T)` - -The @Default decorator assigns a default value to a class property when the request does not provide it. - -- **`value`**: The default value to assign if the field is not present in the request. - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` \ No newline at end of file diff --git a/apps/docs/docs/advanced/inherited-binding.md b/apps/docs/docs/advanced/inherited-binding.md index 8f2003cd..4e3e4a4d 100644 --- a/apps/docs/docs/advanced/inherited-binding.md +++ b/apps/docs/docs/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## inherited-binding +# Inherited Binding Field decorators are also applied to fields declared in parent classes. -This allows you to define common fields in a **base class** and then extend or override them in **child classes**. +This lets you define common fields once in a **base class** and reuse them across **child classes**. -### Example +## Example ```typescript class BaseRequest { @Body() @@ -18,9 +18,25 @@ class CreateUserRequest extends BaseRequest { } ``` -### Result +## Result `CreateUserRequest` will have the following fields: - id : inherited from `BaseRequest` - role : defined in `CreateUserRequest` + +When you pass `CreateUserRequest` to `bindingCargo`, both the inherited `id` and the locally declared `role` are bound and validated together. + +## Redeclaring an Inherited Field + +Re-declaring an inherited field in a child class does **not** replace the parent's definition — the decorators from both classes are merged onto the same field. Re-applying a source decorator this way (for example `@Body()` on a field the parent already sources with `@Body()`) produces a schema error: + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +So each field should be declared in a single class. To change how a field is bound or validated, edit it where it is originally declared rather than redeclaring it in a subclass. + +## Notes + +- Fields are collected across the entire prototype chain, so multiple levels of inheritance are supported. diff --git a/apps/docs/docs/decorators/missing-fields.md b/apps/docs/docs/decorators/missing-fields.md new file mode 100644 index 00000000..3b96b70e --- /dev/null +++ b/apps/docs/docs/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# Handling Missing Fields + +When a field is **missing from the request** (its value is `undefined` or `null`), Express-Cargo needs to know what to do. Two decorators control this behavior: + +- **`@Default(value)`** — substitute a fallback value. +- **`@Optional()`** — allow the field to stay empty without raising a "required" error. + +Because both decide the same thing — what happens when a field is missing — they are **mutually exclusive**. Applying both to one field throws a schema error. + +If a field has **neither** decorator and the value is missing, binding fails with a ` is required` validation error. + +## `@Default(value: T)` + +The `@Default` decorator assigns a default value to a class property when the request does not provide it. + +- **`value`**: The default value to assign if the field is not present in the request. + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### When the Default Is Applied + +The default value is applied **only when the incoming value is `undefined` or `null`** (i.e. the field is missing from the request). Any other value coming from the request is kept as-is. + +This means falsy values such as `0`, `''`, and `false` are treated as real input and do **not** trigger the default: + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| Incoming `quantity` | Bound value | +|-----------------------|------------------------| +| missing / `undefined` | `10` (default applied) | +| `null` | `10` (default applied) | +| `0` | `0` (kept) | +| `5` | `5` (kept) | + +## `@Optional()` + +The `@Optional` decorator marks a field as optional, allowing it to be omitted or set to `undefined`/`null` without triggering a "required" error. When the field is missing, it is left as `null` and validation rules on the field are skipped. + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +Here a missing `discount` binds to `null` and the `@Min(0)` rule is skipped. If `discount` **is** provided, it is validated normally. + +## Choosing Between `@Default` and `@Optional` + +| | Field missing | Field provided | +|---|---|---| +| `@Default(value)` | bound to `value` | value kept and validated | +| `@Optional()` | bound to `null` | value kept and validated | +| neither | ` is required` error | value kept and validated | + +Pick **one** strategy per field: + +```typescript +class Request { + // ❌ Invalid: a field can use only one missing-value strategy + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/docs/decorators/overview.md b/apps/docs/docs/decorators/overview.md index bbe4bbcc..efcf59bc 100644 --- a/apps/docs/docs/decorators/overview.md +++ b/apps/docs/docs/decorators/overview.md @@ -1,6 +1,41 @@ -### What are decorators? +# Decorators Overview + +## What are decorators? Decorators in express-cargo annotate class fields to tell the middleware: - Where to extract data from (e.g., body, query) - How to validate it - How to transform it + +When you pass a class to `bindingCargo`, the middleware reads these decorators to build, validate, and transform a type-safe object that you retrieve with `getCargo`. + +## Decorator Categories + +Decorators are grouped by the role they play in the binding pipeline. + +| Category | Purpose | Examples | Reference | +|-------------------|-------------------------------------------------------------|--------------------------------------------------------------|---------------------------------------------------| +| **Source** | Choose where a field's value comes from | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [Source Decorators](./source-decorators.md) | +| **Virtual** | Compute a field from other fields or from the raw `Request` | `@Virtual`, `@Request` | [Virtual Field Decorators](./virtual.md) | +| **Transform** | Modify a single field's value before binding | `@Transform` | [Transformation Decorator](./transforms.md) | +| **Validation** | Enforce rules on a field's value | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [Validation Decorators](./validators.md) | +| **Missing-value** | Decide what happens when a field is absent | `@Default`, `@Optional` | [Handling Missing Fields](./missing-fields.md) | + +Additional helpers are covered under **Advanced Usage**, such as [`@List`](../advanced/list-decorator.md) for typed arrays, and [`@Type`](../advanced/type-and-polymorphism.md) for nested and polymorphic types. + +## Stacking Decorators + +A single field can carry decorators from multiple categories. They are read together to bind, transform, and validate that field: + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source: read from req.body.email + @Transform((value: string) => value.trim()) // Transform: normalize the value + @MinLength(5) // Validation: enforce a rule + email!: string +} +``` + +Each category is documented in its own page linked above. diff --git a/apps/docs/docs/decorators/validators.md b/apps/docs/docs/decorators/validators.md index a0d5e9a1..ccc3a9d9 100644 --- a/apps/docs/docs/decorators/validators.md +++ b/apps/docs/docs/decorators/validators.md @@ -6,10 +6,6 @@ Validation is not performed by a standalone `validate` function. Instead, it is ## Built-in Validators -### `@Optional()` - -Marks a field as optional, allowing it to be omitted or set to `undefined` without triggering validation errors. - ### `@Min(value: number)` Validates that a number is greater than or equal to the specified minimum value. diff --git a/apps/docs/docs/examples/basic-usage.md b/apps/docs/docs/examples/basic-usage.md index f2ef25ef..d6c7f4a0 100644 --- a/apps/docs/docs/examples/basic-usage.md +++ b/apps/docs/docs/examples/basic-usage.md @@ -1 +1,75 @@ -> Basic POST handler using bindingCargo +# Basic Usage + +This example shows the simplest end-to-end flow of **express-cargo**: define a request class, bind it with the `bindingCargo` middleware, and read the validated result with `getCargo`. + +> This page focuses on the binding flow only. For project setup (TypeScript, `tsconfig`, installing dependencies), see [Getting Started](../getting-started.md). + +## 1. Define a Request Class + +Declare a class and use a source decorator (here `@Body()`) to map each field to a part of the incoming request. + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. Apply the Middleware + +Pass the request class to `bindingCargo` and register it as middleware on the route. The middleware binds and validates the request before your handler runs. + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. Read the bound, type-safe object + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. Example Request + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. Example Result + +`getCargo(req)` returns a fully populated instance of `CreateUserRequest`: + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## Next Steps + +- Pull data from other sources (`@Query`, `@Header`, `@Params`) — see [Source Decorators](../decorators/source-decorators.md) +- Add validation rules such as `@Min`, `@Email` — see [Validation Decorators](../decorators/validators.md) +- Bind nested objects — see [Handling Nested Requests](./nested-request.md) +- Handle validation failures — see [Error Handling](./validation-errors.md) diff --git a/apps/docs/docs/faq.md b/apps/docs/docs/faq.md index a63b09d3..0e5a439b 100644 --- a/apps/docs/docs/faq.md +++ b/apps/docs/docs/faq.md @@ -69,7 +69,7 @@ Additionally, the `reflect-metadata` package must be installed to read type info
Q: How do I avoid errors if a specific field is missing? -**A:** Use the **`@Optional()`** decorator. The field will be bound successfully even if the value is `null` or `undefined`, skipping validation for that field. +**A:** Use the **`@Optional()`** decorator. The field will be bound successfully even if the value is `null` or `undefined`, skipping validation for that field. To substitute a fallback value instead, use `@Default()`. See [Handling Missing Fields](./decorators/missing-fields.md).
### 4. Framework Compatibility diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 3d79b509..00000000 --- a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# Standardwert-Feld-Decorator - -Express-Cargo bietet Decorators zur Definition von Standardwerten für Anfragefelder. Der Decorator weist automatisch einen Wert zu, wenn die eingehende Anfrage keinen bereitstellt (undefined oder null). - -## Integrierter Standardwert-Decorator - -### `@Default(value: T)` - -Der @Default-Decorator weist einer Klasseneigenschaft einen Standardwert zu, wenn die Anfrage diesen nicht bereitstellt. - -- **`value`**: Der Standardwert, der zugewiesen wird, wenn das Feld in der Anfrage nicht vorhanden ist. - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index eeb8cffd..00d3e3d0 100644 --- a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## Vererbte Bindung +# Vererbte Bindung -Feld-Decorators werden auch auf Felder angewendet, die in übergeordneten Klassen deklariert sind. -Dies ermöglicht es Ihnen, gemeinsame Felder in einer **Basisklasse** zu definieren und diese dann in **Unterklassen** zu erweitern oder zu überschreiben. +Feld-Decorators werden auch auf Felder angewendet, die in übergeordneten Klassen deklariert sind. +So können Sie gemeinsame Felder einmal in einer **Basisklasse** definieren und sie über **Unterklassen** hinweg wiederverwenden. -### Beispiel +## Beispiel ```typescript class BaseRequest { @Body() @@ -18,10 +18,25 @@ class CreateUserRequest extends BaseRequest { } ``` -### Ergebnis +## Ergebnis `CreateUserRequest` wird die folgenden Felder haben: - id : von `BaseRequest` geerbt - role : in `CreateUserRequest` definiert + +Wenn Sie `CreateUserRequest` an `bindingCargo` übergeben, werden sowohl das geerbte `id` als auch das lokal deklarierte `role` zusammen gebunden und validiert. + +## Erneutes Deklarieren eines geerbten Feldes + +Das erneute Deklarieren eines geerbten Feldes in einer Unterklasse ersetzt die Definition der übergeordneten Klasse **nicht** — die Decorators beider Klassen werden auf demselben Feld zusammengeführt. Wird ein Source-Decorator auf diese Weise erneut angewendet (zum Beispiel `@Body()` auf einem Feld, das die übergeordnete Klasse bereits mit `@Body()` bezieht), entsteht ein Schema-Fehler: + ``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +Jedes Feld sollte also in einer einzigen Klasse deklariert werden. Um zu ändern, wie ein Feld gebunden oder validiert wird, bearbeiten Sie es dort, wo es ursprünglich deklariert ist, anstatt es in einer Unterklasse erneut zu deklarieren. + +## Hinweise + +- Felder werden über die gesamte Prototypenkette gesammelt, sodass mehrere Vererbungsebenen unterstützt werden. diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..ec1d34cb --- /dev/null +++ b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# Umgang mit fehlenden Feldern + +Wenn ein Feld **in der Anfrage fehlt** (sein Wert ist `undefined` oder `null`), muss Express-Cargo wissen, was zu tun ist. Zwei Decorators steuern dieses Verhalten: + +- **`@Default(value)`** — setzt einen Ersatzwert ein. +- **`@Optional()`** — erlaubt, dass das Feld leer bleibt, ohne einen „required“-Fehler auszulösen. + +Da beide dasselbe entscheiden — was passiert, wenn ein Feld fehlt — schließen sie sich **gegenseitig aus**. Beide auf dasselbe Feld anzuwenden, löst einen Schema-Fehler aus. + +Hat ein Feld **keinen** der beiden Decorators und der Wert fehlt, schlägt die Bindung mit einem ` is required`-Validierungsfehler fehl. + +## `@Default(value: T)` + +Der `@Default`-Decorator weist einer Klasseneigenschaft einen Standardwert zu, wenn die Anfrage diesen nicht bereitstellt. + +- **`value`**: Der Standardwert, der zugewiesen wird, wenn das Feld in der Anfrage nicht vorhanden ist. + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### Wann der Standardwert angewendet wird + +Der Standardwert wird **nur angewendet, wenn der eingehende Wert `undefined` oder `null` ist** (d. h. das Feld fehlt in der Anfrage). Jeder andere aus der Anfrage stammende Wert bleibt unverändert. + +Das bedeutet, dass falsy-Werte wie `0`, `''` und `false` als echte Eingabe behandelt werden und den Standardwert **nicht** auslösen: + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| Eingehendes `quantity` | Gebundener Wert | +|------------------------|----------------------------| +| fehlt / `undefined` | `10` (Standard angewendet) | +| `null` | `10` (Standard angewendet) | +| `0` | `0` (beibehalten) | +| `5` | `5` (beibehalten) | + +## `@Optional()` + +Der `@Optional`-Decorator markiert ein Feld als optional, sodass es weggelassen oder auf `undefined`/`null` gesetzt werden kann, ohne einen „required“-Fehler auszulösen. Fehlt das Feld, bleibt es `null` und die Validierungsregeln für das Feld werden übersprungen. + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +Hier wird ein fehlendes `discount` an `null` gebunden und die Regel `@Min(0)` übersprungen. Wenn `discount` **doch** angegeben wird, wird es normal validiert. + +## Wahl zwischen `@Default` und `@Optional` + +| | Feld fehlt | Feld angegeben | +|---|---|---| +| `@Default(value)` | an `value` gebunden | Wert beibehalten und validiert | +| `@Optional()` | an `null` gebunden | Wert beibehalten und validiert | +| keiner | ` is required`-Fehler | Wert beibehalten und validiert | + +Wählen Sie **eine** Strategie pro Feld: + +```typescript +class Request { + // ❌ Ungültig: ein Feld kann nur eine Strategie für fehlende Werte verwenden + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/overview.md index e009e790..cfd7a577 100644 --- a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -1,6 +1,41 @@ -### Was sind Decorators? +# Decorators-Übersicht + +## Was sind Decorators? Decorators in express-cargo annotieren Klassenfelder, um der Middleware mitzuteilen: - Woher Daten extrahiert werden sollen (z. B. Body, Query) - Wie sie validiert werden sollen - Wie sie transformiert werden sollen + +Wenn Sie eine Klasse an `bindingCargo` übergeben, liest die Middleware diese Decorators, um ein typsicheres Objekt zu erstellen, zu validieren und zu transformieren, das Sie mit `getCargo` abrufen. + +## Decorator-Kategorien + +Decorators werden nach der Rolle gruppiert, die sie in der Binding-Pipeline spielen. + +| Kategorie | Zweck | Beispiele | Referenz | +|-----------|-------|-----------|----------| +| **Source** | Bestimmt, woher der Wert eines Feldes stammt | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [Source-Decorators](./source-decorators.md) | +| **Virtual** | Berechnet ein Feld aus anderen Feldern oder aus dem rohen `Request` | `@Virtual`, `@Request` | [Virtuelle Feld-Decorators](./virtual.md) | +| **Transform** | Ändert den Wert eines einzelnen Feldes vor dem Binden | `@Transform` | [Transformations-Decorator](./transforms.md) | +| **Validation** | Erzwingt Regeln für den Wert eines Feldes | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [Validierungs-Decorators](./validators.md) | +| **Missing-value** | Entscheidet, was passiert, wenn ein Feld fehlt | `@Default`, `@Optional` | [Umgang mit fehlenden Feldern](./missing-fields.md) | + +Weitere Helfer werden unter **Erweiterte Nutzung** behandelt, z. B. [`@List`](../advanced/list-decorator.md) für typisierte Arrays und [`@Type`](../advanced/type-and-polymorphism.md) für verschachtelte und polymorphe Typen. + +## Decorators kombinieren + +Ein einzelnes Feld kann Decorators aus mehreren Kategorien tragen. Sie werden zusammen gelesen, um dieses Feld zu binden, zu transformieren und zu validieren: + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source: aus req.body.email lesen + @Transform((value: string) => value.trim()) // Transform: den Wert normalisieren + @MinLength(5) // Validation: eine Regel erzwingen + email!: string +} +``` + +Jede Kategorie wird auf ihrer eigenen, oben verlinkten Seite dokumentiert. diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/validators.md index 1f23b20f..51b21b4e 100644 --- a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -6,10 +6,6 @@ Die Validierung wird nicht durch eine eigenständige `validate`-Funktion durchge ## Integrierte Validatoren -### `@Optional()` - -Markiert ein Feld als optional, sodass es weggelassen oder auf `undefined` gesetzt werden kann, ohne Validierungsfehler auszulösen. - ### `@Min(value: number)` Validiert, dass eine Zahl größer oder gleich dem angegebenen Mindestwert ist. diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/examples/basic-usage.md index 18ee7b45..bfdf3bfc 100644 --- a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -1 +1,75 @@ -> Einfacher POST-Handler mit bindingCargo +# Grundlegende Verwendung + +Dieses Beispiel zeigt den einfachsten End-to-End-Ablauf von **express-cargo**: eine Request-Klasse definieren, sie mit der `bindingCargo`-Middleware binden und das validierte Ergebnis mit `getCargo` lesen. + +> Diese Seite konzentriert sich nur auf den Binding-Ablauf. Für die Projekteinrichtung (TypeScript, `tsconfig`, Installation der Abhängigkeiten) siehe [Erste Schritte](../getting-started.md). + +## 1. Eine Request-Klasse definieren + +Deklarieren Sie eine Klasse und verwenden Sie einen Source-Decorator (hier `@Body()`), um jedes Feld einem Teil der eingehenden Anfrage zuzuordnen. + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. Die Middleware anwenden + +Übergeben Sie die Request-Klasse an `bindingCargo` und registrieren Sie sie als Middleware auf der Route. Die Middleware bindet und validiert die Anfrage, bevor Ihr Handler ausgeführt wird. + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. Das gebundene, typsichere Objekt lesen + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. Beispielanfrage + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. Beispielergebnis + +`getCargo(req)` gibt eine vollständig gefüllte Instanz von `CreateUserRequest` zurück: + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## Nächste Schritte + +- Daten aus anderen Quellen abrufen (`@Query`, `@Header`, `@Params`) — siehe [Source-Decorators](../decorators/source-decorators.md) +- Validierungsregeln wie `@Min`, `@Email` hinzufügen — siehe [Validierungs-Decorators](../decorators/validators.md) +- Verschachtelte Objekte binden — siehe [Umgang mit verschachtelten Anfragen](./nested-request.md) +- Validierungsfehler behandeln — siehe [Fehlerbehandlung](./validation-errors.md) diff --git a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/faq.md index bd61f802..8e06e381 100644 --- a/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/de/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ Zusätzlich muss das `reflect-metadata`-Paket installiert sein, um Typinformatio
F: Wie vermeide ich Fehler, wenn ein bestimmtes Feld fehlt? -**A:** Verwenden Sie den **`@Optional()`**-Decorator. Das Feld wird auch dann erfolgreich gebunden, wenn der Wert `null` oder `undefined` ist, und die Validierung für dieses Feld wird übersprungen. +**A:** Verwenden Sie den **`@Optional()`**-Decorator. Das Feld wird auch dann erfolgreich gebunden, wenn der Wert `null` oder `undefined` ist, und die Validierung für dieses Feld wird übersprungen. Um stattdessen einen Ersatzwert einzusetzen, verwenden Sie `@Default()`. Siehe [Umgang mit fehlenden Feldern](./decorators/missing-fields.md).
### 4. Framework-Kompatibilität diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 5a5f786f..00000000 --- a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# Decorador de campo predeterminado - -Express-Cargo proporciona decoradores para definir valores predeterminados en los campos de una solicitud. El decorador asigna automáticamente un valor cuando la solicitud entrante no proporciona uno (`undefined` o `null`). - -## Decorador predeterminado integrado - -### `@Default(value: T)` - -El decorador `@Default` asigna un valor predeterminado a una propiedad de clase cuando la solicitud no la proporciona. - -- **`value`**: El valor predeterminado que se asignará si el campo no está presente en la solicitud. - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index 4d319603..6c03824b 100644 --- a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## Enlace heredado +# Enlace heredado Los decoradores de campo también se aplican a los campos declarados en clases padre. -Esto te permite definir campos comunes en una **clase base** y luego extenderlos o sobrescribirlos en **clases hijas**. +Esto te permite definir campos comunes una vez en una **clase base** y reutilizarlos en las **clases hijas**. -### Ejemplo +## Ejemplo ```typescript class BaseRequest { @Body() @@ -18,9 +18,25 @@ class CreateUserRequest extends BaseRequest { } ``` -### Resultado +## Resultado `CreateUserRequest` tendrá los siguientes campos: - id: heredado de `BaseRequest` - role: definido en `CreateUserRequest` + +Cuando pasas `CreateUserRequest` a `bindingCargo`, tanto el `id` heredado como el `role` declarado localmente se enlazan y validan juntos. + +## Volver a declarar un campo heredado + +Volver a declarar un campo heredado en una clase hija **no** reemplaza la definición de la clase padre: los decoradores de ambas clases se combinan en el mismo campo. Volver a aplicar un decorador de origen de esta forma (por ejemplo, `@Body()` en un campo que la clase padre ya obtiene con `@Body()`) produce un error de esquema: + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +Por lo tanto, cada campo debe declararse en una sola clase. Para cambiar cómo se enlaza o valida un campo, edítalo donde se declaró originalmente en lugar de volver a declararlo en una subclase. + +## Notas + +- Los campos se recopilan a lo largo de toda la cadena de prototipos, por lo que se admiten varios niveles de herencia. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..0933c2e2 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# Manejo de campos faltantes + +Cuando un campo **falta en la solicitud** (su valor es `undefined` o `null`), Express-Cargo necesita saber qué hacer. Dos decoradores controlan este comportamiento: + +- **`@Default(value)`** — sustituye con un valor de reserva. +- **`@Optional()`** — permite que el campo quede vacío sin provocar un error «required». + +Como ambos deciden lo mismo —qué ocurre cuando falta un campo— son **mutuamente excluyentes**. Aplicar los dos al mismo campo lanza un error de esquema. + +Si un campo **no tiene ninguno** de los dos decoradores y el valor falta, el enlace falla con un error de validación ` is required`. + +## `@Default(value: T)` + +El decorador `@Default` asigna un valor predeterminado a una propiedad de clase cuando la solicitud no la proporciona. + +- **`value`**: El valor predeterminado que se asignará si el campo no está presente en la solicitud. + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### Cuándo se aplica el valor predeterminado + +El valor predeterminado se aplica **solo cuando el valor entrante es `undefined` o `null`** (es decir, el campo falta en la solicitud). Cualquier otro valor proveniente de la solicitud se mantiene tal cual. + +Esto significa que los valores falsy como `0`, `''` y `false` se tratan como entrada real y **no** activan el valor predeterminado: + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| `quantity` entrante | Valor enlazado | +|----------------------|-----------------------------| +| falta / `undefined` | `10` (predeterminado aplicado) | +| `null` | `10` (predeterminado aplicado) | +| `0` | `0` (mantenido) | +| `5` | `5` (mantenido) | + +## `@Optional()` + +El decorador `@Optional` marca un campo como opcional, permitiendo que se omita o se establezca como `undefined`/`null` sin provocar un error «required». Cuando el campo falta, se deja como `null` y se omiten las reglas de validación del campo. + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +Aquí un `discount` faltante se enlaza a `null` y se omite la regla `@Min(0)`. Si `discount` **sí** se proporciona, se valida normalmente. + +## Elegir entre `@Default` y `@Optional` + +| | Campo faltante | Campo proporcionado | +|---|---|---| +| `@Default(value)` | enlazado a `value` | valor mantenido y validado | +| `@Optional()` | enlazado a `null` | valor mantenido y validado | +| ninguno | error ` is required` | valor mantenido y validado | + +Elige **una** estrategia por campo: + +```typescript +class Request { + // ❌ Inválido: un campo solo puede usar una estrategia de valor faltante + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/overview.md index b019fb52..3ad0be40 100644 --- a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -1,6 +1,41 @@ -### ¿Qué son los decoradores? +# Resumen de decoradores + +## ¿Qué son los decoradores? Los decoradores en express-cargo anotan campos de clase para indicarle al middleware: - De dónde extraer los datos (por ejemplo, body o query) - Cómo validarlos - Cómo transformarlos + +Cuando pasas una clase a `bindingCargo`, el middleware lee estos decoradores para construir, validar y transformar un objeto con seguridad de tipos que recuperas con `getCargo`. + +## Categorías de decoradores + +Los decoradores se agrupan según el rol que cumplen en la canalización de enlace. + +| Categoría | Propósito | Ejemplos | Referencia | +|-----------|-----------|----------|------------| +| **Source** | Elige de dónde proviene el valor de un campo | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [Decoradores de origen](./source-decorators.md) | +| **Virtual** | Calcula un campo a partir de otros campos o del `Request` sin procesar | `@Virtual`, `@Request` | [Decoradores de campo virtual](./virtual.md) | +| **Transform** | Modifica el valor de un solo campo antes del enlace | `@Transform` | [Decorador de transformación](./transforms.md) | +| **Validation** | Aplica reglas al valor de un campo | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [Decoradores de validación](./validators.md) | +| **Missing-value** | Decide qué ocurre cuando un campo está ausente | `@Default`, `@Optional` | [Manejo de campos faltantes](./missing-fields.md) | + +Otros ayudantes se tratan en **Uso avanzado**, como [`@List`](../advanced/list-decorator.md) para arrays tipados y [`@Type`](../advanced/type-and-polymorphism.md) para tipos anidados y polimórficos. + +## Combinar decoradores + +Un solo campo puede llevar decoradores de varias categorías. Se leen en conjunto para enlazar, transformar y validar ese campo: + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source: lee de req.body.email + @Transform((value: string) => value.trim()) // Transform: normaliza el valor + @MinLength(5) // Validation: aplica una regla + email!: string +} +``` + +Cada categoría se documenta en su propia página enlazada arriba. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/validators.md index b3cd122b..2472b2c2 100644 --- a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -6,10 +6,6 @@ La validación no se realiza mediante una función `validate` independiente. En ## Validadores integrados -### `@Optional()` - -Marca un campo como opcional, permitiendo que se omita o se establezca como `undefined` sin provocar errores de validación. - ### `@Min(value: number)` Valida que un número sea mayor o igual que el valor mínimo especificado. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/examples/basic-usage.md index ff6aac86..091a48f8 100644 --- a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -1 +1,75 @@ -> Handler POST básico con bindingCargo +# Uso básico + +Este ejemplo muestra el flujo de extremo a extremo más simple de **express-cargo**: definir una clase de solicitud, enlazarla con el middleware `bindingCargo` y leer el resultado validado con `getCargo`. + +> Esta página se centra solo en el flujo de enlace. Para la configuración del proyecto (TypeScript, `tsconfig`, instalación de dependencias), consulta [Primeros pasos](../getting-started.md). + +## 1. Definir una clase de solicitud + +Declara una clase y usa un decorador de origen (aquí `@Body()`) para asignar cada campo a una parte de la solicitud entrante. + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. Aplicar el middleware + +Pasa la clase de solicitud a `bindingCargo` y regístralo como middleware en la ruta. El middleware enlaza y valida la solicitud antes de que se ejecute tu manejador. + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. Leer el objeto enlazado con seguridad de tipos + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. Solicitud de ejemplo + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. Resultado de ejemplo + +`getCargo(req)` devuelve una instancia completamente poblada de `CreateUserRequest`: + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## Próximos pasos + +- Obtener datos de otras fuentes (`@Query`, `@Header`, `@Params`) — consulta [Decoradores de origen](../decorators/source-decorators.md) +- Agregar reglas de validación como `@Min`, `@Email` — consulta [Decoradores de validación](../decorators/validators.md) +- Enlazar objetos anidados — consulta [Manejo de solicitudes anidadas](./nested-request.md) +- Manejar fallos de validación — consulta [Manejo de errores](./validation-errors.md) diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/faq.md index e30bbacd..9467665b 100644 --- a/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ Además, el paquete `reflect-metadata` debe estar instalado para leer informaci
P: ¿Cómo evito errores si falta un campo específico? -**R:** Usa el decorador **`@Optional()`**. El campo se enlazará correctamente aunque el valor sea `null` o `undefined`, omitiendo la validación para ese campo. +**R:** Usa el decorador **`@Optional()`**. El campo se enlazará correctamente aunque el valor sea `null` o `undefined`, omitiendo la validación para ese campo. Para sustituir con un valor predeterminado en su lugar, usa `@Default()`. Consulta [Manejo de campos faltantes](./decorators/missing-fields.md).
### 4. Compatibilidad con frameworks diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 59bfb894..00000000 --- a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# Décorateur de champ par défaut - -Express-Cargo fournit des décorateurs pour définir des valeurs par défaut pour les champs de requête. Le décorateur assigne automatiquement une valeur lorsque la requête entrante n'en fournit pas (undefined ou null). - -## Décorateur par défaut intégré - -### `@Default(value: T)` - -Le décorateur @Default assigne une valeur par défaut à une propriété de classe lorsque la requête ne la fournit pas. - -- **`value`** : La valeur par défaut à assigner si le champ n'est pas présent dans la requête. - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index ef58f854..1eae04e2 100644 --- a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## Liaison héritée +# Liaison héritée -Les décorateurs de champ sont également appliqués aux champs déclarés dans les classes parentes. -Cela vous permet de définir des champs communs dans une **classe de base** puis de les étendre ou de les remplacer dans des **classes enfants**. +Les décorateurs de champ sont également appliqués aux champs déclarés dans les classes parentes. +Cela vous permet de définir des champs communs une seule fois dans une **classe de base** et de les réutiliser dans les **classes enfants**. -### Exemple +## Exemple ```typescript class BaseRequest { @Body() @@ -18,9 +18,25 @@ class CreateUserRequest extends BaseRequest { } ``` -### Résultat +## Résultat `CreateUserRequest` aura les champs suivants : - id : hérité de `BaseRequest` - role : défini dans `CreateUserRequest` + +Lorsque vous passez `CreateUserRequest` à `bindingCargo`, le `id` hérité et le `role` déclaré localement sont liés et validés ensemble. + +## Redéclarer un champ hérité + +Redéclarer un champ hérité dans une classe enfant ne remplace **pas** la définition de la classe parente — les décorateurs des deux classes sont fusionnés sur le même champ. Réappliquer un décorateur de source de cette manière (par exemple `@Body()` sur un champ que la classe parente source déjà avec `@Body()`) produit une erreur de schéma : + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +Chaque champ doit donc être déclaré dans une seule classe. Pour modifier la façon dont un champ est lié ou validé, modifiez-le là où il est initialement déclaré plutôt que de le redéclarer dans une sous-classe. + +## Remarques + +- Les champs sont collectés sur toute la chaîne de prototypes, ce qui permet de prendre en charge plusieurs niveaux d'héritage. diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..66d3ae69 --- /dev/null +++ b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# Gestion des champs manquants + +Lorsqu'un champ est **manquant dans la requête** (sa valeur est `undefined` ou `null`), Express-Cargo doit savoir quoi faire. Deux décorateurs contrôlent ce comportement : + +- **`@Default(value)`** — substitue une valeur de repli. +- **`@Optional()`** — permet au champ de rester vide sans déclencher d'erreur « required ». + +Comme les deux décident de la même chose — ce qui se passe lorsqu'un champ est manquant — ils sont **mutuellement exclusifs**. Appliquer les deux au même champ déclenche une erreur de schéma. + +Si un champ n'a **aucun** des deux décorateurs et que la valeur est manquante, la liaison échoue avec une erreur de validation ` is required`. + +## `@Default(value: T)` + +Le décorateur `@Default` assigne une valeur par défaut à une propriété de classe lorsque la requête ne la fournit pas. + +- **`value`** : La valeur par défaut à assigner si le champ n'est pas présent dans la requête. + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### Quand la valeur par défaut est appliquée + +La valeur par défaut est appliquée **uniquement lorsque la valeur entrante est `undefined` ou `null`** (c'est-à-dire que le champ est manquant dans la requête). Toute autre valeur provenant de la requête est conservée telle quelle. + +Cela signifie que les valeurs falsy telles que `0`, `''` et `false` sont traitées comme une véritable entrée et **ne déclenchent pas** la valeur par défaut : + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| `quantity` entrant | Valeur liée | +|----------------------|----------------------------| +| manquant / `undefined` | `10` (défaut appliqué) | +| `null` | `10` (défaut appliqué) | +| `0` | `0` (conservé) | +| `5` | `5` (conservé) | + +## `@Optional()` + +Le décorateur `@Optional` marque un champ comme optionnel, lui permettant d'être omis ou défini sur `undefined`/`null` sans déclencher d'erreur « required ». Lorsque le champ est manquant, il est laissé à `null` et les règles de validation du champ sont ignorées. + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +Ici, un `discount` manquant est lié à `null` et la règle `@Min(0)` est ignorée. Si `discount` **est** fourni, il est validé normalement. + +## Choisir entre `@Default` et `@Optional` + +| | Champ manquant | Champ fourni | +|---|---|---| +| `@Default(value)` | lié à `value` | valeur conservée et validée | +| `@Optional()` | lié à `null` | valeur conservée et validée | +| aucun | erreur ` is required` | valeur conservée et validée | + +Choisissez **une seule** stratégie par champ : + +```typescript +class Request { + // ❌ Invalide : un champ ne peut utiliser qu'une seule stratégie de valeur manquante + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/overview.md index cdfcf7f2..4c5968a8 100644 --- a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -1,6 +1,41 @@ -### Que sont les décorateurs ? +# Aperçu des décorateurs + +## Que sont les décorateurs ? Les décorateurs dans express-cargo annotent les champs de classe pour indiquer au middleware : - D'où extraire les données (par exemple, body, query) - Comment les valider - Comment les transformer + +Lorsque vous passez une classe à `bindingCargo`, le middleware lit ces décorateurs pour construire, valider et transformer un objet typé que vous récupérez avec `getCargo`. + +## Catégories de décorateurs + +Les décorateurs sont regroupés selon le rôle qu'ils jouent dans le pipeline de liaison. + +| Catégorie | Rôle | Exemples | Référence | +|-----------|------|----------|-----------| +| **Source** | Choisit d'où provient la valeur d'un champ | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [Décorateurs de source](./source-decorators.md) | +| **Virtual** | Calcule un champ à partir d'autres champs ou du `Request` brut | `@Virtual`, `@Request` | [Décorateurs de champ virtuel](./virtual.md) | +| **Transform** | Modifie la valeur d'un seul champ avant la liaison | `@Transform` | [Décorateur de transformation](./transforms.md) | +| **Validation** | Applique des règles à la valeur d'un champ | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [Décorateurs de validation](./validators.md) | +| **Missing-value** | Décide ce qui se passe quand un champ est absent | `@Default`, `@Optional` | [Gestion des champs manquants](./missing-fields.md) | + +D'autres utilitaires sont traités dans **Utilisation avancée**, comme [`@List`](../advanced/list-decorator.md) pour les tableaux typés et [`@Type`](../advanced/type-and-polymorphism.md) pour les types imbriqués et polymorphes. + +## Combiner les décorateurs + +Un seul champ peut porter des décorateurs de plusieurs catégories. Ils sont lus ensemble pour lier, transformer et valider ce champ : + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source : lit depuis req.body.email + @Transform((value: string) => value.trim()) // Transform : normalise la valeur + @MinLength(5) // Validation : applique une règle + email!: string +} +``` + +Chaque catégorie est documentée sur sa propre page liée ci-dessus. diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/validators.md index 35038fbb..24dd4f96 100644 --- a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -6,10 +6,6 @@ La validation n'est pas effectuée par une fonction `validate` autonome. Au lieu ## Validateurs intégrés -### `@Optional()` - -Marque un champ comme optionnel, lui permettant d'être omis ou défini sur `undefined` sans déclencher d'erreurs de validation. - ### `@Min(value: number)` Valide qu'un nombre est supérieur ou égal à la valeur minimale spécifiée. diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/examples/basic-usage.md index 5ea6cd30..4079b1b8 100644 --- a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -1 +1,75 @@ -> Gestionnaire POST de base utilisant bindingCargo +# Utilisation de base + +Cet exemple montre le flux de bout en bout le plus simple d'**express-cargo** : définir une classe de requête, la lier avec le middleware `bindingCargo` et lire le résultat validé avec `getCargo`. + +> Cette page se concentre uniquement sur le flux de liaison. Pour la configuration du projet (TypeScript, `tsconfig`, installation des dépendances), voir [Premiers pas](../getting-started.md). + +## 1. Définir une classe de requête + +Déclarez une classe et utilisez un décorateur de source (ici `@Body()`) pour mapper chaque champ à une partie de la requête entrante. + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. Appliquer le middleware + +Passez la classe de requête à `bindingCargo` et enregistrez-le comme middleware sur la route. Le middleware lie et valide la requête avant l'exécution de votre gestionnaire. + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. Lire l'objet lié et typé + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. Exemple de requête + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. Exemple de résultat + +`getCargo(req)` retourne une instance entièrement remplie de `CreateUserRequest` : + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## Étapes suivantes + +- Récupérer des données depuis d'autres sources (`@Query`, `@Header`, `@Params`) — voir [Décorateurs de source](../decorators/source-decorators.md) +- Ajouter des règles de validation comme `@Min`, `@Email` — voir [Décorateurs de validation](../decorators/validators.md) +- Lier des objets imbriqués — voir [Gestion des requêtes imbriquées](./nested-request.md) +- Gérer les échecs de validation — voir [Gestion des erreurs](./validation-errors.md) diff --git a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/faq.md index 3460ba1f..f861d1fc 100644 --- a/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/fr/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ De plus, le package `reflect-metadata` doit être installé pour lire les inform
Q : Comment éviter les erreurs si un champ spécifique est manquant ? -**R :** Utilisez le décorateur **`@Optional()`**. Le champ sera lié avec succès même si la valeur est `null` ou `undefined`, en sautant la validation pour ce champ. +**R :** Utilisez le décorateur **`@Optional()`**. Le champ sera lié avec succès même si la valeur est `null` ou `undefined`, en sautant la validation pour ce champ. Pour substituer une valeur de repli à la place, utilisez `@Default()`. Voir [Gestion des champs manquants](./decorators/missing-fields.md).
### 4. Compatibilité des frameworks diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 561bbdbb..00000000 --- a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# デフォルトフィールドデコレータ - -Express-Cargo は、リクエストフィールドのデフォルト値を定義するためのデコレータを提供します。このデコレータは、受信リクエストが値を提供しない場合(undefined または null)に自動的に値を割り当てます。 - -## 組み込みデフォルトデコレータ - -### `@Default(value: T)` - -@Default デコレータは、リクエストがフィールドを提供しない場合にクラスプロパティにデフォルト値を割り当てます。 - -- **`value`**: フィールドがリクエストに存在しない場合に割り当てるデフォルト値。 - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` \ No newline at end of file diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index 4405f861..9b296430 100644 --- a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## 継承バインディング +# 継承バインディング -フィールドデコレータは、親クラスで宣言されたフィールドにも適用されます。 -これにより、**ベースクラス**で共通フィールドを定義し、**子クラス**で拡張またはオーバーライドできます。 +フィールドデコレータは、親クラスで宣言されたフィールドにも適用されます。 +これにより、共通フィールドを**ベースクラス**で一度定義し、**子クラス**全体で再利用できます。 -### 例 +## 例 ```typescript class BaseRequest { @Body() @@ -18,9 +18,25 @@ class CreateUserRequest extends BaseRequest { } ``` -### 結果 +## 結果 `CreateUserRequest` は以下のフィールドを持ちます: - id : `BaseRequest` から継承 -- role : `CreateUserRequest` で定義 \ No newline at end of file +- role : `CreateUserRequest` で定義 + +`CreateUserRequest` を `bindingCargo` に渡すと、継承された `id` とローカルで宣言された `role` の両方がまとめてバインドされ、検証されます。 + +## 継承したフィールドの再宣言 + +子クラスで継承したフィールドを再宣言しても、親クラスの定義は置き換えられ**ません**。両方のクラスのデコレータが同じフィールドにマージされます。この方法でソースデコレータを再適用すると(たとえば、親クラスが既に `@Body()` でソースにしているフィールドに `@Body()` を付けると)、スキーマエラーが発生します。 + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +したがって、各フィールドは単一のクラスで宣言してください。フィールドのバインド方法や検証方法を変更するには、サブクラスで再宣言するのではなく、最初に宣言した場所で編集してください。 + +## 注意点 + +- フィールドはプロトタイプチェーン全体にわたって収集されるため、複数レベルの継承がサポートされます。 diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..0eaf6083 --- /dev/null +++ b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# 欠落フィールドの処理 + +フィールドが**リクエストに存在しない**場合(値が `undefined` または `null`)、Express-Cargo は何をすべきかを知る必要があります。この動作は 2 つのデコレータで制御します。 + +- **`@Default(value)`** — フォールバック値を割り当てます。 +- **`@Optional()`** — 「required」エラーを発生させずに、フィールドを空のままにできます。 + +どちらもフィールドが欠落したときの動作という同じことを決めるため、**相互に排他的**です。両方を同じフィールドに適用すると、スキーマエラーが発生します。 + +フィールドに**どちらのデコレータもなく**値が欠落している場合、バインドは ` is required` バリデーションエラーで失敗します。 + +## `@Default(value: T)` + +`@Default` デコレータは、リクエストがフィールドを提供しない場合にクラスプロパティにデフォルト値を割り当てます。 + +- **`value`**: フィールドがリクエストに存在しない場合に割り当てるデフォルト値。 + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### デフォルト値が適用されるタイミング + +デフォルト値は、**受信した値が `undefined` または `null` の場合にのみ**適用されます(つまり、フィールドがリクエストに存在しない場合)。リクエストから来たその他の値はそのまま保持されます。 + +つまり、`0`、`''`、`false` のような falsy な値は実際の入力として扱われ、デフォルト値を**トリガーしません**。 + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| 受信した `quantity` | バインドされる値 | +|-------------------------|----------------------------| +| 欠落 / `undefined` | `10`(デフォルト適用) | +| `null` | `10`(デフォルト適用) | +| `0` | `0`(保持) | +| `5` | `5`(保持) | + +## `@Optional()` + +`@Optional` デコレータはフィールドをオプションとしてマークし、「required」エラーを発生させずに省略または `undefined`/`null` に設定できるようにします。フィールドが欠落している場合は `null` のままになり、そのフィールドのバリデーションルールはスキップされます。 + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +ここでは、欠落した `discount` は `null` にバインドされ、`@Min(0)` ルールはスキップされます。`discount` が**提供された**場合は、通常どおり検証されます。 + +## `@Default` と `@Optional` の選択 + +| | フィールド欠落 | フィールド提供 | +|---|---|---| +| `@Default(value)` | `value` にバインド | 値を保持して検証 | +| `@Optional()` | `null` にバインド | 値を保持して検証 | +| どちらもなし | ` is required` エラー | 値を保持して検証 | + +フィールドごとに**1 つ**の戦略を選んでください。 + +```typescript +class Request { + // ❌ 無効: フィールドは 1 つの欠落値戦略のみ使用できます + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/overview.md index 5b8897cc..1b61ba16 100644 --- a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -1,6 +1,41 @@ -### デコレータとは? +# デコレータの概要 + +## デコレータとは? express-cargo のデコレータは、クラスフィールドにアノテーションを付与し、ミドルウェアに以下を伝えます: - データの抽出元(例: ボディ、クエリ) - バリデーション方法 - データの変換方法 + +クラスを `bindingCargo` に渡すと、ミドルウェアはこれらのデコレータを読み取り、`getCargo` で取得できる型安全なオブジェクトを構築・検証・変換します。 + +## デコレータのカテゴリ + +デコレータは、バインディングパイプラインで果たす役割ごとにグループ化されます。 + +| カテゴリ | 目的 | 例 | 参照 | +|----------|------|-----|------| +| **Source** | フィールドの値の取得元を選択する | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [ソースデコレータ](./source-decorators.md) | +| **Virtual** | 他のフィールドや生の `Request` からフィールドを計算する | `@Virtual`, `@Request` | [仮想フィールドデコレータ](./virtual.md) | +| **Transform** | バインド前に単一フィールドの値を変更する | `@Transform` | [変換デコレータ](./transforms.md) | +| **Validation** | フィールドの値にルールを適用する | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [バリデーションデコレータ](./validators.md) | +| **Missing-value** | フィールドが存在しない場合の動作を決定する | `@Default`, `@Optional` | [欠落フィールドの処理](./missing-fields.md) | + +その他のヘルパーは **高度な使い方** で扱います。たとえば、型付き配列用の [`@List`](../advanced/list-decorator.md) や、ネスト型・多態型用の [`@Type`](../advanced/type-and-polymorphism.md) などです。 + +## デコレータの組み合わせ + +単一のフィールドは複数のカテゴリのデコレータを持つことができます。それらはまとめて読み取られ、そのフィールドのバインド・変換・検証が行われます。 + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source: req.body.email から読み取る + @Transform((value: string) => value.trim()) // Transform: 値を正規化する + @MinLength(5) // Validation: ルールを適用する + email!: string +} +``` + +各カテゴリは、上記でリンクされた個別のページで説明されています。 diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/validators.md index e24da820..665ed385 100644 --- a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -6,10 +6,6 @@ Express-Cargo は、クラスにバインドされた受信リクエストデー ## 組み込みバリデータ -### `@Optional()` - -フィールドをオプションとしてマークし、省略または `undefined` に設定してもバリデーションエラーが発生しないようにします。 - ### `@Min(value: number)` 数値が指定された最小値以上であることを検証します。 diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/examples/basic-usage.md index af62988b..b0d057c6 100644 --- a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -1 +1,75 @@ -> bindingCargo を使用した基本的な POST ハンドラ \ No newline at end of file +# 基本的な使い方 + +この例では、**express-cargo** の最もシンプルなエンドツーエンドの流れを示します。リクエストクラスを定義し、`bindingCargo` ミドルウェアでバインドし、検証済みの結果を `getCargo` で読み取ります。 + +> このページはバインドの流れのみに焦点を当てています。プロジェクトのセットアップ(TypeScript、`tsconfig`、依存関係のインストール)については、[はじめに](../getting-started.md)を参照してください。 + +## 1. リクエストクラスを定義する + +クラスを宣言し、ソースデコレータ(ここでは `@Body()`)を使って各フィールドを受信リクエストの一部にマッピングします。 + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. ミドルウェアを適用する + +リクエストクラスを `bindingCargo` に渡し、ルートのミドルウェアとして登録します。ミドルウェアは、ハンドラが実行される前にリクエストをバインドして検証します。 + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. バインドされた型安全なオブジェクトを読み取る + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. リクエスト例 + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. 結果例 + +`getCargo(req)` は、完全に値が設定された `CreateUserRequest` のインスタンスを返します。 + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## 次のステップ + +- 他のソースからデータを取得する(`@Query`、`@Header`、`@Params`)— [ソースデコレータ](../decorators/source-decorators.md)を参照 +- `@Min`、`@Email` などのバリデーションルールを追加する — [バリデーションデコレータ](../decorators/validators.md)を参照 +- ネストしたオブジェクトをバインドする — [ネストしたリクエストの処理](./nested-request.md)を参照 +- バリデーション失敗を処理する — [エラー処理](./validation-errors.md)を参照 diff --git a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/faq.md index 492b28ad..1dae9440 100644 --- a/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/ja/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ sidebar_label: よくある質問
Q: 特定のフィールドが存在しない場合にエラーを回避するには? -**A:** **`@Optional()`** デコレータを使用します。値が `null` または `undefined` でもフィールドは正常にバインドされ、そのフィールドのバリデーションはスキップされます。 +**A:** **`@Optional()`** デコレータを使用します。値が `null` または `undefined` でもフィールドは正常にバインドされ、そのフィールドのバリデーションはスキップされます。代わりにフォールバック値を割り当てるには、`@Default()` を使用します。[欠落フィールドの処理](./decorators/missing-fields.md)を参照してください。
### 4. フレームワーク互換性 diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 404b58f6..00000000 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: default -title: 기본값 데코레이터 ---- - -# Default Field Decorator - -Express-Cargo는 요청 필드에 기본값을 정의할 수 있는 데코레이터를 제공합니다. 이 데코레이터는 요청에서 값이 제공되지 않았을 때(undefined 또는 null) 자동으로 기본값을 할당합니다. - -## Built-in Default Decorator - -### `@Default(value: T)` - -@Default 데코레이터는 요청에서 해당 필드가 제공되지 않았을 경우 클래스 프로퍼티에 기본값을 할당합니다. - -- **`value`**: 요청에서 값이 없을 경우 할당할 기본값 - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index c3361686..686aa8b2 100644 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -3,12 +3,12 @@ id: inherited-binding title: 상속 바인딩 --- -## 상속 바인딩 (inherited binding) +# 상속 바인딩 -Field 데코레이터는 부모 클래스에서 선언된 필드도 함께 적용됩니다. -즉, 공통 필드를 **기반 클래스**에 정의하고, 필요한 경우 **자식 클래스**에서 추가하거나 덮어쓸 수 있습니다. +필드 데코레이터는 부모 클래스에서 선언된 필드에도 함께 적용됩니다. +즉, 공통 필드를 **기반 클래스**에 한 번 정의하고 **자식 클래스** 전반에서 재사용할 수 있습니다. -### 예시 (Example) +## 예시 ```typescript class BaseRequest { @Body() @@ -22,9 +22,26 @@ class CreateUserRequest extends BaseRequest { role!: string } ``` -### 결과 -`CreateUserRequest`는 다음과 같은 필드를 가집니다: + +## 결과 +`CreateUserRequest`는 다음과 같은 필드를 가집니다. - id : `BaseRequest`에서 상속됨 - role : `CreateUserRequest`에서 정의됨 + +`CreateUserRequest`를 `bindingCargo`에 전달하면, 상속된 `id`와 로컬에서 선언한 `role`이 함께 바인딩되고 검증됩니다. + +## 상속된 필드 재선언 + +자식 클래스에서 상속된 필드를 다시 선언해도 부모 클래스의 정의가 대체되지 **않습니다**. 두 클래스의 데코레이터가 같은 필드에 병합됩니다. 이런 식으로 소스 데코레이터를 다시 적용하면(예: 부모 클래스가 이미 `@Body()`로 가져오는 필드에 `@Body()`를 추가하면) 스키마 오류가 발생합니다. + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +따라서 각 필드는 하나의 클래스에서만 선언해야 합니다. 필드의 바인딩 방식이나 검증 방식을 바꾸려면, 자식 클래스에서 재선언하지 말고 원래 선언된 곳에서 수정하세요. + +## 참고 + +- 필드는 전체 프로토타입 체인에 걸쳐 수집되므로 여러 단계의 상속을 지원합니다. diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..6ef672b2 --- /dev/null +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,85 @@ +--- +id: missing-fields +title: 필드 누락 처리 +--- + +# 필드 누락 처리 + +필드가 **요청에 존재하지 않을 때**(값이 `undefined` 또는 `null`), Express-Cargo는 어떻게 처리할지 알아야 합니다. 이 동작은 두 개의 데코레이터로 제어합니다. + +- **`@Default(value)`** — 대체 값을 채워 넣습니다. +- **`@Optional()`** — "required" 오류를 발생시키지 않고 필드를 비워 둘 수 있게 합니다. + +둘 다 "필드가 없을 때 무엇을 할지"라는 같은 것을 결정하므로 **상호 배타적**입니다. 같은 필드에 둘을 모두 적용하면 스키마 오류가 발생합니다. + +필드에 **두 데코레이터가 모두 없고** 값이 누락되면, 바인딩은 ` is required` 검증 오류로 실패합니다. + +## `@Default(value: T)` + +`@Default` 데코레이터는 요청이 값을 제공하지 않을 때 클래스 속성에 기본값을 할당합니다. + +- **`value`**: 필드가 요청에 존재하지 않을 때 할당할 기본값. + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### 기본값이 적용되는 시점 + +기본값은 **들어온 값이 `undefined` 또는 `null`일 때만**(즉, 필드가 요청에 누락된 경우에만) 적용됩니다. 요청에서 들어온 그 외의 값은 그대로 유지됩니다. + +즉, `0`, `''`, `false`와 같은 falsy 값은 실제 입력으로 취급되어 기본값을 **트리거하지 않습니다**. + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| 들어온 `quantity` | 바인딩된 값 | +|-----------------------|----------------------| +| 누락 / `undefined` | `10` (기본값 적용) | +| `null` | `10` (기본값 적용) | +| `0` | `0` (유지) | +| `5` | `5` (유지) | + +## `@Optional()` + +`@Optional` 데코레이터는 필드를 선택 사항으로 표시하여, "required" 오류를 발생시키지 않고 필드를 생략하거나 `undefined`/`null`로 설정할 수 있도록 합니다. 필드가 누락되면 `null`로 남고, 해당 필드의 검증 규칙은 건너뜁니다. + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +여기서 누락된 `discount`는 `null`로 바인딩되고 `@Min(0)` 규칙은 건너뜁니다. `discount`가 **제공되면** 정상적으로 검증됩니다. + +## `@Default`와 `@Optional` 중 선택 + +| | 필드 누락 | 필드 제공 | +|---|---|---| +| `@Default(value)` | `value`로 바인딩 | 값 유지 및 검증 | +| `@Optional()` | `null`로 바인딩 | 값 유지 및 검증 | +| 둘 다 없음 | ` is required` 오류 | 값 유지 및 검증 | + +필드마다 **하나의** 전략을 선택하세요. + +```typescript +class Request { + // ❌ 잘못된 사용: 필드는 누락 값 전략을 하나만 사용할 수 있습니다 + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/overview.md index 70f6d5cd..9af5e4ea 100644 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -3,8 +3,44 @@ id: overview title: 개요 --- +# 데코레이터 개요 + ## 데코레이터란 무엇인가요? -express-cargo의 데코레이터는 클래스 필드에 추가하여 미들웨어에게 다음과 같은 정보를 알려줍니다 +express-cargo의 데코레이터는 클래스 필드에 추가하여 미들웨어에게 다음과 같은 정보를 알려줍니다. + - 데이터를 어디에서 추출할지 (예: body, query 등) - 어떻게 유효성 검사를 할지 - 어떻게 변환할지 + +클래스를 `bindingCargo`에 전달하면, 미들웨어는 이 데코레이터들을 읽어 `getCargo`로 가져올 수 있는 타입 안전한 객체를 생성·검증·변환합니다. + +## 데코레이터 카테고리 + +데코레이터는 바인딩 파이프라인에서 맡는 역할에 따라 그룹으로 나뉩니다. + +| 카테고리 | 목적 | 예시 | 참조 | +|----------|------|------|------| +| **Source** | 필드 값을 어디에서 가져올지 선택 | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [소스 데코레이터](./source-decorators.md) | +| **Virtual** | 다른 필드나 원본 `Request`에서 필드를 계산 | `@Virtual`, `@Request` | [가상 필드 데코레이터](./virtual.md) | +| **Transform** | 바인딩 전에 단일 필드 값을 변경 | `@Transform` | [변환 데코레이터](./transforms.md) | +| **Validation** | 필드 값에 규칙을 적용 | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [유효성 검사 데코레이터](./validators.md) | +| **Missing-value** | 필드가 없을 때의 동작을 결정 | `@Default`, `@Optional` | [필드 누락 처리](./missing-fields.md) | + +그 외 헬퍼는 **고급 사용법**에서 다룹니다. 예를 들어 타입 배열용 [`@List`](../advanced/list-decorator.md), 중첩·다형성 타입용 [`@Type`](../advanced/type-and-polymorphism.md) 등이 있습니다. + +## 데코레이터 조합 + +하나의 필드는 여러 카테고리의 데코레이터를 함께 가질 수 있습니다. 이들은 함께 읽혀 해당 필드를 바인딩·변환·검증합니다. + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source: req.body.email에서 읽기 + @Transform((value: string) => value.trim()) // Transform: 값 정규화 + @MinLength(5) // Validation: 규칙 적용 + email!: string +} +``` + +각 카테고리는 위에 링크된 개별 페이지에서 문서화되어 있습니다. diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/validators.md index 1491c81c..71db2eb5 100644 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -13,10 +13,6 @@ title: 유효성 검사 데코레이터 ## 기본 제공 유효성 검사기 (Built-in Validators) -### `@Optional()` - -해당 필드를 선택 사항으로 표시하여, 유효성 검사 오류를 발생시키지 않고 필드를 생략하거나 `undefined`로 설정할 수 있도록 합니다. - ### `@Min(value: number)` 숫자가 지정된 최소값 이상인지 검증합니다. diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/basic-usage.md index 0db57902..08d3029f 100644 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -2,5 +2,79 @@ id: basic-usage title: 기본 사용법 --- - -> Basic POST handler using bindingCargo + +# 기본 사용법 + +이 예제는 **express-cargo**의 가장 단순한 엔드투엔드 흐름을 보여줍니다. 요청 클래스를 정의하고, `bindingCargo` 미들웨어로 바인딩한 뒤, `getCargo`로 검증된 결과를 읽습니다. + +> 이 페이지는 바인딩 흐름에만 집중합니다. 프로젝트 설정(TypeScript, `tsconfig`, 의존성 설치)은 [시작하기](../getting-started.md)를 참고하세요. + +## 1. 요청 클래스 정의 + +클래스를 선언하고 소스 데코레이터(여기서는 `@Body()`)를 사용해 각 필드를 들어오는 요청의 일부에 매핑합니다. + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. 미들웨어 적용 + +요청 클래스를 `bindingCargo`에 전달하고 라우트에 미들웨어로 등록합니다. 미들웨어는 핸들러가 실행되기 전에 요청을 바인딩하고 검증합니다. + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. 바인딩된 타입 안전 객체 읽기 + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. 요청 예시 + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. 결과 예시 + +`getCargo(req)`는 값이 완전히 채워진 `CreateUserRequest` 인스턴스를 반환합니다. + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## 다음 단계 + +- 다른 소스에서 데이터 가져오기 (`@Query`, `@Header`, `@Params`) — [소스 데코레이터](../decorators/source-decorators.md) 참고 +- `@Min`, `@Email` 같은 검증 규칙 추가 — [유효성 검사 데코레이터](../decorators/validators.md) 참고 +- 중첩 객체 바인딩 — [중첩 요청 처리](./nested-request.md) 참고 +- 검증 실패 처리 — [에러 처리](./validation-errors.md) 참고 diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md index 3a48d207..d80075bd 100644 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ title: 자주 묻는 질문
Q: 특정 필드가 없을 수도 있는데, 에러를 피하려면? -**A:** **`@Optional()`** 데코레이터를 사용하세요. 해당 값이 `null`이거나 `undefined`여도 검증을 건너뛰고 정상적으로 바인딩됩니다. +**A:** **`@Optional()`** 데코레이터를 사용하세요. 해당 값이 `null`이거나 `undefined`여도 검증을 건너뛰고 정상적으로 바인딩됩니다. 대신 대체 값을 지정하려면 `@Default()`를 사용하세요. [필드 누락 처리](./decorators/missing-fields.md)를 참고하세요.
### 4. 프레임워크 호환성 diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 1b660c36..00000000 --- a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# Декоратор поля по умолчанию - -Express-Cargo предоставляет декораторы для определения значений по умолчанию для полей запроса. Декоратор автоматически присваивает значение, когда входящий запрос не предоставляет его (undefined или null). - -## Встроенный декоратор по умолчанию - -### `@Default(value: T)` - -Декоратор @Default присваивает значение по умолчанию свойству класса, когда запрос не предоставляет его. - -- **`value`**: Значение по умолчанию для присвоения, если поле отсутствует в запросе. - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index eb21e819..f4349e24 100644 --- a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## Наследование привязки +# Наследование привязки -Декораторы полей также применяются к полям, объявленным в родительских классах. -Это позволяет вам определять общие поля в **базовом классе**, а затем расширять или переопределять их в **дочерних классах**. +Декораторы полей также применяются к полям, объявленным в родительских классах. +Это позволяет определить общие поля один раз в **базовом классе** и повторно использовать их в **дочерних классах**. -### Пример +## Пример ```typescript class BaseRequest { @Body() @@ -18,9 +18,25 @@ class CreateUserRequest extends BaseRequest { } ``` -### Результат +## Результат `CreateUserRequest` будет иметь следующие поля: - id : унаследовано от `BaseRequest` - role : определено в `CreateUserRequest` + +Когда вы передаёте `CreateUserRequest` в `bindingCargo`, и унаследованное `id`, и локально объявленное `role` привязываются и проверяются вместе. + +## Повторное объявление унаследованного поля + +Повторное объявление унаследованного поля в дочернем классе **не** заменяет определение родительского класса — декораторы обоих классов объединяются на одном поле. Повторное применение декоратора источника таким образом (например, `@Body()` на поле, которое родительский класс уже получает через `@Body()`) приводит к ошибке схемы: + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +Поэтому каждое поле следует объявлять в одном классе. Чтобы изменить способ привязки или валидации поля, отредактируйте его там, где оно изначально объявлено, а не объявляйте его повторно в подклассе. + +## Примечания + +- Поля собираются по всей цепочке прототипов, поэтому поддерживается несколько уровней наследования. diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..a0d62a55 --- /dev/null +++ b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# Обработка отсутствующих полей + +Когда поле **отсутствует в запросе** (его значение `undefined` или `null`), Express-Cargo должен знать, что делать. Это поведение управляется двумя декораторами: + +- **`@Default(value)`** — подставляет запасное значение. +- **`@Optional()`** — позволяет полю оставаться пустым без ошибки «required». + +Поскольку оба решают одно и то же — что происходит при отсутствии поля — они **взаимоисключающие**. Применение обоих к одному полю вызывает ошибку схемы. + +Если у поля **нет ни одного** из этих декораторов и значение отсутствует, привязка завершается ошибкой валидации ` is required`. + +## `@Default(value: T)` + +Декоратор `@Default` присваивает значение по умолчанию свойству класса, когда запрос не предоставляет его. + +- **`value`**: Значение по умолчанию для присвоения, если поле отсутствует в запросе. + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### Когда применяется значение по умолчанию + +Значение по умолчанию применяется **только когда входящее значение равно `undefined` или `null`** (то есть поле отсутствует в запросе). Любое другое значение из запроса сохраняется как есть. + +Это означает, что falsy-значения, такие как `0`, `''` и `false`, рассматриваются как реальный ввод и **не** вызывают значение по умолчанию: + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| Входящее `quantity` | Привязанное значение | +|------------------------|--------------------------------| +| отсутствует / `undefined` | `10` (применено по умолчанию) | +| `null` | `10` (применено по умолчанию) | +| `0` | `0` (сохранено) | +| `5` | `5` (сохранено) | + +## `@Optional()` + +Декоратор `@Optional` помечает поле как необязательное, позволяя опустить его или установить в `undefined`/`null` без вызова ошибки «required». Когда поле отсутствует, оно остаётся `null`, а правила валидации для него пропускаются. + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +Здесь отсутствующее `discount` привязывается к `null`, а правило `@Min(0)` пропускается. Если `discount` **предоставлено**, оно валидируется обычным образом. + +## Выбор между `@Default` и `@Optional` + +| | Поле отсутствует | Поле предоставлено | +|---|---|---| +| `@Default(value)` | привязано к `value` | значение сохранено и валидировано | +| `@Optional()` | привязано к `null` | значение сохранено и валидировано | +| ни одного | ошибка ` is required` | значение сохранено и валидировано | + +Выбирайте **одну** стратегию на поле: + +```typescript +class Request { + // ❌ Недопустимо: поле может использовать только одну стратегию отсутствующего значения + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/overview.md index ef9a5dc4..ae88f77b 100644 --- a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -1,6 +1,41 @@ -### Что такое декораторы? +# Обзор декораторов + +## Что такое декораторы? Декораторы в express-cargo аннотируют поля класса, чтобы сообщить промежуточному ПО: - Откуда извлекать данные (например, body, query) - Как их валидировать - Как их преобразовывать + +Когда вы передаёте класс в `bindingCargo`, промежуточное ПО читает эти декораторы, чтобы построить, проверить и преобразовать типобезопасный объект, который вы получаете через `getCargo`. + +## Категории декораторов + +Декораторы сгруппированы по роли, которую они играют в конвейере привязки. + +| Категория | Назначение | Примеры | Ссылка | +|-----------|------------|---------|--------| +| **Source** | Выбирает, откуда берётся значение поля | `@Body`, `@Query`, `@Header`, `@Uri` / `@Params`, `@Session` | [Декораторы источника](./source-decorators.md) | +| **Virtual** | Вычисляет поле из других полей или из необработанного `Request` | `@Virtual`, `@Request` | [Декораторы виртуальных полей](./virtual.md) | +| **Transform** | Изменяет значение одного поля перед привязкой | `@Transform` | [Декоратор преобразования](./transforms.md) | +| **Validation** | Применяет правила к значению поля | `@Min`, `@Max`, `@Email`, `@OneOf`, … | [Декораторы валидации](./validators.md) | +| **Missing-value** | Решает, что происходит, когда поле отсутствует | `@Default`, `@Optional` | [Обработка отсутствующих полей](./missing-fields.md) | + +Дополнительные помощники описаны в разделе **Продвинутое использование**, например [`@List`](../advanced/list-decorator.md) для типизированных массивов и [`@Type`](../advanced/type-and-polymorphism.md) для вложенных и полиморфных типов. + +## Комбинирование декораторов + +Одно поле может нести декораторы из нескольких категорий. Они читаются вместе, чтобы привязать, преобразовать и проверить это поле: + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // Source: чтение из req.body.email + @Transform((value: string) => value.trim()) // Transform: нормализация значения + @MinLength(5) // Validation: применение правила + email!: string +} +``` + +Каждая категория описана на своей собственной странице, ссылки на которые приведены выше. diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/validators.md index 70a40f62..f011cb9c 100644 --- a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -6,10 +6,6 @@ Express-Cargo использует декораторы для валидаци ## Встроенные валидаторы -### `@Optional()` - -Помечает поле как необязательное, позволяя его опустить или установить в `undefined` без вызова ошибок валидации. - ### `@Min(value: number)` Проверяет, что число больше или равно указанному минимальному значению. diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/examples/basic-usage.md index 623b442a..ebfc4cc3 100644 --- a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -1 +1,75 @@ -> Базовый обработчик POST с использованием bindingCargo +# Базовое использование + +Этот пример показывает простейший сквозной поток **express-cargo**: определить класс запроса, привязать его с помощью middleware `bindingCargo` и прочитать проверенный результат через `getCargo`. + +> Эта страница посвящена только потоку привязки. Настройку проекта (TypeScript, `tsconfig`, установка зависимостей) см. в разделе [Начало работы](../getting-started.md). + +## 1. Определить класс запроса + +Объявите класс и используйте декоратор источника (здесь `@Body()`), чтобы сопоставить каждое поле с частью входящего запроса. + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. Применить middleware + +Передайте класс запроса в `bindingCargo` и зарегистрируйте его как middleware на маршруте. Middleware привязывает и проверяет запрос до выполнения вашего обработчика. + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. Прочитать привязанный типобезопасный объект + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. Пример запроса + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. Пример результата + +`getCargo(req)` возвращает полностью заполненный экземпляр `CreateUserRequest`: + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## Дальнейшие шаги + +- Получение данных из других источников (`@Query`, `@Header`, `@Params`) — см. [Декораторы источника](../decorators/source-decorators.md) +- Добавление правил валидации, таких как `@Min`, `@Email` — см. [Декораторы валидации](../decorators/validators.md) +- Привязка вложенных объектов — см. [Обработка вложенных запросов](./nested-request.md) +- Обработка ошибок валидации — см. [Обработка ошибок](./validation-errors.md) diff --git a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/faq.md index 9d69a520..e47459fc 100644 --- a/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/ru/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ sidebar_label: FAQ
В: Как избежать ошибок, если определенное поле отсутствует? -**О:** Используйте декоратор **`@Optional()`**. Поле будет успешно привязано, даже если значение равно `null` или `undefined`, пропуская валидацию для этого поля. +**О:** Используйте декоратор **`@Optional()`**. Поле будет успешно привязано, даже если значение равно `null` или `undefined`, пропуская валидацию для этого поля. Чтобы вместо этого подставить запасное значение, используйте `@Default()`. См. [Обработка отсутствующих полей](./decorators/missing-fields.md).
### 4. Совместимость фреймворков diff --git a/apps/docs/sidebars.ts b/apps/docs/sidebars.ts index 5a2324eb..360f2c89 100644 --- a/apps/docs/sidebars.ts +++ b/apps/docs/sidebars.ts @@ -32,6 +32,7 @@ const sidebars: SidebarsConfig = { 'decorators/virtual', 'decorators/transforms', 'decorators/validators', + 'decorators/missing-fields', ], }, { @@ -41,7 +42,6 @@ const sidebars: SidebarsConfig = { 'advanced/inherited-binding', 'advanced/nested-binding', 'advanced/custom-transformer', - 'advanced/default', 'advanced/list-decorator', 'advanced/type-and-polymorphism', ], From c17a8dd4cab321f4800325f5d838b0dda1a316f8 Mon Sep 17 00:00:00 2001 From: Jaehyun Yoon Date: Thu, 25 Jun 2026 23:43:15 +0900 Subject: [PATCH 2/3] fix: change korean translate --- .../current/decorators/missing-fields.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md index 6ef672b2..28d7bfd6 100644 --- a/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md +++ b/apps/docs/i18n/ko/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -12,7 +12,7 @@ title: 필드 누락 처리 둘 다 "필드가 없을 때 무엇을 할지"라는 같은 것을 결정하므로 **상호 배타적**입니다. 같은 필드에 둘을 모두 적용하면 스키마 오류가 발생합니다. -필드에 **두 데코레이터가 모두 없고** 값이 누락되면, 바인딩은 ` is required` 검증 오류로 실패합니다. +필드에 **두 데코레이터가 모두 작용되지 않고** 값이 누락되면, 바인딩은 ` is required` 검증 오류로 실패합니다. ## `@Default(value: T)` @@ -32,7 +32,7 @@ class Request { 기본값은 **들어온 값이 `undefined` 또는 `null`일 때만**(즉, 필드가 요청에 누락된 경우에만) 적용됩니다. 요청에서 들어온 그 외의 값은 그대로 유지됩니다. -즉, `0`, `''`, `false`와 같은 falsy 값은 실제 입력으로 취급되어 기본값을 **트리거하지 않습니다**. +즉, `0`, `''`, `false`와 같은 falsy 값은 실제 입력으로 취급되어 기본값을 **적용하지 않습니다.**. ```typescript class Request { @@ -76,7 +76,7 @@ class Request { ```typescript class Request { - // ❌ 잘못된 사용: 필드는 누락 값 전략을 하나만 사용할 수 있습니다 + // ❌ 잘못된 사용: 필드는 하나의 누락된 값 처리 전략만 사용할 수 있습니다. @Body() @Default(1) @Optional() From 9f9f5520917cd742c75ab55c0d4ad333d55fdb77 Mon Sep 17 00:00:00 2001 From: Jaehyun Yoon Date: Wed, 1 Jul 2026 14:13:27 +0900 Subject: [PATCH 3/3] docs: fill empty page and unify missing-value decorators for zh --- .../current/advanced/default.md | 19 ----- .../current/advanced/inherited-binding.md | 24 +++++- .../current/decorators/missing-fields.md | 80 +++++++++++++++++++ .../current/decorators/overview.md | 37 ++++++++- .../current/decorators/validators.md | 4 - .../current/examples/basic-usage.md | 76 +++++++++++++++++- .../current/faq.md | 2 +- 7 files changed, 212 insertions(+), 30 deletions(-) delete mode 100644 apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/default.md create mode 100644 apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/missing-fields.md diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/default.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/default.md deleted file mode 100644 index 0be55860..00000000 --- a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/default.md +++ /dev/null @@ -1,19 +0,0 @@ -# 默认字段装饰器 - -Express-Cargo 提供用于为请求字段定义默认值的装饰器。当传入请求没有提供某个值(undefined 或 null)时,该装饰器会自动赋予默认值。 - -## 内置默认值装饰器 - -### `@Default(value: T)` - -当请求没有提供某个类属性时,@Default 装饰器会为该属性赋予默认值。 - -- **`value`**:当请求中不存在该字段时要赋予的默认值。 - -```typescript -class Request { - @Body() - @Default(1) - price!: number; -} -``` diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md index 1716f5f8..afbbc8a6 100644 --- a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md +++ b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced/inherited-binding.md @@ -1,9 +1,9 @@ -## 继承绑定 +# 继承绑定 字段装饰器也会应用到父类中声明的字段。 -这使你可以在**基类**中定义通用字段,然后在**子类**中扩展或覆盖它们。 +这使你可以在**基类**中一次性定义通用字段,并在多个**子类**中复用它们。 -### 示例 +## 示例 ```typescript class BaseRequest { @@ -19,10 +19,26 @@ class CreateUserRequest extends BaseRequest { } ``` -### 结果 +## 结果 `CreateUserRequest` 将包含以下字段: - id:继承自 `BaseRequest` - role:定义在 `CreateUserRequest` 中 + +当你将 `CreateUserRequest` 传给 `bindingCargo` 时,继承而来的 `id` 和本地声明的 `role` 会一起被绑定和验证。 + +## 重新声明继承的字段 + +在子类中重新声明一个继承的字段并**不会**替换父类的定义——两个类的装饰器会被合并到同一个字段上。以这种方式重复应用来源装饰器(例如在父类已用 `@Body()` 作为来源的字段上再次使用 `@Body()`)会产生 schema 错误: + +``` +Update.id: @body + @body cannot be combined; pick a single source +``` + +因此每个字段都应当只在一个类中声明。要更改某个字段的绑定或验证方式,请在它最初声明的位置修改,而不是在子类中重新声明。 + +## 注意事项 + +- 字段会沿整个原型链收集,因此支持多层继承。 diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/missing-fields.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/missing-fields.md new file mode 100644 index 00000000..4324b464 --- /dev/null +++ b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/missing-fields.md @@ -0,0 +1,80 @@ +# 处理缺失字段 + +当某个字段在**请求中缺失**(其值为 `undefined` 或 `null`)时,Express-Cargo 需要知道该如何处理。有两个装饰器可以控制这种行为: + +- **`@Default(value)`** —— 填充一个后备值。 +- **`@Optional()`** —— 允许字段保持为空,而不引发“必填”错误。 + +由于二者决定的是同一件事——字段缺失时会发生什么——所以它们是**互斥的**。同时把两者应用到一个字段上会抛出 schema 错误。 + +如果某个字段**两个装饰器都没有**且值缺失,则绑定会以 ` is required` 验证错误失败。 + +## `@Default(value: T)` + +`@Default` 装饰器会在请求未提供某个类属性时,为其赋一个默认值。 + +- **`value`**:当字段在请求中不存在时要赋的默认值。 + +```typescript +class Request { + @Body() + @Default(1) + price!: number; +} +``` + +### 何时应用默认值 + +只有当**传入值为 `undefined` 或 `null`**(即字段在请求中缺失)时才会应用默认值。来自请求的任何其他值都会原样保留。 + +这意味着诸如 `0`、`''` 和 `false` 之类的假值会被视为真实输入,**不会**触发默认值: + +```typescript +class Request { + @Body() + @Default(10) + quantity!: number +} +``` + +| 传入的 `quantity` | 绑定后的值 | +|-----------------------|------------------------| +| 缺失 / `undefined` | `10`(应用默认值) | +| `null` | `10`(应用默认值) | +| `0` | `0`(保留) | +| `5` | `5`(保留) | + +## `@Optional()` + +`@Optional` 装饰器将字段标记为可选,允许它被省略或设置为 `undefined`/`null`,而不会触发“必填”错误。当字段缺失时,它会保持为 `null`,并跳过该字段上的验证规则。 + +```typescript +class Request { + @Body() + @Min(0) + @Optional() + discount?: number +} +``` + +这里缺失的 `discount` 会绑定为 `null`,并跳过 `@Min(0)` 规则。如果 `discount` **确实**被提供,则会正常验证。 + +## 在 `@Default` 与 `@Optional` 之间选择 + +| | 字段缺失 | 字段已提供 | +|---|---|---| +| `@Default(value)` | 绑定为 `value` | 保留并验证该值 | +| `@Optional()` | 绑定为 `null` | 保留并验证该值 | +| 都没有 | ` is required` 错误 | 保留并验证该值 | + +每个字段只选择**一种**策略: + +```typescript +class Request { + // ❌ 无效:一个字段只能使用一种缺失值策略 + @Body() + @Default(1) + @Optional() + price!: number +} +``` diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/overview.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/overview.md index a021d6bf..6808269f 100644 --- a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/overview.md +++ b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/overview.md @@ -1,7 +1,42 @@ -### 什么是装饰器? +# 装饰器概览 + +## 什么是装饰器? express-cargo 中的装饰器用于标注类字段,告诉中间件: - 从哪里提取数据(例如 body、query) - 如何验证数据 - 如何转换数据 + +当你将一个类传给 `bindingCargo` 时,中间件会读取这些装饰器,构建、验证并转换出一个类型安全的对象,你可以通过 `getCargo` 获取它。 + +## 装饰器分类 + +装饰器按照它们在绑定流程中所扮演的角色进行分组。 + +| 分类 | 用途 | 示例 | 参考 | +|-----------------|---------------------------------------------|--------------------------------------------------------------|---------------------------------------------------| +| **来源** | 选择字段值的来源 | `@Body`、`@Query`、`@Header`、`@Uri` / `@Params`、`@Session` | [来源装饰器](./source-decorators.md) | +| **虚拟** | 从其他字段或原始 `Request` 计算出字段 | `@Virtual`、`@Request` | [虚拟字段装饰器](./virtual.md) | +| **转换** | 在绑定前修改单个字段的值 | `@Transform` | [转换装饰器](./transforms.md) | +| **验证** | 对字段值强制执行规则 | `@Min`、`@Max`、`@Email`、`@OneOf`、… | [验证装饰器](./validators.md) | +| **缺失值** | 决定字段缺失时的处理方式 | `@Default`、`@Optional` | [处理缺失字段](./missing-fields.md) | + +更多辅助装饰器在 **进阶用法** 中介绍,例如用于类型化数组的 [`@List`](../advanced/list-decorator.md),以及用于嵌套和多态类型的 [`@Type`](../advanced/type-and-polymorphism.md)。 + +## 组合使用装饰器 + +单个字段可以同时携带来自多个分类的装饰器。它们会被一起读取,用于绑定、转换和验证该字段: + +```typescript +import { Body, Transform, MinLength } from 'express-cargo' + +class CreateUserRequest { + @Body('email') // 来源:从 req.body.email 读取 + @Transform((value: string) => value.trim()) // 转换:规范化值 + @MinLength(5) // 验证:强制执行规则 + email!: string +} +``` + +上面链接的每个分类都有各自的文档页面。 diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/validators.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/validators.md index 830b2827..52be5dd8 100644 --- a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/validators.md +++ b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/decorators/validators.md @@ -6,10 +6,6 @@ Express-Cargo 使用装饰器验证绑定到类的传入请求数据。 ## 内置验证器 -### `@Optional()` - -将字段标记为可选,允许该字段被省略或设置为 `undefined`,且不会触发验证错误。 - ### `@Min(value: number)` 验证数字大于或等于指定的最小值。 diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/examples/basic-usage.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/examples/basic-usage.md index a1abfabe..732aaca0 100644 --- a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/examples/basic-usage.md +++ b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/examples/basic-usage.md @@ -1 +1,75 @@ -> 使用 bindingCargo 的基础 POST 处理器 +# 基础用法 + +本示例展示 **express-cargo** 最简单的端到端流程:定义一个请求类,用 `bindingCargo` 中间件进行绑定,然后用 `getCargo` 读取已验证的结果。 + +> 本页仅聚焦于绑定流程。关于项目搭建(TypeScript、`tsconfig`、安装依赖),请参见[快速开始](../getting-started.md)。 + +## 1. 定义请求类 + +声明一个类,并使用来源装饰器(这里是 `@Body()`)将每个字段映射到传入请求的某个部分。 + +```typescript +// create-user.request.ts +import { Body } from 'express-cargo' + +export class CreateUserRequest { + @Body('name') + name!: string + + @Body('email') + email!: string +} +``` + +## 2. 应用中间件 + +将请求类传给 `bindingCargo`,并把它注册为路由上的中间件。中间件会在你的处理器运行之前绑定并验证请求。 + +```typescript +import express from 'express' +import { bindingCargo, getCargo } from 'express-cargo' +import { CreateUserRequest } from './create-user.request' + +const app = express() +app.use(express.json()) + +app.post('/users', bindingCargo(CreateUserRequest), (req, res) => { + // 3. 读取已绑定的、类型安全的对象 + const user = getCargo(req) + + res.json({ + message: 'User created!', + data: user, + }) +}) +``` + +## 3. 请求示例 + +```json +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +``` + +## 4. 结果示例 + +`getCargo(req)` 返回一个完整填充的 `CreateUserRequest` 实例: + +```json +{ + "message": "User created!", + "data": { + "name": "Jane Doe", + "email": "jane@example.com" + } +} +``` + +## 后续步骤 + +- 从其他来源提取数据(`@Query`、`@Header`、`@Params`)——参见[来源装饰器](../decorators/source-decorators.md) +- 添加 `@Min`、`@Email` 等验证规则——参见[验证装饰器](../decorators/validators.md) +- 绑定嵌套对象——参见[处理嵌套请求](./nested-request.md) +- 处理验证失败——参见[错误处理](./validation-errors.md) diff --git a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq.md b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq.md index 77c3fffb..cc3c45ec 100644 --- a/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq.md +++ b/apps/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq.md @@ -69,7 +69,7 @@ sidebar_label: 常见问题
Q: 如果某个字段缺失,如何避免错误? -**A:** 使用 **`@Optional()`** 装饰器。即使该字段值为 `null` 或 `undefined`,也可以成功绑定,并跳过该字段的验证。 +**A:** 使用 **`@Optional()`** 装饰器。即使该字段值为 `null` 或 `undefined`,也可以成功绑定,并跳过该字段的验证。如果想改为填充一个后备值,请使用 `@Default()`。参见[处理缺失字段](./decorators/missing-fields.md)。
### 4. 框架兼容性