A minimal Next.js 16 starter that calls the Pipelex API via the @pipelex/sdk SDK to run AI methods (.mthds bundles) from a TypeScript app.
It ships three demo pipelines, presented as tabs:
- Text entities (
methods/extract-entities) — extracts{ people, orgs, dates }from pasted text. - PDF summary (
methods/summarize-pdf) — uploads a PDF in the browser and returns a structured{ title, docType, keyPoints }summary from a cheap OpenAI model. - Image generation (
methods/generate-image) — turns a text prompt into an image withgpt-image-1-mini.
This is a template repository — don't clone it directly. Click the green Use this template button at the top-right of the GitHub page to create your own repo, then clone that.
Make it yours. The fastest path is the bundled /bootstrap skill: open your new repo in Claude Code and run /bootstrap. It renames the template identity (pipelex-starter-js / Pipelex Starter) across package.json, package-lock.json, README, CLAUDE.md, the app title, metadata, release skill, and license text, then runs the checks.
Prefer to do it by hand? The manual equivalent:
- Replace
pipelex-starter-jsinpackage.jsonwith your npm package name, and update the description, author, repository, and license metadata. - Replace
Pipelex Starterinsrc/app/layout.tsxandsrc/app/page.tsxwith your app title. - Update README.md, CLAUDE.md, LICENSE, and
.claude/skills/release/SKILL.mdso they describe your project instead of the template. - Run
npm install --package-lock-onlysopackage-lock.jsonmatches the new package name and version.
- Next.js 16 (App Router) + React 19 + TypeScript 5 (strict)
- Tailwind CSS 3
- Vitest 4 + Testing Library (happy-dom)
- ESLint 9 + Prettier 3, Husky + lint-staged
@pipelex/sdkSDK for Pipelex API calls
- Node.js 22.12+ (the SDK is ESM-only and the e2e specs
require()it, which needs Node's unflaggedrequire(esm)) - Access to a Pipelex API server. You have two options:
- Hosted — currently in private beta. Join the waitlist at go.pipelex.com/waitlist. Once you have access, get an API key at app.pipelex.com and point
PIPELEX_BASE_URLathttps://api.pipelex.com(the default). - Self-hosted — the Pipelex API is open source at github.com/Pipelex/pipelex-api. Run it locally or on your own infra and point
PIPELEX_BASE_URLat your instance (e.g.http://localhost:8000).
- Hosted — currently in private beta. Join the waitlist at go.pipelex.com/waitlist. Once you have access, get an API key at app.pipelex.com and point
cp .env.example .env.local
# edit .env.local and set PIPELEX_API_KEY
make install
make devOpen http://localhost:4100 and try the three example tabs.
methods/
extract-entities/main.mthds # text → { people, orgs, dates }
summarize-pdf/main.mthds # PDF Document → { title, docType, keyPoints }
generate-image/main.mthds # text prompt → generated Image
public/sample-invoice.pdf # sample PDF, so the PDF example works out of the box
src/
config.ts # ExecutionMode + DEFAULT_EXECUTION_MODE
app/ # Next.js App Router (layout, page, globals.css)
actions/ # 'use server' Server Actions — blocking + start + poll trio per pipeline
lib/
pipelexClient.ts # PipelexApiClient singleton
loadBundle.ts # reads the .mthds bundles from disk
blockingRun.ts # the blocking execute path
durableRun.ts # the durable start + poll path
runOutput.ts # main_stuff ?? pipe_output narrowing
errors.ts # classifyPipelineError + PipelineError model
fileEncoding.ts # data-URL validation + Document input envelope
clientFile.ts # browser File → base64 data URL
hooks/useRun.ts # unified blocking|durable client state machine
components/ # ExampleTabs + per-example form/result + ModeToggle + RunStatus
types/ # concept types + parseXxx(RunResults) narrowers
Each example runs in one of two execution modes, switchable per-example at runtime with a small toggle:
- Durable (default) — the Server Action calls
PipelexApiClient.start(), then the browser polls the run by id until it finishes, streaming live status. Survives the hosted gateway's ~30s synchronous cap, so long pipelines (like image generation) succeed. - Blocking — the Server Action calls
PipelexApiClient.execute()and waits. Simpler, but behind the hosted gateway a run over ~30s is cut off and surfaces a clear timeout error pointing you at Durable mode.
The flow, end to end:
- A form calls the
useRun({ mode, blocking, start, poll })hook, which dispatches to the right Server Actions by mode. - The Server Action reads the
.mthdsbundle from disk and calls the SDK (executefor blocking,start+getRunStatus/getRunResultfor durable) with the bundle TOML + inputs. - The Pipelex API runs the pipe and returns loosely-typed output (
main_stuffon the durable path,pipe_outputon blocking — onefindOutputContenthelper reads both). - A
parseXxx(results)narrower insrc/types/validates the output into a typed shape. - The hook drives the result: a live-status card while running, then the result component, or a classified
PipelineErrorshown by<ErrorDisplay>.
Text inputs are plain strings. File inputs (the PDF example) go through one extra step:
- The browser reads the chosen
Fileinto a base64 data URL withfileToDataUrl(src/lib/clientFile.ts).Fileobjects are not serializable across the server boundary — the Server Action only ever receives the resultingstring. - The Server Action validates the data URL (
validateDataUrl) and wraps it in a PipelexDocumentenvelope (buildDocumentInput→{ concept: "Document", content: { url, filename, mime_type } }). - The Pipelex API decodes the data URL server-side — the app never hosts the file itself.
Image outputs (the image example) come back as a URL — a storage URL or a base64 data URL — which renders directly in an <img>.
- Add
methods/<name>/main.mthds(the/mthds-buildskill from the mthds-plugins marketplace can generate one). - Add a loader in
src/lib/loadBundle.ts, a type +parseXxx(results)narrower insrc/types/, and the action trio (run<Name>Blocking,start<Name>Run,poll<Name>Run) insrc/actions/. - Wire it from a component with
useRun({ mode, blocking, start, poll }). The three existing examples are the canonical patterns to copy.
| Target | Purpose |
|---|---|
make dev |
Start the Next.js dev server |
make build |
Production build |
make lint |
ESLint |
make format |
Prettier write |
make format-check |
Prettier check (CI) |
make typecheck |
tsc --noEmit |
make test |
Vitest single pass (unit tests, no API call) |
make agent-test |
Vitest, silent on success (for AI agents) |
make test-e2e |
Optional Playwright e2e — live API, costs an LLM call (prompts first; auto-skips without a key) |
make test-e2e-ui |
Same, with the Playwright UI runner |
make check |
lint + format-check + typecheck |
make all |
check + test + build (does not run e2e — see test-e2e) |
make use-local |
Pack & install sibling ../pipelex-sdk-js into node_modules (alias: ul) |
make use-npm |
Restore the npm-published @pipelex/sdk package (alias: un) |
The Playwright specs are optional — make all never runs them, and you can delete e2e/ entirely if you don't want live tests. They open the dev server and exercise each example tab end-to-end, asserting the expected output.
The three happy-path specs (extract, summarize-pdf, generate-image) hit the live Pipelex API using PIPELEX_API_KEY from .env.local, so they cost an LLM call each. To keep that deliberate and safe:
- They auto-skip without a key. No
PIPELEX_API_KEY? Those specs skip cleanly (you'll see them reported as skipped) instead of failing with an auth error — so a fresh fork can runmake test-e2ebefore configuring credentials. make test-e2eprompts for confirmation before spending, since it costs money. The prompt is skipped in CI / non-interactive shells; passCONFIRM=1 make test-e2eto bypass it in scripts.- It is excluded from
make all. - The fourth spec,
error-display, tests the offline error UX — it needs no key, costs nothing, and runs out of the box. - First-time setup needs the browser binary:
npx playwright install chromium.
If you have the pipelex-sdk-js repo checked out as a sibling directory (../pipelex-sdk-js) and want this app to use it instead of the published npm package:
make use-local # builds ../pipelex-sdk-js, packs it with `npm pack`, installs the tarball into node_modules/@pipelex/sdk
make use-npm # restores the npm versionAliases: make ul / make un. Re-run make use-local after every SDK edit — the tarball is a snapshot, not a live link. We use a tarball install rather than a symlink because Next.js 16's Turbopack does not follow symlinked workspace packages (Module not found: Can't resolve '@pipelex/sdk').
| Variable | Purpose | Default |
|---|---|---|
PIPELEX_BASE_URL |
Pipelex API base URL — hosted (https://api.pipelex.com) or your own self-hosted instance |
https://api.pipelex.com |
PIPELEX_API_KEY |
Bearer token used by the SDK | (required at runtime) |
NEXT_PUBLIC_EXECUTION_MODE |
Default execution mode for the examples — durable or blocking. Each example also has a runtime toggle. |
durable |
This project is licensed under the MIT license. Runtime dependencies are distributed under their own licenses via npm.