@skein-js/nextjs
v0.18.0
Published
Next.js adapter for skein-js — serve the Agent Protocol from App Router or Pages Router API routes.
Maintainers
Readme
@skein-js/nextjs
Next.js adapter for skein-js — serve the Agent Protocol from Next.js API routes.
Part of skein-js — the open-source alternative to LangGraph Platform for TypeScript: a self-hosted Agent Protocol server for LangGraph.js, and a drop-in replacement for the LangGraph CLI.
A thin transport shim over the framework-agnostic @skein-js/agent-protocol
handler table — it adds no protocol logic. Mount the protocol same-origin inside an existing
Next.js app (no separate server, no CORS), ideal for small/medium graphs shipped alongside your
frontend. Supports both the App Router and the Pages Router.
Install
npm i @skein-js/nextjs @langchain/langgraphApp Router (recommended)
A catch-all route re-exports the per-method handlers:
// app/api/[...path]/route.ts
import { createSkeinRouteHandlers } from "@skein-js/nextjs";
export const runtime = "nodejs"; // the background run worker needs a long-lived Node process
export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = createSkeinRouteHandlers({
config: "./langgraph.json",
});Point a browser client at the same origin — no CORS needed:
import { useStream } from "@langchain/langgraph-sdk/react";
const thread = useStream({ apiUrl: "/api", assistantId: "agent" });Pages Router
// pages/api/[...path].ts
import { createSkeinPagesHandler } from "@skein-js/nextjs";
// Let Next know this route settles the response itself (needed for SSE streaming).
export const config = { api: { bodyParser: true, externalResolver: true } };
export default createSkeinPagesHandler({ config: "./langgraph.json" });Both entry points accept the shared { config } | { deps } seam, an optional cors (off by default),
and a basePath (defaults to /api, matching the mount above).
Graphs as plain endpoints (non-chat)
For workloads that aren't chat — a classifier, an extractor, a workflow another service calls — there
is a smaller surface: every graph mounted as POST <basePath>/:graph_id, where the request body is
the graph input and the response is the final state. No threads, assistants, or runs.
// app/api/invoke/[graph_id]/route.ts
import { createSkeinInvokeRouteHandlers } from "@skein-js/nextjs";
export const runtime = "nodejs";
export const { POST } = createSkeinInvokeRouteHandlers({ deps, basePath: "/api/invoke" });Send Accept: text/event-stream to stream the steps instead. See
docs/serving-a-single-graph.md.
Deployment caveat
The background run worker and the in-memory driver need a long-lived Node process — fine on
next start with runtime = "nodejs". For serverless/edge deploys, back skein with the
Redis queue + Postgres store (pass { deps } from
@skein-js/runtime's buildRuntime) so state and runs don't depend on one process.
If you use the langgraph.json on-ramp ({ config }, or buildRuntime), keep the graph loader out
of the bundle:
// next.config.mjs
export default { serverExternalPackages: ["@langchain/langgraph-api", "@typescript/vfs"] };The { deps } / embedPostgresGraphs path never reaches that loader and needs no externals. Full
details, including the "Critical dependency" warning, in
docs/bundling.md.
API
createSkeinRouteHandlers(options): SkeinRouteHandlers— App Router (recommended); returns{ GET, POST, PUT, PATCH, DELETE, OPTIONS }to re-export fromapp/<base>/[...path]/route.ts.createSkeinPagesHandler(options): SkeinPagesHandler— Pages Router; the default export forpages/api/[...path].ts.createSkeinInvokeRouteHandlers(options): SkeinInvokeRouteHandlers— the simplified serving surface (POST <basePath>/:graph_id, body-in / final-state-out); returns{ POST, OPTIONS }forapp/api/invoke/[graph_id]/route.ts.basePathdefaults to/api/invoke; addsstreamMode.- Both accept
SkeinRouteHandlerOptions/SkeinPagesHandlerOptions: the shared{ config, importModule? } | { deps, channels? }seam plus an optionalcors(off by default) and abasePath(defaults to/api, matching the mount).depscomes from@skein-js/runtime'sbuildRuntimefor production Postgres/Redis. getSkeinRuntime(options): Promise<ResolvedProtocolRuntime>— the memoized runtime accessor shared by both routers (survives dev module reloads); its.runtimeis theProtocolRuntime(assistants, handlers, worker).SkeinRuntimeOptions— the shared runtime-resolution option shape, re-exported for typing your own wrappers.- Logging — unlike the NestJS and Fastify adapters, which default to the host framework's own
logger, Next.js owns none, so skein stays silent here until you pass one.
loggerreaches the run engine, so failed graph runs and webhook failures are reported.createConsoleLogger({ level?, prefix? })(re-exported from@skein-js/server-kit) is the one-liner. See errors-and-logging.md. - Low-level serializers:
toWebResponse/webErrorResponse(WebResponse), plussendNodeResponse/sendNodeError(re-exported from@skein-js/server-kit).
Learn more
@skein-js/express— the reference adapter- Building your own adapter · skein-js overview
