@orkify/next
v1.0.0-beta.13
Published
Next.js integration for orkify — cache handlers and browser error tracking
Maintainers
Readme
Next.js integration for orkify — cache handlers and browser error tracking.
Table of Contents
- Installation
- Cache Handlers
- Build & Deploy
- Browser Error Tracking
- Server Actions Encryption Key
- Security Header Stripping
- Version Skew Protection
- Requirements
- License
Installation
npm install @orkify/next is installed automatically as a dependency.
Cache Handlers
orkify replaces Next.js's default cache with a shared in-memory cache that works across cluster workers. Both handlers use @orkify/cache under the hood — tag invalidation (revalidateTag()) propagates across all workers automatically.
Setup
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
// Enable 'use cache' directives (required for Next.js 16)
cacheComponents: true,
// Next.js 16 'use cache' directives — backed by @orkify/cache
cacheHandlers: {
default: require.resolve('@orkify/next/use-cache'),
},
// ISR / route cache — backed by @orkify/cache
cacheHandler: require.resolve('@orkify/next/isr-cache'),
// Disable Next.js's built-in in-memory cache (orkify handles it)
cacheMaxMemorySize: 0,
// Version skew protection — auto-set by `orkify deploy`, optional for `orkify up/run`
deploymentId: process.env.NEXT_DEPLOYMENT_ID || undefined,
};
export default nextConfig;Build & Deploy
orkify deploys require output: 'standalone' in your Next.js config — this produces a self-contained build with all dependencies traced and bundled:
// next.config.ts
const nextConfig: NextConfig = {
output: 'standalone',
// ... rest of config
};The standalone output doesn't include public/ or .next/static/, so copy them after building. In your orkify.yml:
deploy:
install: npm ci
build: >
npm run build &&
cp -r .next/static .next/standalone/.next/static &&
cp -r public/. .next/standalone/public
processes:
- name: app
script: .next/standalone/server.js
execMode: clusterThe entry point is .next/standalone/server.js — a minimal Node.js server that Next.js generates from the traced dependencies.
Handler Details
@orkify/next/use-cache— handles'use cache'directives. Converts between Next.js's stream-based interface and orkify's synchronous cache. Implements staleness checks (hard expiry, revalidation window, soft tags).@orkify/next/isr-cache— handles ISR / route cache. Simpler adapter: get, set, tag-based revalidation.
Both work standalone (npm run dev) and in cluster mode — the cache detects the mode automatically.
revalidateTag() calls in your Next.js app flow through orkify's cache, which broadcasts tag invalidations to all cluster workers via IPC:
// app/actions.ts
'use server';
import { revalidateTag } from 'next/cache';
export async function refreshPosts() {
revalidateTag('posts'); // invalidates across all workers
}ISR Request Coalescing
In cluster mode, multiple workers may detect the same stale cache entry simultaneously. Without coalescing, N workers trigger N parallel revalidations for the same page.
orkify uses the shared cache as a distributed lock. When a worker detects staleness, it sets a short-lived __revalidating:{key} flag. Other workers seeing this flag serve stale content instead of triggering their own revalidation. The lock auto-expires after 30 seconds and is cleared when the fresh entry is stored.
- Hard expiration: not coalesced (entry is genuinely expired, must be regenerated)
- Soft tag invalidation: not coalesced (explicit invalidation should always miss)
- Revalidation window: coalesced (stale-while-revalidate semantics)
Browser Error Tracking
Capture browser errors and route them through orkify's error pipeline. Errors appear on the dashboard alongside server errors — no additional services needed. Errors bundle with the regular telemetry flush — zero additional API calls.
Setup
1. Add the capture component to your root layout:
// app/layout.tsx
import { OrkifyErrorCapture } from '@orkify/next/error-capture';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{children}
<OrkifyErrorCapture />
</body>
</html>
);
}2. Create the API route at app/orkify/errors/route.ts:
export { POST } from '@orkify/next/error-handler';3. Enable source maps for source-mapped stacks on the dashboard:
// next.config.ts — add to the config above
const nextConfig: NextConfig = {
// ... cache handler config from above
experimental: {
serverSourceMaps: true,
},
webpack: (config, { isServer }) => {
if (!isServer) {
config.devtool = 'hidden-source-map';
}
return config;
},
// Turbopack is default in Next.js 16; empty config silences the
// "webpack config without turbopack config" error during builds.
turbopack: {},
};hidden-source-map produces .map files on disk without exposing them to browsers. orkify reads these files server-side to resolve minified stacks.
Error Boundaries
Errors caught by React Error Boundaries (including Next.js error.tsx) don't bubble to window.onerror. Use reportError() to capture them:
// app/error.tsx
'use client';
import { reportError } from '@orkify/next/error-capture';
import { useEffect } from 'react';
export default function ErrorPage({ error }: { error: Error }) {
useEffect(() => {
reportError(error);
}, [error]);
return <p>Something went wrong.</p>;
}What's Captured
window.onerror— uncaught exceptionsunhandledrejection— unhandled promise rejections- Manual
reportError()calls from error boundaries
Each error includes: name, message, stack trace, page URL, and browser info.
Security
- Origin validation: Rejects cross-origin requests. Supports
X-Forwarded-Hostfor reverse proxy setups. - Rate limiting: Max 10 errors per 10 seconds per IP
- Payload validation: Zod schema, 64 KB body limit, 100 stack line cap
- No data reflection: Response is always
{ ok: true }— never echoes input
Stack Normalization
Firefox and Safari stacks are automatically normalized to V8 format (at fn (file:line:col)) before sending, so the entire downstream pipeline handles one format.
How It Works
Browser → POST /orkify/errors → API route handler → process.send() → orkify daemon → dashboardErrors flow through the same IPC path as server errors and bundle with the regular telemetry flush. Zero additional API calls.
Server Actions Encryption Key
Next.js encrypts Server Action payloads. If the key differs between cluster workers or across rolling reloads, Server Actions fail with cryptic decryption errors. orkify auto-generates a stable NEXT_SERVER_ACTIONS_ENCRYPTION_KEY when it detects a Next.js app:
- Generated once at process creation and stored in config
- Consistent across all cluster workers (shared via
config.env) - Survives reloads and daemon restarts (persisted in the snapshot file)
- Skipped if you provide your own key via
--envor shell environment
Security Header Stripping
orkify strips dangerous headers from external requests before they reach your app:
| Header | CVE | Risk |
| ------------------------- | ------------------------- | ------------------------------------------ |
| x-middleware-subrequest | CVE-2025-29927 (CVSS 9.1) | Bypasses Next.js middleware authentication |
| x-now-route-matches | CVE-2024-46982 | Cache poisoning via Vercel routing |
Headers are preserved on loopback requests (127.0.0.1, ::1, ::ffff:127.0.0.1) since Next.js uses them internally. Active in fork mode, cluster mode, and run mode with no configuration needed.
Version Skew Protection
During orkify deploy, old and new workers coexist briefly. If client-side bundle hashes changed, a user who loaded a page from an old worker may request assets that only exist in the new version.
orkify auto-sets NEXT_DEPLOYMENT_ID during deploy (format: v{version}-{artifactSlice}). Next.js uses this to tag asset URLs and handle version mismatches gracefully. If you set NEXT_DEPLOYMENT_ID in your secrets, orkify won't overwrite it.
Requirements
- Node.js 22+
- Next.js 15+ (16+ for
use cache) - Must run under
(GitHub) for error tracking to reach the dashboard
License
Apache-2.0
