This step by step tutorial was developed referencing the following resources: mdn_web docs & rahmanfadhil
- Make new project directory.
mkdir <folder_name>
- Change directory into new project folder.
cd <folder_name>
- Initialise npm and install dependancies.
npm init -y
npm i express mongoose dotenv
npm i nodemon --save-dev
- Update package.json file for run scripts.
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js",
"dev": "nodemon ."
}
- Make project folder structure and files.
mkdir controllers
mkdir models
mkdir routes
touch server.js
touch routes/user.js
touch models/user.model.js
touch controllers/user.controller.js
- Update package.json "main" with new file names.
"main": "server.js",
- Build express server within server.js
const express = require("express")
const app = express()
const port = process.env.port || 3000
app.listen(port, () => {
console.log(`Server running on port: ${port}`)
})
- Confirm server can run in terminal.
npm run dev
Your terminal should confirm your server is running and display port number.
- Install Jest and Supertest frameworks.
npm i -D jest supertest
- Update jacakge.json run scripts to run jest upon test command. The '--watchAll' flag auto runs tests upon change/save, (like observr for Ruby Koans).
"test": "jest --watchAll"
- Create test file in root directory.
touch server.test.js
- Build test file. Note: Supertest framework replaces the need for services like Postman or Insomnia i.e making GET/POST/PATCH/DELETE requests. Supertest Docs
Jest is for unit testing, like RSpec for Ruby. Jest Docs
Within server.test.js
const request = require('supertest')
const app = require('./server')
describe('Mesaging API', () => {
it('GET /all_users ---> array of users and message contents', () => {
return request(app)
.get('/api/all_users')
.expect('Content-Type', /json/)
.expect(200)
.then((response) => {
expect(response.body).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: expect.any(String),
content: expect.any(String)
})
])
)
})
})
it('GET /get_user/id ---> return specific user by ID', () => {
return request(app)
.get('/api/get_user/id')
.expect('Content-Type', /json/)
.expect(200)
.then((response) => {
expect(response.body).toEqual(
expect.objectContaining({
name: expect.any(String),
content: expect.any(String)
})
)
})
})
it('GET /get_user/id ---> turns 404 if ID doenst exist', () => {
return request(app)
.get('/get_user/1233352124142362352135y2gfvrvr')
.expect(404)
})
it('POST /new_user ----> creates new user', () => {
return request(app)
.post('/new_user')
.send({
name: 'Donny',
content: 'This is donnys content'
})
.expect('Content-Type', /json/)
.expect(201).then((response) => {
expect(response.body).toEqual(
expect.objectContaining({
name: 'Donny',
content: 'This is donnys content'
})
)
})
})
})
- Run tests- they should all fail as we have not built API yet.
npm test
-
Create free MongoDB Atlas account
-
Create a cluster and connect to your cluster via MongoDB's native driver. Create new project
Confirm database settings- use the free version!
Create username and password for cluster
Copy the application code for next step.
- Create new .env file to store database application code.
touch .env
- Copy and paste the application code into .env file. Replace & with the password you created during cluster set-up. The < > should be deleted.
ATLAS_URI = mongodb+srv://<username>:<password>@cluster0.ib7cc.mongodb.net/?retryWrites=true&w=majority
- Setup Mongoose within server.js file.
const mongoose = require('mongoose')
require('dotenv').config();
const uri = process.env.ATLAS_URI
mongoose.connect(uri)
const db = mongoose.connection
app.use(express.json())
db.on('error', (error) => {
console.log(error)
})
db.once('connected',() => {
console.log('Database Connected')
})
You should get a message in the terminal confirming database connection.
- Creating database models / schema. In models/user.model.js
const mongoose = require("mongoose")
const userSchema = mongoose.Schema({
name: String,
content: String,
})
module.exports = mongoose.model("userSchema", userSchema)
- Creating controllers. In controllers/user.controller.js
const userModel = require('../models/user.model')
exports.getAllUsers = async (req,res) => {
const all_users = await userModel.find()
res.send(all_users)
}
exports.postNewUser = async (req,res) => {
const new_user = new userModel({
name: req.body.name,
content: req.body.content
})
await new_user.save()
res.send(new_user)
}
exports.getUserByID = async(req,res) => {
try{
const one_user = await userModel.findOne({ _id: req.params.id })
res.send(one_user)
} catch {
res.status(404)
res.send({ error: "User doesn't exist!" })
}
}
exports.editUserByID = async(req,res) => {
try {
const edit_user = await userModel.findOne({ _id: req.params.id })
if (req.body.name) {
edit_user.name = req.body.name
}
if (req.body.content) {
edit_user.content = req.body.content
}
await edit_user.save()
res.send(edit_user)
} catch {
res.status(404)
res.send({ error: "User doesn't exist!" })
}
}
exports.deleteUserByID = async (req,res) => {
try {
await userModel.deleteOne({ _id: req.params.id })
res.status(204).send()
} catch {
res.status(404)
res.send({ error: "Post doesn't exist!" })
}
}
- Create routes to handle CRUD actions. In routes/user.js
const express = require("express")
const router = express.Router()
const User = require("../models/user.model")
const user_controller = require('../controllers/user.controller')
router.get("/all_users", user_controller.getAllUsers)
router.post('/new_user', user_controller.postNewUser)
router.get("/get_user/:id", user_controller.getUserByID)
router.patch("/edit_user/:id", user_controller.editUserByID)
router.delete("/delete_user/:id", user_controller.deleteUserByID)
module.exports = router
- Update server.js to include routes.
const routes = require('./routes/user')
###Full Code
####server.js
const express = require("express")
const app = express()
const port = process.env.port || 3000
const mongoose = require('mongoose')
const routes = require('./routes/user')
require('dotenv').config();
const uri = process.env.ATLAS_URI
mongoose.connect(uri)
const db = mongoose.connection
app.use(express.json())
app.listen(port, () => {
console.log(`Server running on port: ${port}`)
})
app.use('/api', routes)
db.on('error', (error) => {
console.log(error)
})
db.once('connected',() => {
console.log('Database Connected')
})
const mongoose = require("mongoose")
const userSchema = mongoose.Schema({
name: String,
content: String,
})
module.exports = mongoose.model("userSchema", userSchema)
const userModel = require('../models/user.model')
exports.getAllUsers = async (req,res) => {
const all_users = await userModel.find()
res.send(all_users)
}
exports.postNewUser = async (req,res) => {
const new_user = new userModel({
name: req.body.name,
content: req.body.content
})
await new_user.save()
res.send(new_user)
}
exports.getUserByID = async(req,res) => {
try{
const one_user = await userModel.findOne({ _id: req.params.id })
res.send(one_user)
} catch {
res.status(404)
res.send({ error: "User doesn't exist!" })
}
}
exports.editUserByID = async(req,res) => {
try {
const edit_user = await userModel.findOne({ _id: req.params.id })
if (req.body.name) {
edit_user.name = req.body.name
}
if (req.body.content) {
edit_user.content = req.body.content
}
await edit_user.save()
res.send(edit_user)
} catch {
res.status(404)
res.send({ error: "User doesn't exist!" })
}
}
exports.deleteUserByID = async (req,res) => {
try {
await userModel.deleteOne({ _id: req.params.id })
res.status(204).send()
} catch {
res.status(404)
res.send({ error: "Post doesn't exist!" })
}
}
const express = require("express")
const router = express.Router()
const User = require("../models/user.model")
const user_controller = require('../controllers/user.controller')
router.get("/all_users", user_controller.getAllUsers)
router.post('/new_user', user_controller.postNewUser)
router.get("/get_user/:id", user_controller.getUserByID)
router.patch("/edit_user/:id", user_controller.editUserByID)
router.delete("/delete_user/:id", user_controller.deleteUserByID)
module.exports
4E32
= router