Practical: Implement an In-Memory Server for Simplepedia

Initial Due Date: 2024-10-24 11:59PM Final Due Date: 2024-12-09 4:00PM

Github Classroom Gradescope
In this practical, you will use NextJS API routes to implement a simplified version of the Simplepedia API.
Learning Goals:
  • Implement a simple NextJS server with API routes
  • Learn how to test NextJS API routes
Submission: Successfully submitting your assignment is an ordered two-step process:
  1. Commit and push your changes to GitHub
  2. Submit your repository to the Gradescope assignment

Prerequisites

  1. Create the git repository for your practical by accepting the assignment from GitHub Classroom. This will create a new repository for you with a skeleton application already setup for you.
  2. Clone the repository to you computer with đź’» git clone (get the name of the repository from GitHub).
  3. Open up the package.json file and add your name as the author of the package.
  4. Install the module dependencies by executing đź’» npm install in the terminal.

Background

We are going to use NextJS API routes to implement a simplified version of the Simplepedia API. Recall the Simplepedia API is:

Endpoint Method Action
/api/sections GET Fetch a JSON array of all sections in sorted order
/api/articles GET Fetch the entire article collection as an array sorted by title
/api/articles?section=:section GET Fetch all articles in the corresponding section sorted by title
/api/articles POST Add a new article to the collection (the new article should be provided as the JSON-encoded request body)
/api/articles/:id GET Get article with id of :id
/api/articles/:id PUT Update the article with id of :id (entire updated article, including id should be provided as the JSON-encoded request body)

Our server will use “in memory” data storage. In other words, when the server is started, it will read in the contents of seed.json, and store it in a Map with the id as the key and the article object as the value. Review data/articles.js to see how the global object is constructed. Changes will be made to this local copy of the data providing the appearance of persistence, but if the server is restarted, the server will return to the original copy of the data. For proper persistence, we will require some form of database, which we will discuss soon.

We will implement these routes with next-connect so we can minimize the boilerplate needed for matching methods, etc. Recall from class that we construct the endpoint from a chain of .METHOD functions, e.g., .get for GET, that takes a function with the req request and res response objects. The first contains all the information sent in the request (and added by any middleware) and the second is used to construct the response. Note that invoking the response methods, e.g., res.end(...) does not end the function, you need to explicit terminate the function if you want it to end early.

Serving sections

In src/pages/api/sections.js we will implement the /api/sections endpoint. Where indicated by the “TODO” implement code to generate a sorted de-duplicated array of sections (i.e., first letter of the article upper-cased). You can then send this array (e.g., sections) as JSON to the requester with

res.status(200).json(sections);

Hopefully generating sections from the article collection is a familiar task from your programming assignment. Note that articles is a Map, not an array. The Map provides forEach method that can be used to iterate through all the values in the Map, e.g., articles.forEach((article) => ...).

You can test your API implementation using fetch via the browser’s console. Start the development server with 💻 npm run dev then open the application and the browser’s developer tools. In the console, paste and execute a test fetch command. Hopefully see the expected sections array!

fetch("/api/sections")
   .then(resp => resp.json())
   .then(data => { console.log(data); });

Another option to test your API is to use an external application like postman. This gives you a nice UI to test APIs. If you choose to use postman, you’ll need to download the “postman desktop agent” to be able to test APIs running locally!

In the test above, we are implementing minimal error handling. We will try to parse any response as JSON, including any error responses. To get more information about any errors, click over to the Network tab in the browser’s developer tools and click on the failing request. You want to view the full Response to see the complete error message.

Serving a single and multiple articles

In src/pages/api/articles/index.js we will implement the GET /api/articles endpoint. Where indicated by the TODO implement code to return an array of articles, potentially filtered by the section query parameter (i.e., by req.query.section), You can obtain the array of articles with Array.from(articles.values()). As above, you should send a response status of 200.

In src/pages/api/articles/[id].js we will implement the GET /api/articles/:id endpoint. Where indicated by the TODO implement code to return a single article with the corresponding id (i.e., with req.query.id). If the id is valid respond with a status code of 200 and the article as JSON. If the id is not valid (i.e., not present in the articles Map) respond with 400 error status code and corresponding message, e.g.,

res.status(400).end("Invalid article");

Recall that the article ids, and thus the Map keys are integers while the URL parameters, e.g., req.query.id are strings. Once you have implemented these endpoints, test them in the browser.

Review the other endpoint implementations

The skeleton includes code for the other endpoints (POST and PUT). Review those implementations for other examples of how to implement an in-memory server.

Unit testing

So far we have only performed ad-hoc testing the API with the browser. The skeleton also includes unit tests. Run the tests by executing 💻 npm test. The tests (in src/__tests__/api.test.js) are implemented with next-test-api-route-handler a library that makes it easier to unit test API routes (note we are using version 4, which is not compatible with version 3). You will see our familiar testing pattern, in which you define a test suite, use the beforeEach “setup” function to create a consistent test environment (making the tests “Independent” and “Repeatable”), then execute a set of tests. Each of those tests executes a special fetch function, i.e. makes a HTTP request to the API, then makes a set of assertions about the response.

Finishing up

Commit any changes you may have made and then push your changes to GitHub. You should then submit your repository to Gradescope.

Grading

Required functionality:

  • Complete the Simplepedia API
  • Pass all tests
  • Pass all ESLint checks

Recall that the Practical exercises are evaluated as “Satisfactory/Not yet satisfactory”. Your submission will need to implement all of the required functionality (i.e., pass all the tests) to be Satisfactory (2 points).


© Laura Biester, Michael Linderman, and Christopher Andrews 2019-2024.