Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Middleware

Instead of running a Presto router with a dedicated RestServerRef, you can plug it into an existing Chronos HTTP server as middleware. This is useful when you already have a Chronos HttpServerRef (perhaps serving static files or a non-REST protocol) and want to add REST endpoints to it, or when you want to compose several routers into one server.

A RestServerMiddlewareRef wraps a router and behaves like any other Chronos HttpServerMiddlewareRef: for each request it tries to match a route, and if none matches it passes the request on to the next handler in the chain.

Wrapping a router

Create the middleware from a router with RestServerMiddlewareRef.new, then pass it to HttpServerRef.new via the middlewares argument.

var router = RestRouter.init(validate)
router.api(MethodGet, "/api/{id}") do (id: int) -> RestApiResponse:
  RestApiResponse.response("item " & $id.get())

let restMiddleware = RestServerMiddlewareRef.new(router)

The middleware also accepts an optional errorHandler of the same RestRequestErrorHandler type used by the server.

The fall-through handler

The plain Chronos HttpServerRef still needs a process callbackβ€”the final handler that runs when no middleware matched the request. This is where you serve everything that isn't a REST route (or return a 404).

proc process(r: RequestFence): Future[HttpResponseRef] {.
    async: (raises: [CancelledError]).} =
  if r.isOk():
    let request = r.get()
    if request.uri.path == "/health":
      try:
        await request.respond(Http200, "ok")
      except HttpWriteError as exc:
        defaultResponse(exc)
    else:
      defaultResponse()   # -> 404
  else:
    defaultResponse()

let server = HttpServerRef.new(
  initTAddress("127.0.0.1:8080"), process,
  middlewares = [restMiddleware]).get()
server.start()

Chaining multiple routers

You can install several middlewares. They are tried in order; the first router that has a matching route handles the request, and anything unmatched falls through to the next middleware and finally to the process callback.

var
  apiRouter = RestRouter.init(validate)
  adminRouter = RestRouter.init(validate)

apiRouter.api(MethodGet, "/api/{id}") do (id: int) -> RestApiResponse:
  RestApiResponse.response("api")

adminRouter.api(MethodPost, "/admin/{id}") do (
    id: int, contentBody: Option[ContentBody]) -> RestApiResponse:
  RestApiResponse.response("admin")

let server = HttpServerRef.new(
  initTAddress("127.0.0.1:8080"), process,
  middlewares = [
    RestServerMiddlewareRef.new(apiRouter),
    RestServerMiddlewareRef.new(adminRouter)
  ]).get()

Tip

Because matching considers the HTTP method as well as the path, two routers can share the same path and be selected by the request method β€” for example one router handling GET /resource and another handling POST /resource.