Initial Due Date: 2025-04-03 9:45AM
Final Due Date: 2025-04-17 4:15PM
đź’» git clone
(get the name of the repository from GitHub).đź’» pnpm install
in the terminal.Changes in Node 22 are triggering deprecation warnings for the punycode
module. You can ignore this warning. We have tried to suppress this message but have only been partly successful doing so.
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.
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].js. 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.
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 💻 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 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.
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.
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.
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.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 “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.
Commit any changes you may have made and then push your changes to GitHub. You should then submit your repository to Gradescope as described here.
If you attempt to push your changes to GitHub and get an error like the following, your repository on GitHub has commits that you local clone does not (as a result of how GitHub classroom seems to work, see details below). You will need to pull the changes you don’t have. In this case, you can safely and effectively use rebase to do so, i.e., execute 💻 git pull --rebase origin main
in the terminal. Then attempt to push again.
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:csci312a-s25/assignment01...'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
It appears that there can sometimes be a delay between when GitHub classroom creates your repository and when it finishes adding its automatic commits (for the due date, etc.). Thus is it possible (easy) to clone the repository before that process has completed and end up in a situation where the GitHub repository has commits your local clone does not.
Required functionality:
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).