What is middleware?

Middleware is code that runs before your route's loader or action. Think of it as a checkpoint — every request passes through it before reaching your page.

Why use it?

Without middleware, every route that needs a logged-in user has to call requireUser itself:

export async function loader({ request }) {
  let user = await requireUser(request)
  // ... use user
}

That works, but you repeat the same line in every protected route. Middleware moves the check to the layout so it runs once for all child routes.

How it works

  1. The /app layout exports a middleware array with [requireUserMiddleware].
  2. When any /app/* route loads, the middleware runs first.
  3. It calls requireUser, gets the user, and stores it in the route's context.
  4. The loader reads the user from context — no need to call requireUser again.

Here's what the layout looks like:

// app/routes/app/_layout.tsx
import { requireUserMiddleware } from '~/.server/auth/middlewares'

export const middleware = [requireUserMiddleware]

That single line protects every route under /app. Child routes can read the user from context when they need it:

import { requireUser } from '~/.server/auth/middlewares'

export async function loader({ context }) {
  let user = requireUser(context)
  // ... use user
}

One guard, many routes

The real benefit is peace of mind. If you add ten routes under /app, they're all protected automatically. No chance of forgetting the check in one loader. The middleware handles it at the layout level, and every child route inherits the protection.