# SolidStart (/docs/guides/frameworks/solid-start) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Add Prisma ORM to a SolidStart app with orm init, seed a PostgreSQL database, serve users from an API route, and render them in a page. Location: Guides > Frameworks > SolidStart ## Introduction [#introduction] SolidStart is a full-stack framework for building reactive web apps with SolidJS. Its API routes and server functions run on the server, which is where you call Prisma ORM to read from a PostgreSQL database. In this guide, you scaffold a SolidStart project, add Prisma ORM to it with `orm init`, initialize and seed a PostgreSQL database, serve users from an API route, and render them in a page with loading and error states. There is no `create-prisma` template for SolidStart, so this guide follows the add-to-an-existing-project path. Every command, file, and response below was run end to end against a local PostgreSQL database. > [!NOTE] > Using Prisma ORM 7? > > Prisma ORM 8 is the current release. Prisma ORM 7 remains fully supported; the Prisma ORM 7 version of this guide is at [/guides/v7/frameworks/solid-start](https://www.prisma.io/docs/guides/v7/frameworks/solid-start). ## Prerequisites [#prerequisites] * [Node.js](https://nodejs.org) 24 or later (the SolidStart 2 template requires it) * A PostgreSQL connection string, or nothing at all: `npx create-db@latest` can create a [Prisma Postgres](https://www.prisma.io/docs/postgres) database for you ## 1. Scaffold the SolidStart project [#1-scaffold-the-solidstart-project] Create a new SolidStart 2 project from the `basic` TypeScript template: #### bun ```bash bunx create-solid my-solid-prisma-app -s --v2 -t basic --ts ``` #### pnpm ```bash pnpm create solid my-solid-prisma-app -s --v2 -t basic --ts ``` #### yarn ```bash yarn create solid my-solid-prisma-app -s --v2 -t basic --ts ``` #### npm ```bash npm init solid@latest my-solid-prisma-app -- -s --v2 -t basic --ts ``` ```text no-copy ◇ Project created 🎉 ◇ To get started, run: ───╮ │ cd my-solid-prisma-app │ │ npm install │ │ npm run dev │ ``` The flags skip the prompts: `-s` picks SolidStart, `--v2` picks the stable SolidStart 2 line, `-t basic` picks the template, and `--ts` picks TypeScript. Without them, the CLI asks the same questions interactively. The template ships a `pnpm-lock.yaml`. Delete it before you continue if you use npm; otherwise `orm init` reads the lockfile and installs Prisma with pnpm: ```bash cd my-solid-prisma-app rm pnpm-lock.yaml ``` Install the dependencies: #### bun ```bash bun install ``` #### pnpm ```bash pnpm install ``` #### yarn ```bash yarn install ``` #### npm ```bash npm install ``` This writes `package-lock.json`, so the next step picks npm. ## 2. Add Prisma ORM [#2-add-prisma-orm] Run `orm init` from the project root. The flags preselect PostgreSQL and the Prisma Schema Language; drop `--yes` and `--authoring` to answer those questions interactively: #### bun ```bash bunx prisma@latest orm init --yes --target postgres --authoring psl ``` #### pnpm ```bash pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl ``` #### yarn ```bash yarn dlx prisma@latest orm init --yes --target postgres --authoring psl ``` #### npm ```bash npx prisma@latest orm init --yes --target postgres --authoring psl ``` ```text no-copy Updated tsconfig.json with required compiler options. ✔ npm add @prisma/orm-postgres dotenv ✔ npm add -D prisma@latest @types/node ✔ npm add -D @prisma/cli-engine@0.6.3 ✔ Emit the contract │ target: postgres │ authoring: psl │ schema: src/prisma/contract.prisma written ├─ src/prisma/contract.prisma ├─ prisma.config.ts ├─ src/prisma/db.ts ├─ prisma-8.md ├─ .env.example ├─ tsconfig.json ├─ .gitignore ├─ .gitattributes └─ package.json ✔ Done. Open prisma-8.md to get started. ``` The command installs the runtime, writes the Prisma ORM files into the SolidStart project, and emits `src/prisma/contract.json` and `src/prisma/contract.d.ts`, the artifacts your queries are type-checked against. Three files matter for the rest of this guide: * `src/prisma/contract.prisma`: a starter contract with `User` and `Post` models and a one-to-many relation between them * `src/prisma/db.ts`: the Prisma ORM client, constructed once and imported by your routes * `prisma.config.ts`: tells the CLI where the contract lives and reads `DATABASE_URL` from `.env` The starter contract is the same shape the Prisma ORM 7 guide asked you to write by hand: ```prisma title="src/prisma/contract.prisma" // use prisma-8 model User { id Int @id @default(autoincrement()) email String @unique username String? name String? posts Post[] createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } model Post { id Int @id @default(autoincrement()) title String content String? author User @relation(fields: [authorId], references: [id]) authorId Int createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } ``` And the scaffolded client is all the wiring the app needs. There is no `prisma generate`, no generated client directory, and no driver adapter; the emitted contract and the runtime package replace all three: ```typescript title="src/prisma/db.ts" import 'dotenv/config'; import postgres from '@prisma/orm-postgres/runtime'; import type { Contract } from './contract.d'; import contractJson from './contract.json' with { type: 'json' }; export const db = postgres({ contractJson, url: process.env['DATABASE_URL']!, }); ``` `orm init` also adds `"node"` to the `types` array and `resolveJsonModule` to `tsconfig.json` so the JSON contract import type-checks, and leaves the SolidStart settings (`jsx`, `jsxImportSource`, the `~/*` path alias) alone. Now set the database connection. Copy `.env.example` to `.env` and replace the placeholder with your own PostgreSQL connection string, or create a Prisma Postgres database with `npx create-db@latest`; it prints a connection string and a claim URL you can open to keep the database: ```bash title=".env" DATABASE_URL="postgres://user:password@localhost:5432/mydb" ``` Both `prisma.config.ts` and `db.ts` import `dotenv/config`, so the CLI and the dev server read the same file. ## 3. Initialize the database [#3-initialize-the-database] Create the tables the contract declares and sign the database: #### bun ```bash bunx prisma db init ``` #### pnpm ```bash pnpm prisma db init ``` #### yarn ```bash yarn prisma db init ``` #### npm ```bash npx prisma db init ``` ```text no-copy ✔ Introspecting database schema ✔ Planning migration ✔ Initialising database across spaces │ contract: src/prisma/contract.json │ database: postgres://****@localhost:5432/mydb ✔ Applied 5 operation(s) across 1 contract space App space ├─ Create table "Post" ├─ Create table "User" ├─ Add unique constraint on "User" (email) ├─ Create index "Post_authorId_idx_e47547ed" on "Post" ├─ Add foreign key "Post_authorId_fkey" on "Post" └─ marker 91e7f9f035806fa2789a4d726ef7724cad434fd6b00014d47ebf12d6e6bb784e ``` `db init` replaces `prisma migrate dev` from Prisma ORM 7 for the first apply: it creates what is missing and records the contract hash in the database. Later schema changes go through [`db update`](https://www.prisma.io/docs/cli/db-update) for a direct development update or [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) for a checked-in migration. To confirm the database matches the contract at any time, run `npx prisma db verify`. ## 4. Seed the database [#4-seed-the-database] Create `src/prisma/seed.ts`. It creates two users and their posts through the ORM API. `create()` returns the inserted row, so the user's `id` is available for the posts without a second query: ```typescript title="src/prisma/seed.ts" import { db } from "./db.ts"; const users = [ { name: "Alice", email: "alice@prisma.io", posts: [ { title: "Join the Prisma Discord", content: "https://pris.ly/discord" }, { title: "Prisma on YouTube", content: "https://pris.ly/youtube" }, ], }, { name: "Bob", email: "bob@prisma.io", posts: [{ title: "Follow Prisma on Twitter", content: "https://www.twitter.com/prisma" }], }, ]; async function main() { for (const { posts, ...user } of users) { const created = await db.orm.public.User.create(user); for (const post of posts) { await db.orm.public.Post.create({ ...post, authorId: created.id }); } console.log(`Seeded ${created.email} with ${posts.length} post(s)`); } await db.close(); } main().catch((error) => { console.error(error); process.exit(1); }); ``` Node.js 24 runs TypeScript directly, so no extra tooling is needed. Run the script once: ```bash node src/prisma/seed.ts ``` ```text no-copy Seeded alice@prisma.io with 2 post(s) Seeded bob@prisma.io with 1 post(s) ``` Running it a second time fails on the unique `email` constraint, which is expected. The script closes the connection pool at the end because a one-off script would otherwise keep the process alive; the app's routes never do this. ## 5. Serve users from an API route [#5-serve-users-from-an-api-route] SolidStart maps files under `src/routes/api/` to HTTP endpoints. Create `src/routes/api/users.ts`: ```typescript title="src/routes/api/users.ts" import { db } from "~/prisma/db"; export async function GET() { const users = await db.orm.public.User.include("posts").all(); return Response.json(users); } ``` Model access is namespace-qualified on PostgreSQL, so the `User` model is `db.orm.public.User`. `.include("posts")` eager-loads the relation and `.all()` returns the rows as an array. Start the dev server: #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` ```text no-copy VITE v8.3.1 ready in 5509 ms ➜ Local: http://localhost:3000/ ➜ Network: use --host to expose ``` Then request the route: ```bash curl http://localhost:3000/api/users ``` ```json no-copy [ { "createdAt": "2026-09-10 21:45:05.701722+06", "email": "alice@prisma.io", "id": 1, "name": "Alice", "updatedAt": "2026-09-10 21:45:04.523+06", "username": null, "posts": [ { "authorId": 1, "content": "https://pris.ly/discord", "createdAt": "2026-09-10 21:45:05.875837+06", "id": 1, "title": "Join the Prisma Discord", "updatedAt": "2026-09-10 21:45:05.874+06" }, { "authorId": 1, "content": "https://pris.ly/youtube", "createdAt": "2026-09-10 21:45:05.975729+06", "id": 2, "title": "Prisma on YouTube", "updatedAt": "2026-09-10 21:45:05.975+06" } ] }, { "createdAt": "2026-09-10 21:45:05.980593+06", "email": "bob@prisma.io", "id": 2, "name": "Bob", "updatedAt": "2026-09-10 21:45:05.979+06", "username": null, "posts": [ { "authorId": 2, "content": "https://www.twitter.com/prisma", "createdAt": "2026-09-10 21:45:05.984988+06", "id": 3, "title": "Follow Prisma on Twitter", "updatedAt": "2026-09-10 21:45:05.983+06" } ] } ] ``` The route handler is ordinary SolidStart code calling an ordinary Prisma ORM query; there is no framework adapter in between. ## 6. Render the users in a page [#6-render-the-users-in-a-page] Replace `src/routes/index.tsx` with a page that loads the same query. The Prisma ORM 7 guide fetched the API route from the component with `fetch("http://localhost:3000/api/users")`. SolidStart renders pages on the server first, where a relative `fetch("/api/users")` fails with `Invalid URL` and an absolute one hard-codes your host, so the page calls the query through a server function instead. `query` from `@solidjs/router` caches and deduplicates it, and `createAsync` exposes the result to the component: ```typescript title="src/routes/index.tsx" import { Title } from "@solidjs/meta"; import { createAsync, query } from "@solidjs/router"; import { ErrorBoundary, For, Suspense } from "solid-js"; import { db } from "~/prisma/db"; const getUsers = query(async () => { "use server"; try { return await db.orm.public.User.include("posts").all(); } catch (error) { console.error(error); throw new Error("Could not load users"); } }, "users"); export default function Home() { const users = createAsync(() => getUsers()); return (
SolidStart + Prisma

SolidStart + Prisma

Error loading data

}> Loading...

}> {(user) => (

{user.name}

{(post) =>

{post.title}

}
)}
); } ``` Three things to notice: * `"use server"` keeps the Prisma query and the database connection on the server. The browser only receives the rows. * The rows are typed by the contract: `user.name` and `user.posts` autocomplete without importing any generated types. The `User` and `Post` type imports from the Prisma ORM 7 guide are gone because `createAsync` infers the shape from the query. * `` renders the loading state while the query runs and `` renders the error state if it throws. The `catch` block logs the real Prisma error on the server and throws a plain `Error` for the client; see the gotchas below for why. Open [http://localhost:3000](http://localhost:3000) or request the page from the terminal: ```bash curl http://localhost:3000/ ``` The server-rendered HTML contains `

Alice

` and `

Bob

` with their post titles, streamed in after the `Loading...` fallback. Your SolidStart app now reads users and their posts from PostgreSQL through Prisma ORM, over both an API route and a server-rendered page. ## Common gotchas [#common-gotchas] > [!WARNING] > Don't call `db.close()` in API routes or server functions. The client in `src/prisma/db.ts` is constructed once and its connection pool is shared across requests; close it only in one-off scripts like the seed. * **`orm init` installs with pnpm.** The SolidStart template ships a `pnpm-lock.yaml`, and `orm init` picks its package manager from the lockfile it finds. Delete that file before `npm install` (step 1), or use pnpm throughout. * **`fetch("/api/users")` fails during server rendering.** Node.js has no origin to resolve a relative URL against, so the render throws `TypeError: Failed to parse URL from /api/users`. Load data through a `"use server"` function as in step 6; keep the API route for HTTP clients. * **Rethrow a plain `Error` from server functions.** Prisma ORM throws structured errors that carry the SQL state, the failing statement, and a nested cause. When one of those crosses the server-to-browser boundary during server rendering, the page hangs on hydration instead of showing the `` fallback. Catching it and throwing `new Error("Could not load users")` keeps the fallback working and keeps database details out of the browser. * **The first request after `npm run dev` can return a 503.** While Vite is still creating its server environment, a request may answer `Vite environment "ssr" is unavailable`. Wait a second and retry; every request after that succeeds. ## Use with your agent [#use-with-your-agent] To delegate this guide to your coding agent, copy the prompt below and hand it over: ```text Create a new SolidStart app with Prisma ORM, seed it, and serve users from an API route and a page. 1. Scaffold: `npm init solid@latest my-solid-prisma-app -- -s --v2 -t basic --ts`. Delete the `pnpm-lock.yaml` the template ships (otherwise Prisma picks pnpm), then `cd my-solid-prisma-app` and run `npm install`. 2. Add Prisma ORM: `npx prisma@latest orm init --yes --target postgres --authoring psl`. Then run `npx prisma@latest init` so the Prisma agent skills are installed and stay current, and use them. Get a database connection string: use the one I give you, or create a Prisma Postgres database with `npx create-db@latest` and show me the claim URL it prints. Write it to `.env` as `DATABASE_URL`. 3. Run `npx prisma db init` to create the tables from `src/prisma/contract.prisma`. 4. Add `src/prisma/seed.ts` that creates two users with posts through `db.orm.public.User.create` and `db.orm.public.Post.create`, closes with `await db.close()`, and run it once with `node src/prisma/seed.ts`. 5. Add `src/routes/api/users.ts` with a `GET` handler that returns `db.orm.public.User.include("posts").all()` as JSON, and replace `src/routes/index.tsx` with a page that loads the same query through a `"use server"` function wrapped in `query` and `createAsync`, with `` for loading and `` for errors, following https://www.prisma.io/docs/guides/frameworks/solid-start.md. Catch Prisma errors inside the server function and rethrow a plain `Error`. 6. Start `npm run dev` in the background, wait until it reports ready, verify `curl http://localhost:3000/api/users` returns the seeded users and `curl http://localhost:3000/` includes their names, then stop the dev server. ``` The prompt above runs [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) in its first step. If you followed the steps by hand, run it once yourself: it installs the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent and keeps them matching your installed packages: #### bun ```bash bunx --bun prisma@latest init ``` #### pnpm ```bash pnpm dlx prisma@latest init ``` #### yarn ```bash yarn dlx prisma@latest init ``` #### npm ```bash npx prisma@latest init ``` ```text no-copy ✔ Added "postinstall": "prisma skills sync || exit 0" to package.json. ⚠ prisma.config.ts already exists; left untouched. ✔ Synced 3 skills. project: ~/my-solid-prisma-app check: enabled Skill Package Version Installed into prisma-8 @prisma/orm-postgres 8.0.0-rc.17 .claude/skills, .cursor/skills, .agents/skills, .devin/skills prisma-composer-core-concepts @prisma/composer 0.25.0 .claude/skills, .cursor/skills, .agents/skills, .devin/skills prisma-platform-core-concepts prisma 8.0.0-rc.21 .claude/skills, .cursor/skills, .agents/skills, .devin/skills ⚠ [INIT.CONFIG_KEPT] prisma.config.ts already exists, so init left it alone instead of writing the skills section. → Add skills: { agents: ["claude", "cursor", "agents", "devin"] } to the object passed to definePrismaConfig in prisma.config.ts. ``` Prompts you can give your agent after this guide: * "Using the prisma-8 skill, add `GET /api/users/:id` that returns one user with posts or a 404." * "Add a `POST /api/users` route that creates a user from the request body with `db.orm.public.User.create`." * "Turn the user list into a form that creates a post through a SolidStart `action` and revalidates the `users` query." ## Next steps [#next-steps] * Change the schema in `src/prisma/contract.prisma`, then run `npx prisma contract emit` and `npx prisma db update`. SolidStart builds on Vite, so the [Vite plugin](https://www.prisma.io/docs/cli/contract-emit#run-it-automatically) can run `contract emit` for you whenever the contract changes while the dev server runs. * [Learn the fundamentals](https://www.prisma.io/docs/orm/fundamentals/reading-data): filtering, sorting, pagination, and writes. * [Read the Prisma ORM overview](https://www.prisma.io/docs/orm) for the concepts behind contracts and typed queries. * [SolidStart documentation](https://start.solidjs.com/) for routing, server functions, and deployment presets. ## Related pages - [`Astro`](https://www.prisma.io/docs/guides/frameworks/astro): Set up Prisma ORM in an Astro app with create-prisma, from scaffold to rendered data, and deploy it to Prisma Compute. - [`Elysia`](https://www.prisma.io/docs/guides/frameworks/elysia): Build an Elysia API on Prisma ORM with the elysia template and deploy it to Prisma Compute. - [`Hono`](https://www.prisma.io/docs/guides/frameworks/hono): Build a Hono API on Prisma ORM with the hono template, add your own routes, and deploy it to Prisma Compute. - [`NestJS`](https://www.prisma.io/docs/guides/frameworks/nestjs): Set up Prisma ORM in a NestJS app with create-prisma, from scaffold to seeded API to a live deploy on Prisma Compute. - [`Next.js`](https://www.prisma.io/docs/guides/frameworks/nextjs): Set up Prisma ORM in a Next.js app with create-prisma, from scaffold to rendered data, and deploy it to Prisma Compute.