Introduction
This is a simple blogging API using Expressjs and MongoDB as the database. It allows authenticated users to access the blog post and also add blog posts.
These are the endpoints available on the API.
Methods | Endpoints | Description |
---|---|---|
POST | /signup | This is the signup endpoint |
POST | /login | Logs an existing user |
POST | /blog/post | this is to add a blog |
GET | /blog/post | Gets all blog posts in the database |
GET | /blog/post/:id | Get a single blog post using the id |
PUT | /blog/post/:id | Updates the blog post |
DELETE | /blog/post/:id | Deletes the blog post |
Tools used
ExpressJs
MongoDB
Postman or Thunderclient ( a vscode extension)
Prerequisites
Code Editor (most preferably vscode)
Have Knowledge of Javascript and Nodejs
should have installed npm and nodejs
Let's code
Step 1: Set up your project folders
Create a folder, you can call it
myBloggingApi
Open this folder in your code editor
In your code editor terminal run
npm init
, this initializes the npm package and creates a package.json file
- Then install the libraries you need for the project using
npm install <package-name>
. Packages to install include Express, mongoose, dotenv, express, jsonwebtoken e.t.c
Step 2: Set up Database and Database connection
Login to MongoDB atlas or sign up if you have never signed up before
On the MongoDB atlas, click on Connect, then Connect your application, copy the link posted there and create a
.env
file and paste the link there with the name ofMONGODB_CONNECTION_URI
MONGODB_CONNECTION_URI=mongodb+srv://TemitopeAgbaje:<password>.@cluster0.wzcue.mongodb.net/<name-of-database>?retryWrites=true&w=majority
Then create a new folder in myBloggingApi folder, called
database
and then a file calleddb.js
.Paste this code into it
require("dotenv").config();
const mongoose = require("mongoose");
const MONGODB_CONNECTION_URI = process.env.MONGODB_CONNECTION_URI;
mongoose.set("strictQuery", false);
//connect to MongoDB
function connectToMongoDB() {
mongoose.connect(MONGODB_CONNECTION_URI);
mongoose.connection.on("connected", async () => {
console.log("Connected to MongoDB successfully");
});
mongoose.connection.on("error", (err) => {
console.log("Error connecting to MongoDB", err);
});
}
module.exports = { connectToMongoDB };
Remember to install the packages dotenv and mongoose. This code above helps you connect the database
Step 3: Create the index.js and server.js files
Index.js
Express package should have been installed
Install the package body-parser
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
app.use(express.static("public"));
app.use(express.urlencoded({ extended: false }));
app.get("/", (req, res) => {
res.send("Welcome to my blog!");
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: "Something broke!" });
});
module.exports = app;
Server.js
- Add the
PORT
to .env folder
require("dotenv").config(); //process.env
const app = require("./index");
const db = require("./config/db")
const PORT = process.env.PORT || 4500;
db.connectToMongoDB() //connect to mongoDB
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Step 4: Write the Schema
- Create a new folder in myBloggingApi folder, and name it
models
, then create the files foruserModel.js
andblogModel.js
userModel.js
- Install
bcrypt
package
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const Schema = mongoose.Schema;
const userSchema = new Schema({
first_name: { type: String, required: true },
last_name: { type: String, required: true },
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
},
});
userSchema.pre("save", async function (next) {
const hashedPassword = await bcrypt.hash(this.password, 10);
this.password = hashedPassword;
next();
});
userSchema.methods.isPassword = async function (inputedPassword) {
const isCorrectPassword = await bcrypt.compare(
inputedPassword,
this.password
);
return isCorrectPassword;
};
const User = mongoose.model("User", userSchema);
module.exports = User;
userSchema.pre
method using bcrypt helps hash the password before saving it to the databaseuserSchema.methods.isPassword
method using bcrypt helps to compare the password
blogModel.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const blogSchema = new Schema({
title: {
type: String,
required: "Please add a title",
},
description: String,
author: String,
state: {
type: String,
enum: ["draft", "published"],
default: "draft",
},
read_count: { type: Number, default: 0 },
reading_time: String,
tags: Array,
body: {
type: String,
required: "Please add a body",
},
timestamp: Date,
});
const Blog = mongoose.model("Blog", blogSchema);
module.exports = Blog;
Step 5: Create the auth middleware
Create a new folder in myBloggingApi folder, and name it
middleware
then create the fileauth.js
In your vscode terminal, install
passport
andpassport-jwt
package
const passport = require("passport");
const JWTStrategy = require("passport-jwt").Strategy;
const ExtractJWT = require("passport-jwt").ExtractJwt;
require("dotenv").config();
passport.use(
"jwt",
new JWTStrategy(
{
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
},
(payload, done) => {
try {
done(null, payload.user);
} catch (err) {
done(err);
}
}
)
);
Step 6: Create Controllers
Add a new folder with the name
Controllers
and create theauthContoller.js
andblogController.js
filesRun
npm install jsonwebtoken
in your vscode terminal
authContoller.js
const jwt = require("jsonwebtoken");
const User = require("../models/userModel");
exports.signup = async (req, res) => {
const userExistInDB = await User.findOne({ email: req.body.email });
if (userExistInDB) {
return res.status(409).json({ message: "Email exists" });
}
const user = await User.create(req.body);
user.password = undefined;
const payload = { user };
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: "1h",
});
return res.status(201).json({
user,
token,
});
};
exports.login = async (req, res, next) => {
const { email, password } = req.body;
const user = await User.findOne({ email: email });
blogController
if (!user) {
return res.status(400).json({message: "User does not exist"})
}
const isCorrectPassword = await user.isPassword(password);
if (!isCorrectPassword) {
return res.status(400).json({message: "Incorrect Password"})
}
user.password = undefined;
const payload = { user };
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1h" });
return res.status(200).json({ user, token });
};
blogController.js
const blogModel = require("../models/blogModel");
const moment = require("moment");
exports.createBlogPost = async (req, res) => {
const text = req.body.body;
const wpm = 225;
const words = text.trim().split(/\s+/).length;
const time = Math.ceil(words / wpm);
const blogPost = await blogModel.create({
author: req.body.author,
description: req.body.description,
timestamp: moment().toDate(),
reading_time: `${time} mins`,
title: req.body.title,
body: req.body.body,
tags: req.body.tags,
});
return res.status(201).json({ status: "New Post", blogPost });
};
exports.getBlogPosts = async (req, res) => {
const { query } = req;
const {
timestamp,
state,
page = 1,
per_page = 20,
read_count,
reading_time,
author,
title,
tags,
} = query;
let findQuery = {};
if (timestamp) {
findQuery.timestamp = {
$gt: moment(timestamp).startOf("day").toDate(),
$lt: moment(timestamp).endOf("day").toDate(),
};
}
if (state) {
findQuery.state = state;
}
if (read_count) {
findQuery.read_count = read_count;
}
if (reading_time) {
findQuery = {
...findQuery,
reading_time: { $regex: reading_time, $options: "i" },
};
}
if (author) {
// findQuery.author = author;
findQuery = { ...findQuery, author: { $regex: author, $options: "i" } };
}
if (title) {
findQuery = { ...findQuery, title: { $regex: title, $options: "i" } };
}
if (tags) {
findQuery = { ...findQuery, tags: { $in: tags } };
}
const blogPosts = await blogModel
.find(findQuery)
.skip(page - 1)
.limit(per_page);
return res.status(200).json({ status: "All Post Loaded", blogPosts });
};
exports.getBlogPost = async (req, res) => {
try {
const { id } = req.params;
const blogPost = await blogModel.findById({ _id: id });
if (!blogPost) {
return res.status(404).json({ status: "Blog Post Not found" });
}
if (blogPost.state === "published") {
blogPost.read_count = blogPost.read_count += 1;
}
await blogPost.save();
return res.status(200).json({ status: "Blog Post Loaded", blogPost });
} catch (err) {
return res.status(400).json({ message: "Bad Request" });
}
};
exports.updateBlogPost = async (req, res, next) => {
const { id } = req.params;
const blogPost = await blogModel.findById({ _id: id });
if (!blogPost) {
return res.status(400).json({ message: "No blog Post" });
}
if (req.body.state) {
blogPost.state = req.body.state;
}
if (req.body.author) {
blogPost.author = req.body.author;
}
if (req.body.body) {
blogPost.body = req.body.body;
}
if (req.body.tags) {
blogPost.tags = req.body.tags;
}
await blogPost.save();
return res.status(200).json({ status: "Post Updated", blogPost });
};
exports.deleteBlogPost = async (req, res) => {
const { id } = req.params;
const blogPost = await blogModel.findById({ _id: id });
if (!blogPost) {
return res.status(400).json({ message: "No blog Post" });
}
await blogPost.delete();
return res.status(200).json({ status: "Deleted successfully"});
};
Step 7: Routes
- In myBoggingApi folder, add
routes
folder and in it we have theauthRoute.js
andblogRoutes.js
authRoute.js
const express = require("express");
const AuthController = require("../controllers/authController");
const authRouter = express.Router();
authRouter.post("/signup", AuthController.signup);
authRouter.post("/login", AuthController.login);
module.exports = authRouter;
blogRoutes.js
const express = require("express");
const BlogController = require("../controllers/blogController");
const blogRouter = express.Router();
blogRouter.post("/post", BlogController.createBlogPost);
blogRouter.get("/posts", BlogController.getBlogPosts);
blogRouter.get("/post/:id", BlogController.getBlogPost);
blogRouter.put("/post/:id", BlogController.updateBlogPost);
blogRouter.delete("/post/:id",BlogController.deleteBlogPost);
module.exports = blogRouter;
Step 8: Updating index.js
securing the blog route
initialize passport
const express = require("express");
const passport = require("passport");
const blogRouter = require("./routes/blogRoutes");
const authRouter = require("./routes/authRoutes");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
// app.use(express.json());
app.use(express.static("public"));
app.use(express.urlencoded({ extended: false }));
app.use(passport.initialize());
require("./middlewares/auth");
app.get("/", (req, res) => {
res.send("<h2>Welcome to my blog!</h2>");
});
app.use("/blog", passport.authenticate("jwt", { session: false }), blogRouter);
app.use("/", authRouter);
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: "Something broke!" });
});
module.exports = app;
Conclusion
I hope with this article you can create your blogging API.
I hosted it with render, Click here to try it out
Link to the GitHub repository:https://github.com/TemitopeAgbaje/blogging-api
Top comments (0)