DEV Community

Cover image for Extend your Platformatic API with Prisma Client
Ruheni Alex for Prisma

Posted on

Extend your Platformatic API with Prisma Client

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:

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
Enter fullscreen mode Exit fullscreen mode

Now, perform the following actions to get started:

  1. Navigate to the cloned directory:

    cd prisma-platformatic
    
  2. Install dependencies:

    npm install
    
  3. Create a .env file based-off of the .env.example file:

    cp .env.example .env
    
  4. 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;
    
  1. Generate an up and down migration:

    npx db-diff
    
  2. Apply the up migration to your database:

    npx platformatic db migrations apply
    
  3. 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.

  1. Generate Prisma Client:
```
npx prisma generate
```
Enter fullscreen mode Exit fullscreen mode

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"
  }
Enter fullscreen mode Exit fullscreen mode

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"
  }
}

Enter fullscreen mode Exit fullscreen mode

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')
}
Enter fullscreen mode Exit fullscreen mode

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
      }
    }
  })
}
Enter fullscreen mode Exit fullscreen mode

The above snippet does the following:

  1. Imports Prisma Client.
  2. Creates a new instance of Prisma Client.
  3. Defines the incrementPostViewCount mutation type definition. It also defines the corresponding argument and the return type.
  4. Defines the resolver for your mutation.
  5. Makes a query to the database on the Post model to increment a post's view count.
  6. Adds error handling in the event a record with the matching id was not found.
  7. 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
  }
}
Enter fullscreen mode Exit fullscreen mode

Expand to view the response
{
  "data": {
    "incrementPostViewCount": {
      "id": "1",
      "title": "Prisma 💚 Platformatic",
      "viewCount": 1
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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)
  })
}
Enter fullscreen mode Exit fullscreen mode

The above code does the following:

  1. Defines the route for incrementing a post's views.
  2. Retrieves the id from the request parameters.
  3. Makes a query to the database on the Post model to increment the post's view count.
  4. Adds error handling in the event a record with the matching id was not found.
  5. 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)
+  })
}
Enter fullscreen mode Exit fullscreen mode

You can head over to http://localhost:3042/documentation to view the Swagger docs. You should see the endpoint you just created.

Platformatic  auto-generated Swagger documentation

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' 
Enter fullscreen mode Exit fullscreen mode

You can also use Thunder Client on VS Code to run the request. To make a request:

  1. Open up Thunder Client on VS Code.
  2. Create a new request by clicking New Request button on the side bar.
  3. Select the PUT method.
  4. Enter it to the following URL: http://localhost:3042/post/1/views.
  5. Click Send.

You should see the following output on your editor:

Thunder Client  raw `PUT` endraw  request

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)

Collapse
 
imload profile image
Andrew

Thank you for material <3