Skip to content

Psalmzee/WeBlog-API

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

About the project

This is a containerized NodeJS application to be deployed to AKS

This project is a sample blog-API application for the setup of a complete CI/CD Azure pipeline for Amorserv Final Asssessment

The built image will be stored in Azure Container Registry.

# WeBlog
Tools/Languages
  • Javascript
  • Node.js
  • Express.js
  • MongoDB

Requirements

Requirements/Functionalities for the Application
  • Users have a first_name, last_name, email, password.

  • A user is able to signup and signin into the blog app

  • JWT is used as authentication strategy and the token expires after 1 hour.

  • A blog can be in two states; draft and published.

  • Logged in and non-logged in users are able to get a list of published blogs created.

  • Logged in and non-logged in users are able to get a published blog.

  • Logged in users are able to create a blog.

  • When a blog is created, it is in draft state by default.

  • The owner of the blog is able to update the state of the blog to published.

  • The owner of a blog is able to edit the blog in draft or published state

  • The owner of the blog is able to delete the blog in draft or published state

  • The owner of the blog is able to get a list of their blogs.

  • The endpoints are paginated.

  • Created Blogs have title, description, tags, author, timestamp, state, read_count, reading_time and body.

  • The list of blogs endpoint that can be accessed by both logged in and non-logged in users are paginated:

    • It is configured to 20 blogs per page.

    • It is searchable by author, title and tags.

    • It is orderable by read_count, reading_time and timestamp

  • When a single blog is requested, the API returns the user information (the author) with the blog. The read_count of the blog too is updated by 1.

  • An algorithm for calculating the reading_time of the blog was developed.



Development

Prerequisites

Clone this repo

git clone https://github.com/Psalmzee/WeBlog-API.git

Install project dependencies

npm install

Update .env with example.env

Run a development server

npm run dev

For testing, run

npm run test

Models

User

field data_type constraints
username string required, unique
firstName string required
lastName string required
email string required, unique
password string required
articles ref - Article

Article

field data_type constraints
title string required, unique
description string optional
author ref - User
owner string
state string required, default: 'draft', enum: ['draft', 'published']
read_count Number default: 0
reading_time Number
tags array optional
body string required

Usage

Creating a user

  • Route: /api/signup
  • Method: POST

πŸ‘‡ Body

{
  "firstName": "Samson",
  "lastName": "Okeji",
  "username": "samsonokeji",
  "email": "example@gmail.com",
  "password": "Password@123"
}

πŸ‘‡ Response

{
  "status": "success",
  "data": {
    "firstName": "Samson",
    "lastName": "Okeji",
    "username": "samsonokeji",
    "email": "example@gmail.com",
    "articles": [],
    "_id": "6367c296ba7522bd8561e4f6"
  }
}

Logging in

  • Route: /api/login
  • Method: POST

πŸ‘‡ Body

{
  "username": "samsonokeji",
  "password": "Password@123"
}

πŸ‘‡ Response

{
  "token": {token},
  "username": "samsonokeji",
  "name": "Samson Okeji"
}

Create a Blog

  • Route: /api/blog
  • Method: POST
  • Header
    • Authorization: Bearer {token}

πŸ‘‡ Body

{
  "title": "The Rise and fall of the Northern Empire",
  "tags": ["memoirs", "expose", "fun"],
  "description": "History of the northern people of Nigeria, a Popular myth",
  "body": "lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!vlorem ipsum!lorem ipsum!vvvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!"
}

πŸ‘‡ Response

{
  "status": "success",
  "data": {
    "title": "The Rise and fall of the Northern Empire",
    "description": "History of the northern people of Nigeria, a Popular myth",
    "author": "6367c296ba7522bd8561e4f6",
    "state": "draft",
    "read_count": 0,
    "tags": ["memoirs", "expose", "fun"],
    "body": "lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!vlorem ipsum!lorem ipsum!vvvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!",
    "_id": "6367cc2271c384885108032f",
    "createdAt": "2022-11-06T15:00:50.202Z",
    "updatedAt": "2022-11-06T15:00:50.202Z",
    "reading_time": 1
  }
}

Get all published blogs

  • Route: /api/blog

  • Method: GET

  • Header

    • Authorization: Bearer {token}
    • None (Accessible to unauthenticated users)
  • Query params:

    • page (default: 1)

    • size (default: 20)

    • Filters: Limit returned response by passing values to any of the following parameters:

      • author
      /api/blog?author=Author
      
      • title
      /api/blog?title=Title
      
      • tags: Separate multiple values with a comma
      /api/blog?tags=sql,database
      
    • Sort: Sort returned response by passing values matching the fields in the blog to the orderby parameter. To sort in descending order, add a - prefix. Separate multiple values with a comma

      Acceptable values include:

      • author
      • title
      • read_count
      • reading_time
        /api/blog?orderby=title,-read_count
      
    • Fields: Set the fields to display in the returned response by passing values matching the fields in the blog to the fields parameter. To omit any fields, add a - prefix. Separate multiple values with a comma

      Default fields are title and tags. Acceptable values include:

      • author
      • title
      • body
      • read_count
      • reading_time
        /api/blog?fields=body,-tags,reading_time
      

Get all created blogs by authenticated user

  • Route: /api/blog/p

  • Method: GET

  • Header

    • Authorization: Bearer {token}
  • Query params:

    • page (default: 1)

    • size (default: 20)

    • Filters: Limit returned response by passing values to any of the following parameters:

      • state
      /api/blog?state=draft
      
      /api/blog?state=published
      
      • title
      /api/blog?title=Title
      
      • tags: Separate multiple values with a comma
      /api/blog?tags=sql,database
      
    • Sort: Sort returned response by passing values matching the fields in the blog to the orderby parameter. To sort in descending order, add a - prefix. Separate multiple values with a comma

      Acceptable values include:

      • title
      • read_count
      • reading_time
        /api/blog?orderby=title,-read_count
      
    • Fields: Set the fields to display in the returned response by passing values matching the fields in the blog to the fields parameter. To omit any fields, add a - prefix. Separate multiple values with a comma

      Default fields are title and tags. Acceptable values include:

      • author
      • title
      • body
      • read_count
      • reading_time
        /api/blog?fields=body,-tags,reading_time
      

Get specific blog

  • Route: /api/blog/:articleId
  • Method: GET
  • Header
    • Authorization: Bearer {token}
    • None (Published blogs accessible to unauthenticated users)

πŸ‘‡ Response

{
    "status": "success",
    "data": {
        "_id": "6367cc2271c384885108032f",
        "title": "The witcher",
        "description": "The witch who falls inlove",
        "author": {
            "_id": "6367c296ba7522bd8561e4f6",
            "username": "samsonokeji"
        },
        "state": "published",
        "read_count": 1,
        "tags": [
            "memoirs",
            "expose"
        ],
        "body": "lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!vlorem ipsum!lorem ipsum!vvvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!",
        "createdAt": "2022-11-06T15:00:50.202Z",
        "updatedAt": "2022-11-06T19:38:16.100Z",
        "reading_time": 1
    }
}

Update the state of a Blog

  • Route: /api/blog/:articleId
  • Method: PATCH
  • Header
    • Authorization: Bearer {token}

πŸ‘‡ Body

{
  "state": "published"
}

πŸ‘‡ Response

{
  "status": "success",
  "data": {
    "_id": "6367cc2271c384885108032f",
    "title": "The witcher",
    "description": "The witch who falls inlove",
    "author": "6367c296ba7522bd8561e4f6",
    "state": "published",
    "read_count": 0,
    "tags": ["memoirs", "expose", "fun"],
    "body": "lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!vlorem ipsum!lorem ipsum!vvvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!",
    "createdAt": "2022-11-06T15:00:50.202Z",
    "updatedAt": "2022-11-06T16:17:45.137Z",
    "reading_time": 1
  }
}

Update the contents of a Blog

  • Route: /api/blog/:articleId
  • Method: PUT
  • Header
  • Authorization: Bearer {token}

πŸ‘‡ Body

{
  "tags": ["memoirs", "expose"],
  "body": "Here is the updated content: lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!vlorem ipsum!lorem ipsum!vvvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!"
}

πŸ‘‡ Response

{
  "status": "success",
  "data": {
    "_id": "6367cc2271c384885108032f",
    "title": "The witcher",
    "description": "The witch who falls inlove",
    "author": "6367c296ba7522bd8561e4f6",
    "state": "published",
    "read_count": 0,
    "tags": ["memoirs", "expose"],
    "body": "Here is the updated content: lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!vlorem ipsum!lorem ipsum!vvvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!vvlorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!lorem ipsum!",
    "createdAt": "2022-11-06T15:00:50.202Z",
    "updatedAt": "2022-11-06T16:22:29.326Z",
    "reading_time": 1
  }
}

Delete a Blog

  • Route: /api/blog/:articleId
  • Method: DELETE
  • Header
  • Authorization: Bearer {token}


PROJECT OWNER