Simple REST API

The idea of this project is to create a small REST API. Generally, the APIs you create should manipulate data from a database. In this exercice, you will fetch the data from a file and manipulate it in memory. This way you will not be distracted by the details of querying MongoDB.

Instructions

Create the following endpoints:

  • GET /api/authors: List all authors
  • POST /api/authors: Add a new author
  • GET /api/authors/:authorId: Send the author of id authorId
  • PUT /api/authors/:authorId: Upsert the author of id authorId
  • GET /api/authors/:authorId/books: List all books of the author of id authorId
  • GET /api/books: List all books
  • POST /api/books: Add a new book
  • GET /api/books/:bookId: Send the book of id bookId
  • DELETE /api/books/:bookId: Remove the book of id bookId
  • PATCH /api/books/:bookId: Patch the book of id bookId
  • BonusGET /api/authors?sort=name: List all authors sorted by their name

We want the actions of all the endpoints to be defined with the appropriate router. Then, when using the routers, prefix them appropriately.

For every endpoint you will need to send your responses with the approriate HTTP codes. Error cases should be properly handled.

How to start?

The project contains the following files:

  • bin: The folder with your executables
    • www: The node script to start the server. Do not edit this file.
  • data: The folder with the data you will load.
    • authors.js: A JSON file that stores an array of author objects. Do not edit this file.
    • books.js: A JSON file that stores an array of book objects. Do not edit this file.
  • routes: The folder with your routers
    • authors: The router for the endpoints related to authors.
    • books: The router for the endpoints related to books.
  • app.js: The node script to configure your Express application.
  • package.json
  • package-lock.json
  • .gitignore
  • README.md

The commands you will need for this project:

$ npm install # To install all the dependencies
$ npm run dev # To run the node server with Nodemon

You will need to test your server by sending requests with Postman.

Hints

Send JSON

To send data as JSON, you will need to use the method json of the response object like in the following example:

router.get("/endpoint", (req, res) => {
  res.json({ text: "Hello" });
});

Accessing the data

To access the data, you will have to import the JSON files that you need. For example, to access the array of authors from a router, you would write the following:

const authors = require("../data/authors");

You will need to modify the content of those arrays. Bear in mind that when the server restarts, all changes will be lost. In a real application, you will never do this; you would rather use a proper database.

Body Parser

You will need to use body-parser to be able to read the bodies of the requests as JSON. Use the middleware bodyParser.json() with your Express app.

Sending a specific HTTP code

By default, Express sends responses with the code 200, which means Success. If you want to send a response with another HTTP code, you can either use status or sendStatus.

For example to send an error code 404, you would write:

// To send JSON
router.get("/endpoint1", (req, res) => {
  res.status(404).json({ text: "Not found" });
});

// To send text
router.get("/endpoint2", (req, res) => {
  res.status(404).send("Not found");
});

// To not send anything
router.get("/endpoint3", (req, res) => {
  res.sendStatus(404);
});

Array methods

You will use a handful or Array methods, so keep an MDN tab open.

Generating ids

You will need to generate unique ids when creating authors and books. We required for you a function generateId to do that. It takes no arguments and returns each time a unique string.

Expectations

GET /api/authors

Returns the list of all authors:

// GET /api/authors
[
  {
    "id": 1,
    "name": "Leanne Graham",
    "website": "hildegard.org"
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "website": "anastasia.net"
  },
  // ...
  {
    "id": 10,
    "name": "Clementina DuBuque",
    "website": "ambrose.net"
  }
]

POST /api/authors

Create a new author with the data sent in the body. Send back the created author.

// POST /api/authors 
// { "name": "John Doe", "website": "doe.com" }
{
  "id": "<some unique id>",
  "name": "John Doe",
  "website": "doe.com"
}

GET /api/authors/:authorId

Send the author with the id :authorId.

// GET /api/authors/e34bd5b3-9d25-4ed8-b147-b0bd05068b4f
{
  "id": "e34bd5b3-9d25-4ed8-b147-b0bd05068b4f",
  "name": "Ervin Howell",
  "website": "anastasia.net"
}

PUT /api/authors/:authorId

If the author of id :authorId exists, replace it with the provided data. If it doesn't, create an author with the provided data. In all cases, send the author at the end.

If there is an id in the request body, make sure that it matches the one in the URL. Otherwise, send an approriate error.

// PUT /api/authors/e34bd5b3-9d25-4ed8-b147-b0bd05068b4f
// { "name": "John Doe", "website": "doe.com" } 
{
  "id": "e34bd5b3-9d25-4ed8-b147-b0bd05068b4f",
  "name": "John Doe", // It's not Ervin Howell anymore
  "website": "doe.com"
}
// PUT /api/authors/1337
// { "name": "John Doe", "website": "doe.com" } 
{
  "id": "1337",
  "name": "John Doe",
  "website": "doe.com"
}

GET /api/authors/:authorId/books

Send all books of the author that has the id :authorId.

// GET /api/authors/e34bd5b3-9d25-4ed8-b147-b0bd05068b4f
[
  {
    "id": "<some unique id>",
    "author": "e34bd5b3-9d25-4ed8-b147-b0bd05068b4f",
    "title": "et ea vero quia laudantium autem",
    "description":
      "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi"
  },
  {
    "id": "<some unique id>",
    "author": "e34bd5b3-9d25-4ed8-b147-b0bd05068b4f",
    "title": "in quibusdam tempore odit est dolorem",
    "description":
      "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio"
  },
  // ...
]

GET /api/books

Send all books.

[
  {
    "id": "<some unique id>",
    "author": "<some unique id>",
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "description": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "id": "<some unique id>",
    "author": "<some unique id>",
    "title": "qui est esse",
    "description": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  // ...
]

GET /api/books/:bookId

Send the book of id bookId.

// GET /api/books/88fbd04f-4214-4fa4-9f78-8f4d16e8437a
// {
//   "id": "88fbd04f-4214-4fa4-9f78-8f4d16e8437a",
//   "author": "<some unique id>",
//   "title":
//     "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
//   "description":
//     "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
// }

POST /api/books

Create a new book with the data sent in the body. Send back the created book.

// POST /api/authors 
// {
//   "author": "e34bd5b3-9d25-4ed8-b147-b0bd05068b4f",
//   "title": "The Adventures of the Ironhackers",
//   "description": "Nine weeks of sweat and blood from a cohort of passionate developpers ready to conquer the world."
// }

{
  "id": "<some unique id>",
  "author": "e34bd5b3-9d25-4ed8-b147-b0bd05068b4f",
  "title": "The Adventures of the Ironhackers",
  "description": "Nine weeks of sweat and blood from a cohort of passionate developpers ready to conquer the world."
}

DELETE /api/books/:bookId

Remove the book of id :bookId. Do not send anything (you still need to send the right HTTP code).

PATCH /api/books/:bookId

Patch the book of id bookId. You should replace the fields given in the body (but not touch the others).

If there is an id in the request body, make sure that it matches the one in the URL. Otherwise, send an approriate error.

// PATCH /api/books/88fbd04f-4214-4fa4-9f78-8f4d16e8437a
// { description: "A thrilling description for an incomprensible title" }
{
  "id": "88fbd04f-4214-4fa4-9f78-8f4d16e8437a",
  "author": "38502013-9c3d-443c-b53c-0fbe0e5a4271",
  "title":
    "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "description":
    "A thrilling description for an incomprehensible title"
},

GET /api/authors?sort=name

List all authors sorted by name. You can then change name by an other field and sort accordingly.