Build a REST API with Prisma, Node JS and Typescript.
Tags: node, typescript, prisma
Published: 2023-03-11 03:30:01

## Introduction
The process of learning a new technology can often feel tedious, especially in the initial stages when you have yet to see it in action.

This article will guide you through the process of integrating these amazing technologies and demonstrate how to build a small application using them.

During the course of this article, we will develop a blog application that showcases how CRUD operations work in practice, with fundamental functions including:

- Creating new posts
- Retrieving all posts or a specific post
- Modifying post content
- And ultimately, removing a post
- An additional feature we will implement is the inclusion of a like count for each post.

> CRUD means CREATE, READ, UPDATE and DELETE

If you are interested in checking out the code, Check out the github repository [here](https://github.com/joshtom/Node-Prisma-Typescript-Blog-Api)

## Prerequisite
Before you proceed, it's important to be acquainted with the following set of tools:

- Node.js and Express.js
- TypeScript
- Prisma

If you require a brief overview of Prisma, my blog post [here](https://dev.to/joshtom/what-the-heck-is-prisma-26kg) can be a helpful resource.

## Let's Get Started 🚀
Open up your terminal then create and navigate to your folder directory using the below command
```
mkdir prisma-typescript-blog && cd prisma-typescript-blog
```
Next, Initialize the project using yarn
```
yarn init -y
```
Then, Install the Dependencies
```
yarn add -D @types/express @types/node prisma ts-node-dev typescript 
```
```
yarn add express @prisma/client
```
Finally, set up Prisma with the `init` command of the Prisma CLI:
``` 
npx prisma init --datasource-provider sqlite 
```

This creates a new prisma directory with your Prisma schema file and configures Sqlite as your database.

## Set up Prisma Schema

`prisma/prisma.schema`

```js
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Post {
  id         Int      @id @default(autoincrement())
  title      String
  content    String
  likesCount Int      @default(0)
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  // Relations
  comments Comment[]
}

model Comment {
  id        Int      @id @default(autoincrement())
  content   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  post   Post @relation(fields: [postId], references: [id])
  postId Int
}

```
In the above schema we define our Blog Models which includes `Post` and `Comment`

```js
post   Post @relation(fields: [postId], references: [id])
```

This line of code means that there's a one-to-many relationship between the `Post` and `Comment` models. One post can have many comments, and each comment is associated with only one post. The `postId` field in the `Comment` model is used to reference the `id` field in the `Post` model.

Once you are done with this, Update your `package.json` to look like this:
```json
{
  "name": "prisma-typescript-blog",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "ts-node-dev --respawn --transpile-only --exit-child src/server.ts",
    "db:migrate": "npx prisma migrate dev --name user-entity --create-only && npx prisma generate",
    "db:push": "npx prisma db push"
  },
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": [
      "es6",
      "dom"
    ]
  },
  "devDependencies": {
    "@types/express": "^4.17.17",
    "prisma": "^4.11.0",
    "ts-node-dev": "^2.0.0",
    "typescript": "^4.9.5"
  },
  "dependencies": {
    "@prisma/client": "^4.11.0",
    "dotenv": "^16.0.3",
    "express": "^4.18.2"
  }
}

```
- `start` Starts the Node Js Server
- `db:migrate` - Generate the migration file and Prisma Client
- `db:push` - Pushes the Prisma schema to the database

## Migration

At this point, you have a Prisma model but no database yet. Open your terminal and run the following command to create the SQLite database and the “posts” table represented by the model.
```
yarn db:migrate
```
After running the command, the Prisma CLI will analyze your schema and generate a migration file in the `prisma/migrations` directory.

Push the Prisma schema to the database:
```
yarn db:push
```
After synching the SQLite database with the Prisma schema, run `npx prisma studio` to open the Prisma GUI tool in the browser. Prisma studio allows you to view and mutate the data stored in the database.

By now, Your browser should automatically open a new tab and you will see something like this:

![Prisma Studio Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/it83grd0a61h7uhob5e7.png)


## Project Structure 🚧
Edit your project structure to look like below
```
`prisma` -|
          `schema.prisma`
`src` -|
     `controllers`
          -| 
           `post.controller.ts`
     `routes`
          -|
           `post.route.ts`
     `server.ts`
.env
`tsconfig.json`
```

`.env`

```js
DATABASE_URL="file:./dev.db"
```
Replace whatever is in your `.env` file with the above snippet


## Let's move on to the fun part 💃

`server.ts`
```ts
import express, { Request, Response } from "express";
import { PrismaClient } from "@prisma/client";
import PostRouter from "./routes/blog.route";

export const prisma = new PrismaClient();

const app = express();
const port = 8080;

async function main() {
  app.use(express.json());

  // Register API routes
  app.use("/api/v1/post", PostRouter);

  // Catch unregistered routes
  app.all("*", (req: Request, res: Response) => {
    res.status(404).json({ error: `Route ${req.originalUrl} not found` });
  });

  app.listen(port, () => {
    console.log(`Server is listening on port ${port}`);
  });
}

main()
  .then(async () => {
    await prisma.$connect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });
```
Quite a lot is going on here, so let me explain

- `prisma = new PrismaClient()` - Creates a new instance of the Prisma client
- The `express.json()` Registers middleware to parse incoming request bodies as JSON.
- `app.use("/api/v1/post", PostRouter);` Registers API routes using the PostRouter module.
- `app.all("*",...` Defines a catch-all route to handle unregistered routes and return a 404 error response.

When the `main()` function is called, it starts the Express server and connects to the database using the Prisma client. If there are any errors, it logs the error to the console and disconnects from the database.

The code serves as a starting point for building an API server with Node.js, Express.js, and Prisma ORM. It demonstrates how to handle API routes, connect to a database, and start a server that listens for incoming requests.

## Routes

`routes/post.route.ts`

```js
import express from "express";
import PostController from "../controllers/post.controller";

const router = express.Router();

router.post("/create", PostController.createBlogPost);
router.post("/createPostAndComments", PostController.createPostAndComments);
router.get("/getall", PostController.getBlogPosts);
router.get("/get/:id", PostController.getBlogPost);
router.put("/update/:id", PostController.updateBlogPost);
router.delete("/delete/:id", PostController.deleteBlogPost);
router.delete("/deleteall", PostController.deleteAllBlogPosts);
router.post("/like", PostController.likeBlogPost);

export default router;

```
These routes are pretty straight forward except for the `/createPostAndComments` route. Each route handles a specific HTTP method (POST, GET, PUT, or DELETE). The routes are mapped to methods defined in the `PostController` controller, which handles the logic for each of these routes. 

> The `/:id` portion of these routes is a parameter that represents a dynamic value that can be passed in the URL. The `id` parameter is a placeholder for a specific value, such as a unique identifier for a blog post.

> The `createPostAndComments` will help us demonstrate a one to many relationship as it was defined in the Schema earlier.

In the `Post` Prisma model, there is a one-to-many relationship between `Post` and `Comment`, where one `Post` can have many `Comments`, but each `Comment` can only belong to one `Post`. This relationship is modeled in the Prisma schema using the `comments` field in the `Post` model:

```js
model Post {
  id         Int      @id @default(autoincrement())
  title      String
  content    String
  likesCount Int      @default(0)
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  // Relations
  comments Comment[]
}
```
As you can see, the `comments` field is an array of `Comment` objects, which represents the one-to-many relationship.

## Controllers

`controllers/post.controller.ts`

```ts
import { Request, Response } from "express";
import { prisma } from "../server";

const createBlogPost = async (req: Request, res: Response) => {
  try {
    const { title, content } = req.body;
    const newBlogPost = await prisma.post.create({
      data: {
        title,
        content,
      },
    });
    res.status(200).json(newBlogPost);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const createPostAndComments = async (req: Request, res: Response) => {
  try {
    const { title, content, comments } = req.body;
    const newBlogPost = await prisma.post.create({
      data: {
        title,
        content,
        comments: {
          create: comments,
        },
      },
      include: {
        comments: true, // Include the comments in the response
      }
    });
    res.status(200).json(newBlogPost);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const getBlogPosts = async (req: Request, res: Response) => {
  try {
    const blogPosts = await prisma.post.findMany();
    res.status(200).json(blogPosts);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const getBlogPost = async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const blogPost = await prisma.post.findUnique({
      where: {
        id: Number(id),
      },
    });
    res.status(200).json(blogPost);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const updateBlogPost = async (req: Request, res: Response) => {
  try {
    const { id, title, content } = req.body;
    const updatedBlogPost = await prisma.post.update({
      where: {
        id: Number(id),
      },
      data: {
        title,
        content,
      },
    });
    res.status(200).json(updatedBlogPost);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const deleteBlogPost = async (req: Request, res: Response) => {
  try {
    const { id } = req.body;
    const deletedBlogPost = await prisma.post.delete({
      where: {
        id: Number(id),
      },
    });
    res.status(200).json(deletedBlogPost);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const deleteAllBlogPosts = async (req: Request, res: Response) => {
  try {
    const deletedBlogPosts = await prisma.post.deleteMany();
    res.status(200).json(deletedBlogPosts);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const likeBlogPost = async (req: Request, res: Response) => {
  try {
    const { id } = req.body;
    const likedBlogPost = await prisma.post.update({
      where: {
        id: Number(id),
      },
      data: {
        likesCount: {
          increment: 1,
        },
      },
    });
    res.status(200).json(likedBlogPost);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

export default {
  createBlogPost,
  createPostAndComments,
  getBlogPosts,
  getBlogPost,
  updateBlogPost,
  deleteBlogPost,
  deleteAllBlogPosts,
  likeBlogPost,
};


```
This controller is quite bulky, so I will go ahead and explain each method one after the other.

Our methods are listed as follows
- `createBlogPost`
- `createPostAndComments`
- `getBlogPosts`
- `getBlogPost`
- `updateBlogPost`
- `deleteBlogPost`
- `deleteAllBlogPosts`
- `likeBlogPost`

`createBlogPost`
This is a pretty simple method that accepts the `title` and `content`. It first destructures the title and content values from the `req.body` object, which is the request payload.

Also, it uses the `create()` method to create a new blog post in the database. This method returns a Promise, which is why the await keyword is used to wait for the operation to complete before continuing execution.

> When a request is made to the `http://localhost:8080/api/v1/post/create` endpoint with the credentials included in the request body, the `createBlogPost` handler will be evoked to create a new blog post.

![Blog post postman demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1md2s6vf3rje38fs5lq.png)

> Postman is my preferred tool for testing all the endpoints, but feel free to use whichever testing tool you prefer.

`createPostAndComments`

The createPostAndComments method expects the request body to include a `title`, `content`, and an array of `comments`. The method then creates the new Post object using the create() method provided by Prisma, and includes the `comments` in the data object as well.

The `Comment` object will include the `content` field, as the `postId` field will be automatically set by Prisma when the `Comment` is associated with the new `Post`.

Finally, the `include` parameter is used to include the `comments` for the newly created `Post` in the response.

**Here is how the payload and response looks like**

![Example Body](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mavozmdwcfuh5jxoutys.png)

![Example Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tk6i2o2iaklzdlg11bpw.png)

`updateBlogPost`
This method first extracts the `id`, `title`, and `content` properties from the request body using object destructuring.

It then uses Prisma to update the blog post with the specified `id`. The `update()` method is called on the `post` model, passing in an object that specifies the `where` and `data` clauses. The `where` clause specifies the unique identifier for the blog post to be updated, while the `data` clause specifies the new values for the `title` and `content` properties.

`likeBlogPost`
This method first extracts the `id` property from the request body. This `id` represents the unique identifier of the blog post to be liked.

It then uses Prisma `prisma.post.update()` to update the specified blog post. The `update()` method is called on the `post` model, passing in an object that specifies the `where` and `data` clauses. The `where` clause specifies the unique identifier for the blog post to be updated, while the data clause `increments` the `likesCount` property by `1` using the increment keyword

_Here is it in action_

> When a request is made to the `http://localhost:8080/api/v1/post/like` endpoint passing `id` as body, It then invokes the `createBlogPost` handler and `likesCount` will be incremented

![Like Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9anwpw3l7m0k6ci2eeud.png)

> All other methods are quite similar to what has been covered so I won't be going over them

After all these steps, Be sure your server is still running if not run `yarn start` to get it up and running.

## In Conclusion
This article demonstrates how to apply your knowledge of this amazing tools by creating a blog API using Node, Typescript, and Prisma. Additionally, the tutorial covers the process of connecting the API with a SQLite database.

Congratulations on reaching the end of this article! If you found it helpful, please consider giving it a thumbs up and leaving your comments below.

See you in the next 😉🚀