apps/docs/content/docs/guides/frameworks/astro.mdx
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.
Create a new Astro project:
npx create-astro@latest
:::info
astro-prismaUse minimal (empty) template YesYes:::
Navigate into the newly created project directory:
cd astro-prisma
To get started with Prisma, you'll need to install a few dependencies:
npm install prisma tsx @types/pg --save-dev
npm install @prisma/client @prisma/adapter-pg dotenv pg
:::info
If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of @prisma/adapter-pg. For more information, see Database drivers.
:::
Once installed, initialize Prisma in your project:
npx prisma init --db --output ../prisma/generated
:::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 Astro Project" ::: This will create:
prisma/ directory with a schema.prisma fileprisma.config.ts file for configuring Prisma.env file with a DATABASE_URL already setIn 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 = "../prisma/generated"
}
datasource db {
provider = "postgresql"
}
model User { // [!code ++]
id Int @id @default(autoincrement()) // [!code ++]
email String @unique // [!code ++]
name String? // [!code ++]
posts Post[] // [!code ++]
} // [!code ++]
// [!code ++]
model Post { // [!code ++]
id Int @id @default(autoincrement()) // [!code ++]
title String // [!code ++]
content String? // [!code ++]
published Boolean @default(false) // [!code ++]
authorId Int // [!code ++]
author User @relation(fields: [authorId], references: [id]) // [!code ++]
} // [!code ++]
This creates two models: User and Post, with a one-to-many relationship between them.
dotenv to prisma.config.tsTo get access to the variables in the .env file, they can either be loaded by your runtime, or by using dotenv.
Include an import for dotenv at the top of the prisma.config.ts
import "dotenv/config"; // [!code ++]
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: env("DATABASE_URL"),
},
});
Now, run the following command to create the database tables:
npx prisma migrate dev --name init
Then generate Prisma Client:
npx prisma generate
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 "../prisma/generated/client";
import { PrismaPg } from "@prisma/adapter-pg";
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
const prisma = new PrismaClient({
adapter,
});
const userData: Prisma.UserCreateInput[] = [
{
name: "Alice",
email: "[email protected]",
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: "[email protected]",
posts: {
create: [
{
title: "Follow Prisma on Twitter",
content: "https://www.twitter.com/prisma",
published: true,
},
],
},
},
];
export async function main() {
console.log("Starting to seed...");
for (const u of userData) {
await prisma.user.upsert({
where: { email: u.email },
update: {},
create: u,
});
}
console.log("Seeding finished.");
}
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 prisma.config.ts:
import "dotenv/config";
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: `tsx prisma/seed.ts`,
},
datasource: {
url: env("DATABASE_URL"),
},
});
Run the seed script:
npx prisma db seed
And open Prisma Studio to inspect your data:
npx prisma studio
First, create an env.d.ts file in your src directory to provide TypeScript definitions for environment variables:
interface ImportMetaEnv {
readonly DATABASE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
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:
import { PrismaClient } from "../../prisma/generated/client";
import { PrismaPg } from "@prisma/adapter-pg";
const adapter = new PrismaPg({
connectionString: import.meta.env.DATABASE_URL,
});
const prisma = new PrismaClient({
adapter,
});
export default prisma;
:::warning 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.
:::
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" },
});
};
Instead of using fetch() with HTTP requests, Astro recommends importing endpoint functions directly. This approach is more efficient and avoids URL parsing issues.
Start by creating a new type that combines the User and Post models called UserWithPosts:
---
import type { User, Post } from "../../prisma/generated/client"; // [!code ++]
import { GET } from "./api/users.ts"; // [!code ++]
// [!code ++]
type UserWithPosts = User & { posts: Post[] }; // [!code ++]
// [!code ++]
const response = await GET(Astro); // [!code ++]
const users: UserWithPosts[] = await response.json(); // [!code ++]
---
<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 + Prisma</title>
</head>
<body>
<h1>Astro + Prisma</h1>
<ul> // [!code ++]
{users.map((user: UserWithPosts) => ( // [!code ++]
<li> // [!code ++]
<h2>{user.name}</h2> // [!code ++]
<ul> // [!code ++]
{user.posts.map((post: Post) => ( // [!code ++]
<li>{post.title}</li> // [!code ++]
))} // [!code ++]
</ul> // [!code ++]
</li> // [!code ++]
))} // [!code ++]
</ul> // [!code ++]
</body>
</html>
Now start your development server to see your Astro app in action:
npm run dev
Open your browser at http://localhost:4321 to see the users and their posts displayed on the page.
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.
Now that you have a working Astro app connected to a Prisma Postgres database, you can: