---
title: Schema migrations with Prisma and Neon
subtitle: Learn how to manage database schema changes using Prisma Migrate with Neon
enableTableOfContents: true
updatedOn: '2026-02-01T11:47:48.275Z'
---
This tutorial walks you through building a Node.js application with Prisma ORM and Neon, focusing on how to create and apply schema migrations. You'll build a simple book catalog API while learning the migration workflow.
If you just need to connect Prisma to Neon without a full tutorial, see [Connect from Prisma to Neon](/docs/guides/prisma).
## Prerequisites
- A [Neon account and project](/docs/get-started-with-neon/signing-up)
- [Node.js 18+](https://nodejs.org/) installed
- Both connection strings from your Neon Console (pooled and direct)
## Create the project
Set up a new Node.js project with Express and Prisma:
```bash
mkdir neon-prisma-migrations && cd neon-prisma-migrations
npm init -y
npm pkg set type="module"
npm install express dotenv @prisma/client @prisma/adapter-neon
npm install prisma typescript tsx @types/node --save-dev
npx prisma init
```
## Configure Prisma for Neon
### Set up environment variables
Add both connection strings to your `.env` file. Get these from your Neon Console by clicking **Connect**:
```ini shouldWrap
# Pooled connection for your application (note the -pooler suffix)
DATABASE_URL="postgresql://[user]:[password]@[endpoint]-pooler.[region].aws.neon.tech/[dbname]?sslmode=require"
# Direct connection for Prisma CLI (migrations, introspection)
DIRECT_URL="postgresql://[user]:[password]@[endpoint].[region].aws.neon.tech/[dbname]?sslmode=require"
```
Prisma Migrate requires a direct connection to perform schema changes. The pooled connection is used by your application at runtime.
### Configure prisma.config.ts
Update the `prisma.config.ts` file in your project root:
```typescript
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
datasource: {
url: env('DIRECT_URL'),
},
})
```
### Update the Prisma schema
Replace the contents of `prisma/schema.prisma`:
```prisma
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
}
model Author {
id Int @id @default(autoincrement())
name String
bio String?
createdAt DateTime @default(now()) @map("created_at")
books Book[]
@@map("authors")
}
model Book {
id Int @id @default(autoincrement())
title String
authorId Int @map("author_id")
createdAt DateTime @default(now()) @map("created_at")
author Author @relation(fields: [authorId], references: [id])
@@map("books")
}
```
## Create and run your first migration
Run the following command to create your initial migration:
```bash
npx prisma migrate dev --name init
```
This command:
1. Creates a `prisma/migrations` folder with SQL migration files
2. Applies the migration to your Neon database
3. Generates the Prisma Client
You should see output confirming the migration was applied.
## Set up Prisma Client
Create `src/db.ts` to instantiate Prisma Client with the Neon adapter:
```typescript
import 'dotenv/config'
import { PrismaClient } from './generated/prisma'
import { PrismaNeon } from '@prisma/adapter-neon'
const adapter = new PrismaNeon({
connectionString: process.env.DATABASE_URL!,
})
export const prisma = new PrismaClient({ adapter })
```
## Seed the database
Create `src/seed.ts` to populate the database with sample data:
```typescript
import { prisma } from './db'
async function seed() {
const authors = [
{
name: 'J.R.R. Tolkien',
bio: 'Creator of Middle-earth and author of The Lord of the Rings.',
books: {
create: [
{ title: 'The Hobbit' },
{ title: 'The Fellowship of the Ring' },
{ title: 'The Two Towers' },
],
},
},
{
name: 'George R.R. Martin',
bio: 'Author of the epic fantasy series A Song of Ice and Fire.',
books: {
create: [
{ title: 'A Game of Thrones' },
{ title: 'A Clash of Kings' },
],
},
},
]
for (const author of authors) {
await prisma.author.create({ data: author })
}
console.log('✅ Database seeded')
}
seed()
.catch(console.error)
.finally(() => prisma.$disconnect())
```
Run the seed script:
```bash
npx tsx src/seed.ts
```
## Build the API
Create `src/index.ts` with Express endpoints:
```typescript
import express from 'express'
import { prisma } from './db'
const app = express()
const port = process.env.PORT || 3000
app.get('/authors', async (req, res) => {
const authors = await prisma.author.findMany({
include: { books: true },
})
res.json(authors)
})
app.get('/books', async (req, res) => {
const books = await prisma.book.findMany({
include: { author: true },
})
res.json(books)
})
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`)
})
```
Add a start script to `package.json`:
```bash
npm pkg set scripts.start="tsx src/index.ts"
```
Start the server:
```bash
npm start
```
Visit `http://localhost:3000/authors` to see the data.
## Make a schema change
Now let's add a `country` field to the `Author` model to demonstrate the migration workflow.
### Update the model
Modify the `Author` model in `prisma/schema.prisma`:
```prisma
model Author {
id Int @id @default(autoincrement())
name String
bio String?
country String?
createdAt DateTime @default(now()) @map("created_at")
books Book[]
@@map("authors")
}
```
### Generate and apply the migration
```bash
npx prisma migrate dev --name add-author-country
```
Prisma creates a new migration file and applies it. The Prisma Client is automatically regenerated.
### Verify the change
Restart your server and check `http://localhost:3000/authors`. Each author now has a `country` field (set to `null` for existing records).
## Migration workflow summary
The typical workflow for schema changes with Prisma and Neon:
1. **Modify your schema** — Update models in `prisma/schema.prisma`
2. **Generate migration** — Run `npx prisma migrate dev --name descriptive-name`
3. **Review the migration** — Check the generated SQL in `prisma/migrations/`
4. **Test locally** — Verify your application works with the changes
5. **Deploy** — In production, use `npx prisma migrate deploy`
For production deployments, always use `prisma migrate deploy` instead of `prisma migrate dev`. The `deploy` command only applies pending migrations without generating new ones.
## Source code
Find the complete source code for this tutorial on GitHub:
Migrations with Neon and Prisma
## Next steps
- [Connect from Prisma to Neon](/docs/guides/prisma) — Connection setup reference
- [Prisma Migrate documentation](https://www.prisma.io/docs/concepts/components/prisma-migrate) — Deep dive into Prisma migrations
- [Neon branching](/docs/introduction/branching) — Use database branches to test migrations safely