CS 312 - Practical Four
Goals
- Convert Film Explorer to use a RESTful API to fetch data
- Practice using
fetch
,await
andasync
Today, you will be converting the standalone version of Film Explorer to use fetch
to load its data.
Prerequisites
-
Visit the Practical 4 page on Replit (or click the 'Start Project' link for Practical 4 on our team page).
-
Click through to the GitHub classroom assignment to create your private repository.
-
Return to the assignment on Repl.it and go to the Version Control panel.
-
Click the button to create a new git repository (not the one for connecting to an existing repo).
-
Open the shell and follow the instructions on your GitHub repository for connecting an existing repository:
git remote add origin repo-name
whererepo-name
is the name of your repository (e.g.,https://github.com/csci312-s21/practical04-ChristopherPAndrews.git
)git branch -m main
git push -u origin main
Fetching Data with AJAX
This version of Film Explorer, much like Simplepedia is making use of webpack to load the data via an import
statement.
import filmData from './films.json';
While this works, it is atypical. It can cause long initial load times and worse, it means the user only works with a local version of the data, so changes don't persist (try rating a few films and then reloading the page). Your task today is to adapt the standalone Film Explorer to fetch its data from the Film Explorer API and persist the ratings.
The API server
You will be communicating with a server running on Replit at
https://filmexplorer-server.profandrews.repl.co
. The REST interface can be found at
/api/films
, which can be optionally followed by an id. The full API is:
Endpoint | Method | Action |
---|---|---|
/api/films | GET | Fetch the entire movie collection as a JSON array |
/api/films/id | GET | Fetch the movie with id as JSON |
/api/films/id | PUT | Update the movie with id (the new JSON-serialized movie object should be in the request body) |
You can interact with this API directly in your browser by visiting https://filmexplorer-server.profandrews.repl.co/api/films, to see a full list of the movies, or https://filmexplorer-server.profandrews.repl.co/api/films/11 to see the movie with an id
of 11 (Star Wars).
Fetching the Films
Following the example in class, adapt the FilmExplorer
component (in FilmExplorer.js
) to fetch its data from the server API. Specifically:
Step one: Remove the line import filmData from '../../data/films.json';
as we will no longer load the movies directly.
Step two: Add a line before the function definition creating a new const
variable called server
. Set it to "https://filmexplorer-server.profandrews.repl.co"
. I use this value in the tests, so please add export
in front of the statement to make it visible outside of this file.
Step three: Replace the simple call to setFilms
with the fetch call shown below. This is essentially the same code as you saw in lecture. The only difference is that we are using a template string so we can make the server name a variable.
const getData = async () => {
const response = await fetch( `${server}/api/films`);
if (!response.ok) {
throw new Error(response.statusText);
}
const filmData = await response.json();
setFilms(filmData);
};
getData();
Launch the development server with npm run dev
. Make sure you can successfully load the movie data from the server.
Updating the Movie Rating
In the standalone Film Explorer the setRating
method in the FilmExplorer
component uses map()
to create a new list in which the film object being rated is replaced with a new one with a new rating. Your next task will be to modify this function to persist changes back to the server.
Rather than starting by updating the objects, we will first create a modified version of the film, and send it to the server. The server will update its record and send the film's data back. Once the response has been received, you can modify the state with the new record. By using the data returned from the server, we are giving the server the opportunity to validate and possibly further modify the data (in this case, the data is not being further modified, but we will see instances when this is important later).
The steps in your new setRating
method (replacing all of the existing code):
Step one: Comment out the body of the setRating
function. We will want to refer back to this later.
Step two: Find the full record for the film being modified. Since the setRating
method only has the movie's id, you will need to use the find
method on films
to obtain the complete film object. Check out the documentation for Array.find
.
Step three: Create an updated film object with the new rating. Recall that you don't want to modify state, so make a copy of the movie using the spread pattern, i.e.
const newFilm = { ...oldFilm, rating };
Step four: Construct your PUT request to `${server}/api/films/${filmid}`
. By default fetch
uses GET. To create a PUT request, supply an optional init
argument specifying the method, body (serialized JSON representation of the new movie object) and headers specifying that the client is sending JSON
(docs):
fetch(`${server}/api/films/${filmid}`, {
method: 'PUT',
body: JSON.stringify(newFilm),
headers: new Headers({ 'Content-type': 'application/json' }),
});
Step five: Handle the fetch response. You can basically set up the sequence of actions exactly the same as you did for fetching the entire collection. Add const response = await
in front of your fetch
call. Then check the response and see if it is "ok". Finally, parse the json to get the new copy of the film back.
Step six: Update the state with the new data. Use the map
pattern from the commented out code to create a new Array, replacing the old film record with the new one. You do not need to use the spread syntax, just return the new film record at the appropriate moment. Then call setFilms
with the new array.
Step seven: Try it out. Rate some things. You should see that they will persist when you reload the page now.
Note that you are all sharing the same server now, so when you make a change, you are actually changing the rating for everyone. However, since we don't poll for changes, and we only receive data about films that we just changed, you won't see the effect of this unless you reload the page.
Finishing Up
To be accepted, your submission should not have ESLint warnings or errors when run with npm run lint
(or npx eslint .
). Remember than you can fix many errors automatically with npm run lint -- --fix
(although since ESLint can sometimes introduce errors during this process, we suggest committing your code before running "fix" so you can rollback any changes). As described in the README, the assignment skeleton includes the Prettier package and associated hooks to automatically reformat your code to a consistent standard when you commit. Thus do not be surprised if your code looks slightly different after a commit.
Submit your assignment by pushing your changes to the GitHub repository ( git push --all origin
in the shell). Once the repository is fully pushed, submit your repository to Gradescope as described here. You can submit (push to GitHub and submit to Gradescope) multiple times.
Portions of your assignment will undergo automated grading. Make sure to follow the specifications exactly, otherwise the tests will fail (even if your code generally works as intended). Use the provided test suite (run with npm test
) to get immediate feedback on whether your code follows the specification. Because of the increased complexity of a React application, Gradescope can take minutes to run all of the tests. Thus you will be more efficient testing locally and only submitting to Gradescope when you are confident your application meets the specification.
Requirements
- Should fetch data from remote server
- Should update ratings on the server
- Pass all tests
- Pass all ESLint checks
Last updated 03/22/2021