In our previous tutorial, we wanted to practically learn and understand the serverless, container, and server approaches https://dev.to/kevin_odongo35/serverless-container-or-server-approach-4mh5. This tutorial will be fun, a step by step guide.
In today's tutorial, we are going to handle the first approach of using the following:
- Express
- MongoDB // we can use MySQL, PostgreSQL etc
- Node
This is the architecture we are trying to achieve:
On the root folder of the directory (blog-vue-application) that we created in the first tutorial.
Run the following commands in the root folder:
$ mkdir blog-vue-back-end && cd blog-vue-back-end
$ npm init
package name: blog-vue-back-end
version: // press enter
description: Tutorial
entry point: index.js
test command: // press enter
git repository: // press enter
keywords: Mongo DB, Node, Passport and Express
author: your name
licence: MIT
// save
Once done install the following packages
- express
- cors
- node
- body-parser
- mongoose
- mongodb // will be using for testing purposes
- jest // you can use a different testing
- dotenv
$ npm install express mongoose mongodb cors body-parser node express dotenv
Install the nodemon package as a dependency so you don't have to restart your server.
$ npm install -D nodemon
These are APIs that Node.js Express App will export:
Methods | URL | Actions |
---|---|---|
GET | api/blog | get many blogs |
GET | api/blog/:id | get a single blog |
POST | api/blog | create blog |
PUT | api/blog | update blog |
DELETE | api/blog | remove many blogs |
DELETE | api/blog/:id | remove single blog |
This is how your application structure should be:
package.json
Once we have installed everything, update the sections of the script. This is how your package.js file should be:
{
"name": "blog-vue-back-end",
"version": "1.0.0",
"description": "Tutorial Backend for Blog Application",
"main": "index.js",
"scripts": {
"start": "node server/index.js",
"dev": "nodemon server/index.js",
"test-dev": "jest"
},
"keywords": [
"Mongo",
"DB",
"Express",
"Node"
],
"author": "Kevin Odongo",
"license": "MIT",
"dependencies": {
"@shelf/jest-mongodb": "^1.2.3",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jest": "^26.6.3",
"mongodb": "^3.6.3",
"mongoose": "^5.11.14"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}
Create an index.js file in the backend folder. This will be the entry point for our application.
$ touch index.js
index.js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
require('dotenv').config()
const app = express();
// parse application/json
app.use(bodyParser.json())
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }))
var corsOptions = {
origin: 'http://localhost:3000'
}
// use cors options
app.use(cors(corsOptions))
//
const db = require("./app/models");
db.mongoose
.connect(db.url, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
})
.then(() => {
console.log("Connected to the database!");
})
.catch(err => {
console.log("Cannot connect to the database!", err);
process.exit();
});
// routes
const blog = require('./app/routes/blog')
app.use('/api/blog', blog)
// listening port
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
Once we have an entry point for our application let us initialize Mongo DB.
Create a file .env in the root folder
$ touch .env
// add the following
MONGO_DB_URI = mongodb+srv://odongo:password@secretserver.e5kih.mongodb.net/blog?retryWrites=true&w=majority
To get your mongodb url register for a free account here https://account.mongodb.com/account/register. Once you register create a cluster, user, and database.
Add node modules and .env file in the .gitignore file.
$ touch .gitignore
// add the following
node_modules
# local env files
.env.local
.env.*.local
Create a directory called app that will hold the following files.
/app/config/db.config.js
Expose the Mongo DB URI from your environment file
module.exports = {
url: process.env.MONGO_DB_URI
};
/app/controller/blog.controller.js
In this directory we will define how we want to handle the following actions:
- create
- findAll
- findOne
- update
- delete
- deleteAll
const db = require("../models");
const Blog = db.blog;
// Create and Save a new blog
exports.create = (req, res) => {
// Validate request
if (!req.body.content) {
res.status(400).send({ message: "Content can not be empty!" });
return;
}
// Create a blog
const blog = new Blog({
author: req.body.author,
content: req.body.content,
published: req.body.published ? req.body.published : false
});
// Save blog in the database
blog
.save(blog)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the blog."
});
});
};
// Retrieve all blogs from the database.
exports.findAll = (req, res) => {
const content = req.query.content;
var condition = content ? { content: { $regex: new RegExp(content), $options: "i" } } : {};
Blog.find(condition)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving blogs."
});
});
};
// Find a single blog with an id
exports.findOne = (req, res) => {
const id = req.params.id;
Blog.findById(id)
.then(data => {
if (!data)
res.status(404).send({ message: "Not found blog with id " + id });
else res.send(data);
})
.catch(err => {
res
.status(500)
.send({ message: "Error retrieving blog with id=" + id });
});
};
// Update a blog by the id in the request
exports.update = (req, res) => {
if (!req.body) {
return res.status(400).send({
message: "Data to update can not be empty!"
});
}
const id = req.params.id;
Blog.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
.then(data => {
if (!data) {
res.status(404).send({
message: `Cannot update Blog with id=${id}. Maybe Blog was not found!`
});
} else res.send({ message: "Blog was updated successfully." });
})
.catch(err => {
res.status(500).send({
message: "Error updating Blog with id=" + id
});
});
};
// Delete a blog with the specified id in the request
exports.delete = (req, res) => {
const id = req.params.id;
Blog.findByIdAndRemove(id)
.then(data => {
if (!data) {
res.status(404).send({
message: `Cannot delete Blog with id=${id}. Maybe Blog was not found!`
});
} else {
res.send({
message: "Blog was deleted successfully!"
});
}
})
.catch(err => {
res.status(500).send({
message: "Could not delete Tutorial with id=" + id
});
});
};
// Delete all blogs from the database.
exports.deleteAll = (req, res) => {
Blog.deleteMany({})
.then(data => {
res.send({
message: `${data.deletedCount} Blogs were deleted successfully!`
});
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while removing all blogs."
});
});
};
// Find all published blogs
exports.findAllPublished = (req, res) => {
Blog.find({ published: true })
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving blogs."
});
});
};
/app/model/index.js
In this directory, we are going to assemble the following files:
- /app/config/db.config
- /app/model/blog.model.js
Ensure you import this directory in your index file and connect to your Mongo DB.
const dbConfig = require("../config/db.config");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
const db = {};
db.mongoose = mongoose;
db.url = dbConfig.url;
db.blog = require("./blog.model.js")(mongoose);
module.exports = db;
/app/model/blog.model.js
Our blog schema will be in this directory.
module.exports = mongoose => {
const Blog = mongoose.model(
"blog",
mongoose.Schema(
{
author: String,
content: String,
published: Boolean
},
{ timestamps: true }
)
);
// We can add a category to categorize articles
// Incase you want to replace _.id with id
// schema.method("toJSON", function() {
// const { __v, _id, ...object } = this.toObject();
// object.id = _id;
// return object;
// });
// const Blog = mongoose.model("blog", schema);
return Blog;
};
/app/routes/blog.js
This will handle all our routes. Ensure you export this file in the index.js file.
const express = require("express")
const router = express.Router()
const blog = require("../controller/blog.controller");
// /api/blog: GET, POST, DELETE
// /api/blog/:id: GET, PUT, DELETE
// /api/blog/published: GET
// Create a new blog
router.post("/", blog.create);
// Retrieve all blog
router.get("/", blog.findAll);
// Retrieve all published blog
router.get("/published", blog.findAllPublished);
// Retrieve a single blog with id
router.get("/:id", blog.findOne);
// Update a Tutorial with id
router.put("/:id", blog.update);
// Delete a Tutorial with id
router.delete("/:id", blog.delete);
// Create a new Tutorial
router.delete("/", blog.deleteAll);
module.exports = router
Now that we have our backend ready we can integrate the backend and the front-end. You can test your routes using postman or any other tool out there.
Remember our routes are currently not protected therefore you can not go to production like this. We will need to protect our routes and add authentication to our application. In the next article, we are going to handle this.
Open your front-end directory in a new window of vs-code or whatever text editor you are using and run the application.
$ cd .. && cd /blog-vue-front-end
$ npm run serve
// Your front-end will be running on PORT 8080 || PORT 8081
// Your back-end will be running on PORT 3000
Once the application is running let us create the following file in the components folder:
/components/mongo-express-script.js
This will hold all our requests to the backend for mongo-express-script.js.
Install axios in the front-end
$ yarn add axios
In the mongo-express-script.js file add the following:
import axios from "axios";
// create new blog
export const createnewblog = async item => {
let data = {
author: JSON.stringify({
name: item.author.name,
email: item.author.email,
about: item.author.about
}), // replace with auth user
content: JSON.stringify({
title: item.content.title,
src: item.content.src,
text: item.content.text
})
};
let request = {
url: "http://localhost:3000/api/blog", // should be replaced after going to production with domain url
method: "post",
headers: {
"Content-type": "application/json"
},
data: JSON.stringify(data)
};
const response = await axios(request);
return response;
};
// delete blog
export const deleteblog = async item => {
let request = {
url: "http://localhost:3000/api/blog/" + item, // should be replaced after going to production with domain url
method: "delete",
headers: {
"Content-type": "application/json"
}
};
const response = await axios(request);
return response;
};
// update blog
export const updateblog = async item => {
let data = {
author: JSON.stringify({
name: item.author.name,
email: item.author.email,
about: item.author.about
}), // replace with auth user
content: JSON.stringify({
title: item.content.title,
src: item.content.src,
text: item.content.text
}),
published: item.published
};
let request = {
url: "http://localhost:3000/api/blog/" + item._id, // should be replaced after going to production with domain url
method: "put",
headers: {
"Content-type": "application/json"
},
data: JSON.stringify(data)
};
const response = await axios(request);
return response;
};
// get all blog
export const retriveallblog = async () => {
let request = {
url: "http://localhost:3000/api/blog", // should be replaced after going to production with domain url
method: "get",
headers: {
"Content-type": "application/json"
}
};
const response = await axios(request);
return response;
};
Here is how the blog should be working currently with your backend setup. We have completed the integration. Ensure your backend and front end are running concurrently.
What's next?. We need to protect our routes and authentication and go to production.
Here is a repo for the backend https://github.com/kevinodongo/tutorial-blog-backend.git.
Currently, we have not gone to production yet and therefore we can still use the Container or Server approach.
Thank you
Top comments (0)