BikeStore is a Node.js server app uses Express.js framework, written in typescript, integrated with MongoDB is able to manage invertory systems for bikes. This application's API has some features like add and manage products(bikes) orderring system and revenue generation.
-
Secure Inventory Management: You can add, view, update and delete bike details from your database securely validating using Zod and MongooseSchema.
-
Order and Inventory Updates: When user places order, inventory and stocks will be automatically updated.
-
Revenue Calculation: You can calculate the total revenue in one click.
-
Search and Filter Functionality: Search bikes by name, brand, or category for easy access to specific products.
-
TypeScript: We have used typeScript to code this entire application.
-
Express.js: Express.js is the most popular Node.js framework. So, we have used it for creating the Server.
-
MongoDB: Integreting with MongoDB Database
-
Mongoose: Integreting with MongoDB Query
-
Zod: We have used Zod for validating user input data.
- First, clone the repository to your local system
git clone https://github.com/rocktohq/bike-store.git- Open the project folder in your code editor(ie: VS Code)
cd bike-store
code .- Install all dependencies. I'm using npm; you can use yarn or any dependency manager you like
npm install- Setup the environment variables. For that, create a file .env
touch .env- Fillup with your information
PORT=3000 // Port number
DATABASE_URI= // MongoDB URI- If all the steps above are done, start the server
npm run dev- Add a new Bike:
Endpoints:/api/products
Method:POST
Request Body:
{
"name": "GSX-R",
"brand": "SUZUKI",
"price": 200000,
"category": "Sports",
"description": "THE MIGHTY BEAST. The Suzuki GSX-R Dual ABS line had defined sportbike performance for over 30 years, with more than a million sold worldwide.",
"quantity": 7
}Response:
{
"message": "Bike created successfully",
"success": true,
"data": {
"name": "GSX-R",
"brand": "SUZUKI",
"price": 200000,
"category": "Sports",
"description": "THE MIGHTY BEAST. The Suzuki GSX-R Dual ABS line had defined sportbike performance for over 30 years, with more than a million sold worldwide.",
"quantity": 7,
"inStock": true,
"createdAt": "2024-11-22T21:23:16.986Z",
"updatedAt": "2024-11-22T21:23:16.986Z",
"_id": "6740f79d04b0d6ab21c86de9",
"__v": 0
}
}- Get all Bikes:
Endpoints:/api/products
Method:GET
Response:
{
"message": "Bikes retrieved successfully",
"status": true,
"data": [
{
"_id": "Casual",
"bikes": [
{
"_id": "6740ea200046f4e65c3fd45a",
"name": "Xen Xin",
"brand": "Xen",
"price": 10000,
"category": "Casual",
"description": "Casual bike",
"quantity": 5,
"inStock": true,
"createdAt": "2024-11-22T20:29:05.829Z",
"updatedAt": "2024-11-22T20:29:05.829Z",
"__v": 0
}
]
}
]
}- Search for Products:
Query:/api/products?searchTerm=keyWordskeyWords can be product name, brand or category. We have used $regex operator to macth partialy and case-insensitive search.
Response:
{
"message": "Bikes retrieved successfully",
"status": true,
"data": [
{
"_id": "6740ea030046f4e65c3fd452",
"name": "Gonda Z",
"brand": "Gonda",
"price": 10000,
"category": "Sports",
"description": "A lightweight road bike designed for speed and performance.",
"quantity": 5,
"inStock": true,
"createdAt": "2024-11-22T20:29:05.829Z",
"updatedAt": "2024-11-22T20:29:05.829Z",
"__v": 0
},
{
"_id": "6740ea090046f4e65c3fd454",
"name": "Gonda Y",
"brand": "Gonda",
"price": 10000,
"category": "Sports",
"description": "A lightweight road bike designed for speed and performance.",
"quantity": 5,
"inStock": true,
"createdAt": "2024-11-22T20:29:05.829Z",
"updatedAt": "2024-11-22T20:29:05.829Z",
"__v": 0
}
// ...rest data will be here
]
}- Get a Single Bike:
Endpoints:/api/products/productId
Method:GETResponse:
{
"message": "Bike retrieved successfully",
"status": true,
"data": {
"_id": "6740b1ba9d1c3f2d2a486452",
"name": "Pakizum 500",
"brand": "Pakizum",
"price": 10000,
"category": "Road Bike",
"description": "A lightweight road bike designed for speed and performance.",
"quantity": 9,
"inStock": true,
"createdAt": "2024-11-22T16:30:15.177Z",
"updatedAt": "2024-11-22T17:19:02.305Z",
"__v": 0
}
}- Update a Bike:
Endpoints:/api/products/productId
Method:PUTRequest Body:
{
//* Properties to update
"name": "Honada X"
}Response:
{
"message": "Bike updated successfully",
"status": true,
"data": {
"_id": "6740b1ba9d1c3f2d2a486452",
//* Updated name
"name": "Honda X",
"brand": "Pakizum",
"price": 10000,
"category": "Road Bike",
"description": "Meet the revolutionary Honda X Dual ABS with the best power-to-weight ratio and acceleration in the 147.3 cm3 class.",
"quantity": 10,
"inStock": true,
"createdAt": "2024-11-22T16:30:15.177Z",
"updatedAt": "2024-11-22T21:24:04.946Z",
"__v": 0
}
}- Delete a Bike:
Endpoints:/api/products/productId
Method:DELETE
Response:
{
"message": "Bike deleted successfully",
"status": true,
"data": {
"_id": "6740b1ba9d1c3f2d2a486452",
"name": "Honda X",
"brand": "Pakizum",
"price": 10000,
"category": "Road Bike",
"description": "A lightweight road bike designed for speed and performance.",
"quantity": 10,
"inStock": true,
"createdAt": "2024-11-22T16:30:15.177Z",
"updatedAt": "2024-11-22T21:24:08.881Z",
"__v": 0
}
}- Place an Order:
Endpoints:/api/orders
Method:POSTRequest Body:
{
"email": "test@gmail.com",
"product": "6740b1ba9d1c3f2d2a486452",
"quantity": 1,
"totalPrice": 150000
}Response:
{
"message": "Order created successfully",
"status": true,
"data": {
"_id": "6740918f51ff7c69eeb13a0a",
"email": "test@gmail.com",
"product": "6740b1ba9d1c3f2d2a486452",
"quantity": 1,
"totalPrice": 150000,
"createdAt": "2024-11-22T16:39:41.050Z",
"updatedAt": "2024-11-22T16:39:41.050Z"
}
}- Revenue Calculation:
Endpoints:/api/orders/revenue
Method:GET
Response:
{
"message": "Revenue calculated successfully",
"status": true,
"data": {
"totalRevenue": 3600
}
}For input data validation we have used Zod and some custom mongoose validations too.
Example of error message:{
"message": "Price must be positive number!",
"success": false,
"error": {
"name": "ZodError",
"errors": {
"code": "too_small",
"minimum": 0,
"type": "number",
"inclusive": false,
"exact": false,
"message": "Price must be positive number!",
"path": ["price"]
}
},
"stack": "ZodError:..."
}Another example:
{
"message": "Bike not found",
"success": false,
"error": {
"name": "Bike not found",
"errors": {}
},
"stack": ""
}