@railcode/next
v0.1.0
Published
Railcode build preset for Next.js apps. Wraps the OpenNext Cloudflare adapter so an app never ships a wrangler or OpenNext config, and backs Next's caches with the app's Railcode store.
Downloads
213
Readme
@railcode/next
The Railcode build preset for Next.js (App Router) apps. It wraps the
OpenNext Cloudflare adapter so an app
never ships a wrangler or OpenNext config, and backs Next's caches with the
app's own Railcode store.
// railcode.json
{ "app": "my-app", "type": "next", "dist": "dist/client", "server": "dist/server/index.js" }// package.json
{
"scripts": { "dev": "railcode dev", "build": "next build" },
"devDependencies": { "@railcode/next": "^0.1.0" }
}railcode deploy runs railcode-next build; railcode dev runs next dev
with the SDK's dev credentials. railcode init <app> --template next scaffolds
all of this.
What the build does
- Generates the adapter configs in the background — a wrangler config and
an OpenNext config under
node_modules/.railcode/next/(always gitignored, rewritten each run), from Railcode-owned runtime constants. - Runs
opennextjs-cloudflare build, which runs yourbuildscript. Keep that script the plainnext build: OpenNext calls it, so a script that points back at the Railcode build would start a build inside every build. The preset refuses that shape before it starts anything. - Bundles ONE worker module (
dist/server/index.js) with the build's prerendered pages inside it, and copies the static assets todist/client.
The platform serves the files in dist/client itself; every other path goes to
the worker ("type": "next" implies "routes": "all").
Using the SDK
@railcode/sdk works in Server Components, Server Actions, Route Handlers,
after() and instrumentation.ts. It needs a request, and there is none while
next build prerenders — so a page that calls it has to render per request:
import { connection } from "next/server";
import { ctx } from "@railcode/sdk";
export async function me() {
await connection(); // per request, never at build time
return ctx.user;
}(export const dynamic = "force-dynamic" does the same for a whole page when
Cache Components is off.)
Secrets
Set secrets with railcode secrets set NAME and read them as secrets.NAME.
Do not leave them in .env* files: the adapter embeds every env file's values
in the worker bundle, where they stay in deploy history even after you rotate
them. After the adapter runs, the build reads what it actually embedded and
warns, by key name, about every non-NEXT_PUBLIC_ value (in a monorepo the
root's env files count too). It is a warning because plain config lives in env
files as well; the build cannot tell a log level from a credential.
Caches
"use cache", the fetch cache and cache tags (cacheTag, updateTag,
revalidateTag) are stored in your app's store, in the next_cache and
next_tags collections, so they are shared by every isolate and survive cold
starts. Statically prerendered pages are served from the worker bundle.
To change the OpenNext config, add an open-next.config.ts:
import { defineRailcodeConfig } from "@railcode/next/open-next";
export default defineRailcodeConfig({ enableCacheInterception: true });Limits
- Next 16.3, not 16.4 yet. Next 16.4 reads a manifest
(
preview-props.json) the OpenNext adapter does not bundle, so every request to the worker fails withUnexpected loadManifest(...). Keepnexton~16.3.8until the adapter supports it. - One module. A feature that needs a second module (a
.wasm—next/og) fails the build with a named error. - No timed page revalidation (ISR). A prerendered page with
export const revalidate = 60keeps its build-time content until the next deploy; the build warns about each one. Render the page per request, or cache the data with"use cache"+cacheLife, which is stored and expires. - No image optimizer. Set
images: { unoptimized: true }. - Avoid
proxy.ts/ middleware. It adds about 3 MB to the worker; do the same work in a layout, a Route Handler or the page. railcode devruns Next in Node, the deployed app runs in the worker runtime. A Node-only dependency works locally and fails when deployed.
