Server Actions в Next.js с Prisma и PostgreSQL
Server Actions в Next.js взаимодействуют напрямую с общим клиентом Prisma и базой данных PostgreSQL, поэтому операции CRUD выполняются без необходимости в API-маршрутах и запросов с стороны клиента.
Источники: Next.js Server Actions + Prisma + PostgreSQL: The Integration Pattern You Actually Need
В руководствах часто рассматриваются Next.js, Prisma и PostgreSQL как отдельные компоненты: создается структура приложения, генерируется клиентская часть, указывается база данных и выполняются миграции. Однако не упоминается, как эти три компонента объединяются в единый паттерн интеграции, и почему этот паттерн устраняет множество обычных процедур, связанных с полноценной разработкой в стиле full-stack.
Разделение обязанностей ясно: Server Actions переносят задачи за пределы сервера, Prisma формирует запросы с типобезопасностью, а PostgreSQL хранит данные. Каждый слой отвечает за свою задачу. В совокупности они исключают необходимость в отдельном промежуточном уровне: нет специальных API-маршрутов, нет ручного использования циклов fetch, а также отсутствует дополнительная процедура сериализации между компонентом React и базой данных.
Для демонстрации всего паттерна от начала до конца — от схемы до рабочего интерфейса — достаточно простого менеджера задач, без необходимости добавления обработчиков маршрутов для операций CRUD.
Почему именно этот набор инструментов
Server Actions, Prisma и PostgreSQL взаимно дополняют друг друга на каждом уровне полноценного приложения Next.js.
Действия выполняются на сервере, поэтому они могут напрямую импортировать клиент Prisma, не выкладывая учетные данные базы данных в браузер. Prisma обеспечивает типизированный интерфейс для запросов, благодаря чему данные, записанные в PostgreSQL, и данные, считываемые обратно, остаются согласованными в рамках структуры TypeScript. PostgreSQL является надежным, широко поддерживаемым решением и хорошо работает с механизмом миграций Prisma.
Результатом является кратчайший путь от нажатия кнопки до сохранения записи — обычно с меньшим количеством дополнительного кода по сравнению с другими подходами в экосистеме Next.js.
Настройка проекта
Начните с нового приложения Next.js и добавьте Prisma:
npx create-next-app@latest my-app --typescript
cd my-app
npm install prisma @prisma/client
npx prisma init
The prisma init command creates a prisma/ folder with a schema.prisma file and a .env file. Open .env and add your PostgreSQL connection string:
```DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/mydb"
3. Define Your Schema
Open prisma/schema.prisma and define your data model. We will keep it simple — a Task model:
```generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Task {
id String @id @default(cuid())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Run the migration to create the table:
```npx prisma migrate dev --name init
This creates the table and generates the Prisma Client. You are ready to query.
Шаблон интеграции
Вот основная идея. Server Action импортирует клиент Prisma и взаимодействует непосредственно с базой данных — без API-маршрутов, без вызовов fetch на стороне клиента и без дополнительного слоя сериализации между интерфейсом и хранилищем данных. Сначала экспортируется общий экземпляр Prisma, который сохраняется при горячей замене кода во время разработки:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production')
globalForPrisma.prisma = prisma;
Затем определяется модуль Server Actions:
// app/tasks/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { prisma } from '@/lib/prisma';
export async function getTasks() {
return prisma.task.findMany({ orderBy: { createdAt: 'desc' } });
}
5. Wiring It to Your UI
Now use the Server Actions in your page and components:
```// app/tasks/page.tsx
import { getTasks } from './actions';
import { TaskForm } from './TaskForm';
import { TaskList } from './TaskList';
export default async function TasksPage() {
const tasks = await getTasks();
return (
<main>
<h1>My Tasks</h1>
<TaskForm />
<TaskList tasks={tasks} />
</main>
);
}
export async function createTask(formData: FormData) {
const title = formData.get('title') as string;
if (!title?.trim()) return { error: 'Title is required' };
await prisma.task.create({ data: { title } });
revalidatePath('/tasks');
}
export async function toggleTask(id: string, completed: boolean) {
await prisma.task.update({ where: { id }, data: { completed: !completed } });
revalidatePath('/tasks');
}
export async function deleteTask(id: string) {
await prisma.task.delete({ where: { id } });
revalidatePath('/tasks');
}
Подключение к интерфейсу
Эти действия вызываются из страницы. Компонент сервера может напрямую вызывать getTasks() — без использования useEffect, без ручного флага загрузки и без запросов к серверу с клиентской стороны:
// app/tasks/page.tsx
import { getTasks } from './actions';
import { TaskForm } from './TaskForm';
import { TaskList } from './TaskList';
export default async function TasksPage() {
const tasks = await getTasks();
return (
<main>
<h1>My Tasks</h1>
<TaskForm />
<TaskList tasks={tasks} />
</main>
);
}
Итоги
Server Actions в сочетании с Prisma и PostgreSQL обеспечивают полноценную инфраструктуру для работы с данными практически без лишнего кода. Компонент вызывает функцию, эта функция выполняет запись или чтение данных через Prisma, PostgreSQL хранит данные, а функция revalidatePath обновляет интерфейс — всё это обычно реализуется в нескольких десятках строк, без необходимости отдельного слоя API.