How to Implement Authentication Middleware in SXO for Node, Bun, Deno, and Cloudflare Workers

SXO provides a Web Standard-compatible middleware system where you export a default function from src/middleware.js that receives a Web Standard Request and optional env argument; returning a Response short-circuits the pipeline, allowing you to implement authentication guards that work across Node, Bun, Deno, and Cloudflare Workers with minimal platform-specific adjustments.

SXO (Simple X Open) is a lightweight framework designed to run seamlessly across multiple JavaScript runtimes. According to the gc-victor/sxo source code, the middleware architecture leverages Web Standard Request and Response objects, allowing you to write authentication logic once and deploy it everywhere while still accessing platform-specific secrets and bindings.

Understanding SXO's Middleware Architecture

The middleware pipeline in SXO consists of three core components that work together to process requests before they reach your SSR handlers.

The Middleware Loader

Located in src/js/server/middleware.js, the loader scans your project for SRC_DIR/middleware.{js,ts,mjs}. It accepts a default export (either a function or array) or named exports middlewares, middleware, or mw. The loader returns an array of functions, each conforming to the Web Standard signature (request, env?) => Response | void | Promise<...>.

The Middleware Executor

The executeMiddleware function, re-exported from src/js/runtime/middleware.js and defined in src/js/server/core-handler.js, runs loaded functions sequentially. If any middleware returns a Response object, the pipeline short-circuits immediately, sending that response to the client without proceeding to route matching or SSR.

Platform Integration Points

Each runtime adapter injects the middleware array into the request handler created by createProdHandler. The Node adapter (src/js/server/prod/node.js), Bun adapter (src/js/server/prod/bun.js), Deno adapter (src/js/server/prod/deno.js), and Cloudflare Workers adapter (src/js/server/prod/workers.js) all follow this pattern, ensuring consistent middleware execution across platforms.

Creating Authentication Middleware for Different Platforms

To implement authentication, create src/middleware.js in your project root. While the core logic remains consistent across runtimes, accessing environment variables and platform bindings requires platform-specific syntax.

Node and Bun

Both Node.js and Bun use process.env for environment variables. Your middleware receives the standard request object as the first argument.

// src/middleware.js
export default async function auth(request) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response("Missing token", { status: 401 });
  }

  const token = authHeader.slice(7);
  try {
    const payload = await verifyJwt(token, process.env.JWT_SECRET);
    request.user = payload;
  } catch (e) {
    return new Response("Invalid token", { status: 401 });
  }
}

Deno

Deno requires explicit permission to access environment variables using Deno.env.get(). Ensure you run Deno with the --allow-env flag.

// src/middleware.js
export default async function auth(request) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response("Missing token", { status: 401 });
  }

  const token = authHeader.slice(7);
  const secret = Deno.env.get("JWT_SECRET");
  if (!secret) {
    return new Response("Server misconfigured", { status: 500 });
  }

  try {
    const payload = await verifyJwt(token, secret);
    request.user = payload;
  } catch {
    return new Response("Invalid token", { status: 401 });
  }
}

Cloudflare Workers

Cloudflare Workers pass platform-specific bindings (KV namespaces, Durable Objects, secrets) via the env argument, which is the second parameter to your middleware function. Define these bindings in your wrangler.toml configuration.

// src/middleware.js
export default async function auth(request, env) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response("Missing token", { status: 401 });
  }

  const token = authHeader.slice(7);
  const stored = await env.AUTH_TOKENS.get(token);
  if (!stored) {
    return new Response("Invalid token", { status: 401 });
  }

  request.user = JSON.parse(stored);
}

How Middleware Hooks Into the Request Pipeline

Understanding the request flow helps debug authentication issues. When SXO starts a production server, the platform adapter calls loadUserDefinedMiddlewares() from src/js/server/middleware.js to retrieve your middleware array. This array is passed to createProdHandler in src/js/server/core-handler.js via the getMiddleware option.

When an HTTP request arrives, the adapter creates a Web Standard Request object and invokes the unified fetch handler. This triggers executeMiddleware, which iterates through your authentication functions. If your middleware returns a Response (such as a 401 Unauthorized), the pipeline stops immediately. Otherwise, the request proceeds to route matching and SSR.

During development, the loader adds a cache-busting query string (?t=${Date.now()}) to middleware.js, enabling hot-reload without server restarts.

Platform-Specific Environment Access

While the middleware API remains consistent across runtimes, accessing secrets and platform bindings requires platform-specific syntax:

  • Node and Bun: Use process.env.VAR_NAME for environment variables.
  • Deno: Use Deno.env.get("VAR_NAME") and ensure the --allow-env flag is set.
  • Cloudflare Workers: Access bindings via the env parameter (second argument), which receives KV namespaces, Durable Objects, and secrets as defined in wrangler.toml.

This design allows you to write the core authentication logic once and adapt only the configuration retrieval for each deployment target.

Summary

  • SXO uses a Web Standard middleware system defined in src/js/server/middleware.js and executed by executeMiddleware in src/js/server/core-handler.js.
  • Create src/middleware.js and export a default function with the signature (request, env?) => Response | void | Promise<...>.
  • Return a Response from your middleware to short-circuit the pipeline, such as sending a 401 for failed authentication.
  • Node/Bun: Access secrets via process.env.
  • Deno: Access secrets via Deno.env.get() with the --allow-env flag.
  • Cloudflare Workers: Access bindings via the env argument (second parameter).
  • Platform adapters in src/js/server/prod/*.js ensure consistent middleware injection across all runtimes.

Frequently Asked Questions

What file should I create for SXO middleware?

Create a file named middleware.js (or .ts/.mjs) inside your src directory (or the directory specified by the SRC_DIR environment variable). SXO's loader, located at src/js/server/middleware.js, automatically discovers this file and expects either a default export containing a function or array of functions, or named exports called middlewares, middleware, or mw.

How does SXO handle middleware errors?

If your middleware throws an uncaught exception, SXO's executeMiddleware function in src/js/server/core-handler.js will propagate the error. Since middleware runs within the platform's fetch handler, error handling depends on the runtime's default behavior for unhandled exceptions. For production stability, wrap your authentication checks in try-catch blocks and return explicit error Responses with appropriate status codes (such as 500 for server errors or 401 for authentication failures) rather than throwing raw errors.

Can I use the same middleware across all SXO platforms?

Yes. The middleware API is runtime-agnostic and uses Web Standard Request and Response objects, allowing you to share the same src/middleware.js file across Node, Bun, Deno, and Cloudflare Workers deployments. Only the method for accessing environment variables or platform bindings differs between runtimes (such as process.env for Node/Bun, Deno.env.get for Deno, and the env parameter for Cloudflare Workers), which you can handle with simple runtime detection or by using the env parameter where available.

How do I access Cloudflare KV in SXO middleware?

In Cloudflare Workers, platform bindings including KV namespaces are passed via the env argument, which is the second parameter to your middleware function. First, bind your KV namespace in wrangler.toml (for example, [[kv_namespaces]] binding = "AUTH_TOKENS"). Then access it in your middleware via env.AUTH_TOKENS.get(key). The Cloudflare Workers adapter at src/js/server/prod/workers.js ensures the env object flows through the request handler chain to your middleware automatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →