A blogging api
Tags: node, webdev, javascript, api
Published: 2023-01-10 00:13:42

### 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
    
![my package.json file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et1p5dmdzflzl3k3b7kw.png)

* 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 of `MONGODB_CONNECTION_URI`
    

```javascript
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 called `db.js`.
    
* Paste this code into it
    

```javascript
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
    

```javascript
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
    

```javascript
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 for `userModel.js` and `blogModel.js`
    

#### userModel.js

* Install `bcrypt` package
    

```javascript
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 database
    
* `userSchema.methods.isPassword` method using bcrypt helps to compare the password
    

#### blogModel.js

```javascript
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 file `auth.js`
    
* In your vscode terminal, install `passport` and `passport-jwt` package
    

```javascript
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 the `authContoller.js` and `blogController.js` files
    
* Run `npm install jsonwebtoken` in your vscode terminal
    

#### authContoller.js

```javascript
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

```javascript
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 the `authRoute.js` and `blogRoutes.js`
    

#### authRoute.js

```javascript
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

```javascript
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
    

```javascript
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](https://alt-blogging-api.onrender.com) to try it out

Link to the GitHub repository:https://github.com/TemitopeAgbaje/blogging-api