In this part, you will learn how to extend the auto-generated GraphQL and REST API by Platformatic using Prisma Client.
You will create a GraphQL mutation and a REST API endpoint to increment the views on a post record.
If you're interested in learning how to use Prisma to model your database schema and auto-generate database migrations when working with Platformatic, refer to part 2 of this series.
Note: It is recommended you type out the changes over copying and pasting the code.
Prerequisites
Your dev toolbox
To follow along, ensure you have the following installed:
- Node.js v18 or later
- npm v7 or later
- Prisma VSCode Extension (optional)
- Thunder Client VSCode Extension (optional)
- Docker (optional)
Note: If you don't have Docker installed, you can set up a free hosted database on Railway or install PostgreSQL.
Assumed knowledge
This guide will require you have basic familiarity in the following technologies:
- JavaScript
- GraphQL APIs
- REST APIs
Set up your Platformatic API
The starting point of this tutorial is part 2 of this series. If you're following along from the previous part, you can jump to Extend the functionality of your API with a plugin section of this guide.
In this tutorial, you'll use the following starter repository. It contains the setup files for a new Platformatic project.
To get started, clone the repository and checkout to the automated-migrations
branch.
Clone the repository:
git clone -b extend-plugin https://github.com/ruheni/prisma-platformatic.git
Now, perform the following actions to get started:
-
Navigate to the cloned directory:
cd prisma-platformatic
-
Install dependencies:
npm install
-
Create a
.env
file based-off of the.env.example
file:
cp .env.example .env
-
Start the PostgreSQL database with docker:
docker-compose up -d
Note: If you already have an existing database server running locally, update the value of the
DATABASE_URL
in the.env
file with your database's user and password values:DATABASE_URL="postgres://<USER>:<PASSWORD>@localhost:5432/blog"
Connect to your database instance using
psql
or your preferred SQL client. Copy and run the following SQL to create a database:CREATE DATABASE blog;
-
Generate an
up
anddown
migration:
npx db-diff
-
Apply the
up
migration to your database:
npx platformatic db migrations apply
-
Populate the database with sample data:
npx platformatic db seed seed.js
Note: If you're following along from the previous part, you can skip this step.
- Generate Prisma Client:
```
npx prisma generate
```
Project structure and files
The project has the following structure:
The important files in the directory are:
-
.env
: Contains the database connection string for your PostgreSQL database. -
docker-compose.yml
: Defines the Docker image and configuration for your PostgreSQL database. -
package.json
: Defines your application dependencies.platformatic
is currently the only dependency in the project. -
platformatic.db.json
: Defines Platformatic's configuration such as the server's hostname and port, migration directory, and your database's connection string. -
seed.js
: Contains a script for populating the database with seed data. -
schema.prisma
: Defines the structure of your database schema.
The schema.prisma
file contains two models: User
and Post
, with a one-to-many relation.
Extend the functionality of your API with plugins
Define the plugin
property in your platformatic.db.json
file
The first step is defining the plugin configuration in your platformatic.db.json
file. Paste the following snippet to in your configuration file right after the migrations
property:
"plugin": {
"path": "./plugin.js"
}
Expand to view the entire file
{
"server": {
"logger":{
"level": "info"
},
"hostname": "127.0.0.1",
"port": "3042"
},
"core": {
"connectionString": "{DATABASE_URL}"
},
"migrations": {
"dir": "./migrations"
},
"plugin": {
"path": "plugin.js"
}
}
The snippet defines the path to your plugin. The plugin will be registered when your server starts, exposing the custom logic you've defined.
Create a plugin
At the root of your directory, create a new file called plugin.js
. Paste the following snippet to your plugin file:
// ./plugin.js
module.exports = async (app) => {
app.log.info('plugin loaded')
}
The file exports an async function with an app
parameter that's a FastifyInstance
.
When you you start up Platformatic with npx platformatic db start
, Platformatic will register the plugin. "plugin loaded" will also be logged to your console.
With everything set up, let's take a look at how you can extend your API using Prisma Client.
Extend the GraphQL API
You'll extend the GraphQL API by defining a mutation called incrementPostViewCount
. The mutation will have one argument, id
that is an ID
type. The mutation response type will be a Post
object type.
To implement the GraphQL mutation, make the following changes to your plugin.js
file:
// ./plugin.js
// 1.
const { PrismaClient } = require("@prisma/client")
// 2
const prisma = new PrismaClient()
module.exports = async (app) => {
app.log.info('plugin loaded')
//3.
app.graphql.extendSchema(`
extend type Mutation {
incrementPostViewCount(id: ID): Post
}
`)
//4.
app.graphql.defineResolvers({
Mutation: {
incrementPostViewCount: async (_, { id }) => {
// 5.
const post = await prisma.post.update({
where: {
id: Number(id)
},
data: {
viewCount: {
increment: 1
}
}
})
// 6.
if (!post) throw new Error(`Post with id:${id} was not found`)
// 7.
return post
}
}
})
}
The above snippet does the following:
- Imports Prisma Client.
- Creates a new instance of Prisma Client.
- Defines the
incrementPostViewCount
mutation type definition. It also defines the corresponding argument and the return type. - Defines the resolver for your mutation.
- Makes a query to the database on the
Post
model to increment a post's view count. - Adds error handling in the event a record with the matching id was not found.
- Returns the updated post on success.
As you're defining the incrementPostMutation
resolver, you will notice Prisma Client provides auto-completion. On hovering on the incrementPostViewCount
resolver and post
variable, VS Code infers the types for you.
You can head over to http://localhost:3042/graphiql
to try out the mutation you just created.
mutation INCREMENT_POST_VIEWS {
incrementPostViewCount(id: 1){
id
title
viewCount
}
}
Expand to view the response
{
"data": {
"incrementPostViewCount": {
"id": "1",
"title": "Prisma 💚 Platformatic",
"viewCount": 1
}
}
}
Extend the REST API
Next, you'll extend the REST API by defining an endpoint that will accept PUT
requests to increment the views of a post.
To implement the REST endpoint, update ./plugin.js
file with the following snippet:
// ./plugin.js
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()
module.exports = async (app) => {
app.log.info('plugin loaded')
// ...GraphQL API
// 1.
app.put('/post/:id/views', async (req, reply) => {
// 2.
const { id } = req.params
// 3.
const post = await prisma.post.update({
where: {
id: Number(id)
},
data: {
viewCount: {
increment: 1
}
}
})
// 4.
if (!post) reply.code(404).send({ error: `Post with id:${id} was not found` })
// 5.
return reply.send(post)
})
}
The above code does the following:
- Defines the route for incrementing a post's views.
- Retrieves the
id
from the request parameters. - Makes a query to the database on the
Post
model to increment the post's view count. - Adds error handling in the event a record with the matching id was not found.
- Returns the updated post on success.
Expand to see the diff
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()
module.exports = async (app) => {
app.log.info('plugin loaded')
app.graphql.extendSchema(`
extend type Mutation {
incrementPostViewCount(id: ID): Post
}
`)
//4.
app.graphql.defineResolvers({
Mutation: {
incrementPostViewCount: async (_, { id }) => {
// 5.
const post = await prisma.post.update({
where: {
id: Number(id)
},
data: {
viewCount: {
increment: 1
}
}
})
// 6.
if (!post) throw new Error(`Post with id:${id} was not found`)
// 7.
return post
}
}
})
+ // 1.
+ app.put('/post/:id/views', async (req, reply) => {
+ // 2.
+ const { id } = req.params
+ // 3.
+ const post = await prisma.post.update({
+ where: {
+ id: Number(id)
+ },
+ data: {
+ viewCount: {
+ increment: 1
+ }
+ }
+ })
+ // 4.
+ if (!post) reply.code(404).send({ error: `Post with id:${id} was not found` })
+ // 5.
+ return reply.send(post)
+ })
}
You can head over to http://localhost:3042/documentation
to view the Swagger docs. You should see the endpoint you just created.
To try out the endpoint you just created, you can make a request to your endpoint using curl
:
curl -X PUT \
'localhost:3042/post/2/views' \
--header 'Accept: */*' \
--header 'Content-Type: application/json'
You can also use Thunder Client on VS Code to run the request. To make a request:
- Open up Thunder Client on VS Code.
- Create a new request by clicking New Request button on the side bar.
- Select the
PUT
method. - Enter it to the following URL:
http://localhost:3042/post/1/views
. - Click Send.
You should see the following output on your editor:
Recap and next steps
Congratulations! 🎉
Using Prisma Client, you've successfully extended the GraphQL and REST APIs generated with Platformatic API.
In case you missed it, check out part 2 to learn how you can use Prisma to:
- Model your database schema
- Auto-generate fully customizable SQL migrations
You can find the source code for this project on GitHub. If you run into any challenges, feel free to create a GitHub issue or contribute to the repository. You can also reach out to me directly on Twitter
Top comments (1)
Thank you for material <3