Prisma es una herramienta de ORM (Object-Relational Mapping) que ayuda a los desarrolladores a trabajar con bases de datos de una manera más sencilla y eficiente. Con Prisma, los desarrolladores pueden definir modelos de datos y trabajar con ellos como si fueran objetos, sin tener que escribir consultas complejas en SQL. Además, Prisma facilita la implementación de características avanzadas como relaciones, transacciones y migraciones.
Para instalar Prisma, es necesario tener Node.js y npm instalados en el sistema. Luego, se puede instalar Prisma a través de npm con el siguiente comando:
npm install prisma --save
Para crear un nuevo proyecto con Prisma, se puede utilizar el siguiente comando:
npx prisma init
Esto creará un nuevo directorio llamado prisma
en el proyecto, que contendrá los archivos de configuración de Prisma y un archivo schema.prisma
donde vamos a definir nuestro modelo, este es un archivo schema.prisma
convencional:
datasource db {
provider = "postgresql"
url = "postgresql://user:password@localhost:5432/mydb?schema=public"
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
}
Si no entendés nada de lo que acabas de leer, no te preocupes, es solo para que tengas una idea.
Para crear un modelo de datos con Prisma, se utiliza su lenguaje de definición de esquemas (SDL). Dentro del modelo identificamos tres elementos principales, el datasource
, generator
y model
.
El datasource
es una de las cosas más hermosas de esta herramienta, porque es la parte que se encarga de conectarse con nuestra base de datos local con dos simples parámetros, un provider
que define la base de datos que usaremos, como PostgreSQL, MySQL, etc. Y una url
que es la dirección para conectarse a esta base de datos en cuestión. Este es un ejemplo de un datasource
general:
datasource db {
provider = "postgresql"
url = "postgresql://user:password@localhost:5432/mydb?schema=public"
}
NOTA: Para evitar problemas de seguridad, se recomienda fuertemente usar un archivo de variables de entorno (También llamado ENV) para guardar la url
de tu base de datos.
El generator
es una sección que permite definir cómo Prisma debe generar el código necesario para interactuar con la base de datos según el modelo de datos definido en el esquema. Un ejemplo de generator
seria el siguiente:
generator client {
provider = "prisma-client-js"
}
Vamos a comprender al model
como el equivalente a las tablas y columnas de una base de datos convencional, acá tenemos un ejemplo extraído del punto anterior.
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
}
En este modelo, se definen los campos id
, name
, email
y password
. Esto, nuevamente, es el equivalente de una base de datos a una tabla llamada User
, con una clave primaria id
y otras tres columnas o campos que definen a nuestro usuario.
Recomendamos ver la documentación original de Prisma para saber más.
Una de las características más importantes de Prisma es su capacidad para trabajar con relaciones entre diferentes modelos de datos. A continuación, se detallan los tipos de relaciones que pueden ser definidas en el archivo schema.prisma
, y algunas de las propiedades que pueden ser utilizadas para personalizar su comportamiento.
Una relación de uno a muchos se define cuando un modelo tiene una relación con uno o más modelos secundarios. Por ejemplo, un usuario en una aplicación de una lista de tareas, puede tener muchas anotadas, por lo que para un solo usuario, hay muchos registros de tareas pertenecientes a ese usuario. Esto en Prisma se adapta de la siguiente forma:
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
todos Todo[]
}
model Todo {
id Int @id @default(autoincrement())
name String
description String
isFinished Bool
authorId Int
author User @relation(fields: [authorId], references: [id])
}
Como ven, se crea un modelo nuevo, en este caso llamado Todo
para almacenar todos los registros de todos
con sus campos correspondientes, en términos de bases de datos, seria como crear una nueva tabla y agregar algunas columnas. Por otro lado, esto no crea en sí la relación, sino que lo hacen las nuevas líneas que vemos en el ejemplo. El todo Todos[]
declara que existe la posibilidad de que en registros del modelo Todo
se pueda hacer referencia a un registro de un usuario. A sí mismo, del otro lado se hace algo similar, ya que la propiedad author
y authorId
hacen que cada registro, o cada todo
necesite tener el ID
del autor, esto es gracias al authorId
que es usado como clave foránea al id
del modelo User
.
Una relación de muchos a muchos se define cuando dos o más modelos tienen una relación con múltiples instancias de otros modelos. Por ejemplo, muchos usuarios pueden pertenecer a muchos grupos.
model User {
id Int @id @default(autoincrement())
name String
email String @unique
groups Group[] @relation("UserToGroup")
}
model Group {
id Int @id @default(autoincrement())
name String
users User[] @relation("UserToGroup")
}
En este ejemplo, la relación entre User
y Group
se define en ambos modelos usando la propiedad @relation
. El argumento "UserToGroup"
se utiliza para indicar que ambas tablas están relacionadas. Prisma creará automáticamente una tabla de unión para almacenar las relaciones entre los modelos.
Los modelos enum se utilizan para definir un conjunto finito de valores que pueden ser asignados a una columna. Por ejemplo, un modelo Todo
puede tener una columna status
que solo puede ser uno de los valores "done"
,
"in progress"
o "todo"
.
model Todo {
id Int @id @default(autoincrement())
title String
status TodoStatus
}
enum TodoStatus {
DONE
IN_PROGRESS
TODO
}
En este ejemplo, se define el modelo enum TodoStatus
, que se utiliza como tipo para la columna status
en el modelo Todo
. Los valores de TodoStatus
se utilizan para definir los valores posibles que pueden ser asignados a la columna.
Recomendamos ver la documentación original de Prisma para saber más.
Existen algunas propiedades adicionales que pueden ser utilizadas para personalizar el comportamiento de las relaciones. Estas son principalmente dos, onDelete
y onUpdate
. Estas propiedades se usan para definir qué hacer cuando se borra o modifica un registro que tiene relaciones con otros registros. Estas propiedades tienen distintos valores posibles a adoptar para así comportarse de una u otra forma, estos son cinco; Cascade
, Restrict
, NoAction
, SetNull
, SetDefault
.
Esta propiedad define cómo se manejará la eliminación de registros en el modelo principal cuando hay registros relacionados en el modelo secundario. Se comporta diferente según que valor tenga, estos son los distintos comportamientos:
Cascade
: Cuando se elimina un registro en el modelo principal, también se eliminan automáticamente todos los registros relacionados en el modelo secundario.Restrict
: Evita que se elimine un registro en el modelo principal si hay registros relacionados en el modelo secundario.SetNull
: Establece los valores de la clave foránea (como el authorId
en el ejemplo de la relación uno a muchos) en nulo en los registros relacionados en el modelo secundario cuando se elimina un registro en el modelo principal.NoAction
: No se realiza ninguna acción en los registros relacionados en el modelo secundario cuando se elimina un registro en el modelo principal.SetDefault
: Establece los valores de la clave foránea en su valor predeterminado en los registros relacionados en el modelo secundario cuando se elimina un registro en el modelo principal.Esta propiedad define cómo se manejará la actualización de registros en el modelo principal cuando hay registros relacionados en el modelo secundaria. Este es el comportamiento según los posibles valores:
Cascade
: Actualiza automáticamente los registros relacionados en el modelo secundario con los nuevos valores cuando se actualiza un registro en el modelo principal.Restrict
: No permite actualizar un registro el modelo tabla principal si hay registros relacionados en el modelo secundario.SetNull
: Establece los valores de la clave foránea en NULL
en los registros relacionados en el modelo secundario cuando se actualiza un registro el modelo tabla principal.NoAction
: No realiza ninguna acción en los registros relacionados en el modelo secundario cuando se actualiza un registro el modelo tabla principal.SetDefault
: Establece los valores de la clave foránea en su valor predeterminado en los registros relacionados en el modelo secundario cuando se actualiza un registro el modelo tabla principal.Es importante mencionar que estos valores únicamente se aplican a las operaciones de actualización en el modelo principal, es decir, no tienen efecto sobre las operaciones de eliminación de registros. Para esto último se debe usar la propiedad onDelete
.
Es posible que algunos valores estén o no disponibles según la base de datos que uses, te invito a ver la documentación.
Una vez que se ha definido el modelo de datos y se ha configurado la conexión a la base de datos, toca cubrir la parte divertida de Prisma, sus métodos, estos son usados para reemplazar las tan conocidas consultas SQL, porque no, acá no usamos SQL nunca, así que cada vez que queramos hacer un movimiento en la base de datos, utilizaremos estos métodos de a continuación. Siempre que se tengan que usar los métodos, hay que crear un PrismaClient
en el archivo de JavaScript introduciendo el siguiente bloque de código:
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
Además, correr el siguiente comando:
npx prisma generate
Vale aclarar que todos los métodos se usan siguiendo la misma sintaxis de
prisma.[NombreDelModelo].[NombreDelMetodo]
. También que los archivos JSON tanto para introducirlos como parámetros en ciertos métodos como para recibir las respuestas de los mismos.
El método findMany()
se utiliza para recuperar varios registros de una tabla en la base de datos. Por ejemplo, para recuperar todos los usuarios de la tabla User
, se puede utilizar el siguiente código:
const users = await prisma.user.findMany();
El método findOne()
se utiliza para recuperar un registro de una tabla en la base de datos. Por ejemplo, para recuperar un usuario de la tabla User
, se puede utilizar el siguiente código:
const user = await prisma.user.findOne();
NOTA: El findOne()
siempre devuelve el primero de todos los registros que reciba.
El método create()
se utiliza para crear un nuevo registro en una tabla en la base de datos. Por ejemplo, para crear un nuevo usuario en la tabla User
, se puede utilizar el siguiente código:
const newUser = await prisma.user.create({
data: {
name: "Marcelo Guiterrez",
email: "MarceG@gmail.com",
password: "contraseña"
}
});
NOTA: En este caso, el parámetro data
está en formato JSON, este formato lo vemos repetidas no solo en Prisma, sino que en JavaScript en general.
En el caso de que se quiera crear mas de un mismo registro en la misma tabla, se puede pasar a data
una lista con un objeto distinto por registro. Por ejemplo:
const newUsers = await prisma.user.createMany({
data: [
{ name: "Marcelo Guiterrez", email: "MarceG@gmail.com", password: "contraseña" },
{ name: "Gonzalo Vangue", email: "GonzaV@gmail.com", password: "incorrecta" },
{ name: "Juan Perez", email: "JuaniPerez@gmail.com", password: "123456" }
]
});
Es importante aclarar que la idea de pasar una lista con muchos registros puede ser usada en cualquier contexto, no solo en el de crear registros, sino también en el de actualizar o eliminar registros. Ademas de no solo en el de crear en la misma tabla, sino también en el de crear registros en tablas relacionadas, como en el caso de una aplicacion de mensajes, donde mas de una persona estaria relacionada, por ejemplo, con un solo mensaje.
El método update()
se utiliza para actualizar un registro en una tabla en la base de datos. Por ejemplo, para actualizar el nombre del usuario con id
igual a 1 en la tabla User
, se puede utilizar el siguiente código:
const updatedUser = await prisma.user.update({
where: {
id: 1
},
data: {
name: "Marcela Gutierrez"
}
});
El método delete()
se utiliza para eliminar un registro de una tabla en la base de datos. Por ejemplo, para eliminar el usuario con id
igual a 1 de la tabla User
, se puede utilizar el siguiente código:
const deletedUser = await prisma.user.delete({
where: {
id: 1
}
})
Recomendamos ver la documentación original de Prisma para saber más.
Como ya vimos en ejemplos anteriores, dentro del parámetro que le pasamos a cualquier método de Prisma, podemos pasarle otras propiedades que ayudan a poder refinar nuestras consultas y obtener solo la información que necesitamos. Veremos cómo utilizar funciones como select
, orderBy
, contains
, startWith
, endWith
y más para filtrar y ordenar datos de manera efectiva. Además, veremos cómo utilizar la función connect
para obtener información de modelos relacionados en nuestras consultas.
Permite seleccionar qué campos deseas obtener de una entidad. Por ejemplo, si solo quieres obtener el nombre y el correo electrónico de los usuarios, puedes usar select
para obtener solo esos campos.
const users = await prisma.user.findMany({
select: {
name: true,
email: true
}
})
Permite limitar la cantidad de registros que devuelve el método findMany()
. Por ejemplo, si deseas seleccionar los diez primeros usuarios de la base de datos.
const users = await prisma.user.findMany({
take: 10
})
Permite incluir campos de una entidad relacionada en la consulta. Por ejemplo, si deseas obtener todos los posts de un usuario y también incluir los comentarios de cada post, puedes usar include
para obtener toda la información.
const user = await prisma.user.findUnique({
where: {
id: 1
},
include: {
posts: {
include: {
comments: true
}
}
}
})
Permite filtrar los registros en función de los valores de sus campos. Por ejemplo, si solo quieres obtener los usuarios que tengan el nombre "Juan".
const users = await prisma.user.findMany({
where: {
name: "Juan"
}
})
Permite buscar registros que contengan una cadena determinada. Por ejemplo, si deseas buscar todos los usuarios que tengan la palabra "programador" en su descripción.
const users = await prisma.user.findMany({
where: {
description: {
contains: "programador"
}
}
})
Permiten buscar registros que comiencen o terminen con una cadena determinada. Por ejemplo, si deseas buscar todos los usuarios que tengan un apellido que empiece por "a".
const users = await prisma.user.findMany({
where: {
surname: {
startsWith: "a"
}
}
})
Permite ordenar los resultados por uno o más campos. Por ejemplo, si deseas obtener todos los usuarios ordenados por su nombre en orden alfabético.
const users = await prisma.user.findMany({
orderBy: {
name: "asc"
}
})
Permite conectar dos entidades relacionadas en una sola consulta. Por ejemplo, si deseas obtener todos los comentarios que ha hecho un usuario en una publicación específica.
const comments = await prisma.comment.findMany({
where: {
post: {
id: 1
},
author: {
id: 2
}
}
})
Estas son solo algunas de las opciones de filtrado y ordenamiento disponibles en Prisma. Hay muchas más opciones y combinaciones posibles que puedes explorar en la documentación oficial de Prisma. También hay que tener en cuenta que estas opciones pueden encadenarse, permitiéndole al desarrollador, por ejemplo, seleccionar todos los nombres comenzados por la letra "p", que contengan la letra "h" y finalmente ordenarlos en orden alfabético. Hay una cantidad gigante de combinaciones posibles, pensado para poder facilitarle al máximo al desarrollador la tarea de interactuar con la base de datos.
Acá vamos a ver algunos de los comandos más útiles en Prisma, que son esenciales para trabajar con bases de datos y realizar cambios en el esquema de la base de datos. También cómo es posible importar y exportar archivos SQL, lo que permite una mayor flexibilidad en la manipulación de los datos.
El comando npx prisma migrate dev
se utiliza para ejecutar migraciones en una base de datos. Las migraciones son una forma de aplicar los cambios que hicimos en el modelo del schema.prisma
a la base de datos local, además de tener un registro de cambios. La opción dev
se utiliza para ejecutar migraciones en un entorno de desarrollo.
El comando npx prisma migrate deploy
despliega tu esquema de Prisma a la base de datos especificada en el archivo de configuración. Es una buena práctica ejecutar este comando después de hacer cambios en tu esquema de Prisma.
El comando npx prisma studio
comando se utiliza para abrir Prisma Studio, una interfaz gráfica de usuario para explorar los datos almacenados en la base de datos. Prisma Studio es una herramienta muy útil para visualizar y manipular los datos en la base de datos.
El comando npx prisma format
formatea automáticamente tu archivo schema.prisma
para que sea legible y esté bien estructurado. Generalmente después de cualquier cambio en el archivo schema.prisma
se hace un npx prisma format
.
El comando npx prisma db pull
importa la estructura de tu base de datos actual en tu archivo schema.prisma
. Es útil si necesitas actualizar tu archivo de esquema a partir de los cambios en la base de datos.
Estos son algunos de todos los posibles comandos que hay disponibles gracias a Prisma, recomendamos acceder a la documentación original para saber más, y en cualquier caso o problema particular, buscar en internet.