Building a NodeJS REST API using Express, MongoDB, Prisma with Typescript
Tags: node, prisma, mongodb, api
Published: 2023-02-06 12:25:01

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.


```shell
mkdir marketplace-api
cd marketplace-api
npm init -y
```

Install dev dependencies

```
npm i -D @types/node ts-node @types/express prisma typescript tcs-watch 
```

Install app dependencies

```
npm i express dotenv @prisma/client
```

## Initializing Prisma

```shell
npx prisma init
```

## Generating Prisma Schema

```shell
npx prisma generate
```

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

```json
{
  "compilerOptions": {
    "lib": ["esnext"],
    "module": "CommonJS",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}
```
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"
  }
}

```

##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](https://cloud.mongodb.com/).

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


```typescript
// 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
}

```

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

```typescript
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}`);
});
```

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](https://www.prisma.io/docs/guides/database/seed-database).

```typescript
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();
  });
```

```
npx prisma db seed
```

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**

```typescript
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);
  }
});
```

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.

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

**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.

```typescript
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);
  }
});
```


***Update a product***

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

```typescript
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);
  }
});
```

***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.

```typescript
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);
  }
});
```


***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.


```typescript
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);
  }
});
```

##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](https://www.prisma.io/docs/concepts/components/prisma-client/crud#cascading-deletes-deleting-related-records).

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

Thank you!

## Learning References

- https://www.prisma.io/docs/concepts/overview/prisma-in-your-stack/rest
- https://www.prisma.io/docs/guides/database/using-prisma-with-mongodb
- https://www.prisma.io/express