Building a NodeJS REST API using Express, MongoDB, Prisma with Typescript

isnan__h

ishan

Posted on February 6, 2023

Building a NodeJS REST API using Express, MongoDB, Prisma with Typescript

Let's build a full-stack RESTful API with NodeJS.

We will be creating Marketplace API using Express as our backend framework of choice. We will be communicating with the MongoDB database using Atlas on the cloud. All of this will be made easy with Prisma as an ORM with Typescript.

mkdir marketplace-api
cd marketplace-api
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install dev dependencies

npm i -D @types/node ts-node @types/express prisma typescript tcs-watch 
Enter fullscreen mode Exit fullscreen mode

Install app dependencies

npm i express dotenv @prisma/client
Enter fullscreen mode Exit fullscreen mode

Initializing Prisma

npx prisma init
Enter fullscreen mode Exit fullscreen mode

Generating Prisma Schema

npx prisma generate
Enter fullscreen mode Exit fullscreen mode

Create a tsconfig.json file in the root and add the following configuration to it:

{
  "compilerOptions": {
    "lib": ["esnext"],
    "module": "CommonJS",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Below is our package.json file in case if you have missed anything down the road.

{
  "name": "marketplace-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node/dist/src/index.js",
    "dev": "tsc-watch --onSuccess \"node ./dist/index.js\"",
    "watch": "npx ts-node index.ts",
    "build": "tsc",
    "seed": "ts-node prisma/seed.ts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/express": "^4.17.17",
    "@types/node": "^18.11.19",
    "prisma": "^4.9.0",
    "ts-node": "^10.9.1",
    "tsc-watch": "^6.0.0",
    "typescript": "^4.9.5"
  },
  "dependencies": {
    "@prisma/client": "^4.9.0",
    "dotenv": "^16.0.3",
    "express": "^4.18.2"
  }
}

Enter fullscreen mode Exit fullscreen mode

Creating Models

After installing all the dependencies needed for our application we can start with creating Schema for our Application.

We are using MongoDB as our database. We have created the DB connection string from Mongo Atlas. You can generate it from here.

You can use a .env file to list all your environment variables need for your application.

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

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

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

model User {
  id       String     @id @default(auto()) @map("_id") @db.ObjectId
  email    String     @unique
  name     String?
  role     Role       @default(User)
  products Products[]
}

model Products {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  title     String
  imageURL  String
  selling   Boolean  @default(false)
  seller    User     @relation(fields: [sellerId], references: [id])
  sellerId  String   @db.ObjectId
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

enum Role {
  User
  Admin
  Buyer
  Seller
}

Enter fullscreen mode Exit fullscreen mode

We are building an API endpoint which will lets users to list the products they want in the marketplace. They can mark them selling when they are ready to sell.

Each seller will have a product image, is generated by default, title and is selling status.

Creating Express server

import "dotenv/config";
import express from "express";

const app = express();
const PORT = 3000

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

After we add this inside of src folder we can run this with command

npm run dev

This will compile our typescript application and serve taking the filepath described in our scripts inside package.json file.

Seeding DB with User

Create a new file named seed.ts in your prisma folder. This would look something like

You can learn more from official doc here.

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function run() {
  const user = await prisma.user.upsert({
    where: { email: "cody@mail.com" },
    update: {},
    create: {
      name: "Coder Codes",
      email: "cody@mail.com",
    },
  });

  console.log({ user });
}

run()
  .catch((e) => {
    console.log(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
Enter fullscreen mode Exit fullscreen mode
npx prisma db seed
Enter fullscreen mode Exit fullscreen mode

Running this command will create a new user for us in the database. with a new name and a given email and disconnect.

Creating API endpoints

With this in place we are in the good position to start building our API endpoints. We will heavily make the use of awesome Prisma ORM features which will help us to query our database.

Creating new product

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

// create a product
app.post("/products", async (req, res, next) => {
  console.log(req.body, '************'); //Take a look into request object here
  try {
    const newProduct = await prisma.products.create({
      data: { sellerId: 1, ...req.body },
    });

    res.json({ newProduct });
  } catch (error: any) {
    next(error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Here we made a post request to the given url which will create a new products inside of our products document. This will be a seller id of 1 and it takes other data from request.body

Get all products

To get all the list of products we can use the .get() method from express which will receive a callback function using the findMany() method from prisma to return all JSON data of products from our DB.

app.get("/products", async (req, res, next) => {
  try {
    const allProducts = await prisma.products.findMany();
    res.json({ allProducts });
  } catch (error: any) {
    next(error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Get a product by ID

We can add a query params into our URL endpoint. There we could access the id with req.params.id this would eventually return a JSON object which will match the ID that is passed in.

app.get("/products/:id", async (req, res, next) => {
  try {
    const singleProduct = await prisma.products.findUnique({
      where: {
        id: req.params.id,
      },
    });

    res.json({ singleProduct });
  } catch (error: any) {
    next(error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Update a product

We can use a PATCH request which will pass in the ID from teh request query.

app.patch("/products/:id", async (req, res, next) => {
  try {
    const product = await prisma.products.update({
      where: {
        id: req.params.id,
      },
      data: req.body,
    });

    res.json({ product });
  } catch (error: any) {
    next(error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Delete a Product

Using a DELETE request we can find the product matching in the passed parameter ID and delete with the help of Prisma handy delete() method.

app.delete("/products/:id", async (req, res, next) => {
  try {
    await prisma.products.delete({
      where: {
        id: req.params.id,
      },
    });

    res.sendStatus(200);
  } catch (error: any) {
    next(error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Get Single user Product

We can get a user with an ID with the findUnique() method available to us. Taking in the where key to further know the ID we are dealing with from request object.

app.get("/users/:id/products", async (req, res, next) => {
  try {
    const usersWithProducts = await prisma.user.findUnique({
      where: {
        id: req.params.id,
      },
      include: {
        products: {
          where: {
            selling: true,
          },
        },
      },
    });

    const products = usersWithProducts?.products;

    res.json({ products });
  } catch (error: any) {
    next(error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

We have successfully created a simple API endpoint for our Marketplace application. If you want to delete a record with relation, you can read more about Cascading deletes.

I hope this is useful in understanding how we could create API using Express, MongoDB, Prisma ORM with Typescript.

Thank you!

Learning References

💖 💪 🙅 🚩
isnan__h
ishan

Posted on February 6, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related