Connecting a Real Database
Swap the pretend in-memory data for a real database — using Prisma to define a schema and query it straight from a Server Component.
What you will learn
- Explain what an ORM like Prisma does
- Define a model in the Prisma schema
- Query the database directly inside a Server Component
From pretend data to a real database
So far our examples held posts in a plain JavaScript array. That data vanishes when the server restarts and cannot be shared between users. Real apps store data in a database — a program built to save, find and update large amounts of data safely and permanently. A very common choice is PostgreSQL (Postgres), a free, powerful relational database.
What an ORM (Prisma) does for you
Talking to a database normally means writing SQL (Structured Query Language, the language databases speak). An ORM (Object-Relational Mapper) lets you skip raw SQL and work with normal JavaScript objects and methods instead — it translates your JavaScript calls into SQL for you. Prisma is the most popular ORM in the Next.js world.
| Without an ORM | With Prisma (an ORM) |
|---|---|
| Write raw SQL strings by hand | Call methods like prisma.post.findMany() |
| Easy to make typos in SQL | Autocomplete and type-checking help you |
| You map rows to objects yourself | You get JavaScript objects back directly |
Step 1 — describe your data in the schema
Prisma keeps one file, schema.prisma, that describes your tables as models. A model is a blueprint for one kind of record — here, a blog Post with an id, a title and a body.
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
}Note: Output (after running the setup command): The Post model becomes a real "Post" table in your Postgres database, with an auto-incrementing id, a title column and a body column. Prisma also builds a typed client so your editor knows about Post.
Step 2 — create the table and the client
Two terminal commands turn that schema into a real table and into JavaScript you can call. migrate creates/updates the database table to match your schema; generate builds the typed Prisma client your code imports.
# create the table from the schema
npx prisma migrate dev --name init
# (re)build the typed client your code imports
npx prisma generateNote: Output: Applying migration "init" Your database is now in sync with your schema. ✔ Generated Prisma Client The Post table now exists in Postgres, and "@prisma/client" is ready to import in your code.
Step 3 — query the database in a Server Component
This is where Next.js shines. Because Server Components run on the server, you can talk to the database directly inside one — no API route in the middle. The component is async, so you await the query.
// app/blog/page.js (a Server Component)
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default async function BlogPage() {
const posts = await prisma.post.findMany(); // SELECT * FROM Post
return (
<ul>
{posts.map((p) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}Note: Output (assuming two posts are stored): Hello Next.js Why I love React No API route, no fetch — the page queried the database directly on the server. prisma.post.findMany() ran the SQL, and the titles are in the HTML before it reaches the browser.
The full path of one request, step by step
Here is exactly what happens when a visitor opens /blog with this setup:
- The visitor opens
/blog. The browser asks your Next.js server for the page. - On the server, the
async BlogPagecomponent runs. It callsprisma.post.findMany(). - Prisma turns that JavaScript call into the SQL
SELECT * FROM "Post"and sends it to Postgres. - Postgres returns the rows. Prisma hands them back to you as a plain array of JavaScript objects.
- The component builds the
<ul>of titles — the page is rendered with the real data already inside it. - The finished HTML is sent to the browser. The visitor (and search engines) see the real, stored posts immediately.
Inserting and updating data
The same prisma object creates and changes records. You would call these inside a Server Action (after a form submit), then revalidate so the list refreshes.
await prisma.post.create({
data: { title: 'New post', body: 'Hello world' },
});Note: Output (behaviour): A new row is inserted into the Post table with the given title and body and a fresh auto-incremented id. Pair this with revalidatePath("/blog") in a Server Action and the new post appears on the list immediately.
Tip: Keep your database connection string out of your code. Put it in a .env file as DATABASE_URL and Prisma reads it automatically — the next lesson on environment variables covers exactly why secrets belong there.
Watch out: Never create a PrismaClient inside a Client Component or ship it to the browser — it holds your database credentials. Database work belongs in Server Components, Route Handlers and Server Actions, which never leave the server.
Q. In the Next.js App Router, where can you safely run a Prisma database query like prisma.post.findMany()?
✍️ Practice
- Add an
authorfield (a String) to thePostmodel and runprisma migrate devagain. - Query and display posts from the database in a Server Component, showing the title and author.
🏠 Homework
- Replace the in-memory posts array in your blog project with a real Prisma + Postgres model, and read the posts directly in the blog list Server Component.