diff --git a/content/800-guides/390-hono.mdx b/content/800-guides/390-hono.mdx new file mode 100644 index 0000000000..ea45dc8e2b --- /dev/null +++ b/content/800-guides/390-hono.mdx @@ -0,0 +1,392 @@ +--- +title: 'How to use Prisma with Hono' +metaTitle: 'How to use Prisma ORM and Prisma Postgres with Hono' +description: 'Learn how to use Prisma ORM in a Hono app' +sidebar_label: 'Hono' +image: '/img/guides/prisma-hono-cover.png' +completion_time: '15 min' +community_section: true +--- + +## Introduction + +Prisma ORM offers type-safe database access, and [Hono](https://hono.dev/) is built for fast, lightweight web apps. Together with [Prisma Postgres](https://www.prisma.io/postgres), you get a fast, lightweight backend, that can be deployed through Node.js, Cloudflare, or many other runtimes. + +In this guide, you'll learn to integrate Prisma ORM with a Prisma Postgres database in a Hono backend application. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/hono). + +## Prerequisites +- [Node.js 20+](https://nodejs.org) + +## 1. Set up your project + +Create a new Hono project: + +```terminal +npm create hono@latest +``` + +:::info +- *Target directory?* `my-app` +- *Which template do you want to use?* `nodejs` +- *Install dependencies? (recommended)* `Yes` +- *Which package manager do you want to use?* `npm` +::: + +## 2. Install and configure Prisma + +### 2.1. Install dependencies + +To get started with Prisma, you'll need to install a few dependencies: + + + +```terminal +npm install prisma --save-dev +npm install @prisma/extension-accelerate @prisma/client dotenv +``` + + +```terminal +npm install prisma --save-dev +npm install @prisma/client dotenv +``` + + + +Once installed, initialize Prisma in your project: + +```terminal +npx prisma init --db --output ../src/generated/prisma +```` + +:::info +You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Hono Project" +::: +This will create: + +- A `prisma/` directory with a `schema.prisma` file +- A `.env` file with a `DATABASE_URL` already set + +### 2.2. Define your Prisma Schema + +In the `prisma/schema.prisma` file, add the following models and change the generator to use the `prisma-client` provider: + +```prisma file=prisma/schema.prisma +generator client { + //edit-next-line + provider = "prisma-client" + engineType = "client" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +//add-start +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +//add-end +``` + +This creates two models: `User` and `Post`, with a one-to-many relationship between them. + +### 2.3. Configure the Prisma Client generator + +Now, run the following command to create the database tables and generate the Prisma Client: + +```terminal +npx prisma migrate dev --name init +``` +### 2.4. Seed the database + +Let's add some seed data to populate the database with sample users and posts. + +Create a new file called `seed.ts` in the `prisma/` directory: + +```typescript file=prisma/seed.ts +import { PrismaClient, Prisma } from "../src/generated/prisma/client.js"; + +const prisma = new PrismaClient(); + +const userData: Prisma.UserCreateInput[] = [ + { + name: "Alice", + email: "alice@prisma.io", + posts: { + create: [ + { + title: "Join the Prisma Discord", + content: "https://pris.ly/discord", + published: true, + }, + { + title: "Prisma on YouTube", + content: "https://pris.ly/youtube", + }, + ], + }, + }, + { + name: "Bob", + email: "bob@prisma.io", + posts: { + create: [ + { + title: "Follow Prisma on Twitter", + content: "https://www.twitter.com/prisma", + published: true, + }, + ], + }, + }, +]; + +export async function main() { + for (const u of userData) { + await prisma.user.create({ data: u }); + } +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); +``` + +Now, tell Prisma how to run this script by updating your `package.json`: + +```json file=package.json +{ + "name": "my-app", + "type": "module", + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js" + }, + //add-start + "prisma": { + "seed": "tsx prisma/seed.ts" + }, + //add-end + "dependencies": { + "@hono/node-server": "^1.19.5", + "@prisma/client": "^6.16.3", + "@prisma/extension-accelerate": "^2.0.2", + "dotenv": "^17.2.3", + "hono": "^4.9.9" + }, + "devDependencies": { + "@types/node": "^20.11.17", + "prisma": "^6.16.3", + "tsx": "^4.20.6", + "typescript": "^5.8.3" + } +} +``` + +Run the seed script: + +```terminal +npx prisma db seed +``` + +And open Prisma Studio to inspect your data: + +```terminal +npx prisma studio +``` + +## 3. Integrate Prisma into Hono + +### 3.1. Create a Prisma middleware + +Inside of `/src`, create a `lib` directory and a `prisma.ts` file inside it. This file will be used to create and export your Prisma Client instance. Set up the Prisma client like this: + + + + +```tsx file=src/lib/prisma.ts +import type { Context, Next } from 'hono'; +import { PrismaClient } from '../generated/prisma/client.js'; +import { withAccelerate } from '@prisma/extension-accelerate'; + +function withPrisma(c: Context, next: Next) { + if (!c.get('prisma')) { + const databaseUrl = process.env.DATABASE_URL; + + if (!databaseUrl) { + throw new Error('DATABASE_URL is not set'); + } + const prisma = new PrismaClient({ datasourceUrl: databaseUrl }) + .$extends(withAccelerate()); + + c.set('prisma', prisma); + } + return next(); +} +export default withPrisma; +``` + + + + + +```tsx file=src/lib/prisma.ts +import type { Context, Next } from 'hono'; +import { PrismaClient } from '../generated/prisma/client.js'; + +const databaseUrl = process.env.DATABASE_URL; +if (!databaseUrl) { + throw new Error('DATABASE_URL is not set'); +} +const prisma = new PrismaClient({ datasourceUrl: databaseUrl }); + +function withPrisma(c: Context, next: Next) { + if (!c.get('prisma')) { + c.set('prisma', prisma); + } + return next(); +} + +export default withPrisma; +``` + + + +:::warning +We recommend using a connection pooler (like [Prisma Accelerate](https://www.prisma.io/accelerate)) to manage database connections efficiently. + +If you choose not to use one, in long-lived environments (for example, a Node.js server) instantiate a single `PrismaClient` and reuse it across requests to avoid exhausting database connections. In serverless environments or when using a pooler (for example, Accelerate), creating a client per request is acceptable. +::: + +### 3.2 Environment Variables & Types + +By default, Hono does not load any environment variables from a `.env`. `dotenv` handles this and will be read that file and expose them via `process.env`. + +Edit the `src/index.ts` to import `dotenv` and call the `config` method on it. + +```ts file=src/index.ts +import { Hono } from 'hono'; +import { serve } from '@hono/node-server'; + +// Read .env and set variables to process.env +import * as dotenv from 'dotenv'; +dotenv.config(); +``` + +Next, Hono needs additional types to to know that the `withPrisma` middleware will set a `prisma` +key on the Hono Context + +```ts file=src/index.ts +import { Hono } from "hono"; +import { serve } from "@hono/node-server"; +// add-next-line +import type { PrismaClient } from "./generated/prisma/client.js"; + +import * as dotenv from "dotenv"; +dotenv.config(); + +// add-start +type ContextWithPrisma = { + Variables: { + prisma: PrismaClient; + }; +}; +// add-end + +// edit-next-line +const app = new Hono(); + +app.get("/", (c) => { + return c.text("Hello Hono!"); +}); + +serve( + { + fetch: app.fetch, + port: 3000, + }, + (info) => { + console.log(`Server is running on http://localhost:${info.port}`); + } +); +``` + +If using Cloudflare Workers, the environment variables will automatically be set to Hono's contenxt, +so `dotenv` can be skipped. + + +### 3.3. Create A GET Route + +Fetch data from the database using Hono's `app.get` function. This will perform any database queries +and return the data as JSON. + +Create a new route inside of `src/index.ts`: + +Now, create a GET route that fetches the `Users` data from your database, making sure to include each user's `Posts` by adding them to the `include` field: + +```ts file=src/index.ts +import withPrisma from './lib/prisma.js'; + +app.get('/users', withPrisma, async (c) => { + const prisma = c.get('prisma'); + const users = await prisma.user.findMany({ + include: { posts: true }, + }); + return c.json({ users }); +}); +``` + + +### 3.4. Display The Data + +Start the Hono app by call the `dev` script in the `package.json` + +```terminal +npm run dev +``` + +There should be a "Server is running on http://localhost:3000" log printed out. From here, the data +can be viewed by visting `http://localhost:3000/users` or by running `curl` from the command line + +```terminal +curl http://localhost:3000/users | jq +``` + +You're done! You've created a Hono app with Prisma that's connected to a Prisma Postgres database. +For next steps there are some resources below for you to explore as well as next steps for expanding +your project. + +## Next Steps + +Now that you have a working Hono app connected to a Prisma Postgres database, you can: + +- Extend your Prisma schema with more models and relationships +- Add create/update/delete routes and forms +- Explore authentication and validation +- Enable query caching with [Prisma Postgres](/postgres/database/caching) for better performance + +### More Info + +- [Prisma Documentation](/orm/overview/introduction) +- [Hono Documentation](https://hono.dev/docs/) diff --git a/sidebars.ts b/sidebars.ts index e5f245a307..60aa1d19be 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -418,6 +418,7 @@ const sidebars: SidebarsConfig = { "guides/nuxt", "guides/sveltekit", "guides/astro", + "guides/hono", "guides/solid-start", "guides/react-router-7", "guides/tanstack-start", diff --git a/static/img/guides/prisma-hono-cover.png b/static/img/guides/prisma-hono-cover.png new file mode 100644 index 0000000000..ab37ae8929 Binary files /dev/null and b/static/img/guides/prisma-hono-cover.png differ