# SoapJS

SoapJS is a lightweight, flexible framework for building applications with clean architecture — strong separation of concerns, minimal boilerplate. Define entities, use cases, repositories, CQRS, events and auth, and let the framework wire the HTTP, DI and persistence around them.

### What you can build

* **Clean architecture** out of the box — `domain / application / data / api` layers
* **Pick your complexity** — a plain `controller → use case → repository` for simple features, or full **CQRS** with commands, queries, **domain events** and read-model **projections** for complex ones
* **Ports & adapters** — swap MongoDB, SQL, or an in-memory fake without touching your domain
* **JWT / local auth**, middleware, and **WebSockets** via plugins

### See it in action

The **Comics Universe demo** (Marvel & DC) is the canonical, always-current reference — from simple CRUD to event-driven CQRS with a WebSocket live feed: 👉 [github.com/soapjs/soap-node-demo](https://github.com/soapjs/soap-node-demo)

### Next

Quick Start · Clean Architecture · Components · Plugins


# Clean Architecture

## **What is Clean Architecture?**

Clean Architecture is a design philosophy aimed at separating the concerns of software components to enhance maintainability, scalability, and understandability. The main goal is to decouple the business logic of an application from its external elements, such as frameworks and databases, allowing for more flexible and modular development. This approach leads to a system where the core business rules are isolated from external influences, making the code more reusable, testable, and adaptable to changes in technology or business requirements.

## **Benefits of Using Clean Architecture**

Implementing Clean Architecture in your projects comes with multiple advantages:

* **Flexibility**: The separation of concerns facilitates easier changes in the software environment or business policies without affecting the core logic.
* **Maintainability**: Decoupled components are easier to manage, understand, and debug.
* **Testability**: Independent business logic allows for more straightforward unit testing since it's not entangled with external dependencies.
* **Reusability**: Core components can be reused across different projects or within different parts of the same project.
* **Scalability**: Clean architecture makes it easier to scale the application both in terms of functionality and team size.

## **Components of Clean Architecture**

In the context of SoapJS, Clean Architecture is implemented through the following components:

* **Entity**: Represents the application's business data and rules. Entities are the most inner part of the architecture, which means they are independent of database models, external services, or any other external element.
* **Model**: These are data structures used to transfer data between layers (e.g., from a database to business logic). Models are typically derived from entities or transformed into entities.
* **Mapper**: A component responsible for converting data between entities and models. It usually contains two main methods: `toEntity()`, which maps data models to entities, and `fromEntity()`, which does the reverse.
* **Source**: This represents a specific implementation of a data client or provider, such as a database or an external service. It defines how data is fetched or stored.
* **Repository**: Acts as an intermediary between the domain layer and data mapping layers. It uses a Source to interact with data storage and returns data in the form of entities.
* **RepositoryImpl**: The concrete implementation of a repository, containing logic for working with data sources and mappers to deliver and manipulate data as entities or perform other operations.
* **Service**: Functions as a bridge to external services or data sources, unlike repositories which are tied to specific data collections. A Service can facilitate communication with third-party services or various data sources, providing an abstraction layer that allows the domain to interact with the outside world without direct dependency on external systems.
* **Use Case**: These are classes that contain specific business logic and operations. They should have an `execute` method and can access repositories and other components but should remain independent of other application layers.
* **Controller**: Serves as the entry point for business logic, receiving external data (e.g., from a web request), converting it to a format the application can use, and returning the output. Controllers should handle only minimal logic, delegating complex tasks to use cases.
* **Route and RouteIO**: In the context of web frameworks, these components handle the interaction between the framework and the application's controllers. They manage the conversion of incoming requests into the appropriate format for the controllers and then convert the controllers' output back into responses suitable for the outside world.

These components work together to ensure that the application's core functionality remains isolated from external changes and frameworks, thereby adhering to the principles of Clean Architecture. This modular structure allows developers to build, test, and maintain each piece independently, improving the overall quality and durability of the software.


# Quick Start

SoapJS is a lightweight, framework-agnostic **library** for building applications with clean architecture — entities, use cases, repositories, CQRS, auth — with minimal boilerplate. There is no scaffolding CLI: the framework is **convention-driven**, so you (or your AI assistant) simply follow the patterns.

The canonical, always-up-to-date reference is the **Comics Universe demo**: [github.com/soapjs/soap-node-demo](https://github.com/soapjs/soap-node-demo) — a full app from simple CRUD to event-driven CQRS with WebSockets.

### 1. Install

```bash
# core + HTTP + MongoDB
npm install @soapjs/soap @soapjs/soap-express @soapjs/soap-node-mongo reflect-metadata express

# optional: auth
npm install @soapjs/soap-auth bcryptjs
```

Current versions: `@soapjs/soap` 0.10, `@soapjs/soap-express` 0.3, `@soapjs/soap-node-mongo` 0.7, `@soapjs/soap-auth` 0.4.

### 2. Enable decorators

SoapJS uses decorators, so enable them in `tsconfig.json` and import `reflect-metadata` once at your entry point:

```json
{
  "compilerOptions": {
    "target": "ES2021",
    "module": "commonjs",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true
  }
}
```

### 3. A minimal app

```typescript
import 'reflect-metadata';
import { bootstrap, Controller, Get } from '@soapjs/soap-express';
import { Request, Response } from 'express';

@Controller('/hello')
class HelloController {
  @Get('/')
  async hello(_req: Request, res: Response): Promise<void> {
    res.json({ message: 'Hello from SoapJS' });
  }
}

bootstrap({
  port: 3000,
  controllers: [HelloController],
  middleware: { cors: true, helmet: true, logging: true },
  healthCheck: true,
});
```

`bootstrap()` wires Express, middleware, routing, optional auth and CQRS in one call. Run it and hit `http://localhost:3000/hello`.

### 4. Follow the pattern

SoapJS doesn't generate code for you — it gives you the building blocks and a clear structure to follow. Organize features by **dependency direction**:

```
src/
├── config/        ← env + composition root (dependency wiring)
├── common/        ← shared-kernel + cross-cutting
└── features/
    └── <feature>/
        ├── domain/        ← entities, value objects, domain events
        ├── application/   ← use cases, commands/queries, ports
        ├── data/          ← adapters: repository impls, mappers
        └── api/           ← controllers, event consumers, sockets
```

Pick the ceremony that fits each feature: a plain `controller → use case → repository` for simple CRUD, or full CQRS with commands, queries, events and read-model projections for complex domains. The [demo](https://github.com/soapjs/soap-node-demo) shows both side by side.

### Next steps

* **Components** — entities, repositories, controllers, and more
* **Database Interaction Strategies** — Where, RepositoryQuery, native queries
* **Plugins** — MongoDB, Express, auth, and others


# Components

In this section, we'll delve into the intricacies of the different components that form the backbone of our architecture, all based on TypeScript code. Each component plays a pivotal role in the clean and efficient functioning of our application, adhering to the principles of Clean Architecture.

We'll explore each component in detail, understanding its purpose, structure, and how it interacts within the broader system. This deep dive will not only help you grasp the significance of each component but also guide you in implementing them effectively in your TypeScript projects.

From entities that represent the business rules and data structures, to use cases that encapsulate specific business logic, our journey will cover the entire spectrum. We'll also look into repositories, services, and controllers, understanding their roles in data manipulation, business logic execution, and client communication, respectively.

Understanding these components is crucial for maintaining a scalable, maintainable, and testable codebase. Let's embark on this journey to master the building blocks of our system, ensuring you have all the knowledge needed to tailor and enhance your application's architecture.


# Entity

## Entity

An **entity** is a plain TypeScript class representing a domain object — your business model, free of any framework or database concerns. It lives in a feature's `domain/` folder. You write it by hand (SoapJS has no scaffolding CLI):

```typescript
export type CharacterUniverse = 'marvel' | 'dc';
export type CharacterAlignment = 'hero' | 'villain' | 'anti-hero';

export class Character {
  constructor(
    public readonly id: string,
    public readonly name: string,
    public readonly universe: CharacterUniverse,
    public readonly alignment: CharacterAlignment,
    public readonly createdAt: Date,
    public readonly updatedAt: Date,
  ) {}
}
```

### Validated factories

Keep invariants in the domain: expose a `static create()` that validates untrusted input and returns a `Result`, reserving the constructor for trusted rehydration (the mapper, seeds):

```typescript
import { Result, Failure } from '@soapjs/soap';

static create(props: CharacterProps): Result<Character> {
  if (!props.name?.trim()) {
    return Result.withFailure(Failure.fromError(new ValidationError('name is required')));
  }
  return Result.withSuccess(new Character(/* ...validated fields */));
}
```

Entities are pure — no decorators, no DB types. The database shape lives in a Model; a Mapper converts between the two. See the [demo](https://github.com/soapjs/soap-node-demo) for full examples.


# Model

## Model

A **model** (document / DTO) is the **persistence shape** of your data — how a record looks in the database. It's kept separate from the domain Entity so storage concerns never leak into your business model; a Mapper converts between them. Write it by hand in the feature's `data/` folder:

```typescript
// data/character.dto.ts — the MongoDB document shape
export interface CharacterDocument {
  _id: string;
  name: string;
  universe: string;
  alignment: string;
  createdAt: Date;
  updatedAt: Date;
}
```

### Entity vs Model

|               | Entity                    | Model (document/DTO) |
| ------------- | ------------------------- | -------------------- |
| Layer         | `domain/`                 | `data/`              |
| Purpose       | business rules + behavior | storage shape        |
| Knows the DB? | no                        | yes (e.g. `_id`)     |
| Defined as    | class                     | interface / type     |


# Repository

A **repository** is the domain-facing data-access boundary. Use cases and handlers depend on a tech-free **port**; an **adapter** implements it for a specific database. Swap databases by writing a new adapter — the domain never changes.

### The pattern, by hand

SoapJS is convention-driven (no scaffolding CLI) — you write these pieces directly. The [demo](https://github.com/soapjs/soap-node-demo) is the full reference; here's the shape.

#### 1. Port — the contract + DI token

```typescript
import { Result } from '@soapjs/soap';
import { Character } from '../../domain/character.entity';

export abstract class CharacterRepository {
  static readonly Token = 'CharacterRepository';
  abstract add(...characters: Character[]): Promise<Result<Character[]>>;
  abstract findById(id: string): Promise<Result<Character | null>>;
  abstract findAll(filters?: CharacterFilters): Promise<Result<Character[]>>;
}
```

#### 2. Adapter — the implementation

Extend `ReadWriteRepository` and implement the port. Build queries with `Where` — **no database syntax leaks into the adapter**:

```typescript
import { ReadWriteRepository, FindParams, Where } from '@soapjs/soap';

export class CharacterRepositoryMongo
  extends ReadWriteRepository<Character, CharacterDocument>
  implements CharacterRepository {

  async findById(id: string): Promise<Result<Character | null>> {
    const r = await this.find(FindParams.create({ where: new Where().valueOf('_id').isEq(id), limit: 1 }));
    return r.isFailure() ? Result.withFailure(r.failure) : Result.withSuccess(r.content[0] ?? null);
  }

  async findAll(filters: CharacterFilters = {}): Promise<Result<Character[]>> {
    const where = new Where();
    if (filters.universe) where.valueOf('universe').isEq(filters.universe);
    return this.find(FindParams.create({ where, sort: { name: 1 } }));
  }
}
```

`ReadWriteRepository` provides `add`, `find`, `count`, `update`, `remove`. Compose queries with `Where` + `FindParams` / `UpdateParams` / `RemoveParams`.

#### 3. Wire it in the composition root

```typescript
const repo = new CharacterRepositoryMongo(
  new DatabaseContext(new MongoSource(soapMongo, 'characters'), new CharacterMapper(), soapMongo.sessions),
);
container.bindValue(CharacterRepository.Token, repo);
```

Handlers inject the **port**, never the adapter:

```typescript
@Inject(CharacterRepository.Token) private readonly repo: CharacterRepository;
```

### Querying

* **Where conditions** — typed repo methods (*Using Where Conditions*)
* **RepositoryQuery** — named, reusable query objects (*Using QueryBuilder*)
* **Native queries** — the `Source.native()` escape hatch (*Using Native Queries*)

### CQRS read/write split

For CQRS, expose **two** ports — a read repository and a write repository — backed by separate connections (replica vs primary). See the [demo](https://github.com/soapjs/soap-node-demo).


# Source

A **Source** is SoapJS's low-level data-access boundary — one per database collection/table. Adapters and repositories build on it. MongoDB's implementation is `MongoSource`.

> In current code the interface is `Source<DocumentType>` and the MongoDB class is `MongoSource`. (Earlier versions called this component a "collection".)

### Creating a Source

```typescript
import { SoapMongo, MongoSource } from '@soapjs/soap-node-mongo';

const soapMongo = await SoapMongo.create(mongoConfig);
const source = new MongoSource(soapMongo, 'characters');
```

You rarely use a Source directly — wrap it in a `DatabaseContext` with a mapper and hand it to a repository:

```typescript
import { DatabaseContext } from '@soapjs/soap';

const context = new DatabaseContext(source, new CharacterMapper(), soapMongo.sessions);
const repo = new CharacterRepositoryMongo(context);
```

### The Source contract

```typescript
interface Source<DocumentType> {
  find(query?): Promise<DocumentType[]>;
  count(query?): Promise<number>;
  aggregate<T>(query): Promise<T[]>;
  insert(query): Promise<DocumentType[]>;
  update(query): Promise<UpdateStats>;
  remove(query): Promise<RemoveStats>;
  native<ResultType>(query): Promise<ResultType>;   // since @soapjs/soap 0.10 / soap-node-mongo 0.7
}
```

* `find` / `count` accept `FindParams` / `CountParams` (built with `Where`) or a `RepositoryQuery`.
* `aggregate` runs a portable `AggregationParams`.
* `native()` is the **escape hatch** for queries the abstraction can't express — it runs a client-specific query verbatim (see *Using Native Queries*).

### Read/write split

Give each side its own connection — read Source on a replica, write Source on the primary:

```typescript
const writeSource = new MongoSource(soapMongoWrite, 'characters'); // primary
const readSource  = new MongoSource(soapMongoRead, 'characters');  // secondaryPreferred
```

See the [demo](https://github.com/soapjs/soap-node-demo) for a full read/write split.


# Mapper

A **mapper** converts between a domain Entity and its persistence Model, keeping storage details out of the domain. Implement the `Mapper<EntityType, ModelType>` interface in the feature's `data/` folder.

```typescript
import { Mapper } from '@soapjs/soap';
import { Character } from '../domain/character.entity';
import { CharacterDocument } from './character.dto';

export class CharacterMapper implements Mapper<Character, CharacterDocument> {
  toEntity(doc: CharacterDocument): Character {
    return new Character(doc._id, doc.name, doc.universe, doc.alignment, doc.createdAt, doc.updatedAt);
  }

  toModel(entity: Character): CharacterDocument {
    return {
      _id: entity.id,
      name: entity.name,
      universe: entity.universe,
      alignment: entity.alignment,
      createdAt: entity.createdAt,
      updatedAt: entity.updatedAt,
    };
  }
}
```

* `toEntity(model)` — DB document → domain entity (e.g. `_id` → `id`)
* `toModel(entity)` — domain entity → DB document

The mapper is handed to a `DatabaseContext` alongside a Source:

```typescript
const context = new DatabaseContext(source, new CharacterMapper(), soapMongo.sessions);
```


# Use Case

A **use case** (interactor) holds one piece of application logic. It depends on ports (repositories, services), exposes an `execute()` returning a `Result`, and is called directly by a controller — no CQRS bus. This is the "simple path"; for complex domains use CQRS commands/queries instead.

Mark it `@UseCase()` (makes it injectable) and give it a `Token`:

```typescript
import { UseCase } from '@soapjs/soap-express';
import { Inject, Result } from '@soapjs/soap';

@UseCase()
export class CreateComicUseCase {
  static readonly Token = 'CreateComicUseCase';

  constructor(@Inject(ComicRepository.Token) private readonly repo: ComicRepository) {}

  async execute(input: CreateComicInput): Promise<Result<Comic>> {
    const created = Comic.create({ id: randomUUID(), ...input });
    if (created.isFailure()) return Result.withFailure(created.failure);

    const result = await this.repo.add(created.content);
    return result.isFailure() ? Result.withFailure(result.failure) : Result.withSuccess(result.content[0]);
  }
}
```

Bind it in the composition root, then inject it into a controller:

```typescript
container.bindClass(CreateComicUseCase.Token, CreateComicUseCase);

// in the controller:
@Inject(CreateComicUseCase.Token) private readonly createComic: CreateComicUseCase;
const result = await this.createComic.execute(input);
```


# Controller

A **controller** is the HTTP entry point. In soap-express it is **decorator-based**: `@Controller` sets the base path, route decorators map methods to endpoints, and `ResultMapper` turns a `Result` into an HTTP response. Controllers live in a feature's `api/` folder and depend on use cases or CQRS buses.

```typescript
import { Controller, Get, Post, AdminOnly, ResultMapper } from '@soapjs/soap-express';
import { Inject } from '@soapjs/soap';
import { Request, Response } from 'express';

@Controller('/comics')
export class ComicsController {
  constructor(
    @Inject(ListComicsUseCase.Token) private readonly listComics: ListComicsUseCase,
    @Inject(CreateComicUseCase.Token) private readonly createComic: CreateComicUseCase,
  ) {}

  @Get('/')
  async list(req: Request, res: Response): Promise<void> {
    const result = await this.listComics.execute({ publisher: req.query.publisher as string });
    ResultMapper.toResponse(result, res);
  }

  @Post('/')
  @AdminOnly()
  async create(req: Request, res: Response): Promise<void> {
    const result = await this.createComic.execute(req.body);
    ResultMapper.toResponse(result, res, { successStatus: 201 });
  }
}
```

Register it in `bootstrap()`:

```typescript
await bootstrap({ controllers: [ComicsController], container });
```

Decorators:

* **Routes:** `@Get` `@Post` `@Put` `@Delete` `@Patch` `@Head` `@Options`
* **Auth:** `@Auth(strategy)` `@AdminOnly()` `@RolesOnly(roles)` `@SelfOnly()` `@Public()`

A controller can also dispatch CQRS commands/queries via `CommandBus` / `QueryBus` instead of calling use cases directly — see CQRS & Events.


# Routing

SoapJS offers **two ways** to define HTTP endpoints. They share the **same execution semantics**, so pick whichever style you prefer — don't mix three:

1. **Decorators** — routes declared on controller methods (familiar, co-located).
2. **Fluent Router** — routes defined centrally in one place.

Both support pure use-case handlers and `RouteIO` transformation, and both map a `Result` to HTTP the same way.

### Style 1 — Decorators

```typescript
import { Controller, Get, Post, AdminOnly, ResultMapper } from '@soapjs/soap-express';

@Controller('/comics')
export class ComicsController {
  constructor(@Inject(ListComicsUseCase.Token) private readonly listComics: ListComicsUseCase) {}

  @Get('/')
  async list(req: Request, res: Response): Promise<void> {
    const result = await this.listComics.execute({ publisher: req.query.publisher as string });
    ResultMapper.toResponse(result, res);
  }

  @Post('/')
  @AdminOnly()
  async create(req: Request, res: Response): Promise<void> { /* ... */ }
}

await bootstrap({ controllers: [ComicsController], container });
```

Route decorators: `@Get` `@Post` `@Put` `@Delete` `@Patch` `@Head` `@Options`. Auth: `@Auth(strategy)` `@AdminOnly()` `@RolesOnly(roles)` `@SelfOnly()` `@Public()`.

### Style 2 — Fluent Router

Define routes centrally by extending `ExpressRouter`, then register it on the app:

```typescript
import { ExpressRouter } from '@soapjs/soap-express';

class ComicsRouter extends ExpressRouter {
  setupRoutes() {
    this.get('/comics', (req, res) => this.listComics.execute({ publisher: req.query.publisher }));
    this.post('/comics', (req, res) => this.createComic.execute(req.body));
  }
}

const router = new ComicsRouter().initialize();
app.registerRouter(router);
```

Chainable per route: `.useCase(UseCaseClass)`, `.routeIO(IO)`, `.cors()`, `.auth()`, `.rateLimit()`, `.validate(schema)`.

### Pure handlers with use cases

In both styles a route can delegate straight to a use case — the handler receives a plain `input` and returns a `Result`, never touching `req`/`res`:

```typescript
// Decorators:
@Get('/')
@CallUseCase(ListComicsUseCase)
@RouteIO(new PaginationIO())
list() {}                       // body intentionally empty

// Fluent:
this.get('/comics', () => {}).useCase(ListComicsUseCase).routeIO(new PaginationIO());
```

The framework runs: `routeIO.from(req)` → `useCase.execute(input)` → response.

### RouteIO — request/response transformation

A `RouteIO` keeps HTTP shaping out of your handlers:

* `from(req)` — request → use-case **input**
* `to(result, res)` — `Result` → HTTP response

Built-ins: **`PaginationIO`** (page/limit/sort/filters → paginated envelope), **`SimpleIO`** (body in, `content` out), **`FileUploadIO`**. Or pass inline `{ from, to }` mappings to `@RouteIO`.

```typescript
export class PaginationIO implements ExpressIO {
  from(req) { return { page: +req.query.page || 1, limit: +req.query.limit || 10 }; }
  to(result, res) { /* success → envelope; failure → ResultMapper */ }
}
```

### Response handling (consistent across both styles)

Since `@soapjs/soap-express` **0.4.0**, both routing styles dispatch results the same way:

* a **failed** `Result` → always `ResultMapper` (consistent status mapping, e.g. `ValidationError` → 400), even when a RouteIO is set
* a **successful** `Result` **with** a RouteIO → `routeIO.to` (custom shape)
* a **successful** `Result` **without** a RouteIO → `ResultMapper`
* a raw value → `res.json`; a handler that wrote the response itself → no-op

So you can always `return Result.withSuccess(...)` (let the framework respond) or call `ResultMapper.toResponse(result, res)` yourself — both are supported.


# Dependencies

The `Dependencies` component in a SoapJS project plays a crucial role in managing dependencies and ensuring that components such as use cases, repositories, and controllers are correctly instantiated and available throughout your application. This component is automatically generated during the initial project setup when executing the `soap new project` command, but only if the user selects an option for inversion of control (IoC) like the Soap Singleton Container or Inversify.

### Key Features of the Dependencies Component:

* **Automatic Updates**: Each time a new domain layer component (e.g., use case, repository, controller) that requires injection is added via the CLI, the `configure` method within the `Dependencies` component is updated to include this new component. This ensures that all necessary dependencies are correctly managed and injected where needed.
* **IoC Framework Integration**: Depending on the selected IoC option during project setup, the `Dependencies` component will be structured to align with that particular framework's requirements. For example, if Inversify is selected, the configuration will adhere to Inversify's binding and scope mechanisms.
* **Centralized Dependency Management**: By centralizing dependency configuration, the `Dependencies` component simplifies the management of application-wide services, repositories, and controllers, making the codebase more maintainable and scalable.

### Example Configure Method in Dependencies Component:

{% tabs %}
{% tab title="TypeScript" %}

```typescript
export class Dependencies {
  public async configure(container: Container) {
    container
      .bind<ProcessOrderUseCase>(ProcessOrderUseCase.Token)
      .to(ProcessOrderUseCase);
    container
      .bind<CustomerController>(CustomerController.Token)
      .to(CustomerController);
    // Example of setting up dependencies for Inversify:    
    // Binding domain layer components:
    // This code is updated automatically as you add new components.
    const context = {
      collection: new CustomerMongoCollection(mongoClient),
      mapper: new CustomerMongoMapper(),
      queries: new QueryFactory()
    }
    const impl = new CustomerRepositoryImpl(context);
    container.bind<CustomerRepository>(CustomerRepository.Token).toConstantValue(impl);
    container.bind<CustomerController>(CustomerController.Token).to(CustomerController);
  }
}
```

{% endtab %}
{% endtabs %}

In this example, the `configure` method sets up the bindings between interfaces (such as `CustomerRepository`) and their implementations (like `CustomerRepositoryImpl`). This approach decouples the creation and usage of objects, adhering to the principles of IoC and dependency inversion.

By leveraging the `Dependencies` component, SoapJS projects maintain a clean separation of concerns, promoting a more organized and testable codebase. This component ensures that all parts of the application can access the dependencies they require without directly managing their lifecycles, leading to more robust and modular software.


# Service

A **service** is an outbound integration — a client for an external system (a REST API, email, payments, a message broker). Like a repository, it sits behind a **port**: your use cases depend on the tech-free contract, and a concrete adapter makes the call. Swap providers by swapping the adapter; the domain never changes.

### Port — the contract + DI token

Define the port as an abstract class with a static `Token`, so the type and the DI token share one name and handlers never import the adapter:

```typescript
import { Result } from '@soapjs/soap';

export abstract class WeatherService {
  static readonly Token = 'WeatherService';
  abstract getByCity(city: string): Promise<Result<Weather>>;
}
```

### Adapter — the implementation

The adapter performs the actual call and maps the response into your domain, returning a `Result`. It lives in the feature's `data/` folder and is the only place that knows the external API:

```typescript
import { Result, Failure } from '@soapjs/soap';

export class OpenWeatherService implements WeatherService {
  constructor(private readonly apiKey: string) {}

  async getByCity(city: string): Promise<Result<Weather>> {
    try {
      const res = await fetch(`https://api.example.com/weather?q=${city}&key=${this.apiKey}`);
      if (!res.ok) return Result.withFailure(Failure.fromError(new Error(`Weather API ${res.status}`)));
      const data = await res.json();
      return Result.withSuccess(new Weather(data.city, data.tempC));
    } catch (error) {
      return Result.withFailure(Failure.fromError(error));
    }
  }
}
```

### Wire it in the composition root

```typescript
container.bindValue(WeatherService.Token, new OpenWeatherService(config.weatherApiKey));
```

Inject the **port** wherever you need it — a use case or a command handler:

```typescript
constructor(@Inject(WeatherService.Token) private readonly weather: WeatherService) {}

const result = await this.weather.getByCity('Gotham');
```

### Service vs Repository

Both are outbound ports + adapters; the difference is intent:

|          | Repository                       | Service                       |
| -------- | -------------------------------- | ----------------------------- |
| Talks to | your own data store              | an external system / API      |
| Returns  | your entities                    | data mapped into your domain  |
| Built on | `ReadWriteRepository` + a Source | a plain class (or any client) |

Because a service is just a port, it's trivial to stub in tests — bind an in-memory fake under the same `Token`.


# CQRS & Events

SoapJS ships first-class **CQRS** — commands, queries and domain events — wired through your DI container by decorators. It's **opt-in**: enable it with `bootstrap({ cqrs: true })`. For simple features you can skip it entirely and use a plain `controller → use case → repository` flow.

* **Commands** change state (create/update/delete) and return a `Result`.
* **Queries** read state and return a `Result`.
* **Domain events** are facts ("something happened") that any number of consumers can react to.

### Commands

Define a command (a message) and a handler:

```typescript
import { BaseCommand } from '@soapjs/soap/cqrs';
import { CommandHandler } from '@soapjs/soap-express/cqrs';
import { Inject, Result } from '@soapjs/soap';

export class CreateUserCommand extends BaseCommand {
  constructor(public readonly email: string) { super(); }
}

@CommandHandler(CreateUserCommand)
export class CreateUserHandler {
  constructor(@Inject('UserRepository') private readonly repo: UserRepository) {}

  async handle(cmd: CreateUserCommand): Promise<Result<User>> {
    // ...validate, build the entity, persist
    return Result.withSuccess(user);
  }
}
```

### Queries

```typescript
import { BaseQuery } from '@soapjs/soap/cqrs';
import { QueryHandler } from '@soapjs/soap-express/cqrs';

export class GetUserQuery extends BaseQuery<User | null> {
  constructor(public readonly id: string) { super(); }
}

@QueryHandler(GetUserQuery)
export class GetUserHandler {
  constructor(@Inject('UserRepository') private readonly repo: UserRepository) {}
  async handle(query: GetUserQuery): Promise<Result<User | null>> {
    return this.repo.findById(query.id);
  }
}
```

### Wiring & dispatching

With `cqrs: true`, `bootstrap()` binds a `CommandBus` and a `QueryBus` in the container and registers every decorated handler. **Handlers register when their files are imported**, so make sure they're loaded (e.g. imported in your composition root) before bootstrap runs.

```typescript
import { CommandBus, QueryBus } from '@soapjs/soap/cqrs';

@Controller('/users')
export class UsersController {
  constructor(
    @Inject('CommandBus') private readonly commandBus: CommandBus,
    @Inject('QueryBus') private readonly queryBus: QueryBus,
  ) {}

  @Post('/')
  async create(req: Request, res: Response): Promise<void> {
    const result = await this.commandBus.dispatch(new CreateUserCommand(req.body.email));
    ResultMapper.toResponse(result, res, { successStatus: 201 });
  }
}
```

```typescript
await bootstrap({ controllers: [UsersController], container, cqrs: true });
```

### Domain Events

Define events as facts (extend `BaseDomainEvent`) and react with `@EventHandler`:

```typescript
import { BaseDomainEvent } from '@soapjs/soap/domain';
import { EventHandler, IEventHandler } from '@soapjs/soap-express/cqrs';

export class UserCreatedEvent extends BaseDomainEvent<{ id: string; email: string }> {
  constructor(user: User) {
    super('user.created', user.id, { id: user.id, email: user.email });
  }
}

@EventHandler(UserCreatedEvent)
export class SendWelcomeEmail implements IEventHandler<UserCreatedEvent> {
  async handle(event: UserCreatedEvent): Promise<void> {
    // ...send the email
  }
}
```

A command handler publishes events after it succeeds (inject your event bus and call `publish`).

> **Multiple handlers per event (fan-out).** Since `@soapjs/soap-express` 0.3.1 the default DI token is `EventHandler:<eventName>:<handlerClass>`, so several handlers can subscribe to the **same** event without overwriting each other. Pass `{ token }` to override it.

> **Events are not auto-wired.** `bootstrap({ cqrs: true })` wires the command and query **buses**, but `@EventHandler` only *records* consumers in `DecoratorRegistry` — it does not connect them to a bus. You wire the event bus yourself (this is intentional: you choose in-memory, Kafka, RabbitMQ, etc.).

A minimal in-memory wiring, mirroring `wireCqrs`:

```typescript
import { DecoratorRegistry } from '@soapjs/soap-express';

export function wireEvents(container: DIContainer, bus: InMemoryEventBus): void {
  DecoratorRegistry.getEventHandlers().forEach((meta) => {
    if (!container.has(meta.token)) container.bindClass(meta.token, meta.handlerClass);
    bus.subscribe(meta.eventType, container.get(meta.token));
  });
}
```

### Read models & projections

Domain events are the natural way to keep a **read model** in sync: a projector consumes events and updates a denormalized store; queries read the projection instead of aggregating the write store on every request. See the [demo](https://github.com/soapjs/soap-node-demo) for a complete commands → events → projection → WebSocket flow.

### Guidelines

* One handler per command/query; **many** handlers per event.
* Keep handlers thin — orchestrate the domain, return a `Result`.
* Commands read-before-write on the primary for strong consistency; route read queries to a replica if you use a read/write split.


# Examples

Welcome to the foundational guide on building a simple API with SoapJS. Here, we'll walk through the essential components and steps to create a streamlined, efficient, and clean API. This tutorial is designed to provide you with a clear path from setup to execution, ensuring you grasp the core concepts and practical applications of our framework. Let's embark on this journey to simplicity and clarity in API development.


# Simple API

## Simple API

The full, runnable example now lives in the **Comics Universe demo**:

👉 [**github.com/soapjs/soap-node-demo**](https://github.com/soapjs/soap-node-demo)

A complete SoapJS application — Marvel & DC heroes and villains — that grows from a simple `controller → use case → repository` feature (comics) to full event-driven CQRS with a read-model projection and a WebSocket live feed (characters), plus JWT auth and a MongoDB read/write split.

Clone it, then `npm install` → `npm run seed` → `npm run dev`, and read it as the canonical reference for how the pieces fit together.


# Database Interaction Strategies

SoapJS gives you several ways to query — from simple typed conditions to raw native queries. Pick the lightest one that expresses your need:

1. **Advanced Query Composition with the Where Builder** **Introduction:** Compose type-safe, storage-agnostic conditions (`valueOf().isEq()`, `and`/`or`, `brackets`) inside typed repository methods. Your default for most queries.
2. **Reusable Queries with RepositoryQuery** **Introduction:** Wrap complex or reused queries in named `RepositoryQuery` objects whose `build()` returns portable `FindParams` — query logic as first-class, testable units.
3. **Dedicated Repository Methods** **Introduction:** Expose intent-revealing methods (`findThreats()`) on the repository, keeping call sites clean and the query in one place.
4. **Native Queries** **Introduction:** For queries the abstraction can't express (aggregations, vendor operators, raw SQL), drop to `Source.native()` — kept named and quarantined in a `RepositoryQuery`, never smuggled through casts.

The progression goes composed conditions → reusable query objects → repository abstraction → raw native execution: increasing power, decreasing portability.


# Using Where Conditions

The `Where` builder composes **storage-agnostic** conditions — no database syntax in your code. The Source translates a `Where` into the underlying query (e.g. a MongoDB filter), so swapping databases doesn't touch your repository.

```typescript
import { Where, FindParams } from '@soapjs/soap';

const where = new Where()
  .valueOf('universe').isEq('marvel')
  .and.valueOf('alignment').isIn(['villain', 'anti-hero']);

await repo.find(FindParams.create({ where, sort: { name: 1 }, limit: 20 }));
```

### Operators

* equality: `isEq` / `isNotEq`
* ranges: `isLt` `isLte` `isGt` `isGte`
* membership: `isIn` / `isNotIn`
* pattern: `like`
* logical: `and` · `or` · grouping: `brackets(w => ...)`

### Grouping with brackets

Use `brackets` for precedence — e.g. a text search across two fields, AND a universe:

```typescript
const where = new Where()
  .valueOf('universe').isEq('marvel')
  .and.brackets(w => w.valueOf('name').like(q).or.valueOf('alias').like(q));
```

Inside a repository method this keeps the adapter free of any Mongo/SQL syntax:

```typescript
async findAll(filters: CharacterFilters): Promise<Result<Character[]>> {
  const where = new Where();
  if (filters.universe) where.valueOf('universe').isEq(filters.universe);
  return this.find(FindParams.create({ where }));
}
```


# Using Native Queries

When a query can't be expressed with the `Where` builder or a `RepositoryQuery` (aggregations, geo queries, full-text ranking, vendor-specific operators, raw SQL), SoapJS gives you a **sanctioned escape hatch**: `Source.native()`.

Instead of reaching around the repository to the raw database driver, you wrap the native query in a **named `RepositoryQuery`** and hand it to the source. The query stays quarantined in one place, the rest of your code keeps depending on the abstraction.

> Available since `@soapjs/soap` 0.10.0 (interface) and `@soapjs/soap-node-mongo` 0.7.0 (MongoDB implementation).

### The pattern

`Source.native()` executes a client-specific query **verbatim**. The payload is produced by a `RepositoryQuery`'s `build()` — so the dialect (a MongoDB pipeline, a SQL string) lives in a named query object, not scattered through your code.

```typescript
interface Source<DocumentType> {
  // ...find / count / aggregate / update / insert / remove
  native<ResultType = DocumentType[]>(query: DbQuery | RepositoryQuery): Promise<ResultType>;
}
```

`native()` **bypasses the query factory and the entity mapper** — it returns the raw result from the driver. You map it yourself if you need domain entities.

### Example — a MongoDB aggregation

Define the native query as a `RepositoryQuery`. Its `build()` returns the raw MongoDB pipeline:

```typescript
import { RepositoryQuery } from '@soapjs/soap';

export interface UniverseStatRow {
  _id: { universe: string; alignment: string };
  count: number;
}

export class UniverseStatsAggregation extends RepositoryQuery<Record<string, unknown>[]> {
  build(): Record<string, unknown>[] {
    return [
      { $group: { _id: { universe: '$universe', alignment: '$alignment' }, count: { $sum: 1 } } },
      { $sort: { '_id.universe': 1, '_id.alignment': 1 } },
    ];
  }
}
```

Run it from a repository method through the source — no raw `Collection` handle:

```typescript
async statsByUniverse(): Promise<Result<UniverseStat[]>> {
  const rows = await this.context.source.native<UniverseStatRow[]>(new UniverseStatsAggregation());
  return Result.withSuccess(
    rows.map((r) => ({ universe: r._id.universe, alignment: r._id.alignment, count: r.count })),
  );
}
```

#### How the MongoDB source dispatches the payload

`MongoSource.native()` recognizes the shape produced by `build()`:

| `build()` returns                           | Runs as                                       |
| ------------------------------------------- | --------------------------------------------- |
| an **array**                                | aggregation pipeline (`collection.aggregate`) |
| `{ kind: 'aggregate', pipeline, options? }` | aggregation                                   |
| `{ kind: 'find', filter, options? }`        | `collection.find`                             |
| `{ kind: 'command', command }`              | `db.command`                                  |

### Other databases

`native()` is part of the `Source` contract, so every adapter implements it for its own dialect. A SQL source, for example, would accept a `RepositoryQuery` whose `build()` returns a parameterized statement:

```typescript
export class TopSellersQuery extends RepositoryQuery<{ sql: string; params: unknown[] }> {
  build() {
    return { sql: 'SELECT * FROM orders WHERE total > $1 ORDER BY total DESC', params: [1000] };
  }
}
```

### Guidelines

* **Keep it named and quarantined.** A native query belongs in a dedicated `RepositoryQuery` and is called from one repository method — never smuggled through `as any` in a generic query.
* **It is dialect-specific by design.** Swapping the database means rewriting the native query object; the repository method signature and the rest of your code stay the same.
* **It returns raw rows.** `native()` skips the mapper — map to domain entities in the repository method if needed.
* **Reach for it last.** Prefer `Where` (see *Using Where Conditions*) and `RepositoryQuery` (see *Using QueryBuilder*); use `native()` only when the abstraction genuinely can't express the query.


# Using RepositoryQuery

A `RepositoryQuery` is a **named, reusable query object**. Extend it, build a query in `build()`, and pass it to the repository's `find()`. This decouples query construction from call sites and turns complex/reused queries into first-class, testable units.

```typescript
import { RepositoryQuery, FindParams, Where } from '@soapjs/soap';

export class ThreatsQuery extends RepositoryQuery<FindParams> {
  constructor(private readonly universe?: string) { super(); }

  build(): FindParams {
    const where = new Where()
      .brackets(w => w.valueOf('alignment').isEq('villain').or.valueOf('alignment').isEq('anti-hero'));
    if (this.universe) where.and.valueOf('universe').isEq(this.universe);
    return FindParams.create({ where, sort: { name: 1 } });
  }
}
```

```typescript
const result = await repo.find(new ThreatsQuery('dc'));
```

`build()` returns portable `FindParams` (a `Where` + paging/sort) — no database syntax — so the same query object works across storages. For queries this can't express, see Using Native Queries.

> Note: this class was called `QueryBuilder` in older versions; the current name is `RepositoryQuery`.


# Using Dedicated Repository Method

In some cases, when the number of conditions or query variations is limited, it's reasonable to create dedicated methods in the repository that directly use the database client. This approach reduces the need for a large number of methods and simplifies the repository interface. Let's see how to create a dedicated repository method:

### Creating a Dedicated Repository Method

Suppose we have a UserRepository responsible for interacting with the user data in the database. We want to create a method to find users by their age.

1. **Define UserRepository Method**:

```typescript
import { Repository, RepositoryImpl, Result } from '@soapjs/soap';
import { User } from '../path/to/user';

/**
 * UserRepository class responsible for user data operations.
 */
class UserRepository extends RepositoryImpl<User> implements Repository<User> {
  /**
   * Find users by their age.
   * @param {number} age - The age of the users to find.
   * @returns {Promise<Result<User[]>>} - A Promise resolving to an array of users.
   */
  async findUsersByAge(age: number): Promise<Result<User[]>> {
    // Constructing SQL query to find users by age
    const query = `SELECT * FROM users WHERE age = ?`;

    // Executing query using database client
    return await this.context.collection.query(query, [age]);
  }
}
```

2. **Using the Repository Method**:

Now, let's see how to use this repository method to find users by their age:

```typescript
import { UserRepository } from '../path/to/user-repository';

// Creating an instance of UserRepository
const userRepository = new UserRepository(databaseClient);

// Using the repository method to find users by age
const users = await userRepository.findUsersByAge(30);
```

### Conclusion

Creating dedicated methods in the repository tailored to specific query needs can simplify the codebase and provide a more intuitive interface for interacting with the database. This approach is suitable when the number of query variations is limited, and there's a clear separation of concerns between the repository and the database client.


# Plugins


# Node.js


# Soap Express

This page and the plugin itself are still in development, a lot will change.

`@soapjs/soap-express` is the HTTP layer for SoapJS on Express — controllers, routing, middleware, auth guards, and CQRS wiring — assembled with a single `bootstrap()` call.

> Current version: **0.3.x**. Requires `@soapjs/soap` ≥ 0.10.

### Install

```bash
npm install @soapjs/soap @soapjs/soap-express reflect-metadata express
```

### Bootstrap

`bootstrap()` wires Express, middleware, routing, auth and CQRS in one call:

```typescript
import 'reflect-metadata';
import { bootstrap } from '@soapjs/soap-express';

const app = await bootstrap({
  port: 3000,
  container,                 // your pre-wired DIContainer
  controllers: [CharactersController, AuthController],
  middleware: { cors: true, helmet: true, logging: true, compression: true },
  auth: jwtStrategy,         // guards @Auth() routes
  cqrs: true,                // wire CommandBus + QueryBus
  healthCheck: true,
});
```

### Controllers & routes

```typescript
import { Controller, Get, Post, Auth, AdminOnly, ResultMapper } from '@soapjs/soap-express';

@Controller('/characters')
export class CharactersController {
  constructor(@Inject('QueryBus') private readonly queryBus: QueryBus) {}

  @Get('/')
  async list(req: Request, res: Response): Promise<void> {
    const result = await this.queryBus.dispatch(new ListCharactersQuery());
    ResultMapper.toResponse(result, res);
  }

  @Post('/')
  @AdminOnly()
  async create(req: Request, res: Response): Promise<void> { /* ... */ }
}
```

Route decorators: `@Get` `@Post` `@Put` `@Delete` `@Patch` `@Head` `@Options`. Auth decorators: `@Auth(strategy)` `@AdminOnly()` `@RolesOnly(roles)` `@SelfOnly()` `@Public()`.

### Authentication

Strategies come from `@soapjs/soap-auth`. Register them on the app, and pass one as `auth:` in `bootstrap()` to guard `@Auth()` routes:

```typescript
app.registerAuth(soapAuth);            // all HTTP strategies from a soap-auth provider
app.registerAuthStrategy(strategy);    // ...or a single strategy
```

### CQRS & Events

With `cqrs: true`, `bootstrap()` binds `CommandBus` + `QueryBus` and registers every `@CommandHandler` / `@QueryHandler`. Controllers inject the buses and dispatch. Domain events use `@EventHandler` — note they are **registered but not auto-wired**; you connect your own event bus. Full details: CQRS & Events.

### Errors → HTTP

`ResultMapper.toResponse(result, res)` maps a `Result` failure to a status code. Register custom mappings at startup:

```typescript
ResultMapper.register('ValidationError', 400);
ResultMapper.register('DomainRuleError', 422);
```

### Full reference

A complete app (controllers, CQRS, events, auth, sockets): the [Comics Universe demo](https://github.com/soapjs/soap-node-demo).


# Soap MongoDB

This page and the plugin itself are still in development, a lot will change.

`@soapjs/soap-node-mongo` is the MongoDB integration: a `Source` implementation (`MongoSource`), a query factory, and connection management.

> Current version: **0.7.x**. Peer deps: `@soapjs/soap` ≥ 0.10, `mongodb`.

### Install

```bash
npm install @soapjs/soap-node-mongo mongodb
```

### Connect

```typescript
import { SoapMongo, MongoConfig, MongoSource } from '@soapjs/soap-node-mongo';

const config = new MongoConfig('mydb', ['localhost'], ['27017'], user, password);
const soapMongo = await SoapMongo.create(config);
```

### Source

`MongoSource` implements the full `Source` contract and is wrapped in a `DatabaseContext` for a repository:

```typescript
import { DatabaseContext } from '@soapjs/soap';

const source = new MongoSource(soapMongo, 'characters');
const context = new DatabaseContext(source, new CharacterMapper(), soapMongo.sessions);
```

Methods: `find` · `count` · `aggregate` · `insert` · `update` · `remove` · `native`.

### Queries

* **`find` / `count`** take `FindParams` / `CountParams` built with `Where`; the query factory translates `Where` into a MongoDB filter — no Mongo syntax in your repo.
* **`aggregate`** runs a portable `AggregationParams` (`groupBy`, `sum`, `where`, …).

### Native queries

For pipelines, commands, or operators the abstraction can't express, use `native()` with a `RepositoryQuery` whose `build()` returns the raw payload:

| `build()` returns                    | Runs as              |
| ------------------------------------ | -------------------- |
| an array                             | aggregation pipeline |
| `{ kind: 'find', filter, options? }` | `collection.find`    |
| `{ kind: 'command', command }`       | `db.command`         |

See Using Native Queries.

### Read/write split

Set `readPreference` in the connection pool config and use a separate `MongoSource` per connection (read replica vs primary). See the [demo](https://github.com/soapjs/soap-node-demo).


# Soap Auth

`@soapjs/soap-auth` provides authentication strategies and a small manager that wires them into your app. It integrates with `@soapjs/soap-express` in one call.

> Current version: **0.4.x**.

### Strategies

* **JWT** — bearer-token auth (issue + verify access/refresh tokens)
* **Local** — username/password, typically issuing a JWT on login
* **API key**, **Basic**, **OAuth2** (and hybrid OAuth2)

### Install

```bash
npm install @soapjs/soap-auth bcryptjs
```

### Configure

`SoapAuth` holds your strategies, grouped by transport category (`http`, `socket`, …) and exposes `getStrategy(name, type)` / `listStrategies(type)`:

```typescript
import { SoapAuth } from '@soapjs/soap-auth';

const soapAuth = new SoapAuth({
  http: {
    jwt:   { /* access token issuer + user.fetchUser */ },
    local: { /* extractCredentials + verifyCredentials + routes */ },
  },
});
```

See the [demo's `auth.setup.ts`](https://github.com/soapjs/soap-node-demo) for full, working JWT + Local configuration.

### Integrate with soap-express

Register every HTTP strategy on the app, then guard routes with the auth decorators:

```typescript
app.registerAuth(soapAuth);   // pulls all http strategies from SoapAuth
```

```typescript
@Controller('/auth')
export class AuthController {
  @Post('/login')
  async login(req: Request, res: Response): Promise<void> {
    const result = await this.localStrategy.login(req as any);   // verifies + issues JWT
    res.json({ user: result.user, tokens: result.tokens });
  }

  @Get('/me')
  @Auth('jwt')                 // requires a valid JWT
  async me(req: Request, res: Response): Promise<void> { /* ... */ }
}
```

Route guards: `@Auth('jwt')`, `@AdminOnly()`, `@RolesOnly(roles)`, `@SelfOnly()`, `@Public()`.

### Login flow

1. `POST /auth/login` → the Local strategy verifies credentials and issues a JWT.
2. Client sends `Authorization: Bearer <token>` on subsequent requests.
3. `@Auth('jwt')` routes validate the token and attach the user.


# Dart


# Soap Dart

This page and the plugin itself are still in development, a lot will change.

WIP


# Rust

WIP


# Troubleshooting and Support

In this section, you will find a compilation of common issues faced by SoapJS framework users, along with their respective solutions. This is intended to help you resolve problems quickly and efficiently. Topics range from installation and configuration errors to common runtime exceptions. If you encounter an issue not covered here, please refer to the next sections for guidance on how to seek further assistance.

## **Reporting Problems**

If you encounter a problem with the SoapJS framework that is not addressed in the documentation or the common issues section, please report it to our support team. Include detailed information about the problem, including the steps to reproduce it, error messages, and your environment setup. This information will help us understand and resolve the issue more effectively. You can report problems via our GitHub issues.


# Community and Contributions

## **How to Contribute**

The SoapJS framework thrives on community contributions. Whether you're fixing a bug, adding a feature, writing documentation, or providing translations, your contributions are welcome. This section guides you through the contribution process, including how to submit pull requests, adhere to coding standards, and participate in the community discussions. We strive to create an inclusive and respectful environment for all contributors.

## **Community Guidelines**

We believe in fostering a positive and welcoming community. This section outlines our community guidelines, which include expectations for behavior, how to communicate effectively, and how to create a supportive environment. By participating in the SoapJS community, you agree to abide by these guidelines and help maintain a constructive space for everyone.

## **Support Us**

Developing and maintaining an open-source project requires time and resources. If you find the SoapJS framework useful, consider supporting the project. This section provides information on how you can help, whether through direct donations, sponsoring specific features, or contributing your time and skills to the project.


# Contact us

For any inquiries, suggestions, or feedback, please do not hesitate to contact us. Whether you're seeking help, reporting a bug, or just want to share your experience with SoapJS, we're here to listen and assist.

<radoslaw.kamysz@gmail.com>


# Sponsors / Partners

Join our mission to revolutionize the way APIs are built and maintained. By becoming a valued member of our community, you're not just sponsoring a project; you're supporting the spread of clean architecture principles, enabling developers worldwide to create more modular, maintainable, and scalable web applications. Help us shape the future of software development and foster a community dedicated to learning, sharing, and innovation.

[**Become a Sponsor**](https://opencollective.com/soapjs)


# Release Notes

## Release Notes

Versioning is per package. Most recent first.

### @soapjs/soap

#### 0.10.0

* **feat(data):** `Source.native<T>(query)` — a sanctioned escape hatch that runs a native, client-specific query (a MongoDB pipeline, a SQL string, …) produced by a `RepositoryQuery`'s `build()`, bypassing the query factory and mapper.
* **breaking:** `native()` is a required method on the `Source` interface — adapters (`soap-node-*`) must implement it. Hence the minor bump.

### @soapjs/soap-node-mongo

#### 0.7.0

* **feat(source):** implement `MongoSource.native()` — dispatches by payload shape (array → aggregation pipeline; `{ kind: 'find' | 'aggregate' | 'command' }`).
* **chore:** bump `@soapjs/soap` peer dependency to `^0.10.0`.

### @soapjs/soap-express

#### 0.4.0

* **feat(routing):** unified route execution — both endpoint styles (decorators and the fluent `Router`) now share one dispatch path with identical semantics. A **failed `Result` always goes through `ResultMapper`** (consistent status mapping, e.g. `ValidationError` → 400) even when a `RouteIO` is set; a successful `Result` with a `RouteIO` is shaped by `routeIO.to`; everything else falls back to `ResultMapper` / `res.json`.
* **fix(route-io):** `PaginationIO` / `FileUploadIO` / `SimpleIO` delegate failures to `ResultMapper` instead of emitting a hardcoded 400/500.
* **fix(di):** use cases referenced by a route are resolved by `.Token ?? class name` (previously class name only).
* **refactor:** removed the duplicated per-request execution logic shared by the decorator and fluent-router paths.

#### 0.3.2

* **docs:** added a CQRS & Events section, documented `registerAuth(provider)`, and refreshed the README to the current `bootstrap()`-based API.

#### 0.3.1

* **fix(events):** `@EventHandler`'s default DI token now includes the handler class name (`EventHandler:<event>:<handler>`), so multiple handlers can fan out on the same event instead of silently overwriting each other.
* **feat(app):** `SoapExpressApp.registerAuth(provider)` registers all HTTP strategies from a soap-auth-compatible provider, with no hard dependency on soap-auth.

### @soapjs/soap-auth

#### 0.4.0

* Strategies: JWT, Local, API key, Basic, OAuth2 (+ hybrid), managed by the `SoapAuth` class; integrates with soap-express via `app.registerAuth(soapAuth)`.


