Exercise: The NUSMods API
Last updated
Last updated
I'm sure you know what NUSMods is, its a website that has details about every course offered at NUS, as well as a degree planner, a timetable builder and a map of the campus. It also has its own API (documentation here) which we are going to use.
The task is to build a simple page that will have a form. The form will allow a user to type in a course code, and once submitted the form will submit a fetch request to the NUSMods API, request the course data, and then display it to the user. If the course code they enter is invalid, then show them an error message.
It is recommended to try the first steps on your own, until you get to the fetch request, to practice writing HTML and JS.
The API we will use is the one to get the course info given a course code and year. The general URL format is below:
Example:
Copy paste the above URL into your browser address bar to see the response. You'll see the response body in JSON format, like below.
Note: The course code needs to be in all capital letters for the request to succeed.
For this exercise, we'll stick to the year 2023-2024.
So the first step is to ready the form. It will have the following:
A text input field that must have a value before the form is submitted
A submit button
Both of these, along with the form, must be uniquely identifiable as well.
Here's how that will look:
Note that we do not define an action
or method
attribute because we do not want the form to actually submit, we just want it to trigger a fetch request.
Once the request completes, we'll need to put the course details somewhere, so it's a good idea to have a container element ready to accomodate the data. You could also directly place the details in the body, but a container helps to structure the page better. The container should not initially be visible to the user.
(Optional) You may also want to have a separate element ready to show an error message to the user, unless you intend to display an alert instead.
Now we need to add a listener to the form that will wait till it is submitted. Then it should trigger a fetch request.
When a form is submitted, it automatically submits data to its action URL. In this case since there is no action URL defined, the form attempts to submit the input to the page itself, which reloads the page and prevents the rest of the code from being executed. To prevent this, we have the line event.preventDefault()
in the listener function.
event
is a deprecated global variable that refers to the event in question. It is better to use form validation functions to prevent forms from submitting. In this case I am using event
because it is easier, and form validation is out of the scope of this guide.
In our getCourseData
function from above (or whatever you decided to name the function), we need to ready the course code. This is nothing much, just get the input field value and make it all caps:
Note that the value
attribute of an input field returns the value of the field, which in this case is whatever the user has typed into the text field.
Now comes the fetch request. Recall that to get course details based on course code, we submit a request to this URL:
So the resource
parameter for the fetch request will be the above URL with the user's input plugged in. As for the options
parameter, luckily we do not need to specify any because the default options suffice!
So the fetch request looks like this:
Next, we need to check the response status. The API will return 200
if the course code was valid, 404
if it wasn't, and other codes indicate other unforeseen errors. You can decide how you want to process it, but in this case since we don't care what the response code is, we can return null
if there is an error.
Lastly, we need to process the data and present it to the user. For starters, try displaying the course code, course title, description, and how many units it is.
Here's the basic skeleton:
That's the fetch request completed, now in the body of then
we need to display the course details
There are 3 main ways to do this, and it's up to you to choose which one. The implementation is left to you as an exercise.
This approach involves creating elements and adding the text inside of them, then appending them inside the course-container element we made earlier. Make use of the document.createElement
method, the element.appendChild
method and (optionally) the document.createTextNode
method.
The advantage of this is that it is quite flexible to changes in specification. For instance, if I decide to also show the prerequisites and corequisites of every course, its easier to just edit the JS file to create a couple new elements.
The disadvantages of this approach is that every time the fetch request is run, the elements are re-created and re-added to the page.
This approach is fine for instances where requests are not very often (or happen only once or at most twice after the page loads), but in our case it is slower than the other approaches.
For this approach, you'll need to edit the html to add elements for the course details to be contained within, and give them all ids. An example is below:
You'll then need to query for these elements and edit their innerText
properties to contain the data needed.
The advantage of this approach is that the elements are only created once, and you just need to change their values.
The disadvantage of this approach is that the code is not that flexible to changes in specification, since you need to edit both the HTML AND the JS script to implement any changes.
This approach is useful for instances where multiple fetch requests could be performed by the user, like this one.
This approach is the quickest way to do it. Remember the innerHTML
property? It holds everything inside the element in question, including nested elements. This means that it is possible to add elements inside of another one by way of the innerHTML
attribute:
The above code will insert an <h1>
into the div that was selected.
This means we can just format the entire contents of the course-container div into a string, and set the element's innerHTML
to that string.
The advantage of this approach is that it is fast and easy to write, and it is easy to implement changes to the specification.
The disadvantage is that this approach allows for something called HTML Injection. This is when malicious code can be injected into a webpage, and one of the ways to do this is to use the innerHTML
property. Look at the code below:
This adds a <script>
tag inside the div, which causes the code inside it to be executed. Here, the code is just showing a simple alert, but it is possible to write code which behaves much more maliciously, such as creating and submitting invisible forms, reading cookies set by the page, messing with the page content, or worse.
This method, due to the vulnerability it creates, is not recommended for use at all, except in cases where the developer has complete control over the content being added to the innerHTML
or it is extremely certain that the content being read in is safe, and when the contents are not very long.
The code for this exercise can be found here. Approach 1 has been used because we have not yet done a real example of creating+adding elements. Approach 2 has been left as an exercise, and approach 3 has been demonstrated just to show how it works.
This is the end of the JavaScript guide! If you would like to practice more fetch requests, I suggest testing out some of the API endpoints of Reqres. There is also an example here that shows a couple of the endpoints, and a two fetch request examples (one of them using a PUT
request, so take a look at the options
JSON for that request).
The next guide will be on React, a framework of JavaScript that allows to combine HTML, CSS and JS into a single abstraction to make frontend development slightly easier.