Stone.jsDocs
Paradigm

Middleware

Defining middleware

Middleware follows the three forms like everything else. A class carries its dependencies; a factory receives the container; a plain function is the bare pipe. Choose by what the middleware needs.

#The three forms

A class implements handle(event, next). A functional middleware is just that signature as a function. A factory receives the container and returns the pipe, which is how middleware gets its own dependencies.

app/middleware/EnsureJson.tsdeclarativeimperative
import { Middleware, NextPipe } from '@stone-js/core'
import { IncomingHttpEvent, OutgoingHttpResponse } from '@stone-js/http-core'

@Middleware()
export class EnsureJson {
  async handle (event: IncomingHttpEvent, next: NextPipe<IncomingHttpEvent, OutgoingHttpResponse>) {
    if (event.get('contentType') !== 'application/json') {
      throw new RuntimeError('JSON required')       // short-circuit: never calls next
    }
    return next(event)
  }
}
import { defineMiddleware } from '@stone-js/core'

// Functional: the simplest form.
export const ensureJson = defineMiddleware((event, next) => {
  if (event.get('contentType') !== 'application/json') throw new RuntimeError('JSON required')
  return next(event)
})

#Configurable middleware

Middleware that takes options is a function returning a pipe. This is exactly how the built-in guards work: requireAuth() and validate(schema) are factories that return the actual middleware.

app/middleware/rateLimit.tsts
export const rateLimit = (perMinute: number) =>
  defineMiddleware((event, next) => {
    if (overLimit(event, perMinute)) throw new RuntimeError('Too many requests')
    return next(event)
  })

// used on a route: middleware: [rateLimit(60)]

#On the way out

Because a middleware wraps next, it can also transform the response after the handler runs: set a header, wrap a body, measure timing. Await next(event), then work with what it returns.

app/middleware/wrap.tsts
export const envelope = defineMiddleware(async (event, next) => {
  const response = await next(event)
  response.setContent({ data: response.getContent(), meta: { at: Date.now() } })
  return response
})

Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)