├── .gitignore ├── config └── mongoConfig.js ├── middleWare └── authMiddleware.js ├── package.json ├── models ├── userSchema.js └── blogSchema.js ├── server.js ├── routes ├── authRouter.js ├── blogRouter.js └── userRouter.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules/ 3 | .env -------------------------------------------------------------------------------- /config/mongoConfig.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | 4 | 5 | module.exports = async() => { 6 | try { 7 | await mongoose.connect(process.env.MONGODB_URI) 8 | mongoose.connection 9 | console.log('MongoDB Connected'); 10 | } catch (error) { 11 | console.error(error) 12 | } 13 | }; -------------------------------------------------------------------------------- /middleWare/authMiddleware.js: -------------------------------------------------------------------------------- 1 | const jwt= require('jsonwebtoken') 2 | 3 | module.exports = (req,res,next) => { 4 | //get the token from the headers objects 5 | const token = req.header('x-auth-token') 6 | //If no token 7 | 8 | if(!token) { 9 | return res.json('No Token, access is denied!') 10 | } 11 | //If we have a token 12 | try { 13 | const decoded = jwt.verify(token, process.env.SECRET_KEY) 14 | console.log(decoded); 15 | 16 | next() 17 | } catch (error) { 18 | console.log(error); 19 | res.status(400).json('Token not valid') 20 | } 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "techblog_api", 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 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^5.0.1", 15 | "dotenv": "^16.0.1", 16 | "express": "^4.18.1", 17 | "express-validator": "^6.14.1", 18 | "helmet": "^5.1.0", 19 | "jsonwebtoken": "^8.5.1", 20 | "mongoose": "^6.3.8", 21 | "morgan": "^1.10.0", 22 | "nodemon": "^2.0.16" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /models/userSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const userSchema = mongoose.Schema({ 4 | 5 | 6 | username:{ 7 | type: String, 8 | required: true 9 | }, 10 | 11 | email: { 12 | type: String, 13 | required: true 14 | }, 15 | 16 | birthday: { 17 | type: Date, 18 | required: true 19 | }, 20 | 21 | age: { 22 | type: Number, 23 | required: false 24 | 25 | }, 26 | password: { 27 | type: String, 28 | required: true 29 | } 30 | }) 31 | 32 | module.exports = mongoose.model('user', userSchema) -------------------------------------------------------------------------------- /models/blogSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const blogSchema = mongoose.Schema({ 4 | 5 | created_by:{ 6 | type: String, 7 | required: true 8 | }, 9 | 10 | created_at: { 11 | type: Date, 12 | default:Date.now() 13 | }, 14 | 15 | blog_title: { 16 | type: String, 17 | required: true 18 | }, 19 | 20 | blog_content: { 21 | type: String, 22 | required: true 23 | } 24 | 25 | }) 26 | 27 | 28 | 29 | module.exports = mongoose.model('Blog', blogSchema) -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | require('dotenv').config()//init dotenv 3 | 4 | const mongoConfig = require('./config/mongoConfig') 5 | const userRouter = require('./routes/userRouter') 6 | const authRouter = require('./routes/authRouter') 7 | const blogRouter = require('./routes/blogRouter') 8 | 9 | const morgan = require('morgan') 10 | const helmet = require('helmet') 11 | const bcrypt = require('bcrypt') 12 | const nodemon = require('nodemon') 13 | const dotenv = require('dotenv') 14 | //const jsonwebtoken = require('jsonwebtoken') 15 | 16 | const app = express() 17 | const PORT = process.env.PORT || 4000 18 | 19 | 20 | //Middleware==================================================== 21 | app.use(express.json()) 22 | app.use(morgan('dev')) 23 | app.use(helmet()) 24 | 25 | //Routers 26 | 27 | app.use('/users', userRouter) 28 | 29 | app.use('/blogs', blogRouter) 30 | 31 | app.use('/auth', authRouter) 32 | 33 | 34 | app.get('/', (req, res) => { 35 | res.status(200).json('WELCOME TO MY BLOG API!!') 36 | }) 37 | 38 | 39 | 40 | app.listen(PORT, () => { 41 | console.log(`The server is up and running on port: ${PORT}`) 42 | mongoConfig() 43 | }) -------------------------------------------------------------------------------- /routes/authRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const {check, validationResult} = require('express-validator') 3 | const bcrypt = require('bcrypt') 4 | const jwt = require('jsonwebtoken') 5 | const UserModel = require('../models/userSchema') 6 | 7 | const router = express.Router() 8 | 9 | //* User Login 10 | router.post('/',[ 11 | check("email", "Please provide a valid email").isEmail(), 12 | check("password", "Check your password!").notEmpty() 13 | ] , async (req, res) => { 14 | const userData = req.body 15 | 16 | const errors = validationResult(req) 17 | // Checks for validation errors 18 | if (!errors.isEmpty()){ 19 | return res.json(errors.array()) 20 | } 21 | 22 | try { 23 | // Find the user with the provided email 24 | const user = await UserModel.findOne({email: userData.email}) 25 | 26 | if (!user){ 27 | return res.json('User not found!') 28 | } 29 | 30 | // Compare the plain text password to hashed password 31 | const isMatch = await bcrypt.compare(userData.password, user.password) 32 | 33 | if (!isMatch){ 34 | return res.json('Password is not a match!') 35 | } 36 | 37 | //* Create a new JWT Token 38 | 39 | const payload = { 40 | id: user._id, 41 | email: user.email 42 | } 43 | 44 | const TOKEN = jwt.sign(payload, process.env.SECRET_KEY, { expiresIn: "2 Days"}) 45 | 46 | res.status(201).json({ 47 | user: user, 48 | token: TOKEN 49 | }) 50 | 51 | } catch (error) { 52 | console.log(error); 53 | res.status(500).json('Server Error') 54 | 55 | } 56 | 57 | }) 58 | module.exports = router -------------------------------------------------------------------------------- /routes/blogRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const blogModel = require('../models/blogSchema') 3 | const authMiddleware = require('../middleWare/authMiddleware') 4 | 5 | // * Create a Router 6 | const router = express.Router() 7 | 8 | //* GET Blogs 9 | router.get('/', async (req, res) => { 10 | try { 11 | const blogs = await blogModel.find() 12 | res.status(200).json(blogs) 13 | } catch (error) { 14 | console.log(error) 15 | } 16 | }) 17 | 18 | //* CREATE Blogs 19 | router.post('/', authMiddleware, async (req, res) => { 20 | const blogData = req.body // gets the data from the request 21 | console.log(blogData); 22 | try { 23 | const blogs = await blogModel.create(blogData) // create the todo in the db 24 | // send back the response 25 | res.status(201).json(blogs) 26 | // res.status(201).json({data: blogs}) 27 | } catch (error) { 28 | console.error(error) 29 | res.status(400).json('Bad request!!!!!') 30 | } 31 | }) 32 | 33 | 34 | //* GET BLOG BY ID 35 | router.get('/:id', authMiddleware, async (req, res) => { 36 | const id = req.params.id 37 | 38 | try { 39 | const blogs = await blogModel.findById(id) 40 | res.status(200).json(blogs) 41 | } catch (error) { 42 | console.error(error) 43 | res.status(400).json({ 44 | msg: 'Id not found' 45 | }) 46 | } 47 | }) 48 | 49 | 50 | //* UPDATE BLOG BY ID 51 | router.put('/:id',authMiddleware, async (req, res) => { 52 | const id = req.params.id 53 | const newBlogData = req.body 54 | try { 55 | //* find the todo by the id 56 | const blogs = await blogModel.findByIdAndUpdate(id, newBlogData, {new: true}) 57 | res.status(202).json(blogs) 58 | } catch (error) { 59 | console.log(error) 60 | } 61 | }) 62 | 63 | //! DELETE A BLOG BY ID 64 | router.delete('/:id', authMiddleware, async (req, res) => { 65 | const id = req.params.id 66 | 67 | try { 68 | const blogs = await blogModel.findByIdAndDelete(id) 69 | res.status(200).json({msg: 'Blog was deleted!'}) 70 | } catch (error) { 71 | console.log(error); 72 | } 73 | }) 74 | 75 | module.exports = router -------------------------------------------------------------------------------- /routes/userRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const UserModel = require('../models/userSchema') 3 | // pulls out the two function we need from express validator 4 | const {check, validationResult} = require('express-validator') 5 | const bcrypt = require('bcrypt') 6 | const jwt = require('jsonwebtoken') 7 | 8 | // * Create a Router 9 | const router = express.Router() 10 | 11 | //* Create or Register a new User 12 | router.post('/', [ 13 | check('username', "Username is required from Middleware!").notEmpty(), 14 | check("email", "Please use a valid email! from middleware").isEmail(), 15 | check("password", "Please enter a password").notEmpty(), 16 | check("password", "Please enter a password with six or more characters").isLength({min: 6}), 17 | ] ,async (req, res) => { 18 | const userData = req.body 19 | 20 | const errors = validationResult(req) 21 | // Checks for validation errors 22 | if (!errors.isEmpty()){ 23 | return res.json(errors.array()) 24 | } 25 | 26 | try { 27 | // checking if there is an user with this email in the db 28 | const userExist = await UserModel.findOne({email: userData.email}) 29 | // if user exist we return! 30 | if (userExist) { 31 | return res.json({msg: "User already exists!"}) 32 | } 33 | 34 | //* ==== Create New User 35 | // 1 Create the salt 36 | const SALT = await bcrypt.genSalt(12) 37 | // 2 use the salt to create a hash with the user's password 38 | const hashedPassword = await bcrypt.hash(userData.password, SALT) 39 | // 3 assign the hashed password to the userData 40 | userData.password = hashedPassword 41 | // Write the user to the db 42 | const user = await UserModel.create(userData) 43 | 44 | //* create a new JWT Token 45 | 46 | const payload = { 47 | id: user._id, 48 | email: user.email 49 | } 50 | //const SECRET_KEY='MY_SECRET_KEY' 51 | 52 | const TOKEN = jwt.sign(payload, process.env.SECRET_KEY, {expiresIn: "10 Days"}) 53 | 54 | res.status(201).json({ 55 | user: user, 56 | token: TOKEN 57 | }) 58 | 59 | } catch (error) { 60 | console.log(error) 61 | res.status(400).json('Bad request!!!!!') 62 | } 63 | }) 64 | 65 | module.exports = router -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TechBlogAPI 2 | 3 | 4 | A blog API that allows user to register with Authorization Schema. User receives a Token at the header, with the token the user can access Public blogs and create a blog with a username at the endpoint. User can also find blog and can adjust/delete blog with id. 5 | The API protects API with a hashed password. Usernames/Emails must also be unique during the registration process. Website: 6 | 7 | Tech Stack 8 | Server: Node, Express 9 | 10 | Database: MongoDB 11 | 12 | Tools: Postman 13 | 14 | Environment Variables 15 | To run this project, you will need to add the following environment variables to your .env file 16 | 17 | MONGODB_URI 18 | JWT_SECRET 19 | SALT 20 | Run Locally 21 | To clone the project, you must follow all steps and have all dependencies in order to run the project locally. 22 | 23 | Git clone - (https://github.com/Meeks71/blogapi) 24 | 25 | Go to the DOCUMENTS directory 26 | 27 | cd blogapi 28 | 29 | Install dependencies 30 | npm init -y 31 | 32 | npm i: 33 | 34 | -bcrypt 35 | -dotenv 36 | -express 37 | -helmet 38 | -jsonwebtoken 39 | -mongoose 40 | -morgan 41 | 42 | Routes 43 | - Endpoints, Parameters, Schema 44 | server -app.get('/') returns message "WELCOME TO MY BLOG API!!" 45 | 46 | Routes 47 | Auth('/auth') creates Users and Login 48 | 49 | router.post('/'): Register Users, AuthSchema is used. password is hashed 50 | 51 | router.post('/'): Login with username and password only. Token is sent to header for further acess 52 | 53 | router.get('/'): base endpoint Finds all users. Protected with token sent to header No parameters 54 | 55 | Blog('/blog') 56 | router.get('/'): all Public blogs are included, must be logged in and have a token 57 | 58 | router.post('/'): Creates Blog with blogSchema, username parameter(String) name is needed to post blog 59 | 60 | router.put('/:id'): Updates blog with id, parameter(String) id is needed to update blog 61 | 62 | router.get('/:id'): Returns blog associcated to id, parameter(String) id is required to find blog. onlyprivate blogs appear 63 | 64 | router.delete('/:id): Deletes blog associated with id, need a token for Authorization and parameter(string) id is required to delete blog 65 | 66 | 67 | 68 | Schemas 69 | userSchema: 70 | username: 71 | type: String, 72 | required: true 73 | 74 | email: 75 | type: String, 76 | required: true 77 | 78 | birthday: 79 | type: Date, 80 | required: true 81 | 82 | age: 83 | type: Number, 84 | required: true 85 | 86 | 87 | password: 88 | type: String, 89 | required: true 90 | 91 | 92 | 93 | blogSchema: 94 | 95 | username: 96 | type: String 97 | required: true, 98 | 99 | password: 100 | type: String, 101 | required: true, 102 | 103 | email: 104 | type: String, 105 | required: true, 106 | 107 | birthday: 108 | type: Number, 109 | required: true, 110 | 111 | age: type: Number 112 | 113 | 114 | Middleware 115 | verifyToken: gets token from header and check that it matches token sent. 116 | 117 | CreateBlog: uses login function to verify user is logged to aceess and post blogs. userame and password required, if passwords or username do not match 400 error will be sent 118 | 119 | --------------------------------------------------------------------------------