Dinner validation

Request Body Validation

Dinner uses the OpenAPI schema under requestBody as the contract for req.body. If the JSON body does not match, the request stops with a 400 response before the controller action runs.

The YAML Dinner Reads

Validation starts at the operation's JSON body schema. Dinner resolves that schema, converts it for Ajv, compiles it once for the route, and runs it against req.body.

request body shape
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
requestBody

Marks the operation as accepting a body.

application/json

Provides the schema Dinner uses for JSON payloads.

schema

Defines the Ajv validation contract for req.body.

Inline Request Body Schema

For small routes or examples, put the schema directly under schema. This object is the shape Ajv checks before Dinner calls the controller.

server/openapi/users.yaml
# server/openapi/users.yaml
paths:
  /users:
    post:
      x-controller: user.controller
      x-action: create
      summary: Create a user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
              additionalProperties: false
      responses:
        '201':
          description: Created

Reference a Named Schema

In real routes, keep the operation focused on routing and point schema at a named entry under components.schemas. Dinner resolves the reference, then Ajv validates req.body against the resolved schema.

Route references the schema

server/openapi/users.yaml
# server/openapi/users.yaml
paths:
  /users:
    post:
      x-controller: user.controller
      x-action: create
      summary: Create a user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: Created

Components define the schema

server/openapi/components.yaml
# server/openapi/components.yaml
components:
  schemas:
    CreateUserRequest:
      type: object
      required:
        - email
        - password
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8
      additionalProperties: false

The $ref value points to the schema name in components.schemas. It is not the body being validated; it is the schema Dinner gives to Ajv. If the route and components live in different files, load them through Stitch so Dinner receives one OpenAPI document.

Controller Contract

Controllers should treat schema validation as transport validation. Keep business rules in your application logic, but do not duplicate basic required-field, type, format, and range checks in every action.

server/controller/user.controller.ts
// server/controller/user.controller.ts
import { Component } from "@noego/ioc";

@Component()
export default class UserController {
  async create({ body }: { body: { email: string; password: string } }) {
    // Dinner already rejected malformed bodies before this method runs.
    return {
      created: true,
      email: body.email
    };
  }
}

Useful Schema Rules

required

Reject bodies missing fields the controller depends on.

additionalProperties: false

Reject unknown keys instead of silently accepting loose payloads.

format

Validate strings such as email or uri when ajv_formats is enabled.

minLength / maximum

Keep simple boundary checks in the API contract.

enum

Constrain fixed values such as status or sort direction.

pattern

Apply regex checks for fields that have a known text shape.

Invalid Body Response

When Ajv rejects the body, Dinner responds with the route, method, and schema that failed. The controller is not called.

400 response
{
  "error": true,
  "message": "Invalid request body",
  "requirements": {
    "type": "object",
    "required": ["email", "password"],
    "properties": {
      "email": { "type": "string", "format": "email" },
      "password": { "type": "string", "minLength": 8 }
    },
    "additionalProperties": false
  },
  "validation_schema": {
    "type": "object",
    "required": ["email", "password"]
  },
  "path": "/users",
  "method": "post",
  "statusCode": 400
}

Path Constraints Are Route Matching

Request body validation uses Ajv schemas. Path constraints decide whether a URL matches a route before body validation is relevant.

Use the Dinner routes guide for path tokens, route regex, case-sensitive matching, optional segments, splats, and extracted req.params.

Manual Dinner Server Setup

This setup is for manual or standalone @noego/dinner usage. In an @noego/app project, App owns Dinner bootstrapping through bootBackend().

Add JSON parsing before Dinner handles routes. Enable ajv_formats when schemas use string formats such as email.

server/main.ts
import express from "express";
import { Server } from "@noego/dinner";

const app = express();
app.use(express.json());

// Manual/standalone Dinner setup only. @noego/app already wires this pattern for app projects.
await Server.createServer({
  server: app,
  openapi: "server/openapi.yaml",
  controllers_base_path: "server/controller",
  controller_args_provider: async (req, res) => ({
    body: req.body,
    params: req.params,
    query: req.query,
    req,
    res
  }),
  controller_builder: async (Controller) => new Controller(),
  ajv_formats: true
});
NoEgo

© 2025 NoEgo. All rights reserved.