Skip to content
Finowl

Framework · Next.js

Use Finowl in Next.js route handlers and actions

In a Next.js App Router project, Finowl calls belong on the server: in route handlers under app/api, in server actions that forms post to, or in Server Components that render data. The browser never needs to talk to Finowl directly, and it should not, because the API key grants access to your credits. Keeping requests on the server also lets you combine checks with your session, database and logging in one place.

The helper below lives in lib/finowl.ts and starts with import "server-only", so the build fails if a Client Component ever imports it. It reads FINOWL_API_KEY from process.env without a NEXT_PUBLIC_ prefix, sets cache: "no-store" so lookups are never served from a cache, and aborts after ten seconds with AbortSignal.timeout. Errors surface as a typed FinowlError with status, code and retryAfter for handlers and actions to translate.

STEP 1

A reusable Next.js client

Keep your key in the FINOWL_API_KEY environment variable and route every call through one helper, so authentication, timeouts and error handling live in one place.

Install
npm install server-only
Next.js client
// lib/finowl.ts
import "server-only"; // build fails if a Client Component imports this file

const BASE_URL = "https://api.finowl.co";

export class FinowlError extends Error {
  constructor(
    public readonly status: number,
    public readonly code: string,
    public readonly retryAfter: number | null = null,
  ) {
    super(`Finowl API error ${status}: ${code}`);
    this.name = "FinowlError";
  }
}

export async function finowl<T>(path: string, body: unknown, timeoutMs = 10_000): Promise<T> {
  const apiKey = process.env.FINOWL_API_KEY; // no NEXT_PUBLIC_ prefix: never sent to the browser
  if (!apiKey) throw new Error("FINOWL_API_KEY is not set");

  const res = await fetch(`${BASE_URL}${path}`, {
    method: "POST",
    headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
    body: JSON.stringify(body),
    cache: "no-store",
    signal: AbortSignal.timeout(timeoutMs),
  });

  if (res.ok) return (await res.json()) as T;

  const payload = (await res.json().catch(() => null)) as { error?: { code?: string } } | null;
  const retryAfter = res.headers.get("Retry-After");
  throw new FinowlError(res.status, payload?.error?.code ?? "unknown_error", retryAfter ? Number(retryAfter) : null);
}

STEP 2

Call the API you need

  • Check email breaches in a Next.js route handler

    This route handler at app/api/breaches/email/route.ts checks the signed-in user's own address rather than one supplied in the request body. That keeps the endpoint from becoming a public lookup service anyone could use to probe other people's emails or spend your credits. The handler calls Finowl through the shared helper, then maps each breach's Title, BreachDate, DataClasses and IsVerified fields into a smaller camelCase shape for your UI.

  • Check breached passwords in a Next.js server action

    Server actions suit password checks because form data goes straight from the browser to your server code, with no API route of your own to secure. The signup action reads the password from FormData, enforces a minimum length, and asks Finowl's /v1/breaches/password endpoint whether it has been seen in breach data. When breached is true, the action returns an error object that useActionState hands back to the form.

  • Suggest gender from names in Next.js server actions

    This server action, suggestGender, prefills an optional field during onboarding. It trims the first name, adds an ISO 3166-1 alpha-2 country code when your form or profile has one, and calls /v1/gender/name. The result is returned as a discriminated union, so the Client Component can switch on kind: suggested shows a preselected but editable option, ask leaves the field empty, and retry waits before asking again.

A FEW GOOD QUESTIONS

Next.js questions

Other languages and frameworks

Ship with Next.js today

100 free credits every month. Try every request in the sandbox first.