Skip to content
tutorial

Set up and define routes ​

The init callback ​

Register all custom routes inside the init callback. The callback receives the baseline instance as first parameter.

js
const app = baseline({
  root: resolve(import.meta.dirname, ".."),
  init: async ({api}) => {
    // Register your routes here
    api.get("magic", ...);
  },
});

app.init();

Routing Stack Order

Always define routes inside the init callback. Defining routes after app.init() has finished will not work. See Advanced: Routing

Create API routes ​

Use the HTTP method helpers to define your endpoints. Note that all routes defined via baseline.api are automatically prefixed with /api.

js
// Defines a POST endpoint at /api/magic
baseline.api.post("magic", async (req, res) => {
  return res.status(200).send({
    success: true,
  });
});

Validate request data with schemas ​

Baseline uses Zod for all schema definitions. Defining schema.body, schema.query, or schema.path automatically injects validation middleware and populates the req.validated object:

  • req.validated.body
  • req.validated.query
  • req.validated.path

Schemas also enable automatic generation of an interactive API reference at /api/docs.

Useful documentation for creating schema objects:

js
// Defines a POST endpoint at /api/user/:id/magic with validation
baseline.api.post(
  "user/:id/magic",
  {
    schema: {
      tags: ["magic"],
      description: "Perform some magic",
      path: z.object({
        id: z.string()
      }),
      body: z.object({
        spell: z.string(),
      }),
      response: {
        200: {
          description: "OK",
          content: {
            "application/json": {
              schema: z.object({
                success: z.boolean(),
              }),
            },
          },
        },
      },
    },
  },
  async (req, res) => {
    // Access validated and typed data directly from req.validated
    const { id } = req.validated.path; 

    return res.status(200).send({
      success: true,
    });
  },
);

The schema object is passed through to the OpenAPI Schema Object, with the exception of some helpers that make writing a schema a bit easier:

  • schema.body is transformed to schema.requestBody
    • if you define both, they will get merged
  • schema.query is transformed to schema.requestParams.query
  • schema.path is transformed to schema.requestParams.path
  • schema.response is transformed to schema.responses

Boolean Parameter Parsing

To enable automatic boolean parsing for query or path parameters, use z.stringbool in your schema. See Boolean Parameter Parsing.

Secure your routes ​

All API routes require authentication by default. To make an endpoint public, set auth: false in the options object. For more details, see authentication.

js
// Public route: no authentication required
baseline.api.get("/health", { auth: false }, (req, res) => {
  return res.send({ status: "ok" });
});

Use middleware ​

Middleware offered by baseline can be accessed at baseline.api.middleware.

js
//Example: automatically fetching an entity from a path parameter
const { getEntityFromPath } = baseline.api.middleware;

const entity = baseline.database.entities.get("users");
const userFromPathMiddleware = getEntityFromPath(baseline, entity);

baseline.api.get("/user/:id/check", userFromPathMiddleware, (req, res) => {
  const user = req.entity;
  return res.send(`Checked user ${user.id}`);
});

Inject custom middleware ​

Pass multiple handler functions as subsequent arguments to chain middleware and route handlers.

js
import { rateLimit } from 'express-rate-limit'

const limiter = rateLimit({
	windowMs: 15 * 60 * 1000, // 15 minutes
});

// The limiter runs before the route handler
baseline.api.get("/audit", limiter, (req, res) => {
  return res.send("Audit complete");
});

Control middleware order ​

If you need to execute custom middleware before baseline's internal validation or authentication logic, use the middleware.pre option.

The execution order is:

  1. Middleware configured via middleware.pre.
  2. Automatically added middleware (validation from schemas, authentication, file upload handling).
  3. Your route handler functions.

Access raw Express endpoints ​

If you need to bypass the baseline routing system, access the underlying Express instance via baseline.app. Use this for standard Express logic that does not require baseline features.

js
baseline.app.get("/legacy", (req, res) => {
  res.send("Standard Express response");
});

Bypass Warning

When using baseline.app to define routes, baseline will not:

  • Generate API documentation.
  • Inject validation middleware.
  • Enforce authentication.

Learn more ​