@summoniq/nextjs-dev-api-guard
v0.1.0
Published
Development safety guards for Next.js API and route handlers to prevent accidental side-effects during hot-reload request loops.
Maintainers
Readme
@summoniq/nextjs-dev-api-guard
Development safety guards for Next.js route handlers. Prevent accidental side-effects during hot-reload request loops with:
- a development kill switch
- cooldown windows per actor/action
- standardized 429/409 responses for blocked requests
Why this exists
In development, Fast Refresh or tooling loops can issue repeated requests to the same route. If that route triggers expensive work (external APIs, writes, jobs), costs can spike quickly.
This package helps you enforce:
- "Do not run this action in dev unless explicitly enabled"
- "Do not run this action again until cooldown expires"
Install
npm i @summoniq/nextjs-dev-api-guardCore API
createDevActionGuard(config)
Returns one of:
{ ok: true, now }{ ok: false, reason, retryAfterSeconds?, response }
Use it directly when you want full control.
guardRouteAction(config)
Convenience helper for route handlers that reads env vars for dev enablement + cooldown.
Environment variables
You can choose your own env var names per route. Typical pattern:
ALLOW_DEV_<ACTION_NAME>=true|falseADMIN_<ACTION_NAME>_COOLDOWN_SECONDS=45
Examples:
ALLOW_DEV_SCRAPE=false
ADMIN_SCRAPE_COOLDOWN_SECONDS=45Example (Next.js Route Handler)
import { NextRequest, NextResponse } from 'next/server';
import { guardRouteAction } from '@summoniq/nextjs-dev-api-guard';
export async function POST(request: NextRequest) {
const userId = 'current-user-id';
const guarded = await guardRouteAction({
actionName: 'admin_scrape_start',
actorId: userId,
devEnabledEnvVar: 'ALLOW_DEV_SCRAPE',
cooldownEnvVar: 'ADMIN_SCRAPE_COOLDOWN_SECONDS',
defaultCooldownSeconds: 45,
});
if (!guarded.ok) {
return guarded.response;
}
// Safe to execute expensive side-effects now
return NextResponse.json({ ok: true });
}Optional DB-backed cooldown
For multi-instance environments (or to survive process restarts), pass persistence adapters:
getLastAttemptMs({ actionName, actorId })setLastAttemptMs({ actionName, actorId, attemptMs })
This lets you enforce cooldown from Redis/DB instead of memory only.
Response behavior
- 409 Conflict when action is disabled in development
- 429 Too Many Requests during cooldown period
Retry-Afterheader included for cooldown blocks
TypeScript
Built with strict TypeScript types and ESM output.
License
MIT
