├── .gitignore ├── .DS_Store ├── config └── mongoConfig.js ├── middleware └── authMiddleware.js ├── models ├── userSchema.js └── booksSchema.js ├── package.json ├── server.js ├── routes ├── authRouter.js ├── booksRouter.js └── usersRouter.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules/ 3 | .env -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AinurPro/Project_Book_API/HEAD/.DS_Store -------------------------------------------------------------------------------- /config/mongoConfig.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | 4 | module.exports = async()=> { 5 | try { 6 | await mongoose.connect(process.env.MONGODB_URI) 7 | mongoose.connection 8 | console.log('MongoDB is Connected!!!!'); 9 | } catch (error) { 10 | console.error(error) 11 | } 12 | } 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /middleware/authMiddleware.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken') 2 | 3 | module.exports = (req, res, next)=> { 4 | const token = req.header('x-auth-token') 5 | if(!token){ 6 | return res.json("No token is valid") 7 | } 8 | try { 9 | const decoded = jwt.verify(token, process.env.SECRET_KEY) 10 | 11 | // console.log(decoded); 12 | next() 13 | } catch (error) { 14 | console.log(error) 15 | res.status(400).json('Token is not valid!!!') 16 | } 17 | } -------------------------------------------------------------------------------- /models/userSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const userSchema = mongoose.Schema({ 4 | username: { 5 | type:String, 6 | required: true 7 | }, 8 | email: { 9 | type:String, 10 | required: true 11 | }, 12 | birthday: { 13 | type: Date, 14 | 15 | }, 16 | age: { 17 | type: Number, 18 | 19 | 20 | }, 21 | password: { 22 | type:String, 23 | required: true 24 | } 25 | }) 26 | module.exports = mongoose.model('user', userSchema) -------------------------------------------------------------------------------- /models/booksSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const booksSchema = mongoose.Schema({ 3 | created_by: { 4 | type: String, 5 | required: true 6 | }, 7 | created_at: { 8 | type: Date, 9 | required: true 10 | }, 11 | book_title: { 12 | type: String, 13 | required: true 14 | }, 15 | book_content: { 16 | type: String, 17 | required: true 18 | } 19 | // private: { 20 | // boolean: true, 21 | // required: true 22 | // } 23 | }) 24 | module.exports = mongoose.model('books', booksSchema) 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project_book_api", 3 | "version": "1.0.0", 4 | "description": "My Book_API", 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": "Ainur Akh", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^5.0.1", 15 | "cors": "^2.8.5", 16 | "dotenv": "^16.0.1", 17 | "express": "^4.18.1", 18 | "express-validator": "^6.14.2", 19 | "helmet": "^5.1.0", 20 | "jsonwebtoken": "^8.5.1", 21 | "mongoose": "^6.3.7", 22 | "morgan": "^1.10.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | require('dotenv').config() 3 | const morgan = require('morgan') 4 | const helmet = require('helmet') 5 | const cors = require('cors') 6 | 7 | 8 | 9 | const mongoConfig = require('./config/mongoConfig') 10 | const usersRouter = require('./routes/usersRouter') 11 | const booksRouter = require('./routes/booksRouter') 12 | const authRouter = require('./routes/authRouter') 13 | 14 | const app = express() 15 | 16 | const PORT = process.env.PORT || 5500 17 | 18 | 19 | app.use(express.json()) 20 | app.use(morgan('dev')) 21 | app.use(helmet()) 22 | app.use(cors()) 23 | 24 | app.use(express.json()) 25 | app.use('/users', usersRouter) 26 | app.use('/books', booksRouter) 27 | app.use('/auth', authRouter) 28 | 29 | app.get('/', (req, res)=> { 30 | res.status(200).json('Books are always worldwide') 31 | }) 32 | 33 | 34 | 35 | app.listen(PORT, ()=> { 36 | console.log(`Server is running on ${PORT}`); 37 | mongoConfig() 38 | }) 39 | 40 | -------------------------------------------------------------------------------- /routes/authRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bcrypt = require('bcrypt') 3 | const jwt = require('jsonwebtoken') 4 | const usersModel = require('../models/userSchema') 5 | const { json } = require('express') 6 | 7 | 8 | const router = express.Router() 9 | 10 | router.post('/', async(req, res)=>{ 11 | const usersData = req.body 12 | try { 13 | const user = await usersModel.findOne({email: usersData.email}) 14 | if (!user){ 15 | return res.json('User is not found') 16 | } 17 | const isMatch = await bcrypt.compare(usersData.password, user.password) 18 | if(!isMatch){ 19 | return res.json('password is not a match') 20 | } 21 | const payload = { 22 | id: user._id, 23 | email: user.email 24 | } 25 | const SECRET_KEY = 'MY_SECRET_KEY' 26 | const TOKEN = jwt.sign(payload, SECRET_KEY, { expiresIn: "2 days"}) 27 | res.status(201).json({ 28 | user: user, 29 | token: TOKEN 30 | }) 31 | } catch (error) { 32 | console.log(error) 33 | res.status(500).json(`Server is error`) 34 | } 35 | }) 36 | module.exports = router -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project_Book_API 2 | A Project_Book API allows the user to users Schema. User receives a Token at the header and can access the books at the endpoint, also can get/ post/delete the book with id. 3 | The API protects API with a hashed password. 4 | 5 | ## Tech Stack 6 | Server: Node, Express 7 | 8 | Database: Mongo DB 9 | 10 | Tools: Mongoose 11 | 12 | ##Environment Variables 13 | 14 | In order to run the Book_API project, you need to add the environment variables to .env file 15 | 16 | MONGODB_URI 17 | JWT_SECRET 18 | SALT 19 | 20 | ## Run Locally 21 | Clone the project You must follow all steps and have all dependencies in order to run the project locally. 22 | git clone 23 | 24 | Go to the project directory 25 | cd 26 | 27 | Install dependencies 28 | npm init -y 29 | 30 | npm i: 31 | 32 | -bcrypt 33 | -dotenv 34 | -express 35 | -helmet 36 | -jsonwebtoken 37 | -mongoose 38 | -morgan 39 | Routes 40 | Endpoints, Parameters, Schema 41 | server -app.get('/') returns message "API up" 42 | 43 | Auth('/auth') creates Login 44 | userRouter.post('/'): create Users, userSchema is used, password is hashed 45 | booksRouter.get('/'): create, reads, reads by id, updates and deletes books, booksSchema is used 46 | authRouter('/'): creates login 47 | 48 | ### User Schema: 49 | 50 | username: type: String, required: true, 51 | email:type: String, required: true, 52 | birthday: type: Number, required: true, 53 | age: type: Number 54 | password:type: String, required: true 55 | 56 | ### Books Schema: 57 | 58 | username: type: String, require: true 59 | created_by:type: String, required: true, 60 | created_at: type: String, required: true, 61 | book_title: type: String, required: true, 62 | book_content: type: String, required: true, 63 | 64 | 65 | ### Middleware 66 | 67 | authMiddleware: it takes token from the header and is used to protect the routes 68 | 69 | ## Globally posted in Heroku, please use link: https://aas-book-app.herokuapp.com/ 70 | -------------------------------------------------------------------------------- /routes/booksRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const booksModel = require('../models/booksSchema') 3 | const authMiddleware = require('../middleware/authMiddleware') 4 | 5 | 6 | 7 | const router = express.Router() 8 | 9 | router.post('/', authMiddleware, async(req, res)=> { 10 | const booksData = req.body 11 | 12 | try { 13 | const books = await booksModel.create(booksData) 14 | res.status(201).json(books) 15 | } catch (error) { 16 | console.log(error) 17 | res.status(400).json('Bad request!!!!') 18 | } 19 | }) 20 | // get a book by id 21 | router.get('/:id', authMiddleware , async( req, res)=> { 22 | const id = req.params.id 23 | try { 24 | const books = await booksModel.findById(id) 25 | res.status(200).json(books) 26 | } catch (error) { 27 | console.error(error) 28 | res.status(400).json({msg: "Book id is not found!"}) 29 | } 30 | }) 31 | 32 | router.get('/', authMiddleware, async(req, res)=> { 33 | try { 34 | const books = await booksModel.find() 35 | res.status(200).json(books) 36 | } catch (error) { 37 | console.log(error) 38 | 39 | } 40 | }) 41 | // Update book by id 42 | router.put('/:id', authMiddleware, async(req, res)=> { 43 | const id = req.params.id 44 | const newBookData = req.body 45 | try { 46 | const books = await booksModel.findByIdAndUpdate(id, newBookData, {new:true}) 47 | res.status(202).json(books) 48 | } catch (error) { 49 | console.error(error) 50 | } 51 | }) 52 | // Delete the book 53 | router.delete('/:id', authMiddleware, async(req, res)=> { 54 | const id = req.params.id 55 | try { 56 | const books = await booksModel.findByIdAndDelete(id) 57 | res.status(200).json({msg: 'Book was deleted'}) 58 | } catch (error) { 59 | console.log(error) 60 | 61 | } 62 | }) 63 | 64 | module.exports = router -------------------------------------------------------------------------------- /routes/usersRouter.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const UsersModel = require('../models/userSchema') 3 | const {check, validationResult} = require('express-validator') 4 | const bcrypt = require('bcrypt') 5 | const jwt = require('jsonwebtoken') 6 | // const authMiddleware = require('../middleware/authMiddleware') 7 | 8 | 9 | // const router = express.Router() 10 | 11 | // router.get('/', async(req, res)=> { 12 | // try { 13 | // const users = await usersModel.find() 14 | // res.json(users) 15 | // } catch (error) { 16 | // console.error(error) 17 | // } 18 | // }) 19 | 20 | // * Create a Router 21 | const router = express.Router() 22 | 23 | //* Create or Register a new User 24 | router.post('/', [ 25 | check('username', "Username is required from Middleware!").notEmpty(), 26 | check("email", "Please use a valid email! from middleware").isEmail(), 27 | check("password", "Please enter a password").notEmpty(), 28 | check("password", "Please enter a password with six or more characters").isLength({min: 6}), 29 | ] ,async (req, res) => { 30 | const usersData = req.body 31 | 32 | const errors = validationResult(req) 33 | // Checks for validation errors 34 | if (!errors.isEmpty()){ 35 | return res.json(errors.array()) 36 | } 37 | 38 | try { 39 | // checking if there is an user with this email in the db 40 | const userExist = await UsersModel.findOne({email: usersData.email}) 41 | // if user exist we return! 42 | if (userExist) { 43 | return res.json({msg: "User already exist!"}) 44 | } 45 | 46 | 47 | 48 | // router.post('/', async(req, res)=> { 49 | // const usersData = req.body 50 | // try { 51 | // const usersExist = await usersModel.findOne({email: usersData.email}) 52 | // if(usersExist){ 53 | // return res.json({msg: "User already exist"}) 54 | // } 55 | // // Creating SALT 56 | const SALT = await bcrypt.genSalt(10) 57 | const hashedPassword = await bcrypt.hash(usersData.password, SALT) 58 | usersData.password = hashedPassword 59 | const user = await UsersModel.create(usersData) 60 | // res.status(201).json(user) 61 | 62 | // create a new JWT token 63 | const payload = { 64 | id: user._id, 65 | email: user.email 66 | } 67 | const SECRET_KEY = 'MY_SECRET_KEY' 68 | const TOKEN = jwt.sign(payload, SECRET_KEY, {expiresIn: "40 Days"}) 69 | res.status(201).json({ 70 | user: user, 71 | token: TOKEN 72 | }) 73 | 74 | } catch (error) { 75 | console.log(error) 76 | res.status(400).json(" Please try again!!!!") 77 | } 78 | }) 79 | 80 | // Update user by id 81 | router.put('/:id', async(req, res)=> { 82 | const id = req.params.id 83 | const newUserData = req.body 84 | try { 85 | const user = await usersModel.findByIdAndUpdate(id, newUserData, {new:true}) 86 | res.status(202).json(user) 87 | } catch (error) { 88 | console.error(error) 89 | } 90 | 91 | }) 92 | // Delete the user 93 | router.delete('/:id',async(req, res)=> { 94 | const id = req.params.id 95 | try { 96 | await UsersModel.findByIdAndDelete(id) 97 | res.status(200).json({msg: 'User was deleted'}) 98 | } catch (error) { 99 | console.log(error) 100 | 101 | } 102 | }) 103 | module.exports = router 104 | --------------------------------------------------------------------------------