From 3cacb30224562b9c5a87c7c443adbb8fe38ae638 Mon Sep 17 00:00:00 2001 From: thesohailjafri Date: Wed, 11 Oct 2023 19:27:40 +0530 Subject: [PATCH 1/2] ProductStore API --- .../ProductStoreAPI/.env.example | 2 + .../ProductStoreAPI/.gitignore | 2 + .../ProductStoreAPI/README.md | 75 ++++ .../ProductStoreAPI/app.js | 41 ++ .../ProductStoreAPI/controllers/products.js | 73 ++++ .../ProductStoreAPI/db/connect.js | 12 + .../middleware/error-handler.js | 6 + .../ProductStoreAPI/middleware/not-found.js | 3 + .../ProductStoreAPI/models/product.js | 48 +++ .../ProductStoreAPI/package.json | 21 + .../ProductStoreAPI/populate.js | 21 + .../ProductStoreAPI/products.json | 402 ++++++++++++++++++ .../ProductStoreAPI/routes/products.js | 12 + 13 files changed, 718 insertions(+) create mode 100644 Existing_API_Collection/ProductStoreAPI/.env.example create mode 100644 Existing_API_Collection/ProductStoreAPI/.gitignore create mode 100644 Existing_API_Collection/ProductStoreAPI/README.md create mode 100644 Existing_API_Collection/ProductStoreAPI/app.js create mode 100644 Existing_API_Collection/ProductStoreAPI/controllers/products.js create mode 100644 Existing_API_Collection/ProductStoreAPI/db/connect.js create mode 100644 Existing_API_Collection/ProductStoreAPI/middleware/error-handler.js create mode 100644 Existing_API_Collection/ProductStoreAPI/middleware/not-found.js create mode 100644 Existing_API_Collection/ProductStoreAPI/models/product.js create mode 100644 Existing_API_Collection/ProductStoreAPI/package.json create mode 100644 Existing_API_Collection/ProductStoreAPI/populate.js create mode 100644 Existing_API_Collection/ProductStoreAPI/products.json create mode 100644 Existing_API_Collection/ProductStoreAPI/routes/products.js diff --git a/Existing_API_Collection/ProductStoreAPI/.env.example b/Existing_API_Collection/ProductStoreAPI/.env.example new file mode 100644 index 0000000..f1c2ef4 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/.env.example @@ -0,0 +1,2 @@ +MONGO_URI=YOUR_MONGO_URI +PORT=5000 \ No newline at end of file diff --git a/Existing_API_Collection/ProductStoreAPI/.gitignore b/Existing_API_Collection/ProductStoreAPI/.gitignore new file mode 100644 index 0000000..7af7f04 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/.gitignore @@ -0,0 +1,2 @@ +/node_modules +.env \ No newline at end of file diff --git a/Existing_API_Collection/ProductStoreAPI/README.md b/Existing_API_Collection/ProductStoreAPI/README.md new file mode 100644 index 0000000..24c7494 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/README.md @@ -0,0 +1,75 @@ +# Lets build a Product Store API using NodeJS, ExpressJS, MongoDB and Mongoose + +## Table of contents + +- [What is an API?](#what-is-an-api) +- [What is a REST API?](#what-is-a-rest-api) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Installation](#installation) +- [Built With](#built-with) +- [Author](#author) + +## What is an API? + +An API is an application programming interface. It is a set of rules that allow programs to talk to each other. The developer creates the API on the server and allows the client to talk to it. + +## What is a REST API? + +A REST API is an API that conforms to the design principles of the REST, or representational state transfer architectural style. REST APIs are stateless, meaning that calls can be made independently of one another, and contain all of the information necessary to complete itself successfully. + +## Getting Started + +### Prerequisites + +- NodeJS +- MongoDB +- Postman + +### Installation + +- Clone the repository +- Type in the following command in the terminal + +```bash +cd ProductStoreAPI +npm install +``` + +- Create a .env file in the root directory and add the following +- Add your MongoDB connection string and desired port number + +```bash +MONGO_URI = +PORT = +``` + +- Run the following command in the terminal + +```bash +npm start +``` + +- Open Postman and test the API by sending requests to the following endpoints + +```bash +GET http://localhost:3000/api/v1/products +GET http://localhost:3000/api/v1/products?name=Chair +GET http://localhost:3000/api/v1/products?name=Chair&featured=true&company=ikea&sort=price&fields=name,price&numericFilters[price]=<100 +``` + +## Built With + +- [NodeJS](https://nodejs.org/en/) +- [ExpressJS](https://expressjs.com/) +- [MongoDB](https://www.mongodb.com/) +- [Mongoose](https://mongoosejs.com/) +- [Postman](https://www.postman.com/) +- [VS Code](https://code.visualstudio.com/) +- [Git](https://git-scm.com/) + +## Author + +- Github - [thesohailjafri](https://github.com/thesohailjafri) +- LinkedIn - [thesohailjafri](https://www.linkedin.com/in/thesohailjafri/) +- Twitter - [@thesohailjafri](https://twitter.com/thesohailjafri) diff --git a/Existing_API_Collection/ProductStoreAPI/app.js b/Existing_API_Collection/ProductStoreAPI/app.js new file mode 100644 index 0000000..87f63b8 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/app.js @@ -0,0 +1,41 @@ +require('dotenv').config(); +require('express-async-errors'); + +const express = require('express'); +const app = express(); + +const connectDB = require('./db/connect'); +const productsRouter = require('./routes/products'); + +const notFoundMiddleware = require('./middleware/not-found'); +const errorMiddleware = require('./middleware/error-handler'); + +// middleware +app.use(express.json()); + +// routes + +app.get('/', (req, res) => { + res.send('

Store API

products route'); +}); + +app.use('/api/v1/products', productsRouter); + +// products route + +app.use(notFoundMiddleware); +app.use(errorMiddleware); + +const port = process.env.PORT || 3000; + +const start = async () => { + try { + // connectDB + await connectDB(process.env.MONGO_URI); + app.listen(port, () => console.log(`Server is listening port ${port}...`)); + } catch (error) { + console.log(error); + } +}; + +start(); diff --git a/Existing_API_Collection/ProductStoreAPI/controllers/products.js b/Existing_API_Collection/ProductStoreAPI/controllers/products.js new file mode 100644 index 0000000..9e99521 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/controllers/products.js @@ -0,0 +1,73 @@ +const Product = require('../models/product'); + +const getAllProductsStatic = async (req, res) => { + const products = await Product.find({ price: { $gt: 30 } }) + .sort('price') + .select('name price'); + + res.status(200).json({ products, nbHits: products.length }); +}; +const getAllProducts = async (req, res) => { + const { featured, company, name, sort, fields, numericFilters } = req.query; + const queryObject = {}; + + if (featured) { + queryObject.featured = featured === 'true' ? true : false; + } + if (company) { + queryObject.company = company; + } + if (name) { + queryObject.name = { $regex: name, $options: 'i' }; + } + if (numericFilters) { + const operatorMap = { + '>': '$gt', + '>=': '$gte', + '=': '$eq', + '<': '$lt', + '<=': '$lte', + }; + const regEx = /\b(<|>|>=|=|<|<=)\b/g; + let filters = numericFilters.replace( + regEx, + (match) => `-${operatorMap[match]}-` + ); + const options = ['price', 'rating']; + filters = filters.split(',').forEach((item) => { + const [field, operator, value] = item.split('-'); + if (options.includes(field)) { + queryObject[field] = { [operator]: Number(value) }; + } + }); + } + + let result = Product.find(queryObject); + // sort + if (sort) { + const sortList = sort.split(',').join(' '); + result = result.sort(sortList); + } else { + result = result.sort('createdAt'); + } + + if (fields) { + const fieldsList = fields.split(',').join(' '); + result = result.select(fieldsList); + } + const page = Number(req.query.page) || 1; + const limit = Number(req.query.limit) || 10; + const skip = (page - 1) * limit; + + result = result.skip(skip).limit(limit); + // 23 + // 4 7 7 7 2 + + const products = await result; + res.status(200).json({ products, nbHits: products.length }); +}; + +module.exports = { + getAllProducts, + getAllProductsStatic, +}; diff --git a/Existing_API_Collection/ProductStoreAPI/db/connect.js b/Existing_API_Collection/ProductStoreAPI/db/connect.js new file mode 100644 index 0000000..3a3beeb --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/db/connect.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose') + +const connectDB = (url) => { + return mongoose.connect(url, { + useNewUrlParser: true, + useCreateIndex: true, + useFindAndModify: false, + useUnifiedTopology: true, + }) +} + +module.exports = connectDB diff --git a/Existing_API_Collection/ProductStoreAPI/middleware/error-handler.js b/Existing_API_Collection/ProductStoreAPI/middleware/error-handler.js new file mode 100644 index 0000000..e4c81ad --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/middleware/error-handler.js @@ -0,0 +1,6 @@ +const errorHandlerMiddleware = async (err, req, res, next) => { + console.log(err) + return res.status(500).json({ msg: 'Something went wrong, please try again' }) +} + +module.exports = errorHandlerMiddleware diff --git a/Existing_API_Collection/ProductStoreAPI/middleware/not-found.js b/Existing_API_Collection/ProductStoreAPI/middleware/not-found.js new file mode 100644 index 0000000..36090fa --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/middleware/not-found.js @@ -0,0 +1,3 @@ +const notFound = (req, res) => res.status(404).send('Route does not exist') + +module.exports = notFound diff --git a/Existing_API_Collection/ProductStoreAPI/models/product.js b/Existing_API_Collection/ProductStoreAPI/models/product.js new file mode 100644 index 0000000..96df548 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/models/product.js @@ -0,0 +1,48 @@ +const mongoose = require('mongoose') +// use https://json-generator.com/ to generate fake data +// use below code to generate fake data for product collection +// [ +// '{{repeat(50)}}', +// { +// name: '{{company()}} {{random("Bed", "Sofa", "Table", "Desk", "Wardrobe", "Chair")}}', +// price: '{{floating(300, 1000, 2, "$0,0.00")}}', +// featured: '{{bool()}}', +// rating: '{{floating(1, 5, 1, "0.0")}}', +// createdAt: '{{date(new Date(2020, 0, 1), new Date(), "YYYY-MM-ddThh:mm:ss Z")}}', +// company: '{{random("ikea", "liddy", "caressa", "marcos")}}', +// } +// ] +// paste the data in products.json file if want to use different data + +const productSchema = new mongoose.Schema({ + name: { + type: String, + required: [true, 'product name must be provided'], + }, + price: { + type: Number, + required: [true, 'product price must be provided'], + }, + featured: { + type: Boolean, + default: false, + }, + rating: { + type: Number, + default: 4.5, + }, + createdAt: { + type: Date, + default: Date.now(), + }, + company: { + type: String, + enum: { + values: ['ikea', 'liddy', 'caressa', 'marcos'], + message: '{VALUE} is not supported', + }, + // enum: ['ikea', 'liddy', 'caressa', 'marcos'], + }, +}) + +module.exports = mongoose.model('Product', productSchema) diff --git a/Existing_API_Collection/ProductStoreAPI/package.json b/Existing_API_Collection/ProductStoreAPI/package.json new file mode 100644 index 0000000..2755d90 --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/package.json @@ -0,0 +1,21 @@ +{ + "name": "jobs", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "start": "nodemon app.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^8.2.0", + "express": "^4.17.1", + "express-async-errors": "^3.1.1", + "mongoose": "^5.11.10" + }, + "devDependencies": { + "nodemon": "^2.0.7" + } +} diff --git a/Existing_API_Collection/ProductStoreAPI/populate.js b/Existing_API_Collection/ProductStoreAPI/populate.js new file mode 100644 index 0000000..fbcfe2a --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/populate.js @@ -0,0 +1,21 @@ +require('dotenv').config() + +const connectDB = require('./db/connect') +const Product = require('./models/product') + +const jsonProducts = require('./products.json') + +const start = async () => { + try { + await connectDB(process.env.MONGO_URI) + await Product.deleteMany() + await Product.create(jsonProducts) + console.log('Success!!!!') + process.exit(0) + } catch (error) { + console.log(error) + process.exit(1) + } +} + +start() diff --git a/Existing_API_Collection/ProductStoreAPI/products.json b/Existing_API_Collection/ProductStoreAPI/products.json new file mode 100644 index 0000000..f82d96a --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/products.json @@ -0,0 +1,402 @@ +[ + { + "name": "Comtent Chair", + "price": "$634.21", + "featured": true, + "rating": 5, + "createdAt": "2023-09-08T11:24:28 -06:-30", + "company": "caressa" + }, + { + "name": "Satiance Bed", + "price": "$873.35", + "featured": false, + "rating": 4.8, + "createdAt": "2022-08-10T03:33:29 -06:-30", + "company": "liddy" + }, + { + "name": "Geologix Wardrobe", + "price": "$837.76", + "featured": false, + "rating": 4.8, + "createdAt": "2020-09-09T01:28:24 -06:-30", + "company": "ikea" + }, + { + "name": "Zaphire Table", + "price": "$452.34", + "featured": true, + "rating": 2.9, + "createdAt": "2021-06-23T08:08:13 -06:-30", + "company": "caressa" + }, + { + "name": "Mobildata Bed", + "price": "$708.68", + "featured": true, + "rating": 2.6, + "createdAt": "2021-06-10T08:59:03 -06:-30", + "company": "ikea" + }, + { + "name": "Zenco Desk", + "price": "$998.18", + "featured": false, + "rating": 2.5, + "createdAt": "2023-06-16T10:40:47 -06:-30", + "company": "liddy" + }, + { + "name": "Centice Desk", + "price": "$940.16", + "featured": false, + "rating": 3, + "createdAt": "2021-08-12T10:11:48 -06:-30", + "company": "ikea" + }, + { + "name": "Podunk Wardrobe", + "price": "$397.99", + "featured": false, + "rating": 1.6, + "createdAt": "2022-02-14T06:27:42 -06:-30", + "company": "ikea" + }, + { + "name": "Medcom Sofa", + "price": "$851.75", + "featured": true, + "rating": 4.2, + "createdAt": "2023-07-17T12:04:34 -06:-30", + "company": "liddy" + }, + { + "name": "Danja Chair", + "price": "$462.62", + "featured": false, + "rating": 4, + "createdAt": "2021-12-28T03:13:11 -06:-30", + "company": "marcos" + }, + { + "name": "Deviltoe Bed", + "price": "$825.23", + "featured": true, + "rating": 3.2, + "createdAt": "2020-02-05T03:00:46 -06:-30", + "company": "liddy" + }, + { + "name": "Comtours Sofa", + "price": "$332.96", + "featured": true, + "rating": 1, + "createdAt": "2021-12-27T06:45:01 -06:-30", + "company": "marcos" + }, + { + "name": "Icology Chair", + "price": "$775.81", + "featured": false, + "rating": 4.4, + "createdAt": "2020-03-11T01:09:09 -06:-30", + "company": "liddy" + }, + { + "name": "Isonus Table", + "price": "$583.18", + "featured": true, + "rating": 1.3, + "createdAt": "2020-10-08T02:53:13 -06:-30", + "company": "caressa" + }, + { + "name": "Ceprene Bed", + "price": "$672.85", + "featured": false, + "rating": 1.9, + "createdAt": "2021-03-23T06:21:08 -06:-30", + "company": "marcos" + }, + { + "name": "Ecstasia Bed", + "price": "$376.11", + "featured": true, + "rating": 1, + "createdAt": "2022-11-06T03:34:55 -06:-30", + "company": "marcos" + }, + { + "name": "Portalis Sofa", + "price": "$331.23", + "featured": false, + "rating": 1.3, + "createdAt": "2020-07-15T03:30:40 -06:-30", + "company": "marcos" + }, + { + "name": "Opticom Bed", + "price": "$701.97", + "featured": false, + "rating": 3.3, + "createdAt": "2020-03-20T05:08:18 -06:-30", + "company": "ikea" + }, + { + "name": "Kidstock Wardrobe", + "price": "$555.20", + "featured": true, + "rating": 3.6, + "createdAt": "2020-08-06T12:02:59 -06:-30", + "company": "marcos" + }, + { + "name": "Frenex Sofa", + "price": "$872.95", + "featured": true, + "rating": 3.3, + "createdAt": "2021-12-12T01:20:07 -06:-30", + "company": "ikea" + }, + { + "name": "Ozean Sofa", + "price": "$366.51", + "featured": false, + "rating": 2.2, + "createdAt": "2020-03-01T03:46:17 -06:-30", + "company": "marcos" + }, + { + "name": "Xeronk Wardrobe", + "price": "$542.16", + "featured": true, + "rating": 4.2, + "createdAt": "2020-08-08T04:22:31 -06:-30", + "company": "liddy" + }, + { + "name": "Ramjob Wardrobe", + "price": "$766.97", + "featured": true, + "rating": 2.6, + "createdAt": "2023-03-07T03:18:14 -06:-30", + "company": "marcos" + }, + { + "name": "Apexia Table", + "price": "$616.54", + "featured": false, + "rating": 4.1, + "createdAt": "2021-06-25T02:05:14 -06:-30", + "company": "marcos" + }, + { + "name": "Proflex Sofa", + "price": "$398.12", + "featured": true, + "rating": 1.9, + "createdAt": "2020-10-14T05:34:49 -06:-30", + "company": "liddy" + }, + { + "name": "Insectus Chair", + "price": "$384.94", + "featured": true, + "rating": 2.6, + "createdAt": "2023-08-08T08:31:30 -06:-30", + "company": "caressa" + }, + { + "name": "Isotrack Bed", + "price": "$638.79", + "featured": true, + "rating": 4.9, + "createdAt": "2022-09-03T07:16:58 -06:-30", + "company": "marcos" + }, + { + "name": "Farmage Desk", + "price": "$574.12", + "featured": false, + "rating": 1.6, + "createdAt": "2022-10-24T08:31:40 -06:-30", + "company": "caressa" + }, + { + "name": "Emtrak Table", + "price": "$645.48", + "featured": false, + "rating": 2.5, + "createdAt": "2020-01-01T01:34:08 -06:-30", + "company": "liddy" + }, + { + "name": "Buzzworks Chair", + "price": "$686.65", + "featured": false, + "rating": 3.9, + "createdAt": "2020-05-03T06:20:26 -06:-30", + "company": "liddy" + }, + { + "name": "Genmex Table", + "price": "$371.23", + "featured": false, + "rating": 1.5, + "createdAt": "2020-12-07T04:41:38 -06:-30", + "company": "caressa" + }, + { + "name": "Anivet Wardrobe", + "price": "$702.57", + "featured": true, + "rating": 3.8, + "createdAt": "2022-12-29T08:28:16 -06:-30", + "company": "ikea" + }, + { + "name": "Extragen Chair", + "price": "$628.15", + "featured": true, + "rating": 2.3, + "createdAt": "2022-12-23T02:52:27 -06:-30", + "company": "caressa" + }, + { + "name": "Amtas Table", + "price": "$746.82", + "featured": false, + "rating": 3.4, + "createdAt": "2022-09-15T06:09:51 -06:-30", + "company": "marcos" + }, + { + "name": "Metroz Sofa", + "price": "$951.64", + "featured": false, + "rating": 2.2, + "createdAt": "2021-04-03T06:30:28 -06:-30", + "company": "liddy" + }, + { + "name": "Supportal Table", + "price": "$800.82", + "featured": false, + "rating": 2.9, + "createdAt": "2020-07-26T11:13:36 -06:-30", + "company": "caressa" + }, + { + "name": "Stralum Table", + "price": "$769.00", + "featured": true, + "rating": 4.3, + "createdAt": "2022-12-13T11:18:48 -06:-30", + "company": "ikea" + }, + { + "name": "Idetica Sofa", + "price": "$734.60", + "featured": true, + "rating": 1, + "createdAt": "2020-12-22T02:32:40 -06:-30", + "company": "ikea" + }, + { + "name": "Combogene Desk", + "price": "$785.09", + "featured": false, + "rating": 2.5, + "createdAt": "2020-11-28T11:51:34 -06:-30", + "company": "liddy" + }, + { + "name": "Progenex Table", + "price": "$762.43", + "featured": true, + "rating": 2.1, + "createdAt": "2021-03-03T01:50:09 -06:-30", + "company": "marcos" + }, + { + "name": "Zilencio Desk", + "price": "$509.41", + "featured": true, + "rating": 3.6, + "createdAt": "2020-10-05T04:53:39 -06:-30", + "company": "caressa" + }, + { + "name": "Xoggle Sofa", + "price": "$624.20", + "featured": false, + "rating": 2, + "createdAt": "2021-09-11T09:12:53 -06:-30", + "company": "marcos" + }, + { + "name": "Geeky Bed", + "price": "$376.37", + "featured": true, + "rating": 3.9, + "createdAt": "2021-10-06T05:31:59 -06:-30", + "company": "caressa" + }, + { + "name": "Filodyne Chair", + "price": "$751.94", + "featured": false, + "rating": 3.9, + "createdAt": "2022-08-31T04:12:46 -06:-30", + "company": "caressa" + }, + { + "name": "Jetsilk Chair", + "price": "$594.33", + "featured": true, + "rating": 4.6, + "createdAt": "2020-04-08T11:20:34 -06:-30", + "company": "caressa" + }, + { + "name": "Zolarex Chair", + "price": "$577.68", + "featured": true, + "rating": 1.3, + "createdAt": "2021-12-01T09:53:22 -06:-30", + "company": "ikea" + }, + { + "name": "Aquafire Chair", + "price": "$337.66", + "featured": false, + "rating": 1.1, + "createdAt": "2021-10-22T07:02:56 -06:-30", + "company": "liddy" + }, + { + "name": "Inear Table", + "price": "$764.39", + "featured": true, + "rating": 3.6, + "createdAt": "2021-09-25T09:46:40 -06:-30", + "company": "caressa" + }, + { + "name": "Greeker Sofa", + "price": "$302.65", + "featured": false, + "rating": 3.5, + "createdAt": "2023-05-16T06:47:36 -06:-30", + "company": "caressa" + }, + { + "name": "Zolavo Sofa", + "price": "$806.53", + "featured": false, + "rating": 1.1, + "createdAt": "2022-11-13T12:15:42 -06:-30", + "company": "marcos" + } +] diff --git a/Existing_API_Collection/ProductStoreAPI/routes/products.js b/Existing_API_Collection/ProductStoreAPI/routes/products.js new file mode 100644 index 0000000..43d284a --- /dev/null +++ b/Existing_API_Collection/ProductStoreAPI/routes/products.js @@ -0,0 +1,12 @@ +const express = require('express') +const router = express.Router() + +const { + getAllProducts, + getAllProductsStatic, +} = require('../controllers/products') + +router.route('/').get(getAllProducts) +router.route('/static').get(getAllProductsStatic) + +module.exports = router From 5c022b0b0af0fe1c61e084b3717e3849abd6c672 Mon Sep 17 00:00:00 2001 From: thesohailjafri Date: Wed, 11 Oct 2023 19:29:44 +0530 Subject: [PATCH 2/2] readme | how to Populate the product database --- Existing_API_Collection/ProductStoreAPI/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Existing_API_Collection/ProductStoreAPI/README.md b/Existing_API_Collection/ProductStoreAPI/README.md index 24c7494..1fa88b2 100644 --- a/Existing_API_Collection/ProductStoreAPI/README.md +++ b/Existing_API_Collection/ProductStoreAPI/README.md @@ -44,6 +44,12 @@ MONGO_URI = PORT = ``` +- Populate the database by running the following command in the terminal + +```bash +node populate.js +``` + - Run the following command in the terminal ```bash