Hype StackHypeStack

Stack

Hono Prisma Starter

Connecting Hono to Prisma takes about four lines. Everything after those four lines is what this walkthrough is about.

The first Hono and Prisma route needs little code: two imports, a client, and a route. Many repositories called some variation of hono prisma starter stop at that connection. A production application also needs a query strategy, typed client-server calls, local infrastructure, and deployment configuration.

This is a walkthrough of the open-source Hype Stack core, which adds that surrounding setup. After the install and three commands, you have Hono, Prisma, Kysely, and Postgres running behind a React frontend that knows your route types.

Everything in this guide is MIT licensed and the three steps cost nothing to follow. The last section is the honest part: what the core does not have, and what the cards further down the page cost.

What you need installed

Node 20 or newer, pnpm 9 or newer, and Docker. The CLI checks all three before it touches the disk and prints what it found. A missing Docker is a warning rather than a stop, because scaffolding works fine without it, but step two is where you get a database, so you will want it.

Step 1: create the project

bash
npx @hype-stack/cli create my-app

It asks for the project name, the parent folder to drop it in, your editors, and a color theme for the frontend and for the admin app. The editor question does more than it looks like it does. It decides where the bundled coding rules land, .cursor/rules, AGENTS.md, .claude/rules, .github/instructions, or .windsurf/rules, so whichever agent you use starts out knowing how this codebase is organized instead of inventing its own conventions in your repository. Tick more than one and every format ships, which is what you want when half the team is in Cursor and the other half is in Claude Code. The bundled skills come along either way, in the shared .agents/skills folder every one of those agents reads.

The CLI then clones the template, deletes its git history and commits a fresh repository, and rewrites the @hype-stack scope to your project name everywhere it appears: package names, imports, tsconfig path mappings, the backend Dockerfile, the page title. Then it writes stack.json, runs pnpm install, and copies every .env.example to a real .env with local development values already in it.

my-app
  • apps/frontend/

    React 19, TanStack Router, Tailwind v4, shadcn/ui, and an Electron build behind a flag

  • apps/admin/

    a second React app on its own port, one page in it so far

  • apps/backend/
  • docker-compose.yml

    Postgres, Valkey, and object storage for local dev

  • prisma/schema/

    base.prisma only: the two generator blocks and no models yet

  • src/routes/

    the HTTP boundary, one folder per domain, chained in registerRoutes

  • src/features/

    a vertical slice per domain: modules, db access, schemas, tests

  • src/db/postgres/

    where the generated Prisma client and Kysely types land

  • packages/enums/

    config and enums every app imports, so they cannot disagree

  • stack.json

    your workspace paths and the packs installed so far

  • apps/*/.env

    copied from the examples, local defaults filled in

The template is deliberately empty of product features, so you do not need to remove a demo application before adding your own domains.

Step 2: start Postgres, Valkey, and storage

The CLI offers to do this at the end of create. Say yes and it starts the containers, polls Postgres with pg_isready until it accepts connections, then asks before applying migrations. To do it yourself, or later, from apps/backend:

bash
docker compose up -d
pnpm exec prisma generate

There is nothing to migrate yet, which surprises people. base.prisma ships with the two generator blocks and no models, and prisma/migrations holds only the lock file, so the first migration is the one you write in the next section. prisma generate is worth running now anyway. Nx has it as a dependency of the dev servers, so pnpm dev would trigger it, but running it here means your editor is not full of red imports while you read the code.

ServicePortWhat it is for
Postgres 17 with pgvector5436The database. Vector column type is already enabled, so embeddings do not need a second store.
Valkey6381Cache, Redis compatible, with a client wired in src/cache.
RustFS9000S3-compatible object storage, so uploads work locally without an AWS account.
RustFS console9001Web UI for browsing what you uploaded.

Two things are worth knowing before you start the containers. The credentials in apps/backend/.env are development defaults committed to the repository, so treat them as throwaway. And the container names and host ports are fixed, which means a second Hype Stack project on the same machine collides with the first. The CLI recognizes that failure and names the container to stop.

Step 3: run it

bash
pnpm dev

Nx starts three servers in parallel: the Hono API on port 3000, the Vite frontend on 4200, and the admin app on 4100. The React apps hot reload, the API rebuilds and restarts on change.

Moving a port is a two-file job, which is worth knowing before you go looking in the wrong one. The numbers are literals in each app's configs/vite.web.config.mts. The matching FRONTEND_URL and ADMIN_URL in apps/backend/.env are what the backend puts in links and allows through CORS, so change both or the two stop agreeing. To boot a single app instead of all three, name it: nx serve @my-app/frontend.

That completes the setup. The rest of this page describes the architecture included in this hono prisma starter.

What is already wired

Included infrastructure

A real monorepo, not a folder convention

apps/frontend, apps/admin, apps/backend, and packages/enums are pnpm workspaces, and the two React apps import the backend as a dependency. That import makes the type bridge below possible, and it is fiddly enough to wire that most starters skip it.

Rules and skills for whatever agent you use

Editor rules ship with the template and get converted to your editor's format during create, and agent skills are pinned in skills-lock.json. A few project-specific lint rules back them up, and the GitHub Actions workflow that comes with the repository runs build, lint, typecheck, and the tests, so the conventions are checked rather than suggested.

Tests that run against a real database

Vitest, React Testing Library, and Playwright are configured. The backend also ships a second compose file on different ports, so pnpm test:setup boots temporary containers, applies migrations, and runs query tests against Postgres rather than a database mock.

The same app on the desktop

Electron Forge is configured for macOS, Windows, and Linux, and one VITE_APP_TYPE flag decides whether the React app boots as a web SPA or a desktop build. You can leave the desktop path unused if the product only targets the web.

Adding your first feature

The loop below shows how a feature moves from the database schema to a typed frontend call. Step two differs from a plain Prisma setup because it generates both database clients.

One feature, end to end

  1. Add the model

    prisma/schema/ holds one file per feature rather than one long schema, so a new domain is a new file. Write the model there.

  2. Migrate, which regenerates both clients

    pnpm exec prisma migrate dev writes the SQL migration, updates the Prisma client, and regenerates the Kysely DB interface in the same run. Both typed clients come from the same schema.

  3. Write the read and the write

    Reads go in the feature's db/queries on postgres.qb, which is Kysely. Inserts go in db/mutations through Prisma. Anything transactional uses postgres.qb.transaction().

  4. Add the route and chain it

    A thin handler in src/routes/<domain>, a Zod schema through the validate() middleware, and one .route() line in registerRoutes. The schema is the only place the payload shape is written down.

  5. Call it from the frontend

    useFetch(sdk.notes.$get) is typed from that Zod schema. There is no API client to generate or maintain, and deleting the route produces a type error at the call site.

    No build step

The loop generates the Prisma client and Kysely database types from the same schema. The API client is inferred from the route tree.

Why Prisma and Kysely both

Prisma provides the schema and migrations. For reads with several joins and conditional filters, this stack uses Kysely to keep the SQL structure explicit rather than expressing the query through the Prisma API.

Two generator blocks in prisma/schema/base.prisma do that:

prisma
generator client {
  provider        = "prisma-client-js"
  output          = "../../src/db/postgres/generated/client"
  previewFeatures = ["postgresqlExtensions"]
}

generator kysely {
  provider     = "prisma-kysely"
  output       = "../../src/db/postgres/types"
  fileName     = "types.ts"
  enumFileName = "enums.ts"
  camelCase    = true
}
Two query layers

Running an ORM and a query builder side by side means two mental models and two sets of types that slowly disagree about what a column is called.

One schema, both generated

prisma-kysely reads the same schema and emits the Kysely DB interface next to the Prisma client. Neither is hand-maintained, so neither can drift, and prisma generate produces both in one pass.

The payoff shows up on the reads that grow:

ts
let query = postgres.qb.selectFrom("project").selectAll();

if (search) {
  query = query.where((eb) => eb.or([eb("name", "ilike", `%${search}%`), eb("description", "ilike", `%${search}%`)]));
}

return getPaginatedQuery({ query, limit, offset });

That filter is applied only when the request includes a search value, and the query still reads like the SQL it becomes.

One operational note while you are looking at this. The Kysely instance is attached to the Prisma client as an extension, so there is one thing to import but two pg pools open against the same DATABASE_URL. Count them twice when you set max_connections or deploy behind a connection limit.

Types cross HTTP without a codegen step

The other boundary is the network. In this stack, it does not require a folder of hand-maintained api/*.ts wrappers.

Here the whole route tree is a single type. registerRoutes chains every .route() call and returns the composed app, and the SDK type is derived from its return type:

ts
export type ApiRoutesSdk = HonoAppToHyperFetch<ReturnType<typeof registerRoutes>, ApiClient>;

The frontend imports that type and hands it to HyperFetch with createSdk<typeof client, ApiRoutesSdk>(client). Because the type is inferred from ReturnType<typeof registerRoutes> rather than written down, there is no API code generation step after changing a route. Renaming a backend field produces a type error at affected frontend call sites.

What no codegen means here

"No codegen" is a claim about the API surface, not the whole stack. prisma generate is real code generation and you do run it after a schema change, which is what produces the Prisma client and the Kysely types. The distinction that matters is that nothing regenerates an API client, so the backend and the frontend cannot be out of sync because of a stale build artifact.

What is not in the box

There is no login, no user table, no sessions, no password reset, no organizations, and no billing. The admin app boots but has a single page in it. create gives you an empty application with the architecture already decided, which is a different thing from a product with the features stripped out.

Plan around that honestly: after step three you have a backend, a frontend, and nobody signed in. Nothing in the core assumes a particular auth provider, so writing your own is a normal amount of work rather than a fight with the template. If you would rather install something, Better Auth has a Prisma adapter and sits on this schema without much argument. It is not ours, it does not arrive with the template, and you run it yourself. Skip Lucia, which used to be the other answer to this question: the maintainer retired the package and turned the project into a guide for writing sessions from scratch. Read it if you want to own that code, but do not go looking for a dependency.

One thing to be clear about before you scroll. The pack cards below are paid and license-gated, and the template cards are built on those packs. The three steps on this page need none of them.

Below that: the short version of what Hype Stack is, recordings of real apps running on this core, and the questions people usually ask before they run the first command.

Hype Stack

What is Hype Stack?

Every product starts with the same month of work nobody pays you for: sign-up and login, teams and permissions, taking payments, notifications, an admin panel to run the business. Hype Stack is that month, already built and tested. Start from the free open-source app, add the pieces you need with one command, and keep going on the part that is actually your idea.

Everything lands as real code in your own repository, so there is nothing to rent and nothing anyone can switch off. For the engineers: React 19, Hono, Postgres, and a desktop build, typed end to end.

See it running

Templates are curated project starters built on this stack: a layout, feature packs, a custom theme, and bonus pages. The previews below are recordings of the real apps.

Packs in this stack

Every pack ships real source code: frontend, backend, and admin surfaces where the feature needs them. The ones this stack installs come first; the rest can be composed in later.

Questions, answered

Three commands. Run npx @hype-stack/cli create my-app to scaffold and install, docker compose up -d inside apps/backend to start Postgres, Valkey, and object storage, then pnpm dev at the root. The CLI offers to run the Docker step for you at the end of create, so in practice you answer a prompt and then run pnpm dev.