Skip to content

RESTful API simulating a blog - an authenticated user can post new blog posts with a title, content and categories

Notifications You must be signed in to change notification settings

PedroPA94/blogs-api--trybe

Repository files navigation

Welcome!

In this RESTful API simulating a blog, an authenticated user can post a new blog post with a title, content and related categories. It is also possible to create, delete and list users, create and list categories and create, list, delete and update posts. The API uses Json Web Token for authentication.

In this project, MySQL is used as a database manager, and the communications between it and the app are made with the ORM Sequelize. All models and migrations were made by me.

Check the documentation

Like other Back-end projects I did, this project was built with a three-layered software architecture: the Model layer, responsible for communicating with the database, the Service layer in the middle, which validates the business rules, and the Controller layer, which receives and responds HTTP requests.

This project was developed while studying Back-end web development @betrybe. The files I worked on are in the /src folder. I got approval on 100% of this project's requirements.

Database diagram

EER Diagram

Main languages and tools used

  • Node.js
  • Express.js
  • Sequelize
  • MySQL
  • Joi for input data validation
  • Json Web Token (JWT) for authentication
  • Docker
  • Layered Software Architecture

Installation

With Docker
  • Start the blogs_api and blogs_api_db containers with the docker-compose up -d --build command
  • Access the blogs_api container terminal with docker exec -it blogs_api bash
  • In the terminal, install the dependencies with npm install
  • All other node commands must be run inside the container
Without Docker
  • Install the dependencies with npm install (requires node on version 16)
  • Configure a .env file based on the .env.example avaliable.
Commands
  • Run the app with npm start or npm run debug (live reload)
  • To run the project's requirements tests, use npm test for all tests or npm test <test-name> for a specific requirement (ex. npm test req01)
  • Use npm run drop to delete the database
  • Use npm run prestart to create the database and its tables
  • Use npm run seed to populate the tables

Endpoints

POST /login
  • Validates the sent email and password and returns a JWT token.

  • Example request body:
  {
    "email": "string",
    "password": "string"
  }
  • Example of returned token:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}

Examples of invalid requests:

  • Response for request without any of the required fields (status 400):
{ "message": "Some required fields are missing" }
  • Response for request where "email" and/or "password" are incorrect or do not exist (status 400):

    { "message": "Invalid fields" }
GET/user
  • Returns an array with all the registered users ordered by their id, or an empty array if there are no users. Requires a valid token

  • Example:
[
  {
      "id": 1,
      "displayName": "Lewis Hamilton",
      "email": "lewishamilton@gmail.com",
      "image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
  },
  {
      "id": 2,
      "displayName": "Brett Wiltshire",
      "email": "brett@email.com",
      "image": "http://4.bp.blogspot.com/_YA50adQ-7vQ/S1gfR_6ufpI/AAAAAAAAAAk/1ErJGgRWZDg/S45/brett.png"
  },
]
POST /user
  • Creates a new user returns a valid Json Web Token. Validations are done in the request body.

  • Example request body:
{
  "displayName": "Brett Wiltshire",
  "email": "brett@email.com",
  "password": "123456",
  "image": "http://4.bp.blogspot.com/_YA50adQ-7vQ/S1gfR_6ufpI/AAAAAAAAAAk/1ErJGgRWZDg/S45/brett.png"
  // image is not requireed
}
  • Example of response for valid entry:
  {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjp7ImlkIjo1LCJkaXNwbGF5TmFtZSI6InVzdWFyaW8gZGUgdGVzdGUiLCJlbWFpbCI6InRlc3RlQGVtYWlsLmNvbSIsImltYWdlIjoibnVsbCJ9LCJpYXQiOjE2MjAyNDQxODcsImV4cCI6MTYyMDY3NjE4N30.Roc4byj6mYakYqd9LTCozU1hd9k_Vw5IWKGL4hcCVG8"
  }

Examples of invalid requests:

  • Response for request with the "displayName" field with less than 8 characters (status 400):
{
  "message": "\"displayName\" length must be at least 8 characters long"
}
  • Response for request with invalid "email" (status 400):
{
  "message": "\"email\" must be a valid email"
}
  • Response for request with an invalid "password" length (status 400):
{
  "message": "\"password\" length must be at least 6 characters long"
}
  • Response for request with an existing user (status 409):
{
  "message": "User already registered"
}
GET /user/:id
  • Returns the user with the specified id. Requires a valid token

  • Example of response for valid entry:
{
  "id": 1,
  "displayName": "Lewis Hamilton",
  "email": "lewishamilton@gmail.com",
  "image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
}
  • Response for invalid id (status 404):
{
  "message": "User does not exist"
}
DELETE /user/me
  • Deletes the logged in user according to the id in the token. Requires a valid token

GET /categories
  • Returns an array with all the registered categories, or an empty array if there are none. Requires a valid token

  • Example:
[
  {
      "id": 1,
      "name": "Inovação"
  },
  {
      "id": 2,
      "name": "Escola"
  },

  /* ... */
]
POST /categories
  • Adds a new category in the database and returns it with an inserted id. Requires a valid token

  • Example request body:
{
  "name": "Typescript"
}
  • Example of response for valid entry:
{
  "id": 3,
  "name": "Typescript"
}
  • Response for request without a "name" field (status 400):
  { "message": "\"name\" is required" }
GET /post
  • Returns an array with all the registered posts, or an empty array if there are none. Requires a valid token

  • Example:
[
  {
    "id": 1,
    "title": "Post do Ano",
    "content": "Melhor post do ano",
    "userId": 1,
    "published": "2011-08-01T19:58:00.000Z",
    "updated": "2011-08-01T19:58:51.000Z",
    "user": {
      "id": 1,
      "displayName": "Lewis Hamilton",
      "email": "lewishamilton@gmail.com",
      "image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
    },
    "categories": [
      {
        "id": 1,
        "name": "Inovação"
      }
    ]
  },
  
  /* ... */
]
POST /post
  • Adds a new post and links it with its respective categories. Returns it with an inserted id. Requires a valid token

  • Example request body:
{
  "title": "Latest updates, August 1st",
  "content": "The whole text for the blog post goes here in this key",
  "categoryIds": [1, 2]
}
  • Example of response for valid entry:
{
  "id": 3,
  "title": "Latest updates, August 1st",
  "content": "The whole text for the blog post goes here in this key",
  "userId": 1,
  "updated": "2022-05-18T18:00:01.196Z",
  "published": "2022-05-18T18:00:01.196Z"
}

Examples of invalid requests:

  • Response for request without any of the required felds (status 400):
{
  "message": "Some required fields are missing"
}
  • Response for request with non existant "categoryId" (status 400):

{ "message": "one or more "categoryIds" not found" }


</details>

<details>
<summary><strong>GET</strong> <code>/post/:id</code></summary>

<br />

- Returns the post with the specified id. **Requires a valid token**

<br />

- Example of response for valid entry:

```json
{
  "id": 1,
  "title": "Post do Ano",
  "content": "Melhor post do ano",
  "userId": 1,
  "published": "2011-08-01T19:58:00.000Z",
  "updated": "2011-08-01T19:58:51.000Z",
  "user": {
      "id": 1,
      "displayName": "Lewis Hamilton",
      "email": "lewishamilton@gmail.com",
      "image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
  },
  "categories": [
      {
          "id": 1,
          "name": "Inovação"
      }
  ]
}
  • Response for invalid id (status 404):
{
  "message": "Post does not exist"
}
PUT /post/:id
  • Updates and returns the post with the specified id. Only the user that is the author of the post can update it, and only the "title" and "content" fields are updatable. Requires a valid token

  • Example request body:
{
  "title": "Latest updates, August 1st",
  "content": "The whole text for the blog post goes here in this key"
}
  • Example of response for valid entry:
{
  "id": 3,
  "title": "Latest updates, August 1st",
  "content": "The whole text for the blog post goes here in this key",
  "userId": 1,
  "published": "2022-05-18T18:00:01.000Z",
  "updated": "2022-05-18T18:07:32.000Z",
  "user": {
    "id": 1,
    "displayName": "Lewis Hamilton",
    "email": "lewishamilton@gmail.com",
    "image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
  },
  "categories": [
    {
      "id": 1,
      "name": "Inovação"
    },
    {
      "id": 2,
      "name": "Escola"
    }
  ]
}

Examples of invalid requests:

  • Response for request without any of the required felds (status 400):
{
  "message": "Some required fields are missing"
}
  • Response for request to update post of unauthorized user (status 401):
  {
    "message": "Unauthorized user"
  }
DELETE /post/:id
  • Deletes the post with the specified id. Only the user that is the author of the post can delete it. Requires a valid token

Examples of invalid requests:

  • Response for request to delete post of unauthorized user (status 401):

    {
      "message": "Unauthorized user"
    }

- Response for request with invalid id (status 404):
  
  ```json
{
  "message": "Post does not exist"
}
GET /post/search?q=:searchTerm
  • Returns an array of posts matching the searchTerm in their titles and/or content. Returns an empty array if there are no matches. Requires a valid token

  • Example of response for valid entry:
// GET /post/search?q=Vamos que vamos

[
  {
    "id": 2,
    "title": "Vamos que vamos",
    "content": "Foguete não tem ré",
    "userId": 1,
    "published": "2011-08-01T19:58:00.000Z",
    "updated": "2011-08-01T19:58:51.000Z",
    "user": {
      "id": 1,
      "displayName": "Lewis Hamilton",
      "email": "lewishamilton@gmail.com",
      "image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
    },
    "categories": [
      {
        "id": 2,
        "name": "Escola"
      }
    ]
  }
]