4.9 KiB
Global Middleware Documentation
Last Updated: November 30, 2025
File Location: src/middleware.js
This document explains the purpose and functionality of the global Next.js middleware that runs on every request (except API routes and static assets).
Table of Contents
Purpose
The middleware is used for:
- Protecting routes that require authentication.
- Redirecting logged-in users away from routes meant for unauthenticated users.
How It Works
-
The middleware checks the session cookie (
AUTH_COOKIE_NAME) to determine if the user is logged in. -
It uses two route lists:
authRequired: Routes that only logged-in users can access.unauthRequired: Routes that only logged-out users can access.
-
The middleware performs the following checks:
- If the user is not logged in and tries to access a route in
authRequired, they are redirected to the login page (/auth/login) with the original path saved as aredirectquery parameter. After successful login, they will be redirected back to the originally requested page. - If the user is logged in and tries to access a route in
unauthRequired, they are redirected to the home page (/). - If the route is not in either list, the request is allowed to proceed.
- If the user is not logged in and tries to access a route in
-
The middleware ensures that responses are not cached.
Configuration
The middleware is configured to run on all routes, except API routes and static common assets.
To modify the behavior, edit src/middleware.js.
- To add paths only logged-in users can access, append them to
const authRequired = []; - To add restricted paths logged-in users cannot access, append them to
const unauthRequired = [];
Paths are enclosed in quotes and separated by commas.
Examples
To protect a route so that only logged-in users can access it, add the route to authRequired:
const authRequired = ["/grammar/exercises", "/edit_info", "/dashboard"];
To restrict a route so that logged-in users cannot access it, add the route to unauthRequired:
const unauthRequired = ["/auth/login", "/auth/register"];
Current Configuration (as of November 2025):
- Protected routes:
/grammar/exercises,/edit_info - Unauth-only routes:
/auth/login,/auth/register
API Route Helpers
While this global middleware handles page-level redirects, API routes use middleware wrappers for authentication and validation:
withAuth - Authentication & Authorization
Use withAuth from src/backend/middleware/withAuth.js to enforce auth in API route handlers:
import { withAuth } from "@/backend/middleware/withAuth";
// Require any authenticated user
export const GET = withAuth(async (request, { params }) => {
const user = request.user; // populated by withAuth
return new Response("ok");
});
// Require admin
export const POST = withAuth(
async (request) => {
// request.user.is_admin is true here
return new Response("admin ok");
},
{ requireAuth: true, requireAdmin: true }
);
Options:
requireAuth(defaulttrue): Whentrue, unauthenticated requests receive401.requireAdmin(defaultfalse): Whentrue, non-admin users receive401.
withInputValidation - Request Body Validation
Use withInputValidation from src/backend/middleware/withInputValidation.js to validate request bodies with Zod schemas:
import { withInputValidation } from "@/backend/middleware/withInputValidation";
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
username: z.string().min(3),
});
export const POST = withInputValidation(schema, async (req) => {
const body = await req.json(); // validated data
return new Response(JSON.stringify({ success: true }));
});
Composing Middleware
Combine both for authenticated routes with validation:
export const POST = withAuth(
withInputValidation(schema, async (req) => {
const user = req.user; // from withAuth
const data = await req.json(); // validated by withInputValidation
// ...
}),
{ requireAdmin: true }
);
For detailed API middleware documentation, see BACKEND_MIDDLEWARE.md.
Notes
- If an unauthenticated user tries to access a protected page, they will be redirected to the login page. After logging in successfully, the user will be redirected back to the protected page (via the
redirectquery parameter). - This middleware runs on the edge runtime and cannot access client-side state or React hooks.
- For API routes, use the
withAuthandwithInputValidationwrappers instead (see API Route Helpers above). - Session verification uses the
AUTH_COOKIE_NAMEcookie (defined insrc/shared/const.js).