This project is a sample blog-API application for the setup of a complete CI/CD Azure pipeline for Amorserv Final Asssessment
- Javascript
- Node.js
- Express.js
- MongoDB
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.
git clone https://github.com/Psalmzee/WeBlog-API.git
npm install
Update .env with example.env
npm run dev
npm run test
field | data_type | constraints |
---|---|---|
username | string | required, unique |
firstName | string | required |
lastName | string | required |
string | required, unique | |
password | string | required |
articles | ref - 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 |
- 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"
}
}
- Route: /api/login
- Method: POST
π Body
{
"username": "samsonokeji",
"password": "Password@123"
}
π Response
{
"token": {token},
"username": "samsonokeji",
"name": "Samson Okeji"
}
- 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
}
}
-
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 commaAcceptable 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 commaDefault fields are
title
andtags
. Acceptable values include:- author
- title
- body
- read_count
- reading_time
/api/blog?fields=body,-tags,reading_time
-
-
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 commaAcceptable 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 commaDefault fields are
title
andtags
. Acceptable values include:- author
- title
- body
- read_count
- reading_time
/api/blog?fields=body,-tags,reading_time
-
- 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
}
}
- 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
}
}
- 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
}
}
- Route: /api/blog/:articleId
- Method: DELETE
- Header
- Authorization: Bearer {token}
- Project Link: WeBlog
- NAME: Samson Okeji
- EMAIL: engrsamsonokeji@gmail.com