Главная / Статьи

Server Actions в Next.js с Prisma и PostgreSQL

Server Actions в Next.js взаимодействуют напрямую с общим клиентом Prisma и базой данных PostgreSQL, поэтому операции CRUD выполняются без необходимости в API-маршрутах и запросов с стороны клиента.

Издатель: REACTAPP.TOP4 мин чтения917 слов

Источники: 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.