00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Integrazione con Next.js

Prisma e Next.js

Next.js e Prisma sono una combinazione molto popolare. Tuttavia, l’architettura di Next.js (hot-reload in sviluppo, ambienti serverless in produzione) richiede alcune accortezze nella configurazione.

Singleton Pattern per PrismaClient

In sviluppo, Next.js esegue hot-reload ad ogni modifica del codice. Ogni reload crea una nuova istanza di PrismaClient, aprendo nuove connessioni al database fino a esaurire il pool.

Setup Consigliato

Crea un file lib/prisma.ts (o lib/db.ts):

// lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => {
  return new PrismaClient()
}

declare const globalThis: {
  prismaGlobal: ReturnType<typeof prismaClientSingleton>
} & typeof global

const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()

export default prisma

if (process.env.NODE_ENV !== 'production') {
  globalThis.prismaGlobal = prisma
}

Ora importa prisma da questo file ovunque nel progetto:

import prisma from '@/lib/prisma'

Uso nelle API Routes

Le API Routes di Next.js sono funzioni serverless. Ogni richiesta puo’ creare una nuova invocazione.

// app/api/users/route.ts
import { NextResponse } from 'next/server'
import prisma from '@/lib/prisma'

export async function GET() {
  const users = await prisma.user.findMany({
    select: {
      id: true,
      name: true,
      email: true,
      _count: { select: { posts: true } },
    },
  })
  return NextResponse.json(users)
}

export async function POST(request: Request) {
  const body = await request.json()

  try {
    const user = await prisma.user.create({
      data: {
        email: body.email,
        name: body.name,
      },
    })
    return NextResponse.json(user, { status: 201 })
  } catch (error) {
    return NextResponse.json(
      { error: 'Email gia\' in uso' },
      { status: 400 }
    )
  }
}

Route con Parametri Dinamici

// app/api/users/[id]/route.ts
import { NextResponse } from 'next/server'
import prisma from '@/lib/prisma'

export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  const user = await prisma.user.findUnique({
    where: { id: parseInt(params.id) },
    include: {
      posts: { where: { published: true } },
      profile: true,
    },
  })

  if (!user) {
    return NextResponse.json({ error: 'Utente non trovato' }, { status: 404 })
  }

  return NextResponse.json(user)
}

Server Components (App Router)

Con l’App Router, i componenti sono Server Components di default. Puoi usare Prisma direttamente senza API intermedie:

// app/posts/page.tsx
import prisma from '@/lib/prisma'

export default async function PostsPage() {
  const posts = await prisma.post.findMany({
    where: { published: true },
    include: { author: { select: { name: true } } },
    orderBy: { createdAt: 'desc' },
  })

  return (
    <div>
      <h1>Blog</h1>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>di {post.author.name}</p>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  )
}

Pagina con Parametri Dinamici

// app/posts/[slug]/page.tsx
import { notFound } from 'next/navigation'
import prisma from '@/lib/prisma'

export default async function PostPage({
  params,
}: {
  params: { slug: string }
}) {
  const post = await prisma.post.findUnique({
    where: { slug: params.slug },
    include: {
      author: true,
      comments: {
        orderBy: { createdAt: 'desc' },
        include: { author: { select: { name: true } } },
      },
    },
  })

  if (!post) notFound()

  return (
    <article>
      <h1>{post.title}</h1>
      <p>Autore: {post.author.name}</p>
      <div>{post.content}</div>
      <h2>Commenti ({post.comments.length})</h2>
      {post.comments.map((comment) => (
        <div key={comment.id}>
          <strong>{comment.author.name}</strong>
          <p>{comment.text}</p>
        </div>
      ))}
    </article>
  )
}

Server Actions

Le Server Actions permettono di eseguire mutazioni lato server direttamente dai form:

// app/posts/new/page.tsx
import prisma from '@/lib/prisma'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'

export default function NewPostPage() {
  async function createPost(formData: FormData) {
    'use server'

    const title = formData.get('title') as string
    const content = formData.get('content') as string

    await prisma.post.create({
      data: {
        title,
        content,
        published: false,
        authorId: 1, // In un caso reale, dall'autenticazione
      },
    })

    revalidatePath('/posts')
    redirect('/posts')
  }

  return (
    <form action={createPost}>
      <input name="title" placeholder="Titolo" required />
      <textarea name="content" placeholder="Contenuto" required />
      <button type="submit">Pubblica</button>
    </form>
  )
}

Server Action per Toggle

// actions/posts.ts
'use server'

import prisma from '@/lib/prisma'
import { revalidatePath } from 'next/cache'

export async function togglePublish(postId: number) {
  const post = await prisma.post.findUniqueOrThrow({
    where: { id: postId },
  })

  await prisma.post.update({
    where: { id: postId },
    data: { published: !post.published },
  })

  revalidatePath('/posts')
}

export async function deletePost(postId: number) {
  await prisma.post.delete({
    where: { id: postId },
  })

  revalidatePath('/posts')
}

Best Practice per Next.js

1. Non Importare Prisma nei Client Components

// MAI fare questo in un Client Component ('use client')
// import prisma from '@/lib/prisma'  // ERRORE!

// Usa sempre API routes o Server Actions per le mutazioni

2. Gestione Errori

// lib/actions.ts
'use server'

import prisma from '@/lib/prisma'
import { Prisma } from '@prisma/client'

export async function createUser(data: { email: string; name: string }) {
  try {
    const user = await prisma.user.create({ data })
    return { success: true, user }
  } catch (error) {
    if (error instanceof Prisma.PrismaClientKnownRequestError) {
      if (error.code === 'P2002') {
        return { success: false, error: 'Email gia\' registrata' }
      }
    }
    return { success: false, error: 'Errore interno' }
  }
}

3. Script postinstall

Aggiungi la generazione automatica del client nel package.json:

{
  "scripts": {
    "postinstall": "prisma generate"
  }
}

Questo assicura che il Prisma Client venga generato automaticamente dopo ogni npm install, anche in ambienti CI/CD e piattaforme di deploy.