Skip to main content
Version: 4.0.0-preview

Status code

StatusCode is the canonical enum that ExpressoTS uses everywhere a status code crosses a boundary: controller responses, AppError constructors, @Http() decorators, exception filters. It maps the standard RFC 9110 status codes to typed numbers, eliminating the magic-number sprawl that creeps into Express apps.

import { StatusCode } from "@expressots/core";

StatusCode.OK; // 200
StatusCode.Created; // 201
StatusCode.NoContent; // 204
StatusCode.BadRequest; // 400
StatusCode.Unauthorized; // 401
StatusCode.Forbidden; // 403
StatusCode.NotFound; // 404
StatusCode.Conflict; // 409
StatusCode.UnprocessableEntity; // 422
StatusCode.TooManyRequests; // 429
StatusCode.InternalServerError; // 500
StatusCode.BadGateway; // 502
StatusCode.ServiceUnavailable; // 503

Five families:

  • 1xx: informational responses (rarely used directly)
  • 2xx: successful responses
  • 3xx: redirection
  • 4xx: client errors
  • 5xx: server errors

See the MDN status code reference for the full meaning of each code.

Where to use it

@Http(...) decorator

The cleanest pattern: declare the success status next to the route, return the body, and let the framework do the wiring.

import { controller, Get, Http, Post, body } from "@expressots/adapter-express";
import { StatusCode } from "@expressots/core";

@controller("/users")
export class UsersController {
@Post("/")
@Http(StatusCode.Created)
create(@body() dto: CreateUserDTO) {
return this.users.create(dto);
}

@Get("/:id")
@Http(StatusCode.OK)
getById(@param("id") id: string) {
return this.users.findById(id);
}
}

Direct res.status(...)

When you need to choose the status at runtime (e.g. 200 vs 201 vs 304):

import { controller, Get, response } from "@expressots/adapter-express";
import { StatusCode } from "@expressots/core";
import type { Response } from "express";

@controller("/cache")
export class CacheController {
@Get("/:key")
fetch(@response() res: Response, @param("key") key: string) {
const cached = this.cache.get(key);
if (cached === undefined) {
return res.status(StatusCode.NotFound).send();
}
if (cached.notModified) {
return res.status(StatusCode.NotModified).send();
}
return res.status(StatusCode.OK).json(cached.value);
}
}

Inside AppError and Report.Error()

Errors thrown anywhere in the request pipeline carry a status code through to the response:

import { AppError, NotFoundError, StatusCode } from "@expressots/core";

if (!user) {
throw new NotFoundError(`User ${id} not found`, "users.get");
}

if (await this.users.findByEmail(email)) {
throw new AppError(StatusCode.Conflict, "Email already in use", "users.create");
}

// Or with the Report helper:
throw this.report.Error("User already exists", StatusCode.BadRequest, "users.create");

The exception filter pipeline turns these into RFC 7807 problem-details responses with the right status code automatically. See Error handling.

Inside use cases

import { provide, Report, StatusCode } from "@expressots/core";

@provide(CreateUserUseCase)
export class CreateUserUseCase {
constructor(
private readonly users: UserRepository,
private readonly report: Report,
) {}

async execute(data: ICreateUserRequestDTO): Promise<ICreateUserResponseDTO> {
const { name, email } = data;

if (await this.users.findByEmail(email)) {
throw this.report.Error(
"User already exists",
StatusCode.BadRequest,
"users.create",
);
}

const user = await this.users.create(new User(name, email));
return { id: user.id, name: user.name, email: user.email, status: "success" };
}
}

Why use the enum?

  • Refactor-safe: your IDE rejects typos.
  • Searchable: StatusCode.Conflict is greppable; 409 is not.
  • Consistent: the framework also uses StatusCode in its built-in errors and filters, so your code matches its output.

See also