Next.js is a React framework, which is used to build fast and scalable web applications. It provides various features like server-side rendering, API routes, and components for building a web application. You can easily manage your database by using Prisma as ORM tool with it, which is a type-safe and easy to manage database and organize the code. With Prisma, you can define your schema in one place and using that you can generate fully typed database queries automatically, write cleaner and maintainable backend logic.
Prisma generates a client based on your schema, which you can use to interact with your database safely and reliably. It works great with Next.js. You can plug it right into your server actions to read, write, update, or remove data without much hassle. With Prisma and Next.js together, you've got pretty much everything you need to build a complete full-stack app without a bunch of extra setup. This is exactly how SurekhaTech builds modern web solutions efficiently.
Prerequisites
- Basic understanding in JavaScript & React
- Node.js (>=18) and npm/yarn/pnpm
- Understanding of relational database (MySQL, Posgress)
In this blog, we will walk through:
- Integrating Prisma with Next.js
- Creating full CRUD server actions with Prisma
- Important Prisma CLI Commands
Integrating Prisma with Next.js
Step 1: Create Your Next.js Project
- npx create-next-app@latest nextjs-prisma-app –typescript
- cd nextjs-prisma-app
Step 2: Install Dependencies
- npm install prisma --save-dev
- npm install @prisma/client
Step 3: Initialize Prisma
- npx prisma init
- This creates:
prisma/schema.prisma
`.env` with placeholder `DATABASE_URL`
Step 4: Connect to MySQL
- Update your.env file:
DATABASE_URL="mysql://username:password@localhost:3306/mydatabase"
Prisma supports various popular relational databases. So, you can use PostgreSQL, SQL Server (MSSQL) and SQLite.
Step 5: Define schema models in schema.prisma file
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
enum UserType {
DEALER
DISTRIBUTOR
ADMIN
CUSTOMER
}
model Role {
id Int @id @default(autoincrement())
name String
users User[]
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
type UserType
roleId Int
role Role @relation(fields: [roleId], references: [id])
createdAt DateTime @default(now())
}
Step 6: Apply your migration:
- npx prisma migrate dev --name add-user-role-type
This will create another timestamped folder inside prisma/migrations/ with the SQL migration script.
Creating full CRUD server actions with Prisma
Let’s create server actions that you can use in your application and the required types. We can use prisma client relational database queries for CRUD, searching and filtering records from database using prisma client models.
Create type.ts file to define types for CRUD
export type CreateUserInput = {
name: string;
email: string;
roleId: number;
type: 'DEALER' | 'DISTRIBUTOR' | 'ADMIN' | 'CUSTOMER';
};
export type UpdateUserInput = {
name?: string;
email?: string;
roleId?: number;
type?: 'DEALER' | 'DISTRIBUTOR' | 'ADMIN' | 'CUSTOMER';
};
export type UserQueryOptions = {
search?: string;
sortBy?: 'name' | 'email' | 'createdAt';
sortOrder?: 'asc' | 'desc';
page?: number;
pageSize?: number;
};
Server Actions (app/actions/user-role.ts)
'use server';
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
export async function getRoles() {
return prisma.role.findMany();
}
export async function createRole(name: string) {
const role = await prisma.role.create({ data: { name } });
revalidatePath('/users');
return role;
}
export async function getUsers(options: UserQueryOptions = {}) {
const {
search = '',
sortBy = 'createdAt',
sortOrder = 'desc',
page = 1,
pageSize = 10,
} = options;
const where = search
? {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
],
}
: {};
const users = await prisma.user.findMany({
where,
include: { role: true },
orderBy: { [sortBy]: sortOrder },
skip: (page - 1) * pageSize,
take: pageSize,
});
const total = await prisma.user.count({ where });
return {
data: users,
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize),
};
}
export async function createUser(data: CreateUserInput) {
const user = await prisma.user.create({ data });
revalidatePath('/users');
return user;
}
export async function updateUser(id: number, data: UpdateUserInput) {
const user = await prisma.user.update({ where: { id }, data });
revalidatePath('/users');
return user;
}
export async function deleteUser(id: number) {
const user = await prisma.user.delete({ where: { id } });
revalidatePath('/users');
return user;
}
Use server action in your component
'use client';
import { useEffect, useState } from 'react';
import { getUsers } from '../actions/user-role';
export default function UsersPage() {
const [users, setUsers] = useState([]);
const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
useEffect(() => {
getUsers({ search, page, pageSize: 5 }).then((res) => {
setUsers(res.data);
});
}, [search, page]);
return (
<div>
<h1>All Users</h1>
<input
type="text"
placeholder="Search users..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} ({user.email}) - {user.type} - Role: {user.role?.name}
</li>
))}
</ul>
<button disabled={page === 1} onClick={() => setPage((p) => p - 1)}>Previous</button>
<button onClick={() => setPage((p) => p + 1)}>Next</button>
</div>
);
}

Important Prisma CLI Commands
`npx prisma migrate dev --name init`
In accordance with the schema, this command creates a Prisma Client and performs a database migration. In the 'prisma/migrations' folder, a new folder will be created. A timestamped migration folder and a.sql file will be included in the new folder. 'prisma/migrations/20250703125434_init/migration.sql' is one example.
The Prisma schema migration changes are reflected in the SQL file. As a result, versioning and tracking database changes is simple.
`npx prisma db pull`
This command will inspect the current schema from the database and change your `schema.prisma` file appropriately if you're dealing with an existing database. It lets you use Prisma and create types without having to manually define database schema models.
npx prisma generate
With this command, your database's TypeScript/JavaScript interface, known as the Prisma Client, is renewed. This command is helpful when you manually edit the Prisma schema by adding or changing columns, relationships, building a new schema model, etc. It guarantees that your code can access the most recent types and queries.
Conclusion
Using Next.js & Prisma ORM makes it easier to build modern, type-safe, and scalable applications with minimal configuration. Using Prisma improves safety and makes it easy to use with Next.js server actions for managing your database.
If you're interested in how Next.js is transforming modern web development, check out our related post: Next.js: A Real Game Changer for Upcoming Web App Development