├── .gitignore ├── config └── mongoConfig.js ├── middleware └── authMiddleware.js ├── package.json ├── models ├── userSchema.js └── blogSchema.js ├── routes ├── userRouter.js ├── blogRouter.js └── authRouter.js ├── server.js └── README.MD /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules/ 3 | .env -------------------------------------------------------------------------------- /config/mongoConfig.js: -------------------------------------------------------------------------------- 1 | //Initialize Mongoose 2 | const mongoose = require('mongoose') 3 | 4 | //Setup the connection with MongoDB 5 | module.exports = async () =>{ 6 | try { 7 | //Make a connection with mongoDB 8 | await mongoose.connect(process.env.MONGODB_URI) 9 | mongoose.connection //checking the connection 10 | console.log('MongoDB Connected') 11 | } catch (error) { 12 | console.error(error) 13 | } 14 | } -------------------------------------------------------------------------------- /middleware/authMiddleware.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken') 2 | 3 | module.exports =async(req,res,next) =>{ 4 | // get the token from the headers object 5 | 6 | const token = req.header('x-auth-token') 7 | 8 | if(!token){ 9 | return res.json('No Token Access denied') 10 | } 11 | 12 | try { 13 | 14 | const decoded = jwt.verify(token,process.env.SECRET_KEY) 15 | console.log(decoded) 16 | req.user=decoded 17 | next() 18 | } catch (error) { 19 | res.status(400).json('Token not valid') 20 | } 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blogsapi-project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.0.1", 14 | "dotenv": "^16.0.1", 15 | "express": "^4.18.1", 16 | "express-validator": "^6.14.1", 17 | "helmet": "^5.1.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongoose": "^6.3.7", 20 | "morgan": "^1.10.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /models/userSchema.js: -------------------------------------------------------------------------------- 1 | 2 | //Import Mongoose 3 | const mongoose = require('mongoose') 4 | 5 | //Create Users Schema 6 | const userSchema = mongoose.Schema({ 7 | username:{ 8 | type:String, 9 | required:true 10 | }, 11 | email:{ 12 | type:String, 13 | required:true 14 | }, 15 | birthday:{ 16 | type:Date, 17 | required:true 18 | }, 19 | age:{ 20 | type:Number, 21 | 22 | }, 23 | password:{ 24 | type:String, 25 | required:true 26 | } 27 | }) 28 | 29 | //Creates a Model of the name Users using the userSchema 30 | module.exports = mongoose.model ('Users', userSchema) -------------------------------------------------------------------------------- /models/blogSchema.js: -------------------------------------------------------------------------------- 1 | //Import Mongoose 2 | 3 | const mongoose = require('mongoose') 4 | 5 | //Create the blogs Schema 6 | //Added user key to store the user id of the user who created the blog 7 | const blogSchema = mongoose.Schema( 8 | { 9 | created_by : { 10 | type:String, 11 | required:true 12 | }, 13 | created_at :{ 14 | type :Date, 15 | default:Date.now(), 16 | required:true 17 | }, 18 | blog_title : { 19 | type :String, 20 | required:true 21 | }, 22 | blog_content :{ 23 | type: String, 24 | required: true 25 | }, 26 | private :{ 27 | type: Boolean, 28 | required : true 29 | }, 30 | user:{ 31 | type:mongoose.Schema.Types.ObjectId, 32 | ref :'Users' 33 | 34 | } 35 | } 36 | ) 37 | 38 | //Creates a Model of the name Blogs using the blogSchema 39 | 40 | module.exports =mongoose.model('Blogs', blogSchema) -------------------------------------------------------------------------------- /routes/userRouter.js: -------------------------------------------------------------------------------- 1 | const express =require('express') 2 | 3 | const userModel = require('../models/userSchema') 4 | 5 | //Create a Router 6 | const router = express.Router() 7 | 8 | 9 | //Get all Users 10 | router.get('/', async(req,res)=>{ 11 | 12 | try { 13 | const users = await userModel.find() 14 | res.status(200).json(users) 15 | } catch (error) { 16 | console.log(error) 17 | } 18 | 19 | }) 20 | 21 | //Update Users 22 | 23 | router.put('/:id', async(req,res)=>{ 24 | 25 | const id =req.params.id 26 | const newUserData = req.body 27 | try { 28 | //Find the user by id and update with the new values 29 | const user = await userModel.findByIdAndUpdate( id, newUserData,{new:true}) 30 | res.status(202).json(user) 31 | } catch (error) { 32 | console.log(error) 33 | } 34 | 35 | }) 36 | 37 | //Delete user 38 | 39 | router.delete('/:id', async(req,res)=>{ 40 | const id =req.params.id 41 | try { 42 | const user = await userModel.findByIdAndDelete(id) 43 | res.status(200).json('User Deleted') 44 | } catch (error) { 45 | console.log(error) 46 | } 47 | }) 48 | module.exports =router -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 2 | //Setup Express and PORT 3 | const express= require('express') 4 | const app= express() 5 | // Heroku sets up the port else when we run locally, we use 4000 6 | //The PORT variable is kept empty 7 | PORT = process.env.PORT || 4000 8 | 9 | //Initiate the dotenv config 10 | require('dotenv').config() 11 | const mongoConfig = require('./config/mongoConfig') 12 | 13 | //Initiate Morgan - 14 | const morgan = require('morgan') 15 | app.use(morgan('dev')) 16 | 17 | //Initiate Helmet 18 | const helmet = require('helmet') 19 | app.use(helmet()) 20 | 21 | 22 | 23 | //Middleware 24 | app.use(express.json()) 25 | 26 | //Setup the route for Blogs 27 | const blogsRouter = require('./routes/blogRouter') 28 | app.use('/blogs', blogsRouter) 29 | const authRouter = require('./routes/authRouter') 30 | app.use('/auth', authRouter) 31 | const userRouter = require('./routes/userRouter') 32 | app.use('/users', userRouter) 33 | 34 | //Setup the Root Route 35 | app.get('/',(req,res)=>{ 36 | 37 | res.status(200).json("Welcome to Blogs API Project") 38 | }) 39 | 40 | //Listen to the port 41 | app.listen(PORT,()=>{ 42 | console.log(`Server is running on port :${PORT}`) 43 | mongoConfig() 44 | 45 | }) -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Blogs API Project 2 | 3 | https://nbh-blog-api.herokuapp.com 4 | 5 | ## Key Features 6 | 7 | 1. The Blog App has the functionality of Blogging and User Management. 8 | 2. The User is expected to Register with the system and subsequently login to the system for creating/editing Blogs. 9 | 3. A valid login or registration is mandatory to access the Blogs. 10 | 4. Any authenticated user can Create blogs, While only the owner of Blog can Modify or Delete it. 11 | 5. The Blogs can be Created, Read , Updated or Deleted. 12 | 6. Users can be Registered , Viewed, Updated ,Deleted. 13 | 7. A Registered user can login to the system at any point of time. 14 | 15 | 16 | ## Blogs Schema 17 | 18 | * created_by: **_string, required_** 19 | * created_at: **_date, required Default to current date_** 20 | * blog_title: **_string, required_** 21 | * blog_content: **_string, required_** 22 | * private: **_boolean, required_** 23 | * user : **_ObjectId, refrences User Schema, {id, email}_** 24 | 25 | 26 | ## User Schema 27 | * username: **_string, required_** 28 | * email: **_string, required_** 29 | * birthday: **_date, required_** 30 | * age: **_number_** 31 | * password: **_string, required_** 32 | 33 | ## User Validation 34 | 35 | * UserName : Needs to be Alphanumeric 36 | * Email : Should be a valid email type 37 | * Password : Should be a minimum of 8 Characters 38 | * Age : Should be greater than 13 39 | 40 | 41 | ## Environment Variables 42 | 43 | * MONGO_DB_URI 44 | * MY_SECRET_KEY 45 | 46 | 47 | ## Technology 48 | 49 | * This Project has been developed using NodeJS, Express and data is stored in MongoDB. 50 | * We have used Postman to test the routes and the application is hosted on Heroku. 51 | * User password is hashed using bcrypt 52 | * User authentication is performed using jsonwebtoken 53 | 54 | ### Also, the following packages have been installed 55 | 56 | 57 | * nodemon 58 | * bcrypt 59 | * dotenv 60 | * express 61 | * express-validator 62 | * helmet 63 | * jsonwebtoken 64 | * mongoose 65 | * morgan 66 | 67 | 68 | ## Routes - Functionality 69 | 70 | ### app.use('/blogs') - Blogs Router 71 | 72 | 1. router.get('/') - An authorized user can view all public and private Blogs 73 | 2. router.get('/public') - An authorized user can view all Non private Blogs 74 | 3. router.post('/') - An authorized user can create a new Blog 75 | 4. router.get('/:id') - An authorized user can view a specific Blog 76 | 5. router.put('/:id') - Only the user who created the Blog can Modify it 77 | 6. router.delete('/:id') - Only the user who created the Blog can Delete it 78 | 79 | ### app.use('/auth') -Authentication Router 80 | 81 | 1. router.post('/registration') - Create a new user after checking the Validation rules, Hash the password and Provide a validation session token 82 | 2. router.post('/login') - Login an existing User and Provide a validation session token 83 | 84 | ### app.use('/users') -Users Router 85 | 86 | 1. router.get('/') - Get All users 87 | 2. router.put('/:id') - Modify an User's Details 88 | 3. router.delete('/:id') - Delete an User 89 | -------------------------------------------------------------------------------- /routes/blogRouter.js: -------------------------------------------------------------------------------- 1 | const express =require('express') 2 | 3 | const blogModel = require('../models/blogSchema') 4 | 5 | //Create a Router 6 | const router = express.Router() 7 | 8 | //Get the middleware for token authorisation 9 | const authMiddleware =require('../middleware/authMiddleware') 10 | 11 | // Get Blogs 12 | router.get('/',authMiddleware, async(req,res) =>{ 13 | 14 | try { 15 | const blogs = await blogModel.find() 16 | res.status(200).json(blogs) 17 | 18 | } catch (error) { 19 | console.log(error) 20 | } 21 | 22 | }) 23 | 24 | // Get only blogs that are not private - private is false 25 | router.get('/public',authMiddleware, async(req,res) =>{ 26 | 27 | try { 28 | const blogs = await blogModel.find({private: 'false'}) 29 | res.status(200).json(blogs) 30 | 31 | } catch (error) { 32 | console.log(error) 33 | } 34 | 35 | }) 36 | // Create Blogs 37 | router.post('/',authMiddleware,async(req,res)=>{ 38 | 39 | const blogData =req.body // getting the data from the request 40 | //Assigning the user id of the user who created the blog 41 | blogData.user = req.user.id 42 | console.log(blogData) 43 | try { 44 | 45 | const blog = await blogModel.create(blogData) // Create the todo in the Database 46 | //send back the response 47 | res.status(201).json(blog) 48 | } catch (error) { 49 | console.error(error) 50 | res.status(400).json('Bad Request!') 51 | } 52 | 53 | }) 54 | 55 | // Get blogs by Id 56 | router.get('/:id',authMiddleware,async(req,res)=>{ 57 | const id = req.params.id 58 | try { 59 | 60 | const blog = await blogModel.findById(id) 61 | res.status(200).json(blog) 62 | } catch (error) { 63 | console.error(error) 64 | res.status(400).json({msg: 'ID not found'}) 65 | } 66 | 67 | }) 68 | 69 | // Update blogs by ID 70 | 71 | router.put('/:id',authMiddleware,async(req,res)=>{ 72 | const id = req.params.id 73 | const newblogData = req.body 74 | try { 75 | const blogToUpdate= await blogModel.findById(id) 76 | console.log('User who created Blog -- ' + blogToUpdate.user) 77 | console.log('User who logged In -- ' + req.user.id) 78 | //Making sure that the user who created the blog can only modify it 79 | if(blogToUpdate.user._id.toString() !== req.user.id ) 80 | { 81 | return res.status(400).json("Not Authorized to Update the Blog") 82 | } 83 | //find the blog by id 84 | const blog = await blogModel.findByIdAndUpdate(id, newblogData,{new:true}) 85 | res.status(202).json({Message : 'Blog Updated', Blog : blog}) 86 | } catch (error) { 87 | console.log(error) 88 | } 89 | }) 90 | 91 | // Delete a Todo 92 | router.delete('/:id',authMiddleware, async(req,res) => { 93 | 94 | const id = req.params.id 95 | 96 | try { 97 | 98 | const blogToDelete = await blogModel.findById(id) 99 | 100 | //Making sure that the user who created the blog can only delete it 101 | console.log('User who created Blog -- ' + blogToDelete.user) 102 | console.log('User who logged In -- ' + req.user.id) 103 | if(blogToDelete.user._id.toString() !== req.user.id ) 104 | { 105 | return res.status(400).json("Not Authorized to Delete") 106 | } 107 | 108 | const blog = await blogModel.findByIdAndDelete(id) 109 | res.status(200).json({msg :'Blog was deleted'}) 110 | } catch (error) { 111 | console.log(error) 112 | } 113 | }) 114 | 115 | module.exports = router -------------------------------------------------------------------------------- /routes/authRouter.js: -------------------------------------------------------------------------------- 1 | const express =require('express') 2 | 3 | const userModel = require('../models/userSchema') 4 | 5 | //Create a Router 6 | const router = express.Router() 7 | 8 | const {check, validationResult} = require( 'express-validator') 9 | const bcrypt = require('bcrypt') 10 | const jwt =require('jsonwebtoken') 11 | //Create a new user 12 | 13 | router.post('/registration',[ 14 | check('username', 'Enter a valid alphanumeric username').trim().notEmpty().isAlphanumeric(), 15 | check('email','Please enter a valid email format').trim().notEmpty().isEmail(), 16 | check('password', 'Please enter a password with 8 or more characters').trim().isLength({min:8}), 17 | check('age','Age needs to be numeric and greater than 13').trim().isInt({min :13}), 18 | check('birthday', 'Enter valid Birthday').isDate() 19 | ], async(req,res)=>{ 20 | const userData =req.body // getting the data from the request 21 | 22 | //Check for Validation Errors 23 | const errors=validationResult(req) 24 | 25 | if(!errors.isEmpty()){ 26 | return res.json(errors.array()) 27 | } 28 | 29 | try { 30 | 31 | //Validate if the email already exist in DB 32 | const userExist =await userModel.findOne({email:userData.email}) 33 | //Return this msg if exists 34 | if(userExist){ 35 | return res.json({msg: 'User already exist'}) 36 | } 37 | 38 | // 1 Create the salt 39 | const SALT = await bcrypt.genSalt(10) 40 | // 2 use the salt to create a hash with the user's password 41 | const hashedPassword = await bcrypt.hash(userData.password, SALT) 42 | // 3 assign the hashed password to the userData 43 | userData.password = hashedPassword 44 | 45 | const user = await userModel.create(userData) 46 | 47 | //Create a new JWT token 48 | const payload= { 49 | id: user._id, 50 | email: user.email 51 | } 52 | 53 | const TOKEN = jwt.sign(payload, process.env.SECRET_KEY) 54 | //send back the response 55 | res.status(201).json({ 56 | user :user, 57 | token:TOKEN 58 | }) 59 | } catch (error) { 60 | console.error(error) 61 | res.status(400).json('Bad Request!') 62 | } 63 | }) 64 | 65 | router.post('/login',[ 66 | check("email","pleases provide a valid email").trim().isEmail(), 67 | check("password","Check your password").trim().notEmpty() 68 | ],async(req,res)=>{ 69 | const userData = req.body 70 | 71 | //Check for Validation Errors 72 | const errors=validationResult(req) 73 | if(!errors.isEmpty()){ 74 | return res.json(errors.array()) 75 | } 76 | try { 77 | //Find the user with the provided email 78 | const user = await userModel.findOne({email : userData.email}) 79 | if(!user){ 80 | return res.json('User not found') 81 | } 82 | 83 | //compare plain text password to hashed password 84 | const isMatch = await bcrypt.compare(userData.password, user.password) 85 | 86 | if(!isMatch){ 87 | return res.json('Password is not a match') 88 | } 89 | 90 | //Create a new JWT token 91 | const payload= { 92 | 93 | id: user._id, 94 | email: user.email 95 | } 96 | 97 | const TOKEN = jwt.sign(payload, process.env.SECRET_KEY) 98 | 99 | res.status(201).json({ 100 | msg: 'Login Sucessful', 101 | user :user, 102 | token:TOKEN 103 | }) 104 | 105 | // res.status(200).json('Login Success!') 106 | } catch (error) { 107 | console.log(error) 108 | res.status(500).json('Server Error') 109 | } 110 | 111 | }) 112 | 113 | 114 | module.exports = router --------------------------------------------------------------------------------