Next.js Usage
Recommended patterns for using glyde with Next.js App Router. Framework-agnostic core, framework-specific patterns.
Key insight: Server Components cannot write cookies. Token refresh must happen in Next.js middleware (before SSR), not in the HTTP client. glyde provides the interceptor hooks — you wire the auth logic where it belongs.
Naming Convention
| Term | Where | Job |
|---|---|---|
| tower | Route Handlers, Server Actions | Calls external API with auth headers from cookies |
| passenger | Client Components, browser | Calls /api/* proxy routes, redirects on 401 |
Server Instance (tower)
// lib/api/server.ts — "tower" (server-side instance)
import plane from "glyde"
import { cookies } from "next/headers"
export async function tower() {
const api = plane({ baseURL: process.env.API_BASE_URL })
const cookieStore = await cookies()
api.interceptors.request.use((config) => {
const token = cookieStore.get("access_token")?.value
if (token) {
config.headers = { ...config.headers, Authorization: `Bearer ${token}` }
}
return config
})
return api
}Client Instance (passenger)
// lib/api/client.ts — "passenger" (client-side instance)
"use client"
import plane from "glyde"
export const passenger = plane({ baseURL: "/api/proxy" })
passenger.interceptors.response.use(
(response) => response,
(error) => {
if (error?.status === 401) window.location.href = "/login"
throw error
}
)Route Handler
// app/api/users/route.ts
import { tower } from "@/lib/api/server"
import { isHttpError } from "glyde"
export async function GET(request: Request) {
const api = await tower()
try {
const { data } = await api.get("/users/", { signal: request.signal })
return Response.json(data)
} catch (err) {
if (isHttpError(err)) {
return new Response(null, { status: err.status })
}
throw err
}
}Token Refresh via Middleware
Since Server Components cannot write cookies, handle token refresh proactively in Next.js middleware. This ensures Server Components always have a valid token.
// middleware.ts — proactive token refresh before SSR
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export async function middleware(request: NextRequest) {
const access = request.cookies.get("access_token")?.value
const refresh = request.cookies.get("refresh_token")?.value
// If access token is missing/expired but refresh exists — refresh proactively
if (!access && refresh) {
const res = await fetch(`${process.env.API_BASE_URL}/auth/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh }),
})
if (res.ok) {
const { access: newToken } = await res.json()
const response = NextResponse.next()
response.cookies.set("access_token", newToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
})
return response
} else {
// Refresh failed — clear cookies and redirect to login
const response = NextResponse.redirect(new URL("/login", request.url))
response.cookies.delete("access_token")
response.cookies.delete("refresh_token")
return response
}
}
return NextResponse.next()
}
export const config = {
matcher: ["/dashboard/:path*", "/api/proxy/:path*"],
}Architecture
Browser (passenger)
│
▼
Next.js Route Handler (/api/proxy/*)
│ ← tower() adds auth headers
▼
External API (Django, FastAPI, etc.)
Middleware runs BEFORE SSR:
├── Check access_token cookie
├── If expired + refresh_token exists → refresh proactively
└── Server Components always get fresh token