With Next.js
expect-status works in every Next.js context — Server Components, Server Actions, Route Handlers, Middleware, and client-side hooks. No adapter needed.
Server Components
Section titled “Server Components”Fetch data in Server Components and let errors propagate to the nearest error.tsx:
import { expectStatus } from "expect-status";
async function OrganisationPage({ params }: { params: { id: string } }) { const org = await expectStatus( 200, client.getOrganisation({ params: { id: params.id } }), { 404: "Organisation not found." }, );
return <h1>{org.name}</h1>;}Non-success statuses throw → caught by the nearest error.tsx boundary. The error message is the per-status message or the extracted backend message.
With error.tsx
Section titled “With error.tsx”"use client";
import { ExpectStatusError } from "expect-status";
export default function ErrorPage({ error }: { error: Error }) { if (error instanceof ExpectStatusError && error.status === 404) { return <p>Organisation not found.</p>; } return <p>{error.message}</p>;}Server Actions
Section titled “Server Actions”Use expectStatus in Server Actions for form submissions:
"use server";
import { expectStatus } from "expect-status";import { redirect } from "next/navigation";
export async function createOrganisation(formData: FormData) { const org = await expectStatus( 201, client.createOrganisation({ body: { name: formData.get("name") as string }, }), { 409: "An organisation with that name already exists.", 422: "Please check the form and try again.", }, );
redirect(`/org/${org.id}`);}In the client component, catch the error with useActionState:
"use client";
import { useActionState } from "react";import { createOrganisation } from "./actions";
export function CreateOrgForm() { const [error, action, isPending] = useActionState( async (_prev: string | null, formData: FormData) => { try { await createOrganisation(formData); return null; } catch (err) { return (err as Error).message; } }, null, );
return ( <form action={action}> <input name="name" /> <button disabled={isPending}>Create</button> {error && <p className="text-red-500">{error}</p>} </form> );}Server Actions with recover
Section titled “Server Actions with recover”For Server Actions that return structured results (no throw):
"use server";
export async function createOrganisation(formData: FormData) { const result = await expectStatus( 201, client.createOrganisation({ body: { name: formData.get("name") as string }, }), { 409: "An organisation with that name already exists.", recover: (err) => ({ error: err.message }), }, );
if ("error" in result) { return result; // { error: string } } redirect(`/org/${result.id}`);}Route Handlers
Section titled “Route Handlers”Use expectStatus in Route Handlers to proxy or aggregate upstream APIs:
import { expectStatus } from "expect-status";import { NextResponse } from "next/server";
export async function GET( _req: Request, { params }: { params: { id: string } },) { const result = await expectStatus( 200, client.getOrganisation({ params: { id: params.id } }), { throws: false }, );
if (result.ok) { return NextResponse.json(result.data); } return NextResponse.json( { message: result.error.message }, { status: result.status }, );}Middleware
Section titled “Middleware”Use expectStatus in Next.js Middleware to validate upstream calls (e.g. auth token verification) at the edge:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";import { fetchExpect } from "expect-status/fetch";
export async function middleware(request: NextRequest) { const token = request.cookies.get("session")?.value; if (!token) { return NextResponse.redirect(new URL("/login", request.url)); }
try { await fetchExpect(`${process.env.AUTH_API_URL}/verify`, 200, { init: { headers: { Authorization: `Bearer ${token}` }, }, }); return NextResponse.next(); } catch { return NextResponse.redirect(new URL("/login", request.url)); }}
export const config = { matcher: ["/dashboard/:path*", "/org/:path*"],};Client-side with TanStack Query
Section titled “Client-side with TanStack Query”For client-side data fetching, combine expectStatus with TanStack Query:
"use client";
import { useQuery } from "@tanstack/react-query";import { expectStatus } from "expect-status";
export function useOrganisation(id: string) { return useQuery({ queryKey: ["org", id], queryFn: () => expectStatus(200, client.getOrganisation({ params: { id } })), });}See With TanStack Query for more patterns.
Configured instance
Section titled “Configured instance”For production apps, create a shared configured instance:
import { createExpectStatus } from "expect-status";
export const expectStatus = createExpectStatus({ fallbackMessage: "Something went wrong.", groups: { auth: [401, 403] }, defaults: { auth: "Please sign in.", "5xx": "Service unavailable. Please try again.", }, onError: (err, response) => { console.error("[API Error]", { status: response.status, message: err.message, }); },});Then import from lib/expect-status everywhere — default messages, error logging, and error class apply automatically.
See also
Section titled “See also”- With TanStack Query — client-side data fetching
- fetchExpect API — native fetch integration
- Error Resolution — how errors are resolved