Cómo crear una API de GraphQL con Prisma e implementarla en la plataforma de aplicaciones
Los autores seleccionaron el programa Write for Donations.
Introducción
GraphQL es un lenguaje de consulta para API que consta de un lenguaje de definición de esquemas y un lenguaje de consulta, lo que permite a los consumidores de API obtener solo los datos que necesitan para admitir consultas flexibles. GraphQL permite a los desarrolladores evolucionar la API mientras satisface las diferentes necesidades de múltiples clientes, por ejemplo, iOS, Android y variantes web de una aplicación. Además, el esquema GraphQL agrega un grado de seguridad de tipos a la API y, al mismo tiempo, sirve como una forma de documentación para su API.
Prisma es un conjunto de herramientas de base de datos de código abierto con tres herramientas principales:
- Prisma Client: generador de consultas con seguridad de tipos y generación automática para Node.js y TypeScript.
- Prisma Migrate: sistema de migración y modelado de datos declarativos.
- Prisma Studio: GUI para ver y editar datos en su base de datos.
Prisma facilita el trabajo con bases de datos para los desarrolladores de aplicaciones que desean centrarse en implementar características de valor agregado en lugar de dedicar tiempo a flujos de trabajo de bases de datos complejos (como migraciones de esquemas o escribir consultas SQL complicadas).
En este tutorial, usará GraphQL y Prisma en combinación, ya que sus responsabilidades se complementan entre sí. GraphQL proporciona una interfaz flexible para sus datos para usar en clientes, como interfaces y aplicaciones móviles; GraphQL no está vinculado a ninguna base de datos específica. Aquí es donde entra Prisma para manejar la interacción con la base de datos donde se almacenarán sus datos.
La plataforma de aplicaciones de DigitalOcean proporciona una manera perfecta de implementar aplicaciones y aprovisionar bases de datos en la nube sin preocuparse por la infraestructura. Esto reduce la sobrecarga operativa de ejecutar una aplicación en la nube; especialmente con la capacidad de crear una base de datos PostgreSQL administrada con copias de seguridad diarias y conmutación por error automatizada. App Platform tiene compatibilidad nativa con Node.js para simplificar la implementación.
Construirá una API de GraphQL para una aplicación de blogs en JavaScript utilizando Node.js. Primero usará Apollo Server para construir la API de GraphQL respaldada por estructuras de datos en memoria. Luego, implementará la API en la plataforma de aplicaciones de DigitalOcean. Finalmente, usará Prisma para reemplazar el almacenamiento en memoria y conservar los datos en una base de datos PostgreSQL e implementar la aplicación nuevamente.
Al final del tutorial, tendrá una API de GraphQL de Node.js implementada en DigitalOcean, que maneja las solicitudes de GraphQL enviadas a través de HTTP y realiza operaciones CRUD en la base de datos de PostgreSQL.
Puede encontrar el código de este proyecto en el repositorio de la comunidad de DigitalOcean.
requisitos previos
Antes de comenzar esta guía, necesitará lo siguiente:
- Una cuenta de GitHub.
- Una cuenta de DigitalOcean.
- Contribuir al código abierto: Primeros pasos con Git para instalar y configurar Git en su computadora.
- Cómo instalar Node.js y crear un entorno de desarrollo local para instalar y configurar Node.js en su computadora.
- Docker instalado en su computadora (para ejecutar la base de datos PostgreSQL localmente).
La familiaridad básica con Node.js, GraphQL y PostgreSQL es útil, pero no es estrictamente necesaria para este tutorial.
Paso 1: Crear el proyecto Node.js
En este paso, configurará un proyecto Node.js con npm e instalará las dependencias apollo-server
y graphql
. Este proyecto será la base de la API de GraphQL que construirá e implementará a lo largo de este tutorial.
Primero, crea un nuevo directorio para tu proyecto:
- mkdir prisma-graphql
A continuación, navegue al directorio e inicialice un proyecto npm vacío:
- cd prisma-graphql
- npm init --yes
Este comando crea un archivo package.json
mínimo que se usa como archivo de configuración para su proyecto npm.
Recibirá el siguiente resultado:
OutputWrote to /Users/your_username/workspace/prisma-graphql/package.json:
{
"name": "prisma-graphql",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Ahora está listo para configurar TypeScript en su proyecto.
Instala las dependencias necesarias:
- npm install apollo-server graphql --save
Este comando instala dos paquetes como dependencias en su proyecto:
apollo-server
es la biblioteca HTTP que utiliza para definir cómo se resuelven las solicitudes de GraphQL y cómo obtener datos.graphql
es la biblioteca que usará para construir el esquema de GraphQL.
Has creado tu proyecto e instalado las dependencias. En el siguiente paso, definirá el esquema de GraphQL.
Paso 2: Definición del esquema y los solucionadores de GraphQL
En este paso, definirá el esquema de GraphQL y los resolvers correspondientes. El esquema definirá las operaciones que la API puede manejar. Los resolutores definirán la lógica para manejar esas solicitudes utilizando estructuras de datos en memoria, que reemplazará con consultas a la base de datos en el siguiente paso.
Primero, crea un nuevo directorio llamado src
que contendrá tus archivos fuente:
- mkdir src
Luego ejecute el siguiente comando para crear el archivo para el esquema:
- nano src/schema.js
Agregue el siguiente código al archivo:
const { gql } = require('apollo-server')
const typeDefs = gql`
type Post {
content: String
id: ID!
published: Boolean!
title: String!
}
type Query {
feed: [Post!]!
post(id: ID!): Post
}
type Mutation {
createDraft(content: String, title: String!): Post!
publish(id: ID!): Post
}
`
El esquema de GraphQL se define mediante la plantilla etiquetada gql
. Un esquema es una colección de definiciones de tipo (por lo tanto, typeDefs
) que juntas definen la forma de las consultas que se pueden ejecutar en su API. Esto convertirá la cadena del esquema de GraphQL al formato que espera Apollo.
El esquema introduce tres tipos:
Publicación
define el tipo de una publicación en su aplicación de blogs y contiene cuatro campos donde cada campo va seguido de su tipo: por ejemplo,Cadena
.Query
define la consultafeed
que devuelve varias publicaciones como indican los corchetes y la consultapost
que acepta un único argumento y devuelve una solaPublicación
.Mutación
define la mutacióncreateDraft
para crear un borradorPost
y la mutaciónpublish
que acepta unid
y devuelve unaPublicación
.
Cada API de GraphQL tiene un tipo de consulta y puede o no tener un tipo de mutación. Estos tipos son los mismos que un tipo de objeto regular, pero son especiales porque definen el punto de entrada de cada consulta de GraphQL.
A continuación, agregue la matriz posts
al archivo src/schema.js
, debajo de la variable typeDefs
:
...
const posts = [
{
id: 1,
title: 'Subscribe to GraphQL Weekly for community news ',
content: 'https://graphqlweekly.com/',
published: true,
},
{
id: 2,
title: 'Follow DigitalOcean on Twitter',
content: 'https://twitter.com/digitalocean',
published: true,
},
{
id: 3,
title: 'What is GraphQL?',
content: 'GraphQL is a query language for APIs',
published: false,
},
]
Defina la matriz posts
con tres publicaciones predefinidas. La estructura de cada objeto post
coincide con el tipo Post
que definiste en el esquema. Esta matriz contiene las publicaciones que serán atendidas por la API. En un paso posterior, reemplazará el arreglo una vez que se hayan introducido la base de datos y Prisma Client.
A continuación, defina el objeto resolvers
agregando el siguiente código debajo de la matriz posts
que acaba de definir:
...
const resolvers = {
Query: {
feed: (parent, args) => {
return posts.filter((post) => post.published)
},
post: (parent, args) => {
return posts.find((post) => post.id === Number(args.id))
},
},
Mutation: {
createDraft: (parent, args) => {
posts.push({
id: posts.length + 1,
title: args.title,
content: args.content,
published: false,
})
return posts[posts.length - 1]
},
publish: (parent, args) => {
const postToPublish = posts.find((post) => post.id === Number(args.id))
postToPublish.published = true
return postToPublish
},
},
Post: {
content: (parent) => parent.content,
id: (parent) => parent.id,
published: (parent) => parent.published,
title: (parent) => parent.title,
},
}
module.exports = {
resolvers,
typeDefs,
}
Los resolutores se definen siguiendo la misma estructura que el esquema de GraphQL. Cada campo en los tipos del esquema tiene una función de resolución correspondiente cuya responsabilidad es devolver los datos para ese campo en su esquema. Por ejemplo, el solucionador Query.feed()
devolverá las publicaciones publicadas filtrando la matriz posts
.
Las funciones de resolución reciben cuatro argumentos:
parent
es el valor de retorno del resolver anterior en la cadena de resolver. Para los resolutores de nivel superior, el padre esundefined
, porque no se llama a ningún resolutor anterior. Por ejemplo, al realizar una consultafeed
, se llamará al solucionadorquery.feed()
con el valorparent
undefined
y luego se llamará a los resolutores dePost
dondeparent
es el objeto devuelto por el resolutorfeed
.args
lleva los parámetros para la consulta. Por ejemplo, la consultapost
recibirá elid
de la publicación que se buscará.context
es un objeto que se pasa a través de la cadena de resolución en la que cada resolución puede escribir y leer, lo que permite a las resoluciones compartir información.info
es una representación AST de la consulta o mutación. Puede leer más sobre los detalles de esta serie de Prisma en Conceptos básicos de GraphQL.
Dado que context
y info
no son necesarios en estos resolutores, solo se definen parent
y args
.
Guarde y salga del archivo una vez que haya terminado.
Nota: Cuando una resolución devuelve el mismo campo que el nombre de la resolución, como las cuatro resoluciones para Post
, Apollo Server las resolverá automáticamente. Esto significa que no tiene que definir explícitamente esos resolutores.
- Post: {
- content: (parent) => parent.content,
- id: (parent) => parent.id,
- published: (parent) => parent.published,
- title: (parent) => parent.title,
- },
Exporta el esquema y los resolutores para que pueda usarlos en el siguiente paso para crear una instancia del servidor con Apollo Server.
Paso 3: Crear el servidor GraphQL
En este paso, creará el servidor GraphQL con Apollo Server y lo vinculará a un puerto para que el servidor pueda aceptar conexiones.
Primero, ejecute el siguiente comando para crear el archivo para el servidor:
- nano src/server.js
Agregue el siguiente código al archivo:
const { ApolloServer } = require('apollo-server')
const { resolvers, typeDefs } = require('./schema')
const port = process.env.PORT || 8080
new ApolloServer({ resolvers, typeDefs }).listen({ port }, () =>
console.log(`Server ready at: http://localhost:${port}`),
)
Aquí, crea una instancia del servidor y pasa el esquema y los resolutores del paso anterior.
El puerto al que se vinculará el servidor se establece desde la variable de entorno PORT
. Si no se configura, el valor predeterminado será 8080
. App Platform establecerá automáticamente la variable de entorno PORT
y garantizará que su servidor pueda aceptar conexiones una vez implementadas.
Guardar y salir del archivo.
Su API GraphQL está lista para ejecutarse. Inicie el servidor con el siguiente comando:
- node src/server.js
Recibirá el siguiente resultado:
OutputServer ready at: http://localhost:8080
Se considera una buena práctica agregar un script de inicio a su archivo package.json
para que el punto de entrada a su servidor sea claro. Si lo hace, permitirá que App Platform inicie el servidor una vez implementado.
Primero, detenga el servidor presionando CTRL+C
. Luego, para agregar un script de inicio, abra el archivo package.json
:
- nano package.json
Agregue el texto resaltado al objeto scripts
en package.json
:
{
"name": "prisma-graphql",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node ./src/server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"apollo-server": "^3.11.1",
"graphql": "^16.6.0"
}
}
Guardar y salir del archivo.
Ahora puede iniciar el servidor con el siguiente comando:
- npm start
Recibirá el siguiente resultado:
Output> prisma-graphql@1.0.0 start
> node ./src/server.js
Server ready at: http://localhost:8080
Para probar la API de GraphQL, abra la URL de la salida, que lo llevará a Apollo GraphQL Studio. Haga clic en el botón Consultar su servidor en la página de inicio para interactuar con el IDE.
Apollo GraphQL Studio es un IDE donde puede probar la API enviando consultas y mutaciones.
Por ejemplo, para probar la consulta feed
, que solo devuelve publicaciones publicadas, ingrese la siguiente consulta en el lado izquierdo del IDE y envíe la consulta presionando el botón Ejecutar o reproducir:
query {
feed {
id
title
content
published
}
}
La respuesta mostrará un título de Suscríbase a GraphQL Weekly
con su URL y Siga a DigitalOcean en Twitter
con su URL.
Haga clic en el botón +
en la barra sobre su consulta anterior para crear una nueva pestaña. Luego, para probar la mutación createDraft
, ingresa la siguiente mutación:
mutation {
createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
id
title
content
published
}
}
Después de enviar la mutación mediante el botón de reproducción, recibirá una respuesta con Implementación de una API de GraphQL en DigitalOcean
dentro del campo título
como parte de la respuesta.
Nota: Puede elegir qué campos devolver de la mutación agregando o eliminando campos dentro de las llaves ({}
) después de createDraft
. Por ejemplo, si quisiera devolver solo el id
y el title
, podría enviar la siguiente mutación:
mutation {
createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
id
title
}
}
Ha creado y probado con éxito el servidor GraphQL. En el siguiente paso, creará un repositorio de GitHub para el proyecto.
Paso 4: Crear el repositorio de GitHub
En este paso, creará un repositorio de GitHub para su proyecto e impulsará sus cambios para que la API de GraphQL se pueda implementar automáticamente desde GitHub a App Platform.
Primero, detenga el servidor de desarrollo presionando CTRL+C
. Luego inicialice un repositorio desde la carpeta prisma-graphql
usando el siguiente comando:
- git init
A continuación, utilice los siguientes dos comandos para enviar el código al repositorio:
- git add src package-lock.json package.json
- git commit -m 'Initial commit'
Ahora que los cambios se han confirmado en su repositorio local, creará un repositorio en GitHub y enviará sus cambios.
Vaya a GitHub para crear un nuevo repositorio. Para mantener la coherencia, nombre el repositorio prisma-graphql y luego haga clic en Crear repositorio.
Después de crear el repositorio, envíe los cambios con los siguientes comandos, lo que incluye cambiar el nombre de la rama local predeterminada a main
:
- git remote add origin git@github.com:your_github_username/prisma-graphql.git
- git branch -M main
- git push --set-upstream origin main
Has confirmado y enviado con éxito los cambios a GitHub. A continuación, conectará el repositorio a App Platform e implementará la API de GraphQL.
Paso 5: Implementación en la plataforma de aplicaciones
En este paso, conectará el repositorio de GitHub que acaba de crear a DigitalOcean y luego configurará App Platform para que la API de GraphQL se pueda implementar automáticamente cuando envíe cambios a GitHub.
Primero, visite la página Plataforma de aplicaciones en DigitalOcean Cloud Console y haga clic en el botón Crear aplicación .
Verá las opciones del proveedor de servicios con GitHub como predeterminado.
Si no ha configurado DigitalOcean en su cuenta de GitHub, haga clic en el botón Administrar acceso para ser redirigido a GitHub.
Puede seleccionar todos los repositorios o repositorios específicos. Haga clic en Instalar y autorizar y, a continuación, se le redirigirá a la creación de la plataforma de aplicaciones de DigitalOcean.
Elija el repositorio your_github_username/prisma-graphql
y haga clic en Next. La implementación automática está seleccionada de forma predeterminada y puede dejarla seleccionada para mantener la coherencia en las implementaciones.
En la página Recursos, haga clic en el botón Editar plan para elegir un plan adecuado. Seleccione el plan Básico con el tamaño del plan que necesita (este tutorial usará el plan Básico de $5.00/mes).
Luego presione Atrás para volver a la página de creación.
Si presiona el icono del bolígrafo junto al nombre de su proyecto, puede personalizar la configuración de la aplicación. Se abrirá la página Configuración de la aplicación:
Asegúrese de que Ejecutar comando esté configurado como npm start
. De forma predeterminada, App Platform establecerá el puerto HTTP en 8080
, que es el mismo puerto al que configuró su servidor GraphQL para enlazar.
Cuando haya terminado de personalizar la configuración, presione el botón Atrás para volver a la página de configuración. Luego, presione el botón Siguiente para pasar a la página Variables de entorno.
Sus variables de entorno no necesitarán más configuración en este momento. Haga clic en el botón Siguiente.
En la página de información, puede ajustar los detalles y la ubicación de la aplicación. Edite la información de su aplicación para elegir la región en la que desea implementar su aplicación. Confirme los detalles de su aplicación presionando el botón Guardar. Luego, haga clic en el botón Siguiente.
Podrá revisar todas las opciones seleccionadas en la página Revisar. Luego haga clic en Crear recursos. Será redirigido a la página de la aplicación, donde verá el progreso de la implementación inicial.
Una vez que finalice la compilación, recibirá una notificación que indica que su aplicación está implementada.
Ahora puede visitar su API GraphQL implementada en la URL debajo del nombre de la aplicación en su Consola DigitalOcean. Estará vinculado a través del subdominio ondigitalocean.app
. Cuando abra la URL, GraphQL Playground se abrirá de la misma manera que en el Paso 3 de este tutorial.
Conectó con éxito su repositorio a App Platform e implementó su API GraphQL. A continuación, desarrollará su aplicación y reemplazará los datos en memoria de la API de GraphQL con una base de datos.
Paso 6: Configuración de Prisma con PostgreSQL
Hasta ahora, ha creado una API de GraphQL utilizando la matriz posts
en memoria para almacenar datos. Si su servidor se reinicia, se perderán todos los cambios en los datos. Para asegurarse de que sus datos se conserven de forma segura, reemplazará la matriz posts
con una base de datos PostgreSQL y utilizará Prisma para acceder a los datos.
En este paso, instalará la CLI de Prisma, creará su esquema de Prisma inicial (el archivo de configuración principal para su instalación de Prisma, que contiene el esquema de su base de datos), configurará PostgreSQL localmente con Docker y conectará Prisma a él.
Comience instalando Prisma CLI con el siguiente comando:
- npm install --save-dev prisma
Prisma CLI ayudará con los flujos de trabajo de la base de datos, como ejecutar migraciones de bases de datos y generar Prisma Client.
A continuación, configurará su base de datos PostgreSQL utilizando Docker. Cree un nuevo archivo Docker Compose con el siguiente comando:
- nano docker-compose.yml
Agregue el siguiente código al archivo recién creado:
version: '3.8'
services:
postgres:
image: postgres:14
restart: always
environment:
- POSTGRES_USER=test-user
- POSTGRES_PASSWORD=test-password
volumes:
- postgres:/var/lib/postgresql/data
ports:
- '5432:5432'
volumes:
postgres:
Este archivo de configuración de Docker Compose es responsable de iniciar la imagen Docker oficial de PostgreSQL en su máquina. Las variables de entorno POSTGRES_USER
y POSTGRES_PASSWORD
establecen las credenciales para el superusuario (un usuario con privilegios de administrador). También utilizará estas credenciales para conectar Prisma a la base de datos. Reemplace test-user
y test-password
con sus credenciales de usuario.
Finalmente, define un volumen donde PostgreSQL almacenará sus datos y vinculará el puerto 5432
en su máquina al mismo puerto en el contenedor Docker.
Guardar y salir del archivo.
Con esta configuración en su lugar, puede iniciar el servidor de base de datos PostgreSQL con el siguiente comando:
- docker-compose up -d
Puede tardar unos minutos en cargarse.
Puede verificar que el servidor de la base de datos se está ejecutando con el siguiente comando:
- docker ps
Este comando generará algo similar a:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
198f9431bf73 postgres:10.3 "docker-entrypoint.s…" 45 seconds ago Up 11 seconds 0.0.0.0:5432->5432/tcp prisma-graphql_postgres_1
Con el contenedor de PostgreSQL ejecutándose, ahora puede crear su configuración de Prisma. Ejecute el siguiente comando desde Prisma CLI:
- npx prisma init
Como práctica recomendada, todas las invocaciones de Prisma CLI deben tener el prefijo npx
para garantizar que utilice su instalación local.
Se imprimirá una salida como esta:
Output✔ Your Prisma schema was created at prisma/schema.prisma
You can now open it in your favorite editor.
Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb.
3. Run prisma db pull to turn your database schema into a Prisma schema.
4. Run prisma generate to generate the Prisma Client. You can then start querying your database.
More information in our documentation:
https://pris.ly/d/getting-started
Después de ejecutar el comando, la CLI de Prisma genera un archivo dotenv llamado .env
en la carpeta del proyecto para definir la URL de conexión de su base de datos, así como una nueva carpeta anidada llamada prisma
que contiene el archivo schema.prisma
. Este es el archivo de configuración principal para su proyecto Prisma (en el que incluirá su modelo de datos).
Para asegurarse de que Prisma conozca la ubicación de su base de datos, abra el archivo .env
:
- nano .env
Ajuste la variable de entorno DATABASE_URL
con sus credenciales de usuario:
DATABASE_URL="postgresql://test-user:test-password@localhost:5432/my-blog?schema=public"
Utiliza las credenciales de la base de datos test-user
y test-password
, que se especifican en el archivo Docker Compose. Si modificó las credenciales en su archivo Docker Compose, asegúrese de actualizar esta línea para que coincida con las credenciales en ese archivo. Para obtener más información sobre el formato de la URL de conexión, visite los documentos de Prisma.
Ha iniciado correctamente PostgreSQL y configurado Prisma utilizando el esquema de Prisma. En el siguiente paso, definirá su modelo de datos para el blog y utilizará Prisma Migrate para crear el esquema de la base de datos.
Paso 7: Definición del modelo de datos con Prisma Migrate
Ahora definirá su modelo de datos en el archivo de esquema Prisma que acaba de crear. Luego, este modelo de datos se asignará a la base de datos con Prisma Migrate, que generará y enviará las instrucciones SQL para crear las tablas que corresponden a su modelo de datos.
Dado que está creando un blog, las entidades principales de la aplicación serán usuarios y publicaciones. En este paso, definirá un modelo Post
con una estructura similar al tipo Post
en el esquema de GraphQL. En un paso posterior, desarrollará la aplicación y agregará un modelo de Usuario
.
Nota: la API de GraphQL se puede ver como una capa de abstracción para su base de datos. Al crear una API de GraphQL, es común que el esquema de GraphQL se parezca mucho al esquema de su base de datos. Sin embargo, como abstracción, los dos esquemas no necesariamente tendrán la misma estructura, lo que le permitirá controlar qué datos desea exponer sobre la API, ya que algunos datos pueden considerarse confidenciales o irrelevantes para la capa de la API.
Prisma utiliza su propio lenguaje de modelado de datos para definir la forma de los datos de su aplicación.
Abra su archivo schema.prisma
desde la carpeta del proyecto donde se encuentra package.json
:
- nano prisma/schema.prisma
Nota: Puede verificar desde la terminal en qué carpeta se encuentra con el comando pwd
, que generará el directorio de trabajo actual. Además, enumerar los archivos con el comando ls
lo ayudará a navegar por su sistema de archivos.
Agregue las siguientes definiciones de modelo:
...
model Post {
id Int @default(autoincrement()) @id
title String
content String?
published Boolean @default(false)
}
Usted define un campo. El modelo se asignará a una tabla de base de datos; los campos representan las columnas individuales.
Los campos id
tienen los siguientes atributos de campo:
@default(autoincrement())
establece un valor predeterminado de incremento automático para la columna.@id
establece la columna como la clave principal de la tabla.
Guardar y salir del archivo.
Con el modelo en su lugar, ahora puede crear la tabla correspondiente en la base de datos utilizando Prisma Migrate con el comando migrate dev
para crear y ejecutar los archivos de migración.
En tu terminal, ejecuta el siguiente comando:
- npx prisma migrate dev --name init --skip-generate
Este comando crea una nueva migración en su sistema de archivos y la ejecuta en la base de datos para crear el esquema de la base de datos. El indicador --name init
especifica el nombre de la migración (se usará para nombrar la carpeta de migración que se crea en su sistema de archivos). El indicador --skip-generate
omite la generación de Prisma Client (esto se hará en el siguiente paso).
Este comando generará algo similar a:
OutputEnvironment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "my-blog", schema "public" at "localhost:5432"
PostgreSQL database my-blog created at localhost:5432
Applying migration `20201201110111_init`
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20201201110111_init/
└─ migration.sql
Your database is now in sync with your schema.
Su directorio prisma/migrations
ahora está poblado con el archivo de migración de SQL. Este enfoque le permite realizar un seguimiento de los cambios en el esquema de la base de datos y crear el mismo esquema de base de datos en producción.
Nota: si ya usó Prisma Migrate con la base de datos my-blog
y hay una inconsistencia entre las migraciones en la carpeta prisma/migration
y el esquema de la base de datos, se le pedirá que reinicie la base de datos con el siguiente resultado:
Output? We need to reset the PostgreSQL database "my-blog" at "localhost:5432". All data will be lost.
Do you want to continue? › (y/N)
Puede resolver esto ingresando y
que restablecerá la base de datos. Tenga en cuenta que esto hará que se pierdan todos los datos de la base de datos.
Ya ha creado su esquema de base de datos. En el siguiente paso, instalará Prisma Client y lo utilizará en sus resolutores GraphQL.
Paso 8: uso de Prisma Client en los solucionadores de GraphQL
Prisma Client es un Object Relational Mapper (ORM) autogenerado y con seguridad de tipos que puede usar para leer y escribir datos mediante programación en una base de datos desde una aplicación Node.js. En este paso, instalará Prisma Client en su proyecto.
En su terminal, instale el paquete Prisma Client npm:
- npm install @prisma/client
Nota: Prisma Client proporciona un completo autocompletado mediante la generación de código basado en su esquema de Prisma en la carpeta node_modules
. Para generar el código, utiliza el comando npx prisma generate
. Esto normalmente se hace después de crear y ejecutar una nueva migración. En la primera instalación, sin embargo, esto no es necesario ya que se generará automáticamente en un gancho postinstall
.
Después de crear la base de datos y el esquema de GraphQL e instalar Prisma Client, ahora utilizará Prisma Client en los solucionadores de GraphQL para leer y escribir datos en la base de datos. Hará esto reemplazando la matriz posts
, que ha usado hasta ahora para almacenar sus datos.
Cree y abra el siguiente archivo:
- nano src/db.js
Agregue las siguientes líneas al nuevo archivo:
const { PrismaClient } = require('@prisma/client')
module.exports = {
prisma: new PrismaClient(),
}
Este código importa Prisma Client, crea una instancia de él y exporta la instancia que usará en sus resolutores.
Ahora guarde y cierre el archivo src/db.js
.
A continuación, importará la instancia de prisma
a src/schema.js
. Para hacerlo, abra src/schema.js
:
- nano src/schema.js
Agregue esta línea para importar prisma
desde ./db
en la parte superior del archivo:
const { prisma } = require('./db')
...
Luego elimine la matriz posts
eliminando las líneas que están marcadas con el símbolo de guión (-
):
...
-const posts = [
- {
- id: 1,
- title: 'Subscribe to GraphQL Weekly for community news ',
- content: 'https://graphqlweekly.com/',
- published: true,
- },
- {
- id: 2,
- title: 'Follow DigitalOcean on Twitter',
- content: 'https://twitter.com/digitalocean',
- published: true,
- },
- {
- id: 3,
- title: 'What is GraphQL?',
- content: 'GraphQL is a query language for APIs',
- published: false,
- },
-]
...
A continuación, actualizará los solucionadores de Query
para obtener publicaciones publicadas de la base de datos. Primero, elimine las líneas existentes en resolvers.Query
, luego actualice el objeto agregando las líneas resaltadas:
...
const resolvers = {
Query: {
feed: (parent, args) => {
return prisma.post.findMany({
where: { published: true },
})
},
post: (parent, args) => {
return prisma.post.findUnique({
where: { id: Number(args.id) },
})
},
},
...
Aquí, utiliza dos consultas de Prisma Client:
findMany
obtiene publicaciones cuyo campopublish
esfalse
.findUnique
obtiene una sola publicación cuyo campoid
es igual al argumentoid
GraphQL.
Según la especificación de GraphQL, el tipo ID
se serializa de la misma manera que una String
. Por lo tanto, convierte a un Number
porque el id
en el esquema de Prisma es un int
.
A continuación, actualizará el solucionador de Mutation
para guardar y actualizar publicaciones en la base de datos. Primero, elimine el código en el objeto resolvers.Mutation
y las líneas Number(args.id)
, luego agregue las líneas resaltadas:
const resolvers = {
...
Mutation: {
createDraft: (parent, args) => {
return prisma.post.create({
data: {
title: args.title,
content: args.content,
},
})
},
publish: (parent, args) => {
return prisma.post.update({
where: {
id: Number(args.id),
},
data: {
published: true,
},
})
},
},
}
Está utilizando dos consultas de Prisma Client:
create
para crear un registroPost
.update
para actualizar el campo publicado del registroPost
cuyoid
coincide con el del argumento de consulta.
Finalmente, elimine el objeto resolvers.Post
:
...
-Post: {
- content: (parent) => parent.content,
- id: (parent) => parent.id,
- published: (parent) => parent.published,
- title: (parent) => parent.title,
-},
...
Tu schema.js
ahora debería decir lo siguiente:
const { gql } = require('apollo-server')
const { prisma } = require('./db')
const typeDefs = gql`
type Post {
content: String
id: ID!
published: Boolean!
title: String!
}
type Query {
feed: [Post!]!
post(id: ID!): Post
}
type Mutation {
createDraft(content: String, title: String!): Post!
publish(id: ID!): Post
}
`
const resolvers = {
Query: {
feed: (parent, args) => {
return prisma.post.findMany({
where: { published: true },
})
},
post: (parent, args) => {
return prisma.post.findUnique({
where: { id: Number(args.id) },
})
},
},
Mutation: {
createDraft: (parent, args) => {
return prisma.post.create({
data: {
title: args.title,
content: args.content,
},
})
},
publish: (parent, args) => {
return prisma.post.update({
where: {
id: Number(args.id),
},
data: {
published: true,
},
})
},
},
}
module.exports = {
resolvers,
typeDefs,
}
Guarde y cierre el archivo.
Ahora que actualizó los resolutores para usar Prisma Client, inicie el servidor para probar el flujo de datos entre la API de GraphQL y la base de datos con el siguiente comando:
- npm start
Una vez más, recibirá el siguiente resultado:
OutputServer ready at: http://localhost:8080
Abra Apollo GraphQL Studio en la dirección de la salida y pruebe la API de GraphQL usando las mismas consultas del Paso 3.
Ahora confirmará sus cambios para que los cambios se puedan implementar en App Platform. Detenga el servidor Apollo con CTRL+C
.
Para evitar comprometer la carpeta node_modules
y el archivo .env
, verifique el archivo .gitignore
en la carpeta de su proyecto:
- cat .gitignore
Confirma que tu archivo .gitignore
contiene estas líneas:
node_modules
.env
Si no es así, actualice el archivo para que coincida.
Guardar y salir del archivo.
Luego ejecute los siguientes dos comandos para confirmar los cambios:
- git add .
- git commit -m 'Add Prisma'
Recibirá una respuesta de salida como esta:
Outputgit commit -m 'Add Prisma'
[main 1646d07] Add Prisma
9 files changed, 157 insertions(+), 39 deletions(-)
create mode 100644 .gitignore
create mode 100644 docker-compose.yml
create mode 100644 prisma/migrations/20201201110111_init/migration.sql
create mode 100644 prisma/migrations/migration_lock.toml
create mode 100644 prisma/schema.prisma
create mode 100644 src/db.js
Ha actualizado sus resolutores GraphQL para usar Prisma Client para realizar consultas y mutaciones en su base de datos, luego confirmó todos los cambios en su repositorio remoto. A continuación, agregará una base de datos PostgreSQL a su aplicación en App Platform.
Paso 9: Crear y migrar la base de datos de PostgreSQL en la plataforma de aplicaciones
En este paso, agregará una base de datos PostgreSQL a su aplicación en App Platform. Luego, usará Prisma Migrate para ejecutar la migración contra él para que el esquema de la base de datos implementada coincida con su base de datos local.
Primero, visite la consola de la plataforma de aplicaciones y seleccione el proyecto prisma-graphql que creó en el paso 5.
A continuación, haga clic en el botón Crear y seleccione Crear/Adjuntar base de datos en el menú desplegable, lo que le llevará a una página para configurar su base de datos.
Elija Base de datos de desarrollo, seleccione un nombre y haga clic en Crear y adjuntar.
Se le redirigirá de nuevo a la vista Proyecto, donde habrá una barra de progreso para crear la base de datos.
Una vez creada la base de datos, ejecutará la migración de la base de datos en la base de datos de producción en DigitalOcean desde su máquina local. Para ejecutar la migración, necesitará la cadena de conexión de la base de datos alojada.
Para obtenerlo, haga clic en el icono db en la sección Componentes de la pestaña Configuración.
En Detalles de conexión, presione Ver y luego seleccione Cadena de conexión en el menú desplegable. Copie la URL de la base de datos, que tendrá la siguiente estructura:
postgresql://db:some_password@unique_identifier.db.onlinux-console.net:25060/db?sslmode=require
Luego, ejecute el siguiente comando en su terminal, asegurándose de establecer your_db_connection_string
en la URL que acaba de copiar:
- DATABASE_URL="your_db_connection_string" npx prisma migrate deploy
Este comando ejecutará las migraciones contra la base de datos en vivo con Prisma Migrate.
Si la migración se realiza correctamente, recibirá el siguiente resultado:
OutputPostgreSQL database db created at unique_identifier.db.onlinux-console.net:25060
Prisma Migrate applied the following migration(s):
migrations/
└─ 20201201110111_init/
└─ migration.sql
Migró con éxito la base de datos de producción en DigitalOcean, que ahora coincide con el esquema de Prisma.
Nota: Si recibe el siguiente mensaje de error:
OutputError: P1001: Can't reach database server at `unique_identifier.db.onlinux-console.net`:`25060`
Navegue al tablero de la base de datos para confirmar que su base de datos ha sido aprovisionada. Es posible que deba actualizar o deshabilitar las Fuentes confiables para la base de datos.
Ahora puede implementar su aplicación presionando sus cambios de Git con el siguiente comando:
- git push
Nota: App Platform hará que la variable de entorno DATABASE_URL
esté disponible para su aplicación en tiempo de ejecución. Prisma Client utilizará esa variable de entorno con env(DATABASE_URL)
en el bloque datasource
de su esquema Prisma.
Esto activará automáticamente una compilación. Si abre la consola de App Platform, tendrá una barra de progreso de implementación.
Una vez que la implementación se realice correctamente, recibirá un mensaje de Implementación activa.
Ahora ha realizado una copia de seguridad de su API GraphQL implementada con una base de datos. Abra la aplicación Live, que lo llevará a Apollo GraphQL Studio. Pruebe la API de GraphQL con las mismas consultas del Paso 3.
En el paso final, desarrollará la API de GraphQL agregando el modelo User
.
Paso 10: Agregar el modelo de usuario
Su API GraphQL para blogs tiene una sola entidad llamada Post
. En este paso, evolucionará la API definiendo un nuevo modelo en el esquema de Prisma y adaptando el esquema de GraphQL para hacer uso del nuevo modelo. Introducirá un modelo de Usuario
con una relación de uno a muchos con el modelo Publicación
, que le permitirá representar al autor de las publicaciones y asociar varias publicaciones a cada usuario. . Luego, desarrollará el esquema de GraphQL para permitir la creación de usuarios y la asociación de publicaciones con usuarios a través de la API.
Primero, abra el esquema de Prisma:
- nano prisma/schema.prisma
Agrega las líneas resaltadas para agregar el campo authorId
al modelo Post
y para definir el modelo User
:
...
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
posts Post[]
}
Ha agregado los siguientes elementos al esquema de Prisma:
- Dos campos de relación:
autor
ypublicaciones
. Los campos de relación definen conexiones entre modelos a nivel de Prisma y no existen en la base de datos. Estos campos se utilizan para generar el Cliente Prisma y para acceder a las relaciones con el Cliente Prisma. - El campo
authorId
, al que hace referencia el atributo@relation
. Prisma creará una clave externa en la base de datos para conectarPost
yUser
. - El modelo
User
para representar a los usuarios.
El campo author
en el modelo Post
es opcional pero le permite crear publicaciones que no están asociadas con un usuario.
Guarde y salga del archivo una vez que haya terminado.
A continuación, cree y aplique la migración localmente con el siguiente comando:
- npx prisma migrate dev --name "add-user"
Cuando la migración se realice correctamente, recibirá el siguiente mensaje:
OutputEnvironment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "my-blog", schema "public" at "localhost:5432"
Applying migration `20201201123056_add_user`
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20201201123056_add_user/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client (4.6.1 | library) to ./node_modules/@prisma/client in 53ms
El comando también genera Prisma Client para que pueda hacer uso de la nueva tabla y campos.
Ahora ejecutará la migración en la base de datos de producción en App Platform para que el esquema de la base de datos sea el mismo que su base de datos local. Ejecute el siguiente comando en su terminal y establezca DATABASE_URL
en la URL de conexión de App Platform:
- DATABASE_URL="your_db_connection_string" npx prisma migrate deploy
Recibirá el siguiente resultado:
OutputEnvironment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "db", schema "public" at "unique_identifier.db.onlinux-console.net:25060"
2 migrations found in prisma/migrations
Applying migration `20201201123056_add_user`
The following migration have been applied:
migrations/
└─ 20201201123056_add_user/
└─ migration.sql
All migrations have been successfully applied.
Ahora actualizará el esquema y los solucionadores de GraphQL para utilizar el esquema de base de datos actualizado.
Abra el archivo src/schema.js
:
- nano src/schema.js
Actualice typeDefs
con las líneas resaltadas de la siguiente manera:
...
const typeDefs = gql`
type User {
email: String!
id: ID!
name: String
posts: [Post!]!
}
type Post {
content: String
id: ID!
published: Boolean!
title: String!
author: User
}
type Query {
feed: [Post!]!
post(id: ID!): Post
}
type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(authorEmail: String, content: String, title: String!): Post!
publish(id: ID!): Post
}
input UserCreateInput {
email: String!
name: String
posts: [PostCreateWithoutAuthorInput!]
}
input PostCreateWithoutAuthorInput {
content: String
published: Boolean
title: String!
}
`
...
En este código actualizado, agrega los siguientes cambios al esquema de GraphQL:
- El tipo
User
, que devuelve una matriz dePost
. - El campo
author
al tipoPost
. - La mutación
createUser
, que esperaUserCreateInput
como tipo de entrada. - El tipo de entrada
PostCreateWithoutAuthorInput
utilizado en la entradaUserCreateInput
para crear publicaciones como parte de la mutacióncreateUser
. - El argumento opcional
authorEmail
para la mutacióncreateDraft
.
Con el esquema actualizado, ahora actualizará los resolutores para que coincidan con el esquema.
Actualice el objeto resolvers
con las líneas resaltadas de la siguiente manera:
...
const resolvers = {
Query: {
feed: (parent, args) => {
return prisma.post.findMany({
where: { published: true },
})
},
post: (parent, args) => {
return prisma.post.findUnique({
where: { id: Number(args.id) },
})
},
},
Mutation: {
createDraft: (parent, args) => {
return prisma.post.create({
data: {
title: args.title,
content: args.content,
published: false,
author: args.authorEmail && {
connect: { email: args.authorEmail },
},
},
})
},
publish: (parent, args) => {
return prisma.post.update({
where: { id: Number(args.id) },
data: {
published: true,
},
})
},
createUser: (parent, args) => {
return prisma.user.create({
data: {
email: args.data.email,
name: args.data.name,
posts: {
create: args.data.posts,
},
},
})
},
},
User: {
posts: (parent, args) => {
return prisma.user
.findUnique({
where: { id: parent.id },
})
.posts()
},
},
Post: {
author: (parent, args) => {
return prisma.post
.findUnique({
where: { id: parent.id },
})
.author()
},
},
}
...
El solucionador de mutaciones createDraft
ahora usa el argumento authorEmail
(si se pasa) para crear una relación entre el borrador creado y un usuario existente.
El nuevo solucionador de mutaciones createUser
crea un usuario y publicaciones relacionadas mediante escrituras anidadas.
Los solucionadores User.posts
y Post.author
definen cómo resolver los campos posts
y author
cuando el Usuario
o Post
. Estos utilizan la API fluida de Prisma para obtener las relaciones.
Guardar y salir del archivo.
Inicie el servidor para probar la API de GraphQL:
- npm start
Comience probando el solucionador createUser
con la siguiente mutación de GraphQL:
mutation {
createUser(data: { email: "natalia@prisma.io", name: "Natalia" }) {
email
id
}
}
Esta mutación creará un usuario.
A continuación, pruebe el solucionador createDraft
con la siguiente mutación:
mutation {
createDraft(
authorEmail: "natalia@prisma.io"
title: "Deploying a GraphQL API to App Platform"
) {
id
title
content
published
author {
id
name
}
}
}
Puede obtener el autor
siempre que el valor de retorno de una consulta sea Post
. En este ejemplo, se llamará al solucionador Post.author
.
Cierra el servidor cuando termines de probar.
Luego confirme sus cambios y presione para implementar la API:
- git add .
- git commit -m "add user model"
- git push
Es posible que las actualizaciones tarden unos minutos en implementarse.
Evolucionó con éxito su esquema de base de datos con Prisma Migrate y expuso el nuevo modelo en su API de GraphQL.
Conclusión
En este artículo, creó una API GraphQL con Prisma y la implementó en la plataforma de aplicaciones de DigitalOcean. Definió un esquema GraphQL y resoluciones con Apollo Server. Luego usó Prisma Client en sus resolutores de GraphQL para persistir y consultar datos en la base de datos de PostgreSQL. Como siguiente paso, puede ampliar la API de GraphQL con una consulta para obtener usuarios individuales y una mutación para conectar un borrador existente a un usuario.
Si está interesado en explorar los datos en la base de datos, consulte el repositorio prisma-examples
.
Puede encontrar el código de este proyecto en el repositorio de la comunidad de DigitalOcean.