Build Paytm-Like App Backend Tutorial
Build Paytm-Like App Backend Tutorial
Week 8.2
Recap Everything, Build PayTM Backend
It's important to note that this session primarily focuses on the backend section
of the application. For the frontend portion, be sure to check out the content
covered in the subsequent 8.4 lecture.
Things to do
Backend
Frontend
Solution
[Link] 1/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Step 1
Solution
Step 2
Solution
Solution
Solution
1. Add cors
Hint
Solution
2. Add body-parser
Hint
Solution
3. Add jsonwebtoken
4. Export JWT_SECRET
Solution
Solution
1. Signup
Solution
2. Route to sign in
Solution
Solution
Step 7 - Middleware
Solution
Solution
[Link] 2/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Hints
Solution
Accounts table
Solution
Solution
Solution
Solution
2. Route requests to it
Solution
Solution
Final Solution
Code
Error
Get balance
Make transfer
[Link] 3/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Things to do
Clone the 8.2 repository from [Link]
💡
Please keep a MongoDB URL handy before you proceed. This will be your primary database for this
assignment
1. Create a free one here - [Link]
2. There is a Dockerfile in the codebase, you can run mongo locally using it.
Backend
1. Express - HTTP Server
[Link] 4/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
// [Link]
const express = require("express");
const app = express();
Frontend
1. React - Frontend framework
// [Link]
function App() {
return (
<div>
Hello world
</div>
)
}
To start off, create the mongo schema for the users table
[Link] 5/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Solution
Simple solution
// backend/[Link]
const mongoose = require('mongoose');
[Link] = {
User
};
Elegant Solution
// backend/[Link]
const mongoose = require('mongoose');
maxLength: 50
},
lastName: {
type: String,
required: true,
trim: true,
maxLength: 50
}
});
[Link] = {
User
};
Step 1
Create a new file backend/routes/[Link] that exports a new express router.
( How to create a router - [Link] )
Solution
// backend/api/[Link]
const express = require('express');
[Link] = router;
Step 2
Import the router in [Link] and route all requests from /api/v1 to it
[Link] 7/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Solution
// backend/[Link]
const express = require("express");
const rootRouter = require("./routes/index");
[Link]("/api/v1", rootRouter);
Solution
// backend/routes/[Link]
const express = require('express');
[Link] = router;
Solution
// backend/routes/[Link]
const express = require('express');
[Link] 8/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link]("/user", userRouter)
[Link] = router;
1. Add cors
Since our frontend and backend will be hosted on separate routes, add the cors middleware to
backend/[Link]
Hint
Look at [Link]
Solution
// backend/[Link]
const express = require('express');
const cors = require("cors");
[Link](cors());
[Link] = router;
2. Add body-parser
Since we have to support the JSON body in post requests, add the express body parser middleware
to backend/[Link]
You can use the body-parser npm library, or use [Link]
[Link] 9/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Hint
[Link]
335803cd048c
Solution
// backend/[Link]
const express = require('express');
const cors = require("cors");
const rootRouter = require("./routes/index");
[Link](cors());
[Link]([Link]());
[Link]("/api/v1", rootRouter);
3. Add jsonwebtoken
We will be adding authentication soon to our application, so install jsonwebtoken library. It’ll be
useful in the next slide
4. Export JWT_SECRET
Export a JWT_SECRET from a new file backend/[Link]
Solution
//backend/[Link]
[Link] = {
JWT_SECRET: "your-jwt-secret"
}
[Link] 10/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Solution
// backend/[Link]
... Existing code
[Link](3000);
1. Signup
This route needs to get user information, do input validation using zod and store the information in
the database provided
If all goes well, we need to return the user a jwt which has their user id encoded as follows -
{
userId: "userId of newly added user"
}
💡
Note - We are not hashing passwords before putting them in the database. This is standard practise
that should be done, you can find more details here - [Link]
passwords-in-nodejs/
Method: POST
Route: /api/v1/user/signup
Body:
{
username: "name@[Link]",
[Link] 11/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
firstName: "name",
lastName: "name",
password: "123456"
}
Response:
{
message: "User created successfully",
token: "jwt"
}
{
message: "Email already taken / Incorrect inputs"
}
Solution
const zod = require("zod");
const { User } = require("../db");
const jwt = require("jsonwebtoken");
const { JWT_SECRET } = require("../config");
[Link] 12/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
if (existingUser) {
return [Link](411).json({
message: "Email already taken/Incorrect inputs"
})
}
[Link]({
message: "User created successfully",
token: token
})
})
2. Route to sign in
Let’s an existing user sign in to get back a token.
Method: POST
Route: /api/v1/user/signin
Body:
{
username: "name@[Link]",
password: "123456"
}
Response:
Status code - 200
{
token: "jwt"
}
[Link] 13/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
{
message: "Error while logging in"
}
Solution
const signinBody = [Link]({
username: [Link]().email(),
password: [Link]()
})
if (user) {
const token = [Link]({
userId: user._id
}, JWT_SECRET);
[Link]({
token: token
})
return;
}
[Link](411).json({
message: "Error while logging in"
})
})
[Link] 14/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Solution
// backend/routes/[Link]
const express = require('express');
if (existingUser) {
return [Link](411).json({
message: "Email already taken/Incorrect inputs"
})
}
[Link] 15/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link]({
message: "User created successfully",
token: token
})
})
if (user) {
const token = [Link]({
userId: user._id
}, JWT_SECRET);
[Link]({
token: token
})
return;
}
[Link](411).json({
message: "Error while logging in"
})
})
[Link] = router;
[Link] 16/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Step 7 - Middleware
Now that we have a user account, we need to gate routes which authenticated users can hit.
For this, we need to introduce an auth middleware
3. Puts the userId in the request object if the token checks out.
Header -
Authorization: Bearer <actual token>
Solution
const { JWT_SECRET } = require("./config");
const jwt = require("jsonwebtoken");
try {
const decoded = [Link](token, JWT_SECRET);
[Link] = [Link];
next();
} catch (err) {
return [Link](403).json({});
}
};
[Link] = {
authMiddleware
}
[Link] 17/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
1. password
2. firstName
3. lastName
Whatever they send, we need to update it in the database for the user.
Use the middleware we defined in the last section to authenticate the user
Method: PUT
Route: /api/v1/user
Body:
{
password: "new_password",
firstName: "updated_first_name",
lastName: "updated_first_name",
}
Response:
Status code - 200
{
message: "Updated successfully"
}
{
message: "Error while updating information"
}
Solution
[Link] 18/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
await [Link]([Link], {
_id: [Link]
})
[Link]({
message: "Updated successfully"
})
})
{
users: [{
firstName: "",
lastName: "",
_id: "id of the user"
[Link] 19/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
}]
}
Hints
[Link]
not-work-properly
[Link]
Solution
[Link]("/bulk", async (req, res) => {
const filter = [Link] || "";
[Link]({
user: [Link](user => ({
username: [Link],
firstName: [Link],
lastName: [Link],
_id: user._id
}))
})
})
Accounts table
The Accounts table will store the INR balances of a user.
[Link] 20/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
{
userId: ObjectId (or string),
balance: float/number
}
In the real world, you shouldn’t store `floats` for balances in the database.
You usually store an integer which represents the INR value with
decimal places (for eg, if someone has 33.33 rs in their account,
you store 3333 in the database).
There is a certain precision that you need to support (which for india is
2/4 decimal places) and this allows you to get rid of precision
errors by storing integers in your DB
Solution
const accountSchema = new [Link]({
userId: {
type: [Link], // Reference to User model
ref: 'User',
required: true
},
balance: {
type: Number,
required: true
}
});
[Link] = {
Account
}
[Link] 21/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link]("mongodb://localhost:27017/paytm")
[Link] 22/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link] = {
User,
Account,
};
// Example usage
transferFunds('fromAccountID', 'toAccountID', 100);
Solution
1. What if the database crashes right after the first request (only the balance is decreased for one
user, and not for the second user)
Solution
[Link]("/signup", async (req, res) => {
const { success } = [Link]([Link])
if (!success) {
return [Link](411).json({
message: "Email already taken / Incorrect inputs"
})
}
if (existingUser) {
return [Link](411).json({
message: "Email already taken/Incorrect inputs"
})
}
await [Link]({
userId,
balance: 1 + [Link]() * 10000
})
[Link]({
[Link] 24/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Solution
// backend/routes/[Link]
const express = require('express');
[Link] = router;
2. Route requests to it
Send all requests from /api/v1/account/* in routes/[Link] to the router created in step 1.
Solution
// backend/user/[Link]
const express = require('express');
const userRouter = require("./user");
const accountRouter = require("./account");
[Link]("/user", userRouter);
[Link]("/account", accountRouter);
[Link] = router;
[Link] 25/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
{
balance: 100
}
Solution
[Link]("/balance", authMiddleware, async (req, res) => {
const account = await [Link]({
userId: [Link]
});
[Link]({
balance: [Link]
})
});
{
to: string,
amount: number
}
[Link] 26/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Response:
Status code - 200
{
message: "Transfer successful"
}
{
message: "Insufficient balance"
}
{
message: "Invalid account"
}
if (!toAccount) {
return [Link](400).json({
message: "Invalid account"
})
}
await [Link]({
userId: [Link]
[Link] 27/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
}, {
$inc: {
balance: -amount
}
})
await [Link]({
userId: to
}, {
$inc: {
balance: amount
}
})
[Link]({
message: "Transfer successful"
})
});
[Link]();
const { amount, to } = [Link];
if (!toAccount) {
await [Link]();
return [Link](400).json({
message: "Invalid account"
});
}
[Link] 28/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
Final Solution
Code
[Link] 29/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
// backend/routes/[Link]
const express = require('express');
const { authMiddleware } = require('../middleware');
const { Account } = require('../db');
const { default: mongoose } = require('mongoose');
[Link]({
balance: [Link]
})
});
[Link]();
const { amount, to } = [Link];
if (!toAccount) {
await [Link]();
[Link]("Invalid account")
return;
}
[Link] 30/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
transfer({
userId: "65ac44e10ab2ec750ca666a5",
body: {
to: "65ac44e40ab2ec750ca666aa",
amount: 100
}
})
transfer({
userId: "65ac44e10ab2ec750ca666a5",
body: {
to: "65ac44e40ab2ec750ca666aa",
amount: 100
}
})
[Link] = router;
Error
Try to send a few calls via postman to ensure you are able to sign up/sign in/get balance
Get balance
Make transfer
[Link] 32/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link] 33/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link] 34/35
6/17/24, 10:49 AM Take your development skills from 0 to 100 and join the 100xdevs community
[Link] 35/35