CS 312 - Practical Seven
November 04, 2025 at 11:59 PM
Goals
- Implement a simple NextJS server with API routes
- Learn how to test NextJS API routes
Prerequisites
Create the git repository for your practical by accepting the assignment from GitHub Classroom. This will create a new repository for you with the Next/React infrastructure in place.
Clone the repository to you computer with
git clone(get the name of the repository from GitHub).Open up the
package.jsonfile and add your name as the author of the package.Install the module dependencies by typing
pnpm installin the shell in the terminal in the root directory of your package (the directory that contains the package.json file).Get some practice with our new workflow and start by making a feature branch in your repository.
Make sure that you have completed the Getting Started steps and have Docker Desktop installed.
Background
We are going to use NextJS API routes to implement a simplified version of the Simplepedia API. Our Simplepedia API wil look like this:
| 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.ts 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 would connect this to Supabase or some other database.
Recall that the files in the pages/api directory implement the server endpoints, i.e., NextJS executes this code on the server, not on the client (in the browser). NextJS routes requests to /api/... to the files in pages/api using its mapping rules (e.g., /api/articles/42) is mapped to api/articles/[id].ts. These files don’t contain React components, instead the export handler functions that expect request and response objects as arguments and construct and appropriate response (typically JSON-encoded data).
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.ts 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 pnpm run dev then open the application and your browser’s developer tools. In the console, paste and execute a test fetch command. Hopefully you see the expected sections array!
fetch("/api/sections")
.then(resp => resp.json())
.then(data => { console.log(data); });In the code 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.ts 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].ts 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 pnpm test. The tests (in src/__tests__/api.test.ts) 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 “arrange” a consistent test environment (making the tests “Independent” and “Repeatable”), then execute a set of tests. Each of those tests “acts” by executing a special fetch function, i.e. makes a HTTP request to the API, then makes a set of assertions about the response.
Finishing Up
- Make sure the tests are passing (with
pnpm test), there are no linting errors (withpnpm run lint), and no type errors (withpnpm run typecheck) - Add and commit your changes and push those commit(s) to GitHub.
- Submit your repository to Gradescope
Requirements
- Complete the Simplepedia API
- Pass all tests
- Pass all Biome 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).