The FinVista Backend powers the entire trading ecosystem β handling authentication, user management, market data, and seamless interactions between the frontend landing page and the trading dashboard.
It is built with Node.js, Express.js, MongoDB, Redis, and Firebase, designed to be scalable, secure, and realistic, simulating a true fintech-grade backend.
-
π Multi-Step Authentication
- Secure login/signup flow with Phone OTP or UserID + Email OTP.
-
π Auto-Generated User Data
- After signup, random user related data (a userImage, Bank Account Number, Bank Name, PanCard Number, Unique UserId, Demat (BO) Number, 4 digit Support Code & Segment) is auto-generated to make the dashboard look realistic and engaging .
-
π Preloaded Market Stocks
- Database comes preloaded with 300+ stock entries π.
- When users search for a stock, results are instantly fetched from the DB.
- Users can buy stocks, and see them reflected in:
πΌ /holdings/allβ Current holdingsπ /order/allβ Order status & history
-
π User Account Management
- Reset Password
- Forgot UserID
- Change Password
- Update Profile
- Logout securely
-
β‘ High Performance with Redis
OTPs and temporary session data are cached in Redis for blazing-fast authentication.
| Category | Technologies |
|---|---|
| βοΈ Core Backend | |
| βοΈ Cloud & Storage | |
| π Authentication & Security | |
| π© Communication & Utilities | |
| π οΈ Dev Tools |
-
Signup
- User registers via Landing Page.
- Phone/email OTP verification.
- Password setup.
- Random portfolio & data auto-generated for user realism.
-
Login
- Via Phone + Password β OTP popup (from test numbers).
- Or UserID + Password β OTP sent to registered email.
-
Post-Login Features
- Access dashboard.
- Buy stocks, view holdings, check orders.
- Manage account settings.
π zerodha-backend
βββ π config
β βββ π cloudinary.js
β βββ π firebaseAdmin.js
β βββ π jwt.js
β βββ π redisClint.js
βββ π controller
β βββ π user
β βββ π user.login.js
β βββ π user.register.js
β βββ π user.updateProfile.js
β βββ βοΈ holding.controller.js
β βββ βοΈ order.controller.js
β βββ βοΈ position.controller.js
β βββ βοΈ stock.controller.js
βββ π database
β βββ π dbConnection.js
βββ π docs
βββ π helper
β βββ π order.helper.js
β βββ π sendMail.js
β βββ π stock.helper.js
β βββ π user.helper.js
βββ π middleware
β βββ π auth.middleware.js
β βββ π error.js
β βββ π refresh.middleware.js
β βββ π wrapError.js
βββ π models
β βββ π holding.model.js
β βββ π order.model.js
β βββ π position.model.js
β βββ π user.model.js
βββ π node_modules
βββ π routers
β βββ π user
β βββ π login.router.js
β βββ π register.router.js
β βββ π test.route.js
β βββ π user.router.js
β βββ π holding.route.js
β βββ π order.router.js
β βββ π position.router.js
β βββ π stock.route.js
βββ π schemas
β βββ π holdings.schema.js
β βββ π order.schemas.js
β βββ π positions.schemas.js
β βββ π stock.schema.js
β βββ π user.schema.js
βββ π services
β βββ π stockService.js
βββ π .env
βββ π .gitignore
βββ π app.js
βββ π index.js
βββ π package-lock.json
βββ π package.json
βββ π README.md
Note : The base URL for all endpoints is https://your-domain.com/api/v1.
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/user/register/signup |
POST |
Initiates the phone number signup process. | None |
/user/register/verify-mobile |
POST |
Verifies a phone number with an OTP. | None |
/user/register/lead-info |
POST |
Saves user lead information (name, email) and sends an email OTP. | None |
/user/register/set-password |
POST |
Finalizes account creation by setting a password after both phone and email are verified. | None |
/user/login |
POST |
Authenticates a user with a User ID or phone number and password. Sends an OTP for verification. | None |
/user/login/verify-otp |
POST |
Verifies the OTP sent during the login process to grant tokens. | None |
/user/profile |
GET |
Retrieves the profile details of the authenticated user. | JWT Token |
/user/profile/upload |
POST |
Updates the authenticated user's profile image. | JWT Token |
/user/profile/update-password |
POST |
Changes the authenticated user's password. | JWT Token |
/user/forgot-password |
POST |
Sends a password reset link to the user's registered email. | None |
/user/reset-password/:token |
POST |
Resets the user's password using a valid reset token. | None |
/user/forgot-userId |
POST |
Sends the user's User ID to their registered email. | None |
/user/logout |
POST |
Logs out the user by clearing the refresh token. | JWT Token |
-
Description: Initiates user registration by accepting a phone number. This is the first step of the two-factor authentication for creating an account.
-
Request Body:
{
"phone": "9123456789"
}- Response Body
{
"success": true,
"message": "OTP sent to phone"
}After OTP verification, if user already exist then
- Error Response (User Exists):
{
"success": false,
"message": "User already exists"
}- Response (User Already Exists):
{
"success": true,
"userExists": true,
"message": "User already has an account with the same number.",
"data": {
"user": { ... },
"tokens": { ... }
}
}-
Description: Verifies the user's mobile number by authenticating the Firebase-generated token.
-
Request Body :
{
"idToken": "eyJhbGciOiJIUzI1NiI..."
}- Response (Success):
{
"success": true,
"message": "Phone verified"
}-
Description: Collects the user's name and email. An OTP is sent to the provided email for verification. This endpoint is used for two distinct actions: sending the OTP and then verifying it.
-
Request Body (Step 1: Send OTP):
{
"step": "sendOtp",
"phone": "9123456789",
"name": "John Doe",
"email": "[email protected]"
}- Response (Step 1: OTP Sent):
{
"success": true,
"message": "Email OTP sent"
}- Request Body (Step 2: Verify OTP):
{
"step": "verifyOtp",
"email": "[email protected]",
"otp": "123456"
}- Response (Step 2: OTP Verified):
{
"success": true,
"message": "Email verified"
}- Error Response (Invalid OTP):
{
"success": false,
"message": "Invalid or expired otp"
}- Description: Finalizes the account creation by setting a password and generating all user-related data such as
userId,bankAccountNumber, and adefault profile image. - Request Body:
{
"phone": "9123456789",
"password": "Techie_root1$"
}- Response
{
"success": true,
"message": "Account created successfully",
"data": {
"user": {
"id": "60c72b2f9b1d8c001f3e7a1b",
"name": "John Doe",
"email": "[email protected]",
"phone": "9123456789"
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1Ni...",
"refreshToken": "eyJhbGciOiJIUzI1Ni..."
}
}
}- Error Response (Phone/Email Not Verified):
{
"success": false,
"message": "Phone not verified"
}-
Description: This endpoint authenticates a user using their password. The identifier can be either a 7-digit
user IDor a 10-digitphone number. Based on the identifier type, a one-time password (OTP) is sent to either theuser's emailorphone numberfor the next step. -
Request Body
{
"identifier": "QFS161I",
"password": "Techie_root1$"
}OR
{
"identifier": "9123456789",
"password": "Techie_root1$"
}
- Response Body (for 7-digit User ID):
If a 7-digit user ID is provided, an OTP is sent to the user's registered email.
{
"success": true,
"message": "OTP sent to your email.",
"auth_type": "email",
"identifier": "QFS161I"
}OR
- Response Body (for 10-digit Phone Number):
If a 10-digit phone number is provided, the password is verified, and the client is instructed to handle the OTP verification via Firebase Authentication.
{
"success": true,
"message": "Password verified. Please verify OTP using Firebase.",
"auth_type": "phone",
"identifier": "9123456789"
}-
Description: Finalizes the login process by verifying the OTP received in the previous step. Upon successful verification, the server issues JWT access and refresh tokens.
-
Request Body (for Email OTP): This request is used for users who logged in with a 7-digit user ID.
{
"identifier": "QFS161I",
"otp": "123456"
}- Request Body (for Phone OTP):
This request is for users who logged in with a 10-digit phone number. The client sends a static "verified" string after the phone number has been successfullyverifiedby Firebase on the client-side.
{
"identifier": "9123456789",
"otp": "verified"
}- Response Body :
{
"success": true,
"message": "Login successful.",
"data": {
"user": {
"id": "60c72b2f9b1d8c001f3e7a1b",
"name": "John Doe",
"email": "[email protected]",
"phone": "9123456789",
"userId": "QFS161I"
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1Ni...",
"refreshToken": "eyJhbGciOiJIUzI1Ni..."
}
}
}These endpoints manage user profile data, including updates, and handle account recovery processes.
- Description: Retrieves the full profile details of the authenticated user.
- Authentication: Requires a valid JWT access token.
- Response:
{
"success": true,
"data": {
"_id": "60c72b2f9b1d8c001f3e7a1b",
"userId": "QFS161I",
"name": "John Doe",
"email": "[email protected]",
"phone": "9123456789",
"profile": "https://res.cloudinary.com/example/image/upload/v123456789/profile_image.png",
"bankAccountNumber": "5683572980674532",
"bankName": "Kotak Mahindra Bank",
"panCardNumber": "ABCDE1234F",
"dematNumber": "DP12345678",
"supportCode": "20240901-XYZ"
}
}-
Description: Updates the authenticated user's profile image. The image file is uploaded as
multipart/form-data. -
Authentication: Requires a valid JWT access token.
-
Request Body:
- This endpoint expects a
multipart/form-datarequest with a field namedprofilecontaining the image file.
- This endpoint expects a
-
Response
{
"success": true,
"message": "Profile image updated successfully",
"profileUrl": "https://res.cloudinary.com/example/image/upload/v123456789/new_profile.png"
}- Description: Changes the authenticated user's password after verifying their old password.
- Authentication: Requires a valid JWT access token.
- Request body:
{
"oldPassword": "oldPassword123",
"newPassword": "newPassword456"
}- Response:
{
"success": true,
"message": "Password updated successfully"
}- Error Response (Incorrect Old Password):
{
"success": false,
"message": "Incorrect old password"
}- Description: Sends a password reset link to the user's registered email. This link contains a unique, time-limited token.
- Authentication: None.
- Request Body:
{
"email": "[email protected]"
}- Response
{
"success": true,
"message": "A password reset link has been sent to your email."
}- Error Response (User Not Found):
{
"success": false,
"message": "User not found with provided email"
}-
Description: Resets the user's password using the token received via email.
-
Authentication: None.
-
URL Parameters:
:token(string): The password reset token from the email link.
-
Request Body:
{
"newPassword": "newStrongPassword123"
}- Response:
{
"success": true,
"message": "Password reset successfully."
}- Error Response (Invalid/Expired Token):
{
"success": false,
"message": "Password reset token is expired or invalid"
}- Description: Sends the user's unique 7-digit User ID to their registered email address.
- Authentication: None.
- Request Body:
{
"email": "[email protected]"
}- Response:
{
"success": true,
"message": "UserId send in your registered email id"
}- Error Response (User Not Found):
{
"success": false,
"message": "With this email user not found"
}- Description: Logs out the authenticated user by deleting their refresh token from the Redis cache. This invalidates future login sessions.
- Authentication: Requires a valid JWT access token.
- Response:
{
"success": true,
"message": "Logged out successfully!"
}- Error Response (Session Not Found):
{
"success": false,
"message": "Session not found."
}| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/holdings/allHoldings |
GET |
Fetches all stock holdings for the authenticated user, including live price data. | JWT Token |
- Description: Retrieves a list of the user's current stock holdings with enriched live data.
- Response:
[ { "name": "TCS", "qty": 5, "avg": 3500, "price": 3550, "currentValue": 17750, "pnl": 250, "net": "+1.43%", "day": "+50.00", "isLoss": false }, { "name": "INFY", "qty": 10, "avg": 1500, "price": 1450, "currentValue": 14500, "pnl": -500, "net": "-3.33%", "day": "-50.00", "isLoss": true } ]
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/positions/all-positions |
GET |
Fetches all open stock positions for the authenticated user, including live price data. | JWT Token |
- Description: Retrieves a list of the user's current intraday positions.
- Response:
[ { "name": "HDFC", "product": "MIS", "qty": 20, "avg": 1750, "price": 1765.5, "pnl": 310, "net": "+0.89%", "day": "+15.50", "isLoss": false } ]
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/order/new-order |
POST |
Places a new stock order (buy/sell). | JWT Token |
/order/all-orders |
GET |
Fetches the order history for the authenticated user. | JWT Token |
- Description: Creates a new order and updates the user's holdings in a single transaction.
- Request Body:
{ "quantity": 5, "price": 3550, "symbol": "TCS" } - Response:
{ "success": true, "message": "Order placed successfully and holding updated", "order": { "_id": "6516a504a37b3f001f54a83c", "user": "60c72b2f9b1d8c001f3e7a1b", "transactionId": "ORD-12345678", "ISIN": "INE001A01018", "symbol": "TCS", "quantity": 5, "price": 3550, "total": 17750, "status": "Executed", "tradeDate": "2023-09-29T10:00:00.000Z" } }
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/stocks/update |
PUT |
(Internal) Updates all stock prices with a random fluctuation. | None |
/stocks/search?q=query |
GET |
Searches for stocks based on a query string. | None |
- Description: Searches for stocks whose symbol matches the query.
- Response:
{ "success": true, "data": [ { "_id": "60c72b2f9b1d8c001f3e7a1c", "symbol": "TCS", "companyName": "Tata Consultancy Services Ltd.", "price": 3550, "change": 50, "percentChange": 1.43 } ] }
- Clone the Repository
git clone https://github.com/Afzal14786/zerodha-backend.git
cd zerodha-backend
- Install Dependencies
npm install
- Environment Variables
Create a .env file in root with:
### BACKEND PORT
PORT=5174
### FRONTEND API
FRONTEND_API=http://localhost:3000
### DASHBOARD API
DASHBOARD_API=http://localhost:5173
### JWT
JWT_SECRET=<your_jwt_secret_key>
JWT_EXPIRES_IN=<expiry_day>
JWT_REFRESH_SECRET=<jwt_refresh_token>
JWT_REFRESH_EXPIRES_IN=<jwt_refresh_expires>
MONGO_URL=<your_mongodb_url>
# Cloudinary SetUp
CLOUD_NAME=<your_cloudinary_name>
CLOUD_API_KEY=<your_cloud_api>
CLOUD_SECRET_KEY=<your_cloud_secret_key>
### Redis Configuration
REDIS_HOST=<your_redisdb_host>
REDIS_PORT=<your_redisdb_port_number>
REDIS_PASSWORD=<your_redis_password>
### Firebase Configiration
FIREBASE_SERVICE_ACCOUNT=<your_firebase_servie_.json_format>
### email verification
SMTP_USER=<your_email_account>
SMTP_PASS=<your_app_password>
- Run Development Server
npm run dev
β οΈ Important: All the data except yourname,
Internally there are function running for generating dummy data for making the project more reliestics .
If you are interested how it is generated so frequently, go through theapi endpointsfor order section as well as go throughregister api endpoint.
So, you get to know what kind of dummy data is generated for make this project realistics .
The Dashboard Repository contains the core authenticated user experience of the platform. Once users complete the multi-step signup and verification process on the FinVista landing page, the user redirected to the dashboard, where user can:
- Search Strocks and place order
- Manage their personal watchlist
- Access detailed analytics on selected stocks
- Navigate through a secure and responsive UI built specifically for active users
π Repository Link: Dashboard Repository
The frontend repository contain all the fintech related information as well as the signup process or creating an account .
- Signup
- Create an account by entering basic details like : Name, Email, Phone Number & Set The Password
π Repository Link: FinVista Repository
If you found this project helpful, informative, or fun to work with:
- Give it a π star on @GitHub
- Follow me for more cool projects and updates:
Thank you for your support β Happy Coding! ππ¨βπ»β¨