Skip to content

ExcViral/movie-rentals-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

77 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

[toc]

Table of Contents

movie-rentals-app

This is an imaginary service for renting out movies

API Reference

[1] Users & Authentication

[1.1] Registering a new user

Creates a new user and returns a JSON object containing email, id and username of newly created user. It also returns a Json web token as a header under key 'x-auth-token'.

  • URL

    /api/users

  • Method:

    POST

  • Data Params

    Required Payload:

    {
    	"username": "viral",
    	"email": "[email protected]",
    	"password": "1234"
    }

    Payload Restrictions:

    • Payload Cannot be empty
    • username field is compulsory, it should be a string, minimum 3 chars, maximum 255 chars. It should be unique.
    • emailfield is compulsory, it should be a valid email address. It should be unique.
    • password should be a string, minimum 3 characters.
  • Success Response:

    • Code: 200 Content:

      {
          "_id": "603e0691c7a0563b56871ff9",
          "username": "excviral",
          "email": "[email protected]"
      }

      Headers: key: 'x-auth-token' value: [json web token]

  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 400 BAD REQUEST Content: Username Taken

    OR

    • Code: 400 BAD REQUEST Content: email Taken

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[1.2] Logging in a user

Authenticates a user with valid username and password and returns a Json Web Token.

  • URL

    /api/login

  • Method:

    POST

  • Data Params

    Required Payload:

    {
    	"username":"viral",
    	"password":"1234"
    }

    Payload Restrictions:

    • Payload Cannot be empty
    • username field is compulsory, it should be a string, minimum 3 chars, maximum 255 chars.
    • password should be a string, minimum 3 characters.
  • Success Response:

    • Code: 200 Content: login successful

      Headers: key: 'x-auth-token' value: [json web token]

  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 400 BAD REQUEST Content: Invalid Username or Password

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[1.3] Get a User's Profile

Returns a JSON Object containing the details of currently logged in User.

  • URL

    api/users/me

  • Method:

    GET

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Success Response:

    • Code: 200 Content:

      {
          "isAdmin": false,
          "_id": "6015285312e6795da5db21a4",
          "username": "viral",
          "email": "[email protected]",
          "__v": 0
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: We cannot find the requested user.

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[1.4] Get all Users' Details

Returns an array of JSON Objects containing the details of all Users of the application.

  • URL

    api/users/all

  • Method:

    GET

  • Authentication: Sign In Required

  • Authorisation: Admin Only

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Success Response:

    • Code: 200 Content:

      [
          {
              "isAdmin": true,
              "_id": "601526529d719b5bfe793b66",
              "username": "excviral",
              "email": "[email protected]",
              "__v": 0
          },
          {
              "isAdmin": false,
              "_id": "6015285312e6795da5db21a4",
              "username": "viral",
              "email": "[email protected]",
              "__v": 0
          },
          ...UserObjects
      ]
  • Error Response:

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[2] Genres

[2.1] Get All Genres

Returns an array of JSON objects containing all available Movie Genres.

  • URL

    /api/genres

  • Method:

    GET

  • Authentication: None

  • Authorisation: None

  • Headers: None

  • Success Response:

    • Code: 200 Content:

      [
          {
              "_id": "6006e6fde82ab570192c2232",
              "name": "Action"
          },
          {
              "_id": "6007a9600e5de71cd196c422",
              "name": "Adventure"
          },
          ...GenreObjects
      ]
  • Error Response:

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[2.2] Get a Single Genre

Returns a JSON Object containing the requested Genre.

  • URL

    /api/genres/:id

  • Method:

    GET

  • Authentication: None

  • Authorisation: None

  • Headers: None

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Success Response:

    • Code: 200 Content:

      {
          "_id": "6006e6fde82ab570192c2232",
          "name": "Action"
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: Requested Genre Doesnot Exist

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[2.3] Create a New Genre

Creates a new genre and returns a JSON object containing newly created Genre.

  • URL

    /api/genres

  • Method:

    POST

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Data Params

    Required Payload:

    {"name":"Genre Name"}

    Payload Restrictions:

    • Payload cannot be empty
    • Name field is compulsory, it should be a string
    • Minimum Length of Genre Name: 3 Characters
  • Success Response:

    • Code: 200 Content:

      {
          "_id": "603dde13392fd72f95bfbf70",
          "name": "Thriller",
      }
  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 400 BAD REQUEST Content: This Genre already exists

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[2.4] Update a Genre

Edits the requested genre and returns a JSON object of the updated Genre.

  • URL

    /api/genres/:id

  • Method:

    PUT

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Data Params

    Required Payload:

    {"name":"Genre Name"}

    Payload Restrictions:

    • Payload cannot be empty
    • Name field is compulsory, it should be a string
    • Minimum Length of Genre Name: 3 Characters
  • Success Response:

    • Code: 200 Content:

      {
          "_id": "601bed72e1cd8456826f665f",
          "name": "Crime",
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: Requested Genre Doesnot Exist

    OR

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 400 BAD REQUEST Content: This Genre already exists

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[2.5] Delete a Genre

Deletes the Genre and returns a JSON object containing the deleted Genre.

  • URL

    /api/genres/:id

  • Method

    DELETE

  • Authentication: Sign In Required

  • Authorisation: Admin Only

  • Headers:

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Success Response:

    • Code: 200 Content:

      {
          "_id": "601bed72e1cd8456826f665f",
          "name": "Crime",
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: Requested Genre Doesnot Exist

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[3] Customers

[3.1] Create a new Customer

Creates a customer and returns a JSON object containing all the properties of newly created customer object.

  • URL

    /api/customers

  • Method:

    POST

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Data Params

    Required Payload:

    {
    	"name": "bobba fett",
    	"phoneNumber": "8765846952",
    	"isGold": true
    }

    Payload Restrictions:

    • Payload cannot be empty
    • name field is required, it should be a string, minimum length 3 characters
    • phoneNumber field is required, it should be a string of 10 characters long
    • isGold is not compulsory. If not specified, default value is false.
  • Success Response:

    • Code: 200 Content:

      {
          "isGold": true,
          "_id": "603defb7c7a0563b56871fdd",
          "name": "Bobba Fett",
          "phoneNumber": "8765846952",
          "__v": 0
      }
  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[3.2] Get all Customers

Returns an array of JSON objects containing all available Customers (Sorted by Name).

  • URL

    /api/customers

  • Method:

    GET

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Success Response:

    • Code: 200 Content:

      [
          {
              "isGold": true,
              "_id": "6007e6323ed4dd4ca07aab60",
              "name": "Banrang Bhaijan",
              "phoneNumber": "9400768200",
              "__v": 0
          },
          {
              "isGold": true,
              "_id": "601cd6e6d1d9e62a9ec7ff0b",
              "name": "Bobba Fett",
              "phoneNumber": "8765846952",
              "__v": 0
          },
          ...CustomerObjects
      ]
  • Error Response:

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[3.3] Get a single Customer

Returns a JSON Object containing the data of requested customer.

  • URL

    /api/customers/:id

  • Method:

    GET

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Success Response:

    • Code: 200 Content:

      {
          "isGold": true,
          "_id": "603defb7c7a0563b56871fdd",
          "name": "Bobba Fett",
          "phoneNumber": "8765846952",
          "__v": 0
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: Sorry, we are unable to find the requested customer in our records ...

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[3.4] Update a Customer

Updates and returns a JSON object containing the updated Customer.

  • URL

    /api/customers/:id

  • Method:

    PUT

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Data Params

    Required Payload:

    {
    	"name": "shantilal khurana",
    	"phoneNumber": "9999999999",
    	"isGold": false
    }

    Payload Restrictions:

    • Payload cannot be empty
    • name field is required, it should be a string, minimum length 3 characters
    • phoneNumber field is required, it should be a string of 10 characters long
    • isGold is not compulsory. If not specified, default value is false.
  • Success Response:

    • Code: 200 Content:

      {
          "isGold": false,
          "_id": "6007e594d1fd424c5a024007",
          "name": "Shantilal Khurana",
          "phoneNumber": "9999999999",
          "__v": 0
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: 'Sorry, we are unable to find the requested customer in our records ...'

    OR

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[3.5] Delete a Customer

Deletes the Customer object and returns a JSON object containing the deleted Customer.

  • URL

    /api/customers/:id

  • Method:

    DELETE

  • Authentication: Sign In Required

  • Authorisation: Admin Only

  • Headers:

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Success Response:

    • Code: 200 Content:

      {
          "isGold": false,
          "_id": "6007e594d1fd424c5a024007",
          "name": "Shantilal Khurana",
          "phoneNumber": "9999999999",
          "__v": 0
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: 'Sorry, we are unable to find the requested customer in our records ...'

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[4] Movies

[4.1] Create a new Movie

Creates a movie and returns a JSON object containing all the properties of newly created movie object.

  • URL

    /api/movies

  • Method:

    POST

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Data Params

    Required Payload:

    {
    	"title": "Martian",
    	"genreId": "6007ab01d3b6651e3ece3b2a",
    	"numberInStock": 5
    	"dailyRentalRate": 1
    }

    Payload Restrictions:

    • Payload cannot be empty
    • title field is required, it should be a string, minimum length 3 characters.
    • genreId field is required, it should be a string containing a valid ObjectId.
    • numberInStock is a number. It is not compulsory. If not specified, default value is automatically set to 0. The upper limit is set to 255. The lower limit is set to 0.
    • dailyRentalRate is a number. It is not compulsory. If not specified, default value is automatically set to 0. The upper limit is set to 255. The lower limit is set to 0.
  • Success Response:

    • Code: 200 Content:

      {
          "numberInStock": 5,
          "dailyRentalRate": 1,
          "_id": "603dff7ec7a0563b56871fe6",
          "title": "Martian",
          "genre": {
              "_id": "6007ab01d3b6651e3ece3b2a",
              "name": "Science Fiction"
          },
          "__v": 0
      }
  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 404 NOT FOUND Content: 'Invalid Genre, The selected genre does not exist in database ...'

      This will be sent when invalid GenreId is passed by user.

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[4.2] Get all Movies

Returns an array containing JSON objects containing data of all the available movies.

  • URL

    /api/movies

  • Method:

    GET

  • Authentication: None

  • Authorisation: None

  • Headers: None

  • Success Response:

    • Code: 200 Content:

      [
          {
              "numberInStock": 4,
              "dailyRentalRate": 0,
              "_id": "600b013f103a640466fcad3d",
              "title": "Star Trek 2009",
              "genre": {
                  "_id": "6007ab01d3b6651e3ece3b2a",
                  "name": "Science Fiction"
              },
              "__v": 0
          },
          {
              "numberInStock": 9,
              "dailyRentalRate": 1,
              "_id": "600b0242103a640466fcad3f",
              "title": "Inception",
              "genre": {
                  "_id": "6007ab01d3b6651e3ece3b2a",
                  "name": "Science Fiction"
              },
              "__v": 0
          },
          ...MovieObjects
      ]
  • Error Response:

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[4.3] Get a single Movie

Returns a JSON object containing data of the requested movie.

  • URL

    /api/movies/:id

  • Method:

    GET

  • Authentication: None

  • Authorisation: None

  • Headers: None

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Success Response:

    • Code: 200 Content:

      {
          "numberInStock": 10,
          "dailyRentalRate": 1,
          "_id": "600b0242103a640466fcad3f",
          "title": "Inception",
          "genre": {
              "_id": "6007ab01d3b6651e3ece3b2a",
              "name": "Science Fiction"
          },
          "__v": 0
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: Sorry, we are unable to find the movie you have requested!

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[4.4] Update a Movie

Updates a movie object and then returns it in a JSON object.

  • URL

    /api/movies/:id

  • Method:

    PUT

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Data Params

    Required Payload:

    {
    	"title": "Inception",
    	"genreId": "6007ab01d3b6651e3ece3b2a",
    	"numberInStock": 10,
    	"dailyRentalRate": 1
    }

    Payload Restrictions:

    • Payload cannot be empty
    • title field is required, it should be a string, minimum length 3 characters.
    • genreId field is required, it should be a string containing a valid ObjectId.
    • numberInStock is a number. It is not compulsory. If not specified, default value is automatically set to 0. The upper limit is set to 255. The lower limit is set to 0.
    • dailyRentalRate is a number. It is not compulsory. If not specified, default value is automatically set to 0. The upper limit is set to 255. The lower limit is set to 0.
  • Success Response:

    • Code: 200 Content:

      {
          "numberInStock": 10,
          "dailyRentalRate": 1,
          "_id": "600b0242103a640466fcad3f",
          "title": "Inception",
          "genre": {
              "_id": "6007ab01d3b6651e3ece3b2a",
              "name": "Science Fiction"
          },
          "__v": 0
      }
  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 404 NOT FOUND Content: 'Invalid Genre, The selected genre does not exist in database ...'

      This will be sent when invalid GenreId is passed by user.

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[4.5] Delete a Movie

Deletes the movie object and returns it in a JSON object.

  • URL

    /api/movies/:id

  • Method:

    DELETE

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • URL Params

    Required:

    id=[MongoDB ObjectId]

  • Success Response:

    • Code: 200 Content:

      {
          "numberInStock": 4,
          "dailyRentalRate": 0,
          "_id": "600b013f103a640466fcad3d",
          "title": "Star Trek 2009",
          "genre": {
              "_id": "6007ab01d3b6651e3ece3b2a",
              "name": "Science Fiction"
          },
          "__v": 0
      }
  • Error Response:

    • Code: 404 NOT FOUND Content: 'Sorry, we are unable to find the movie you have requested to delete!'

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[5] Rentals

[5.1] Create a Rental

Creates a rental object and then returns the newly created rental object in a JSON object.

  • URL

    /api/rentals

  • Method:

    POST

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Data Params

    Required Payload:

    {
    	"customerId": "6007e6323ed4dd4ca07aab60",
    	"movieId": "600b0242103a640466fcad3f"
    }

    Payload Restrictions:

    • Payload cannot be empty
    • customerId field is required, it should be a string containing a valid ObjectId.
    • movieId field is required, it should be a string containing a valid ObjectId.
  • Success Response:

    • Code: 200 Content:

      {
          "rentalFee": 5,
          "_id": "603e0227c7a0563b56871fe9",
          "customer": {
              "_id": "6007e6323ed4dd4ca07aab60",
              "name": "Banrang Bhaijan",
              "phoneNumber": "9400768200",
              "isGold": true
          },
          "movie": {
              "_id": "600b0242103a640466fcad3f",
              "title": "Inception",
              "dailyRentalRate": 1
          },
          "dateOut": "2021-03-02T09:15:19.472Z"
      }
  • Error Response:

    • Code: 400 BAD REQUEST Content: Joi Validation Error (Custom Message)

      This will be sent when payload restrictions are not met.

    OR

    • Code: 400 BAD REQUEST Content: Sorry, the selected movie is out of stock

      This will be sent when an out of stock movie is requested.

    OR

    • Code: 404 NOT FOUND Content: 'Sorry, we are unable to find the movie you have requested!'

      This will be sent when invalid movieId is passed by user.

    OR

    • Code: 404 NOT FOUND Content: 'Sorry, we are unable to find the requested customer in our records ...'

      This will be sent when invalid customerId is passed by user.

    OR

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[5.2] Get all Rentals

Returns an array of JSON objects containing data of all the rental objects.

  • URL

    /api/rentals

  • Method:

    GET

  • Authentication: Sign In Required

  • Authorisation: None

  • Headers

    Required:

    key: x-auth-token value: [json web token]

  • Success Response:

    • Code: 200 Content:

      [
          {
              "rentalFee": 5,
              "_id": "603e0227c7a0563b56871fe9",
              "customer": {
                  "_id": "6007e6323ed4dd4ca07aab60",
                  "name": "Banrang Bhaijan",
                  "phoneNumber": "9400768200",
                  "isGold": true
              },
              "movie": {
                  "_id": "600b0242103a640466fcad3f",
                  "title": "Inception",
                  "dailyRentalRate": 1
              },
              "dateOut": "2021-03-02T09:15:19.472Z"
          },
          {
              "rentalFee": 5,
              "_id": "601cd9b5eeaf052c748b448d",
              "customer": {
                  "_id": "6007e6323ed4dd4ca07aab60",
                  "name": "Banrang Bhaijan",
                  "phoneNumber": "9400768200",
                  "isGold": true
              },
              "movie": {
                  "_id": "600b0242103a640466fcad3f",
                  "title": "Inception",
                  "dailyRentalRate": 1
              },
              "dateOut": "2021-02-05T05:37:57.545Z"
          },
          ...RentalObjects
      ]
  • Error Response:

    • Code: 500 INTERNAL SERVER ERROR Content: Oh no! Something bad happened. Please come back later when we fixed that problem. Thanks.
  • Sample Call:

    <Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable.>

    [TO BE ADDED LATER]

  • Notes:

    <This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here.>

    [TO BE ADDED LATER]

[Project Log]

This project is a part of Mosh Hamedani's Udemy course on node.js

Genres API

Create a service for managing the list of movie genres.

Route: http://movie-rentals-app.com/api/genres

Rubric

  • EndPoint for getting the list of all Genres
  • EndPoint for getting a single Genre
  • EndPoint for creating a new Genre
  • EndPoint for updating existing Genres
  • EndPoint for deleting existing Genres

Restructure the movie-rentals-app

So far everything we have built will be in one file. It is not a good way to build an app. So, let's restructure it in a clean and manageable way.

Rubric

  • Create a new folder named routes to store all our routes for various endpoints.
  • We have already implemented the generes endpoint, move the code into a new file named genres.js and save it in routes/ folder.
  • Refactor all the code in index.js as well as in genres.js

Add Persistence to Genres API

So far we have been storing our data in arrays, and all the data was wiped out once the code restarted. Now that we know how to work with MongoDB database let's add persistence to our project.

Rubric

  • Get rid of all the arrays
  • Create a new database for storing all the data for our movie-rentals-app
  • Refactor the code to use our new database while retaining all existing functionalities

Customers API

Create a service to manage the details of customers

Route: http://movie-rentals-app.com/api/customers

Rubric

  • Create a new collection named customers

    • A customer should have the following properties:

      ​ name: String

      ​ phone: Numbers

      ​ isGold: Boolean

  • The Customers API should have the following functionalities

    • EndPoint for getting the list of all customers from the database
    • EndPoint for getting a single customer document using id from the database
    • EndPoint for creating a customer document in the database
    • EndPoint for updating a customer document in the database
    • EndPoint for Deleting a customer document in the database

Restructure the movie-rentals-app

To keep our applications maintainable, we should ensure that each module is responsible for only and only one thing. This is called Single Responsibility Principle

If you look at our app now, the routes/genres.js, and the route/customers.js have mongoose schemas, route handler functions, and joi validation schema+function.

This makes the files too big ... Difficult to Maintain!

Our route handler files should only be responsible to handle the routes, it should not be responsible for declaring schema and creating mongoose model. It should also not have the Joi Validator functions.

  • Move the Schema and Joi validator functions to models/genres.js and models/customers.js files.
  • Refactor routes/genres.js, and the route/customers.js

Now we have a singular responsibility for routes/genres.js, and the route/customers.js and also for models/genres.js and models/customers.js

Movies API

Create a new service to manage the catalogue of movies.

Route: http://movie-rentals-app.com/api/movies

Rubric

  • Create a new collection of movies
  • The shape of movie document should be as follows:
    • title: String
    • genre: Genre document should be embedded using Hybrid Approach
    • numberInStock: Number
    • dailyRentalRate: Number
  • Implement CRUD operations to manage movies catalogue
    • Endpoint to create a new movie
      • Receive Json object containing Title of movie, Id of genre, Number in Stock, and Daily rental rate.
      • In hybrid approach, you should take the Id of genre from client, and while creating/updating movie, you should query the db to get genre from the db, and embed the properties you want along with its original id for reference.
    • Endpoint to get all movies
    • Endpoint to get a single movie by its id
    • Endpoint to update a movie
    • Endpoint to delete a move by its id

Rentals API

  • Create a new service to manage the catalogue of movies.

    Route: http://movie-rentals-app.com/api/rentals

    Rubric
    • Create a new collection for rentals

      • The shape of the rental document should be:

        • It should have customer property
          • Donot reuse the customer schema, use Hybrid Approach for embedding
          • Take in customerId as input from client, and then we can embed whatever properties we need!
          • In this case, let's have the customer's name, phoneNumber, and isGold property.
          • Properties like isGold can be used to provide special features to customer, like additional discounts, early access, first priority ... etc
          • customer property should be required
        • It should have a movie property
          • Donot reuse the movie schema, use Hybrid Approach for embedding
          • Take in movieId as input from client, and then we can embed whatever properties we need!
          • In this case, let's have the movie's title, and dailyRentalRate properties
          • In future, we will use daily rental rate to calculate the rentalFee
          • movie property should be required
        • It should have a dateOut property of type date, and should be required. This property will track when movie was rented. This property should not be sent by client, it should be set on server.
        • It should have a dateReturned property, again of type date, and should be not be required, as it will be set on a later date. This property will track the date of returned. This property should not be sent by client, it should be set on server.
        • It should have a property called rentalFee, of number type, and cannot be less than zero. This property should not be sent by client, it should be set on server.
      • Also create a Joi validate function for rental, we will only be taking customerId and movieId from the client, so this function should validate whether the objectIds sent by client are valid objectIds.

    • The API should have the following endpoints

      • Endpoint to get the list of all rentals, sort by dateOut
      • Endpoint to create a rental
        • You should first create a rental document
        • Then you should decrement the number of DVDs in stock ...
        • Here we have a transaction problem. We have two separate operations. It is possible that after we save the rental document, something goes wrong (eg. server crash, connection to db drops), and we are not able to decrement the stock of DVDs. The second operation will not complete! Here we need transactions. With transaction, we can ensure that both these operations will update the state of our data in the database, or none of them will be applied. They should be atomic The both should complete, or both should roll back.

Adding Authentication and Authorization

Nearly all applications out there require some kind of authentication and authorization

Authentication (Users API & Login API)

The process of identifying if a person is who they claim they are.

That is verifying credentials at the time on logging in.

  • Create a new service for managing the users

    Route: http://movie-rentals-app.com/api/users

    • Create a new collection for users
      • The shape of the User document should be:
        • It should have username property. For the username property, in the Schema Type object, you must set unique property to true. This is to ensure that we don't store two documents with same username in our database.
        • It should have an email property. For the email property, in the Schema Type object, you must set unique property to true. This is to ensure that we don't store two documents with same email in our database.
        • It should have a password property. You must store only the hash of the password, not the plain-text password
        • It should have isAdmin property. This is a Boolean field, and it must be used for protecting some routes such that only admins are allowed to access them.
        • It should have its own method for generating a Json Web Token (JWT) for a User. As the payload of JWT, you should pass an object with properties _id and isAdmin [Encapsulate JWT Generation Logic in Mongoose Models]
          • Use the jsonwebtoken package to generate JWT
          • The private or secret key used to generate JWT must be read from an environment variable using the config module [Never store App Secrets in File Itself, Store them in environment variables on server]
      • The API should have the following endpoints:
        • Endpoint for registering a new user:
          • An Http post request must be sent to the route /api/users
          • HTTP Post request because we are creating a new resource, In this case, a new user.
          • The process of registering a new user should be:
            • Client must send a json object in HTTP Post request containing properties username, email, and password. We can access this object from req.body
            • First validate the req.body using Joi, if invalid object, return 400 Bad request error and terminate request
            • Then check if the username or email already exist in our database, if they do, return 400 Bad request, and ask client to send new username or email and terminate request
            • If everything is okay, hash the password using bcrypt
              • first generate a random salt using bcrypt (async)
              • then hash the plain-text password using bcrypt (async), then overwrite the plain-text password in the User object with its hash.
            • Then save the User to our database
            • Then generate a new JWT for this user using the method we defined in the User Schema
            • Finally complete the registration process by sending the JWT to the client, along with his registered email and username (don't send back hashed password)
              • The JWT must be sent in an http header under custom name like x-auth-token, so that the front-end client software can save this JWT for further authorisation in order to be able to access protected routes.
        • Endpoint for accessing a user's profile:
          • A logged in user should be able to access his profile using the route /api/users/me
          • First check if the user is authorized to access the route using the authorization middleware. (if he sends a valid JWT in his request's header, he should be authorized.)
          • Decode the payload sent by client in the JWT, read the _id of user from decoded payload, and fetch details from database and send it to the client (exclude password field).
        • Endpoint for accessing all user's details [PROTECTED ROUTE: ADMIN ONLY]:
          • Only a logged in user with admin privileges should be able to access this route: /api/users/all
          • First check if the user is authorized to access the route using the authorization middlewares. (if he sends a valid JWT in his request's header and he is an Admin, he should be authorized.)
          • Fetch all the users and send the data to the client.
  • Create a new service for allowing users to log in

    Route: http://movie-rentals-app.com/api/login

    • The API should have a single End Point to allow users to log in
      • The module for handling this logic must be named auth.js
      • An Http post request must be sent to /api/login
      • We will be using an Http Post request here, because we are creating a new login request or a login command.
      • Another reason for using Http Post request is sometimes we want to store all individual logins in our database. So it makes perfect sense to use Post request here.
      • The process of logging in a user should be:
        • Client must send in a valid Json object with properties username and password. We can access this object from req.body
        • First validate the req.body using Joi, if invalid object, return 400 Bad request error and terminate request
        • Then check if the username exists in our database, if not then terminate the request with 400 Bad request error, but send a vague message saying username or password invalid. We must never specify which one is wrong.
        • If the username exists in our database, we compare the verify if the hash of the password sent by client matches with the hash stored in our database
          • If the hash doesn't match, we terminate the request with 400 Bad request error, but send a vague message saying username or password invalid. We must never specify which one is wrong.
          • If the hash matches, we generate a new JWT for this user using the method we defined in the User Schema
        • Finally complete the login process by sending the JWT to the client, along with a message that login is successful.
          • The JWT must be sent in an http header under custom name like x-auth-token, so that the front-end client software can save this JWT for further authorisation in order to be able to access protected routes.

Authorization

The process of determining whether a given user has the permission to perform a given operation.

Not all users can have all permissions. Certain routes need to be protected, so that only authorized users can access them.

The best way to handle this is to put this authorization logic in a middleware function. Then we apply this middleware function to the route handlers that need to be protected. This middleware function sits in front of our route in the request-response pipeline, and only allows authorized users to pass through.

  • Create a middleware function to verify that a user is logged in
    • Once a user logs in, we send him a jwt, from then on, every time a user accesses any route, he is supposed to send this jwt as a part of his request headers. In this middleware, we verify the same.
    • Logic to be implemented:
      • The jwt token should be available under req.header under the name x-auth-token. Try to read this token.
        • If token is not found, terminate the request and send 401 request denied error.
        • Otherwise, decode the token using the jsonwebtoken module.
          • If the token is invalid, send 400 Bad Request error to the client with a message that the token is not valid
          • If token is valid, then extract the payload, and populate the req.user property, so that further middlewares or route handlers can make use of the payload data
        • If we were able to successfully decode the jwt that means user is authorized, and logged in. Now after populating the req.user property, we can pass control to the next middleware or route handler function by calling next()
  • Create a middleware function to verify that a user is Admin
    • Some routes are to be protected such that only users with Admin privileges can access them. We will include the properties related to roles in our jwt payload, so that we can directly authorize the Admin user without having to look up into our database to check if the user is Admin.
    • This middleware must be executed only after the first authorization middleware has been executed, i.e. the user is verified that he is logged in with a valid jwt, and the req.user property is populated
    • Logic to be implemented:
      • Since we have an isAdmin property (Boolean) in our User's document, all we have to do is check if the isAdmin property in req.user (populated by first authorization middleware) set to true (if true, user is admin)
        • If isAdmin = False, it means that the user is not an Admin, and he is not supposed to access this route, therefore terminate the request and send 403 Forbidden error with message that access denied
        • If isAdmin = True, simply pass the control to the next middleware / route handler
  • Protecting the Routes
    • Genres API
      • Public access allowed (no login required)
        • EndPoint for getting the list of all Genres
        • EndPoint for getting a single Genre
      • Only Logged In Users should be allowed to access these routes (Apply first authorization middleware auth.js)
        • EndPoint for creating a new Genre
        • EndPoint for updating existing Genres
      • Only users with Admin privileges should be allowed to access these routes (Apply auth.js and admin.js )
        • EndPoint for deleting existing Genres
    • Customers API
      • Only Logged In Users should be allowed to access these routes (Apply first authorization middleware auth.js)
        • EndPoint for getting the list of all customers from the database
        • EndPoint for getting a single customer document using id from the database
        • EndPoint for creating a customer document in the database
        • EndPoint for updating a customer document in the database
      • Only users with Admin privileges should be allowed to access these routes (Apply auth.js and admin.js )
        • EndPoint for Deleting a customer document in the database
    • Movies API
      • Public access allowed (no login required)
        • Endpoint to get all movies
      • Only Logged In Users should be allowed to access these routes (Apply first authorization middleware auth.js)
        • Endpoint to create a new movie
        • Endpoint to update a movie
        • Endpoint to delete a move by its id
    • Rentals API
      • Only Logged In Users should be allowed to access these routes (Apply first authorization middleware auth.js)
        • Endpoint to get the list of all rentals
        • Endpoint to create a rental
    • Users API
      • Public access allowed (no login required)
        • EndPoint to create a new user
      • Only Logged In Users should be allowed to access these routes (Apply first authorization middleware auth.js)
        • EndPoint to access user's personal profile
      • Only users with Admin privileges should be allowed to access these routes (Apply auth.js and admin.js )
        • EndPoint to access all users general profiles

Refactor the Route Handlers: Express Error Middleware

In our current implementation, every route handler has a try-catch block. This is repetitive code in every catch block which we should move to the special Express Error Middleware Function.

Implement the Express Error Middleware

  • Make a new file in the middleware/ folder named error.js
  • This file error.js should export a middleware function with four arguments: error object, request object, response object and the next method. It should have the following logic:
    • TODO: The logic to log errors
    • Terminating the request-response cycle by sending a 500 Internal Server Error to the client with a friendly message.

Refactor the code to use Express Error Middleware

  • Replace the code in every route handler with a call to next() and pass the exception object to this method.
  • Import this error middleware function in the index.js file and register it after all the route handler middlewares.

Refactor the Route Handlers: Remove Try-Catch Blocks

Right now in every Route Handler function we have a try-catch block. This distracts us from the actual logic inside these Route Handlers, and only adds extra noise to our code. Also, a part of the code is still being repeated, every catch block is still the same in every route handler, the only code that differs is the code inside the try block.

Ideally, we should move this high level code somewhere else in a single function. We need to have a template like function.

In order to do this, we have to satisfy two constraints:

  • The app.get() method requires a function reference which can accept the parameters: request object, response object and the next method.
  • We need to pass the route handler function as an argument to our template asyncMiddleware function, so we compulsorily need to call this template function and pass our route handler function as an argument.

In order to satisfy both these constraints, our asyncMiddleware function should behave like a factory function, i.e. whenever we call it and pass a route handler function, it should wrap this route handler function inside a try-catch block and return the whole thing as a new route handler function.

Essentially this is what will happen:

  • We will call this asyncMiddleware function inside app.get() or router.get() or other http methods and pass our route handler function (without any try-catch block).
  • Since express http methods require a function reference and not a function call, our asyncMiddleware function will return a function reference to a new route handler (wrapped in a try-catch block), which then express can call it during the runtime and pass req, res and next arguments.

Implement the asyncMiddleware factory function

  • asyncMiddleware should take in a route handler as an argument. This route handler will be an async function.
  • asyncMiddleware should return an anonymous async function definition
    • This anonymous async function should accept three parameters: req a request object, res a response object, and next a method referencing to the next middleware. Express will call this anonymous function at runtime, and pass these three parameters at runtime.
    • This anonymous async function should only contain a try-catch block.
      • In the try block, we simply call and await for the route handler function that was passed as an argument to the asyncMiddleware function. We also pass the the req and res as arguments to this route handler function call.
      • In the catch block, we simply call the next() method and pass our exception object to it as next(exception). This tells express that in case of an exception, it must pass control to the special error middleware that we implemented in error.js and registered in index.js after all route handler middlewares.

Refactor the code to use asyncMiddleware and remove try-catch

  • In every module inside routes/ folder where where have route handlers:
    • Import the asyncMiddleware function using require statement
    • In each route handler:
      • Extract the code inside the try block to outside the try block, then remove the try-catch block.
      • Wrap the route handler function inside the asyncMiddleware function call.

Implement logic to log the errors in a log file

  • Install package winston
  • In the Express Error Middleware, implement logic to store all errors in a logFile.log file.
  • In the index.js implement logic to handle all unexpected exceptions, also log them.
  • In the index.js implement logic to handle all unexpected promise rejections, also log them.

Refactor index.js

In index.js, currently we have a lack of separation of concerns. The are too many require statements in our index.js. Then we have code for handling and logging errors. Then we have something completely different, like configuration. Then we have logic for connecting to the database. Then we set up routes, middlewares etc... These are all different concerns, and mixing them together in a single module is not at all good.

Our index.js module should only orchestrate these different concerns. So, the details of this orchestration should be moved into other modules. For example, setting up routes and connecting to database are separate concerns, so they should be put in their own modules.

We can create a new folder named startup/ where we can create new modules for each of these concerns and store details(code) there.

Extract all routes to a separate module

  • Create a new module named startup/routes.js
  • This module should export a function, which should take in app object of expressjs as an argument.
    • We do this because we want to have a single app object in our entire application. We don't want to load express and create a new app object for each module. So, we pass a reference to this module while requiring it by calling the exported function and passing the app object.
  • Move all the code that require our route handler modules to this file
  • Move all the route handler middlewares to this file
  • Move all the other dependencies that are required
  • Finally import this module in index.js and then call it to set up the routes

Extract and Refactor the database logic to a separate module

  • Create a new module named startup/db.js

  • This module should export a function, which when called will setup and connect our application to the database

  • Move the mongoose.connect() logic from index.js to startup/db.js along with all the dependencies, and remove them from index.js

  • Refactor the code:

    • Currently our db initialisation looks like this:

      mongoose
      	.connect(connStr, { useNewUrlParser: true, useUnifiedTopology: true })
      	.then(() => {
      		console.log('Connected to the database');
      	})
      	.catch(err => {
      		console.log('Could not connect to mongoDB', err);
      	});

      Here, in the .then() block, we are simply printing to the console, we must instead use winston to log an informational message to our logs.

      Also, we have called .catch(), but we are not really doing anything here, in reality, if there is any error, we must log in using winston. So, what we should do is remove the .catch() block completely, so that it will become an unhandled promise rejection upon failure. This will then be catched by our global unhandled promise rejection handler logic automatically and logged into our logs and our process will be terminated.

Extract the logging logic to a separate module

  • Create a new module named startup/logging.js
  • This module must export a function, which when called should setup the logic to handle and log unhandled exceptions and promise rejections

Extract the config logic to a separate module

  • Create a new module named startup/config.js
  • This module should export a function, which when called should check if essential config settings like if environment variables are set and so on...
  • Extract all the logic for checking essential config settings like if environment variables are set and so on... and move it to this module
  • Also, instead of manually logging an error here, throw an exception with Error object so that our global unhandled exception handler catches it. Don't simply throw a string error, as Error object will contain a stack trace, normal string won't contain that.

References

API Documentation Style Guide

NodeJs Course

Releases

No releases published

Packages

No packages published