Inicio / Artículos

Prisma sin Prisma Client: consultas de GraphQL seguras desde el punto de vista tipológico en Next.js

Aprenda cómo utilizar Prisma para el trabajo con esquemas, generar tipos Kysely con prisma-kysely, elegir un dialecto Neon o pg, y realizar consultas de forma segura desde el código del servidor de Next.js.

Editor: REACTAPP.TOP6 min de lectura1411 palabras

Fuentes: **Using Prisma for Schema and Kysely for Queries in a Next.js App**

Prisma es excelente para el diseño de esquemas, pero Prisma Client representa una dependencia considerable que hay que incluir en cada función serverless cuando lo único que se desea es SQL seguro desde el punto de vista tipológico. Una división práctica sería: Prisma gestiona el esquema durante el desarrollo, un generador lo convierte en tipos de TypeScript, y Kysely ejecuta las consultas en producción. Esta guía muestra cómo configurarlo en un proyecto Next.js App Router y compara el dialecto HTTP de Neon con un pool clásico de pg.

Si prefiere el enfoque convencional con Prisma Client en tiempo de ejecución, consulte cómo configurar Prisma 7 en un proyecto Next.js 16.

Cómo se dividen las responsabilidades

Cada herramienta en esta configuración tiene una tarea clara:

  • Prisma se encarga del diseño de la base de datos: el archivo de esquema, la sincronización o migración de la base de datos, y herramientas como Prisma Studio para explorar los datos.
  • Kysely es el generador de consultas en tiempo de ejecución. Escribes consultas con formato SQL en TypeScript; el compilador verifica los nombres de las tablas y columnas, y la propia biblioteca es pequeña.
  • prisma-kysely actúa como vínculo: es un generador de Prisma que emite definiciones de tipo Kysely directamente a partir de tu esquema Prisma.

Prisma permanece dentro de tu flujo de trabajo como desarrollador, mientras que Kysely es la única capa de base de datos en el código desplegado. El compromiso: pierdes la API de Prisma Client que tiene en cuenta las relaciones, como los include anidados y las escrituras anidadas, así como la posibilidad de realizar uniones de escritura por tu cuenta. Si aún estás eligiendo una capa de datos en general, la comparación entre SQL bruto, Prisma y Drizzle aborda la decisión de forma más amplia.

Instalación de los paquetes

El entorno en tiempo de ejecución necesita Kysely además de un dialecto y un controlador; esto instala la pila basada en Neon (pnpm aquí, pero también funcionan npm o yarn):

pnpm add kysely kysely-neon @neondatabase/serverless
pnpm add -D prisma prisma-kysely

Ten en cuenta que prisma y prisma-kysely son dependencias de desarrollo: solo gestionan el esquema y generan código.

Definición del esquema y generación de tipos Kysely

El esquema es un Prisma normal, con la diferencia de que el generador es prisma-kysely (que escribe en ./types) en lugar del generador de cliente habitual. Define User y Post con una relación uno-a-muchos.

// db/schema.prisma

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator kysely {
  provider = "prisma-kysely"
  output   = "./types"
}

model User {
  id    Int    @id @default(autoincrement())
  name  String
  email String  @unique
  posts Post[]
}
model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  authorId  Int
  createdAt DateTime @default(now())
  author    User     @relation(fields: [authorId], references: [id])
}

prisma generate ahora genera tipos Kysely en lugar de un cliente, y prisma db push aplica el esquema a la base de datos:

pnpm prisma generate
pnpm prisma db push

El resultado es un archivo como db/types/kysely.d.ts que contiene una interfaz DB que describe cada tabla. Tres consideraciones importantes:

  • El esquema se encuentra en db/schema.prisma, no en el prisma/schema.prisma predeterminado de Prisma, por lo que hay que indicarle a la CLI su ubicación (por ejemplo con --schema) según lo exija la versión de Prisma.
  • db push sincroniza la base de datos sin registrar archivos de migración. Para datos de producción, prisma migrate dev y prisma migrate deploy generan migraciones versionadas manteniendo el resto del flujo de trabajo sin cambios.
  • Las versiones recientes de Prisma trasladan la configuración de conexión a un archivo separado; si se rechaza la línea url, consulte la documentación actual.
  • Escripciones de paquete para el ciclo diario

    Algunas scripts hacen que el flujo de trabajo sea evidente para el equipo:

    {
      "scripts": {
        "db:push": "prisma db push",
        "db:generate": "prisma generate",
        "db:studio": "prisma studio"
      }
    }
    

    Con estos en su lugar, un cambio de esquema se reduce a dos comandos:

    # To generate the types
    pnpm db:generate
    # To push to the remote DB
    pnpm db:push
    

    La base de datos y los tipos de Kysely permanecen alineados, por lo que el compilador marca cualquier consulta que utilice una columna renombrada o eliminada.

    Elegir un dialecto de Kysely

    Kysely no habla directamente con la base de datos. Un dialecto se interponga entre el generador de consultas y el controlador, y sabe cómo compilar y enviar consultas para una base de datos y un método de conexión específicos. Los dialectos oficiales abarcan PostgreSQL, MySQL y SQLite; los comunitarios cubren servicios como Neon, PlanetScale y Turso. Para Postgres, existen dos opciones que sirven para la mayoría de los casos.

    Neon over HTTP para despliegues serverless

    En Vercel o una plataforma similar, el controlador HTTP de Neon encaja perfectamente: cada consulta es una solicitud sin estado, por lo que no hay conexión persistente que mantener entre invocaciones breves. La configuración envuelve a neon() dentro de NeonDialect y tipa a Kysely con el DB generado:

    // db/index.ts
    import { Kysely } from 'kysely'
    import { NeonDialect } from 'kysely-neon'
    import { neon } from '@neondatabase/serverless'
    import type { DB } from './types/kysely'
    
    const db = new Kysely<DB>({
      dialect: new NeonDialect({
        neon: neon(process.env.DATABASE_URL!),
      }),
    })
    
    export default db
    

    El ! solo silencia a TypeScript; no valida nada. La limitación más grande es que kysely-neon a través de HTTP no puede ejecutar transacciones ni consultas basadas en sesiones, por lo que es adecuado para operaciones simples de CRUD y páginas con mucho tráfico de lectura, en lugar de tareas que requieren múltiples instrucciones. Con el controlador WebSocket de Neon no necesitas kysely-neon en absoluto: pasa el Pool de Neon al PostgresDialect integrado en Kysely. La API de kysely-neon también ha cambiado entre versiones, así que compárala con su README actual.

    node-postgres para servidores de larga duración

    Para un servidor Node.js que funcione durante mucho tiempo, el controlador oficial pg es la opción más completa, ya que incluye transacciones, gestión de pools y sentencias preparadas. Se integra con el propio PostgresDialect de Kysely:

    // db/index.ts
    import { Kysely, PostgresDialect } from 'kysely'
    import { Pool } from 'pg'
    import type { DB } from './types/kysely'
    
    const db = new Kysely<DB>({
      dialect: new PostgresDialect({
        pool: new Pool({
          connectionString: process.env.DATABASE_URL,
        }),
      }),
    })
    
    export default db
    

    Elegir entre ellos

    La diferencia radica en el modelo de conexión. kysely-neon es HTTP sin estado: ligero y adecuado para entornos serverless, pero no permite transacciones. pg mantiene abiertas conexiones TCP en grupo, con todo el conjunto de funciones disponibles, lo que es adecuado para servidores que permanecen activos continuamente. Utilice kysely-neon en plataformas serverless como Vercel o Netlify, y pg o el grupo de WebSocket de Neon cuando necesite realizar transacciones. Tenga cuidado con los grupos pg dentro de funciones serverless: cada instancia abre sus propias conexiones y puede agotar el límite del banco de datos sin un gestor externo de conexiones.

    Consultas desde un componente del servidor

    Cualquier código del lado del servidor puede importar db. Este componente del servidor muestra una lista de usuarios, seleccionando tres columnas ordenadas por id:

    // app/users/page.tsx
    import db from '@/db'
    
    export default async function UsersPage() {
      const users = await db
        .selectFrom('user')
        .select(['id', 'name', 'email'])
        .orderBy('id')
        .execute()
    
      return (
        <div>
          <h1>Users</h1>
          <ul>
            {users.map(u => (
              <li key={u.id}>{u.name} ({u.email})</li>
            ))}
          </ul>
        </div>
      )
    }
    

    Dado que DB proviene del esquema de Prisma, si un nombre de tabla o columna está escrito incorrectamente se falla la verificación de tipos, y users se infiere con exactamente los campos seleccionados.

    Un problema: Prisma nombra las tablas según los modelos por defecto, por lo que este esquema crea una tabla User, y los tipos generados utilizan ese nombre. selectFrom('user') solo realiza la verificación de tipos si se agrega @@map("user") al modelo; de lo contrario, la consulta utiliza 'User'. También hay que mantener db fuera de los Componentes Cliente, ya que accede a un secreto exclusivo del servidor.

    El flujo de trabajo diario

    Una vez que todo está configurado, el bucle se ve así:

    1. Modificar los modelos en db/schema.prisma.
    2. Volver a generar los tipos y aplicar los cambios con los dos scripts.
    3. Utilizar db desde Componentes Servidor, controladores de rutas o Acciones Servidor.
  • Valide la entrada con Zod o herramientas similares antes de insertarla; los tipos de Kysely verifican la estructura de la consulta, no los valores del formulario.
  • El layout resultante mantiene todo lo relacionado con la base de datos bajo db/:

    📦 my-next-app
    ├── 📁 app
    │   └── 📁 users
    │       └── page.tsx
    ├── 📁 db
    │   ├── schema.prisma
    │   ├── index.ts
    │   └── 📁 types
    │       └── kysely.d.ts
    ├── 📁 node_modules
    ├── .env
    ├── package.json
    ├── pnpm-lock.yaml
    └── tsconfig.json
    

    Conclusión

    Esta separación permite mantener el flujo de trabajo del esquema de Prisma y Studio sin incluir Prisma Client, y ofrece una capa de consultas similar a SQL cuyos tipos siguen el esquema.

    • Mantenga prisma y prisma-kysely como dependencias de desarrollo; solo Kysely, un dialecto y un driver se envían a producción.
    • Use el dialecto Neon HTTP para cargas de trabajo serverless simples, y cámbiese a un PostgresDialect con conexión compartida cuando necesite transacciones.
    • Tenga cuidado con la denominación de las tablas: los tipos generados siguen los nombres de la base de datos, que por defecto son los nombres de los modelos de Prisma.
  • Pasen de db push a migraciones adecuadas antes de que los datos en la base de datos comiencen a ser importantes.
  • Lecturas relacionadas