A CLI tool for scaffolding out a new Next.js application with @schemavaults/auth, @schemavaults/theme, and @schemavaults/dbh configured.
Every scaffolded app also ships a typed HTTP API layer built on @schemavaults/openapi-operations: each route under src/app/api/ is defined once (zod request/response schemas, auth requirements, handler) and served as its own Hono app from its Next.js route.ts. public/openapi.json is generated from the same definitions on every bun run dev / bun run build (git-ignored; bun run openapi:check verifies the route files against the catalogue in lint and CI) and served at /openapi.json; /docs renders the document with @schemavaults/openapi-docs-ui. An api-routes Claude Code skill in the app's .claude/skills/ explains how to add routes so they are registered in the document.
When initializing a project, the CLI also installs the @schemavaults/dbh database-migrations Claude Code skill into the new project's .claude/skills/ (via npx skills add), so coding agents know how to author migrations in the format this template scaffolds. It additionally scaffolds a nextjs-docs skill that points coding agents at the version-matched Next.js documentation bundled with the installed next package (node_modules/next/dist/docs/) instead of web searches or memory.
Use the latest version of @schemavaults/init-next-app published to NPM:
npx @schemavaults/init-next-app my-new-app-nameAny value not provided via a flag is requested via an interactive stdin prompt.
For scripts and CI where stdin prompts are not possible, pass every value as a flag:
npx @schemavaults/init-next-app my-new-app-name \
--display-name "My New App" \
--description "A short description of my new app" \
--client-app-id "my-new-app" \
--api-server-id "my-api-server" \
--auth-server-url "https://auth.schemavaults.com"| Flag | Description |
|---|---|
--display-name <name> |
Human-readable project name. |
--description <text> |
Project description. |
--client-app-id <id> |
SCHEMAVAULTS_CLIENT_APP_ID written to .env.local. Validated with appIdSchema from @schemavaults/app-definitions: 2-64 characters of lowercase alphanumerics, hyphens, and underscores, starting with an alphanumeric and not ending with a hyphen or underscore (UUIDs remain valid). |
--api-server-id <id> |
SCHEMAVAULTS_API_SERVER_ID written to .env.local. Validated with apiServerIdSchema from @schemavaults/app-definitions (same format as --client-app-id). |
--auth-server-url <url> |
SCHEMAVAULTS_AUTH_SERVER_URL written to .env.local (must be an http(s) URL). Defaults to https://auth.schemavaults.com; set this to point the app at a self-hosted auth server, e.g. https://auth.acmecorp.com. When prompted interactively, press enter to accept the default. |
--deployment <strategy> |
vercel or none. With vercel, the app also gets a vercel.json, a publish-to-vercel job in .github/workflows/ci.yml, and VERCEL_* entries in .env.example. |
The generated application lives in this repository as a real Next.js project,
templates/schemavaults-next-app/, and is rendered by
@jalexw/mould. Every file you see there is copied to the new
project as-is, apart from:
- Placeholders such as
xxx_project_name_xxxandxxx_display_name_xxx, which are replaced with the values you pass. They are lowercase, identifier-safe tokens, so the template itself is a valid npm package name / env value / JSX text and installs and type-checks unmodified. The auth server URL is substituted from its literal defaulthttps://auth.schemavaults.com. - Conditional blocks wrapped in
# mould:if deployment == vercel…# mould:endifcomment lines (.github/workflows/ci.yml,.env.example), andvercel.json, which is only copied when--deployment vercelis chosen. _gitignoreand_env.local, written to the new project as.gitignoreand.env.local. npm would otherwise rename a real.gitignoreinside the published package, and keeping every.env*(other than.env.example) out of the template means a real env file can never be committed or shipped by accident.@schemavaults/*versions inpackage.json, which the CLI bumps to the latest published versions after rendering; the template pins real versions so it installs on its own.
The mapping is declared in .mouldconfig.json.
After rendering, the CLI runs bun install, bun run auth-codegen and installs the
database-migrations Claude skill, as before.
bun installbun run build
node ./dist/index.js test-appWork on the template like on any other Next.js app — with type checking, eslint and your editor's tooling:
cd templates/schemavaults-next-app
bun install
bun run typecheck # runs auth-codegen first; its output is git-ignored and never shipped
bun run lint # also checks the API route files against the operations catalogue
bun run openapi:generate # writes the git-ignored public/openapi.json (dev and build do this too)To run the template itself, copy _env.local to .env.local first; every .env* except
.env.example is ignored by git and excluded from the published package.
Rules of thumb:
- Never add a
.gitignoreor a.env*file inside the template; edit_gitignore/_env.localfor the generated app and the repository root.gitignorefor development artefacts. - New placeholders must be declared under
substitutionsin.mouldconfig.json; new build artefacts must be listed in three places:ignorePatternsin.mouldconfig.json(so mould never copies them), the root.gitignore(so git never tracks them), and the negatedfilesentries in the rootpackage.json(sonpm packnever ships them — a checkout where the template was just installed would otherwise put itsnode_modules/in the tarball). scripts/test-template.shchecks all of the above and that the template type-checks.
bun run test # end-to-end: pack, scaffold test-app from the tarball, install, typecheck, lint, build
bun run test:template # the template directory itself installs, type-checks and lints