Fragno

Middleware

Intercept and process requests before they reach the route handlers.

Edit on GitHub

Middleware can be used to intercept and process requests before they reach the route handlers defined as part of a Fragment. This can be used to implement features such as authentication, rate limiting, and selectively disable routes.

Middleware is fully type-safe and can be used to access the request body, path parameters, query parameters, etc.

Basic Usage

Middleware is defined as part of the Fragment's server configuration. It is called for every request and can modify the request before it reaches the route handlers. Fragno only supports a single middleware method; it is not possible to add multiple middleware handlers like in other frameworks.

Returning undefined (or void) will continue the request to the route handler. Returning a Response will short-circuit the request and return the Response. The first argument of the withMiddleware callback handler contains the input context, which can be used to access the request body, path parameters, query parameters, and other request data. The second argument contains the output context, which can be used to create responses.

lib/example-fragment-server.ts
import { createExampleFragment } from "@fragno-dev/example-fragment";

export function createExampleFragmentInstance() {
  return createExampleFragment({
    // Fragment-specific config fields
    someApiKey: process.env.EXAMPLE_API_KEY!,
  }).withMiddleware(async ({ queryParams, path, method }, { error, json }) => {
    const q = queryParams.get("q");

    if (q === "secret") {
      return undefined;
    }

    return error({ message: "Unauthorized", code: "UNAUTHORIZED" }, 401);
  });
}

Modifying the Request

Request bodies and query parameters currently cannot be modified in middleware. See Issue #19.

Route-specific Usage

ifMatchesRoute can be used to execute middleware only for specific routes. It has full type safety for parameters and request input. The callback passed to ifMatchesRoute is only executed when the given route is matched.

Returning the Response

Make sure that the response returned from the ifMatchesRoute callback is also returned from the withMiddleware callback.

lib/example-fragment-server.ts
import { createExampleFragment } from "@fragno-dev/example-fragment";
import { logger } from "@/lib/logger";

export function createExampleFragmentInstance() {
  return createExampleFragment({}).withMiddleware(async ({ ifMatchesRoute }) => {
    const createResponse = await ifMatchesRoute("POST", "/users", async ({ input }) => {
      const body = await input.valid();
      logger.log(`Creating user with ID: ${body.id}`);
    });

    const deleteResponse = await ifMatchesRoute("DELETE", "/users/:id", async () => {
      return error(
        {
          message: "Deleting users has been disabled.",
          code: "DELETE_USERS_DISABLED",
        },
        403,
      );
    });

    if (deleteResponse) {
      return deleteResponse;
    }
  });
}