RiserBob logoRiserBobv1.0.0
HomeDocsCLIRelease notes

Getting started

  • Introduction
  • Installation
  • Quick start
  • Project structure

Core concepts

  • Routing
  • Request
  • Responses
  • Error handling

Middleware

  • Middleware
  • Authentication
  • Logger and CORS

Data

  • Validation

Structure and tooling

  • Modules
  • CLI

Examples

  • Build a project
  • Mongoose
  • Drizzle ORM
  • Sequelize
  • JWT and cookies
  • File uploads

Packages

  • Cookies

Resources

  • Troubleshooting
  • Release notes
  • License
  • Security
  • Contributing
Middleware

Middleware

Compose behaviour around your routes: globally, or right next to the route it protects.

A middleware receives the request, the response helper and a next function. Return next() to continue, or return a Response to stop the chain.

TypeScript
import type { SpongMiddleware, SpongRequest } from 'riser-bob' const logger: SpongMiddleware<SpongRequest> = async (  spongReq,  spongRes,  spongNext) => {  console.log(spongReq.method, spongReq.path)  return spongNext()}
ArgumentTypeDescription
spongReqSpongRequestThe request, shared along the chain.
spongResSpongResponseThe response helper, so a middleware can answer with SpongUnauthorized() and friends.
spongNext() => Promise<Response>Runs the rest of the chain and resolves with its Response.

Global middleware

TypeScript
bob.spongUse(loggerMiddleware)
Global middleware is attached when a route is registered, so call spongUse before registering the routes it should apply to.

Route-specific middleware

Pass middleware after the handler. It runs only for that route.

TypeScript
bob.spongGet('/admin', handler, adminOnly)

You can pass several. They run in the order you list them.

TypeScript
bob.spongDelete('/api/users/:id', handler, auth.middleware, admin.middleware)

Execution order

  • Global middleware runs first, in the order you registered it.
  • Route-specific middleware runs next, in the order you passed it.
  • The handler runs last.
  • Code placed after await spongNext() runs on the way back, in reverse order.
TypeScript
const timing: SpongMiddleware<SpongRequest> = async (  spongReq,  spongRes,  spongNext) => {  const start = performance.now()  const response = await spongNext() // everything after this runs on the way back   console.log(spongReq.path, performance.now() - start, 'ms')  return response}

Stopping the chain

Return a Response instead of calling spongNext() and nothing further runs.

TypeScript
const maintenance: SpongMiddleware<SpongRequest> = async () => {  return new Response('Back soon', { status: 503 })}
Calling spongNext() more than once in the same middleware throws an error. Call it once and return its result.

A class-based middleware

When a middleware needs configuration or helpers, write it as a class with a middleware property. This is how AuthMiddleware is built, and it fits the module layout. The example below allows only users with the admin role. Put it after the auth middleware, because it reads spongReq.user.

src/middlewares/admin.middleware.ts
import type { SpongRequest, SpongResponse } from "riser-bob"; export class AdminMiddleware {  readonly middleware = async (    spongReq: SpongRequest,    spongRes: SpongResponse,    spongNext: () => Promise<Response>,  ): Promise<Response> => {    if (!spongReq.user) {      return spongRes.SpongUnauthorized({        success: false,        message: "Authentication required",      });    }     if (spongReq.user.role !== "admin") {      return spongRes.SpongForbidden({        success: false,        message: "Admin access required",      });    }     return spongNext();  };}
users.route.ts
this.bob.spongDelete(  "/api/users/:id",  this.controller.delete,  this.authMiddleware.middleware, // sets spongReq.user  this.adminMiddleware.middleware, // reads spongReq.user);

MiddlewarePipeline

Routes build their own pipeline from your middleware. If you need to compose middleware by hand, MiddlewarePipeline is available. execute takes the request, the response helper and the final handler that runs when every middleware called next.

TypeScript
import { MiddlewarePipeline } from 'riser-bob' const pipeline = new MiddlewarePipeline<SpongRequest>()pipeline.use(logger).use(timing) const response = await pipeline.execute(spongReq, spongRes, async () => {  return new Response('done')})
PreviousError handlingNextAuthentication

On this page

  • Global middleware
  • Route-specific middleware
  • Execution order
  • Stopping the chain
  • A class-based middleware
  • MiddlewarePipeline