@inkanjs/next
An inkan app in Next.js: route handlers, the Pages Router, a typed client for Server Components.
An inkan app inside a Next.js project: as App Router
route handlers, as a Pages Router API route, and as a typed client for Server Components.
Contracts, validation, problems, /docs and the inspector all come along.
npm install @vxnsin/inkan @inkanjs/nextThe app keeps its routes under /api, and its docs, document and inspector too, so every
link on the docs page points where Next will route it:
// server/app.ts
import { , , } from "@vxnsin/inkan";
const = ().("/teas/:id", { : .({ : .() }) }, ({ }) => findTea(.));
export const = ({ : "/api/docs", : "/api/openapi.json", : "/api/_inkan" })
.("/api", );One catch-all route hands every method to it:
// app/api/[...path]/route.ts
import { } from "@/server/app";
import { } from "@inkanjs/next";
export const { , , , , , , } = ();/api/docs is the docs page, /api/_inkan the inspector while next dev runs. Next does
not tell a route handler who is asking, so the inspector, which only answers a loopback
address, gets 127.0.0.1 in development and nothing in production. Pass
handlers(app, { remote: (req) => … }) to decide yourself.
Pages Router
pages/api/[...path].ts does the same for the Pages Router. Next reads the body itself
unless told not to, so the route says so; the contract then sees the bytes that came:
// pages/api/[...path].ts
import { } from "@/server/app";
import { } from "@inkanjs/next";
export const = { : { : false, : true } };
export default ();Next only reads config when it is written out in the file, which is why it is not exported
from here. Left out, the route still works: the body Next parsed is written back for the app.
Server Components
direct(app) is the typed client, talking to the app in the same process: no port, no
network, and still the same contracts, hooks and problems as a request from outside.
// app/teas/[id]/page.tsx
import { } from "next/headers";
import { } from "@inkanjs/next";
import { } from "@/server/app";
export default async function ({ }: { : <{ : string }> }) {
const = (, { : async () => ({ : (await ()).() }) });
const = await .get("/api/teas/:id", { : { : ((await ).) } });
if (!.ok) return <>{.problem.detail}</>;
return <>{.data.name}</>; // typed from the contract
}The paths are the app's, prefix and all. The type sees the routes of a chain and of mount;
those a plugin adds with register work, but are not typed.
All three run in CI inside a real Next.js: next build, next start, then requests.
inkan check and inkan diff work on server/app.ts as they do anywhere.