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
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
npx prisma init
Generating Prisma Schema
npx prisma generate
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
}
}
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.
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
}
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}`);
});
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();
});
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
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.
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.
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.
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.
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.
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.
I hope this is useful in understanding how we could create API using Express, MongoDB, Prisma ORM with Typescript.
Thank you!
Top comments (4)
Great :) Easy to understand, and, I got interested in
Prisma
.Thank you for writing such a helpful article.
I apologize for bringing up a minor point, but there were a typos:
I replaced ts-node with tsx due ERR_UNKNOWN_FILE_EXTENSION error.
npm i -D tsx
npx tsx src/index.ts
stackoverflow.com/a/76343394/1214237