@novelcaptcha/next
v0.1.0
Published
Next.js Server Actions helpers for NovelCaptcha. Wraps @novelcaptcha/node with headers()/cookies() awareness.
Maintainers
Readme
@novelcaptcha/next
Server-side helpers for Next.js (App Router and Pages Router). Wraps @novelcaptcha/node with:
- Environment-driven singleton client
- IP forwarding from
x-forwarded-for/x-real-ipautomatic - Server Action factories for verify + challenge issue
- Discriminated-union result so form UIs can render errors without try/catch
npm install @novelcaptcha/next @novelcaptcha/nodeEnvironment variables
NOVELCAPTCHA_SECRET_KEY=sec_...
NOVELCAPTCHA_SITEKEY=sk_...
NOVELCAPTCHA_ORIGIN=https://your-host # optional, defaults to api.novelcaptcha.comThe secret is server-only. Never expose it via NEXT_PUBLIC_*.
App Router: Server Actions
// app/login/actions.ts
"use server";
import { headers } from "next/headers";
import { createVerifyAction, createChallengeAction } from "@novelcaptcha/next";
export const requestChallenge = createChallengeAction({
sitekey: process.env.NOVELCAPTCHA_SITEKEY!,
type: "invisible",
context: "login",
});
const verifyCaptcha = createVerifyAction({
minScore: 0.3,
tokenField: "captchaToken",
});
export async function login(formData: FormData) {
const verify = await verifyCaptcha(formData, headers());
if (!verify.ok) return { error: verify.error };
// verify.result.visitorId, verify.result.score are available here
// ... your login logic
}verify.error is one of "bad_token" | "invalid_secret" | "verify_unavailable". Render distinct UI for each.
Pages Router: API routes
// pages/api/login.ts
import { getClient, extractRemoteIp } from "@novelcaptcha/next";
import { InvalidTokenError, NetworkError } from "@novelcaptcha/next";
export default async function handler(req, res) {
const client = getClient();
try {
const result = await client.verify(req.body.captchaToken, {
remoteIp: extractRemoteIp(new Headers(req.headers as any)),
});
if (result.score < 0.3) return res.status(403).json({ error: "captcha_failed" });
// ... login
res.json({ ok: true });
} catch (err) {
if (err instanceof InvalidTokenError) return res.status(400).json({ error: "bad_token" });
if (err instanceof NetworkError) return res.status(503).json({ error: "verify_unavailable" });
throw err;
}
}API
getClient(options?): NovelCaptcha // singleton, uses env vars by default
extractRemoteIp(headers): string | undefined
verifyFromHeaders(token, headers): Promise<VerifyResult>
createVerifyAction({ minScore, tokenField? }): (formData, headers) => VerifyActionResult
createChallengeAction(defaults): (overrides?) => CreateChallengeResultRe-exports the whole surface of @novelcaptcha/node so you can import InvalidTokenError, VerifyResult, etc. from a single package.
License
MIT
