How to use Prisma ORM with Astro
Introduction
Prisma ORM offers type-safe database access, and Astro is built for performance. Together with Prisma Postgres, you get a fast, content-first stack with zero cold starts and end-to-end speed.
In this guide, you'll learn to integrate Prisma ORM with a Prisma Postgres database in an Astro project from scratch. You can find a complete example of this guide on GitHub.
Prerequisites
1. Set up your project
Create a new Astro project:
npx create-astro@latest
- Where should we create your new project?
astro-prisma
- How would you like to start your new project?
Use minimal (empty) template
- Install dependencies? (recommended)
Yes
- Initialize a new git repository? (optional)
Yes
2. Install and Configure Prisma
2.1. Install dependencies
To get started with Prisma, you'll need to install a few dependencies:
- Prisma Postgres (recommended)
- Other databases
npm install prisma tsx --save-dev
npm install @prisma/extension-accelerate @prisma/client
npm install prisma tsx --save-dev
npm install @prisma/client
Once installed, initialize Prisma in your project:
npx prisma init --db --output ../src/generated/prisma
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 Astro Project"
This will create:
- A
prisma/
directory with aschema.prisma
file - A
.env
file with aDATABASE_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:
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
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])
}
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:
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:
import { PrismaClient, Prisma } from "../src/generated/prisma";
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();
Now, tell Prisma how to run this script by updating your package.json
:
{
"name": "extinct-eclipse-minimal",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.7.0",
"@prisma/extension-accelerate": "^1.3.0",
"astro": "^5.7.10"
},
"devDependencies": {
"prisma": "^6.7.0",
"tsx": "^4.19.4"
}
}
Run the seed script:
npx prisma db seed
And open Prisma Studio to inspect your data:
npx prisma studio
3. Integrate Prisma into Astro
3.1. Create a Prisma Client
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.
mkdir src/lib
touch src/lib/prisma.ts
Set up the Prisma client like this:
- Prisma Postgres (recommended)
- Other databases
import { PrismaClient } from "../generated/prisma";
import { withAccelerate } from "@prisma/extension-accelerate";
const prisma = new PrismaClient({
datasourceUrl: import.meta.env.DATABASE_URL,
}).$extends(withAccelerate());
export default prisma;
import { PrismaClient } from "../generated/prisma";
const prisma = new PrismaClient({
datasourceUrl: import.meta.env.DATABASE_URL,
})
export default prisma;
We recommend using a connection pooler (like Prisma Accelerate) to manage database connections efficiently.
If you choose not to use one, avoid instantiating PrismaClient
globally in long-lived environments. Instead, create and dispose of the client per request to prevent exhausting your database connections.
3.2. Create an API route
An API route is the best way to fetch data from your database in an Astro app.
Create a new file called api/users.ts
in the src/pages
directory:
mkdir src/pages/api
touch src/pages/api/users.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:
import type { APIRoute } from "astro";
import prisma from "../../lib/prisma";
export const GET: APIRoute = async () => {
const users = await prisma.user.findMany({
include: { posts: true },
});
return new Response(JSON.stringify(users), {
headers: { "Content-Type": "application/json" },
});
};
Next, you'll call this route from the index.astro
file and display it.
3.3. Fetch the data from the API route
Start by create a new type that combines the User
and Post
models called UserWithPosts
:
---
import type { User, Post } from "../generated/prisma";
type UserWithPosts = User & { posts: Post[] };
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>
Fetch the data from the API route and set it's type to the UserWithPosts
type you just created:
---
import type { User, Post } from "../generated/prisma";
type UserWithPosts = User & { posts: Post[] };
const response = await fetch("http://localhost:4321/api/users");
const users: UserWithPosts[] = await response.json();
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>
3.4. Display the data
With the variables from the frontmatter now available throughout your file, you can display the list of users.
Just below the <h1>
tag, map through the users
array, add the UserWithPosts
type to the users, and display the names of the users:
---
import type { User, Post } from "../generated/prisma";
type UserWithPosts = User & { posts: Post[] };
const response = await fetch("http://localhost:4321/api/users");
const users: UserWithPosts[] = await response.json();
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro + Prisma</h1>
{users.map((user: UserWithPosts) => (
<li>
<h2>{user.name}</h2>
</li>
))}
</body>
</html>
Finally, display the Posts
below the respective User
and set the Post
type:
---
import type { User, Post } from "../generated/prisma";
type UserWithPosts = User & { posts: Post[] };
const response = await fetch("http://localhost:4321/api/users");
const users: UserWithPosts[] = await response.json();
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro + Prisma</h1>
<ul>
{users.map((user: UserWithPosts) => (
<li>
<h2>{user.name}</h2>
<ul>
{user.posts.map((post: Post) => (
<li>{post.title}</li>
))}
</ul>
</li>
))}
</ul>
</body>
</html>
You're done! You've just created an Astro app with Prisma that's connected to a Prisma Postgres database. Below are some next steps to explore, as well as some more resources to help you get started expanding your project.
Next Steps
Now that you have a working Astro 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 for better performance
More Info
Stay connected with Prisma
Continue your Prisma journey by connecting with our active community. Stay informed, get involved, and collaborate with other developers:
- Follow us on X for announcements, live events and useful tips.
- Join our Discord to ask questions, talk to the community, and get active support through conversations.
- Subscribe on YouTube for tutorials, demos, and streams.
- Engage on GitHub by starring the repository, reporting issues, or contributing to an issue.