Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions apps/docs/docs/advanced/default.md

This file was deleted.

24 changes: 20 additions & 4 deletions apps/docs/docs/advanced/inherited-binding.md
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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.
80 changes: 80 additions & 0 deletions apps/docs/docs/decorators/missing-fields.md
Original file line number Diff line number Diff line change
@@ -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 `<field> 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 | `<field> 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
}
```
37 changes: 36 additions & 1 deletion apps/docs/docs/decorators/overview.md
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 0 additions & 4 deletions apps/docs/docs/decorators/validators.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
76 changes: 75 additions & 1 deletion apps/docs/docs/examples/basic-usage.md
Original file line number Diff line number Diff line change
@@ -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<CreateUserRequest>(req)

res.json({
message: 'User created!',
data: user,
})
})
```

## 3. Example Request

```json
{
"name": "Jane Doe",
"email": "jane@example.com"
}
```

## 4. Example Result

`getCargo<CreateUserRequest>(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)
2 changes: 1 addition & 1 deletion apps/docs/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Additionally, the `reflect-metadata` package must be installed to read type info
<details>
<summary><b>Q: How do I avoid errors if a specific field is missing?</b></summary>

**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).
</details>

### 4. Framework Compatibility
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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.
Loading
Loading