Express CRUD Assignment¶
Overview of the CRUD Assignment¶
An important part of any web application is the ability to insert, update and delete info. An amusing acronym for the ability to create data, read, update and delete data is CRUD. This assignment is fondly called the CRUD assignment.
This assignment description is long, but I recommend you read all of it before you start. Come back and re-read sections when you get to that part of the implementation, but I think it helps to see the big picture before starting.
Goals/Content¶
It's also nice to be able to present people with nice menus of search results. Your app should also be able to deal with multiple collections without hassling the user. This assignment allows you to do that.
In this app, you will occasionally do a redirect, whenever the URL should be changed. Otherwise, just render the correct result. Don't do any unnecessary re-directs.
In this assignment, you are required to use flashing. I suggest creating a partial to render the flashed messages. You must also have a static CSS file with a little bit of style rules in it. I'm not looking for fancy CSS; I just want to ensure that everyone knows how to use CSS in a real app.
Completing this assignment demonstrates the following skills:
You won't need sessions or cookies.
Time¶
This is a difficult assignment. I've made some simplifications, but I don't know how long it will take in Express. Historically, this takes almost everyone less than 12 hours to complete using Python/Flask. If it's taken you that long and you're not done, stop and get some help.
Reference Solution¶
Let's start with an example, using my CRUD solution.
Note Because this app allows you to upload data into a (my) database, I have prevented access from those outside the Wellesley community. To access the app from off-campus, either
- use the VPN to get an on-campus IP address (see the FAQ) or
- use some credentials (username/password) that I have sent before
Please don't feel that you have to present exactly the same interface that I do. I'm sure you can think of something more interesting, creative, or beautiful. Nevertheless, it exhibits the functionality I want, and your focus should be on functionality.
Using the App¶
Test it using the following procedure: I've numbered these so we can refer
to various use cases
:
- Click on the link to insert a movie. It brings you to a page with a form for doing that.
- Fill out the form incompletely. For example, I entered a TT of 666, a title of "Dumb Movie" and submitted the form. The app must detect the incomplete form, flash a message about the missing inputs, and bring you back to try again.
- Fill out the form completely and submit. It will flash that 666 is a TT that is already in use and bring you back to the form. Change the TT to 777 and submit.
- You must be brought to a form for updating the movie.
- Fill out the form completely and submit. You must be brought to a form for updating the movie.
- Click on "select from incomplete movies". You'll see a SELECT menu of movies for which either the release year or director is missing or null. Because you couldn't insert your movie with a director, your movie must appear on this menu. Selecting it must take you to the update page for your movie.
- You must be able to update the movie title. I changed mine to "dumb movie 2".
- You must be able to update the year.
- You must be able to update the director by supplying an NM. Try "0" for Alan Smithee or "123" for George Clooney; use any NM you want.
- You must be able to update the addedby to any staff ID who is in the database. (Ideally, this app should supply the staff member's name, but we're not doing that.)
- Finally, you must be able to delete the movie, using a separate
form that appears on the
/update/
page.
That procedure shows you how your app must work and gives you a way to test that it does work. Note that you are allowed to "view source" on any pages in the reference solution to look at the HTML of the rendered pages. You're also allowed to look at the URLs and even use the network tab of the developer tools to see the requests, responses, and redirects. That's all part of the API, so it is not cheating to look at it and learn from it.
There's a video in our videos if you'd like to see it in action.
Now let's look at some of these pieces in detail.
Insert¶
The insert form is straightforward. Feel free to look at my HTML/CSS source for how to do the form, but you've done forms, so that shouldn't be too difficult.
Note that you could check for required inputs in the form and prevent submission if they are missing, but I suggest that you don't. That's because it's important for apps to validate input in the back-end. Validating in the front-end is an optional improvement, but doing so makes it harder to test the back-end validation, so don't. Save that excellent idea for your project.
Your back end will get the three values from the form. It must insert the new movie, unless that tt is already in use. There are two acceptable approaches:
- You do a
find
followed by anif
statement and then ainsert
, or - use
update
combined with{upsert: true}
that achieves insert-if-not-exists behavior in one fell swoop.
If you use the second approach, you should know that the document
returned by update
looks like the following if the TT is in use:
{
acknowledged: true,
modifiedCount: 0,
upsertedId: null,
upsertedCount: 0,
matchedCount: 1 // matched a movie so the tt is in use
}
If the "insert if not exists" succeeds, because the TT is not in use, the result looks like this:
{
acknowledged: true,
modifiedCount: 0,
upsertedId: new ObjectId("64308806183c4ef490d3d68f"),
upsertedCount: 1, // inserted 1 document
matchedCount: 0 // no match
}
If the tt is in use, flash an error and rerender the page, filling in the current data.
Note that inserting a movie should have an addedby
value. You can
make up your own staff id value. You can hard-code that into your
Python app, but use a named variable/constant, defined at the top of
the file or after the prelude, rather than having a random integer in
the code.
Search¶
The search page has a simple form on it that searches for a movie using a partial title. It will redirect the browser to the update page for the first movie that matches. If no movie matches, flash a message and let the user try again.
Actually, because we've done search in an earlier homework, I'll allow you to skip this feature. You're busy enough.
Select¶
The select page has a simple form with a menu of movies that are
incomplete, defined as either the director
or the release
is
missing or null.
Do a "view source" on my solution to see that although the user will
see the title of the movie, it's the TT that is sent to the back
end, because each menu item specifies the TT as the value
of that
menu item.
Updating A Movie¶
The update form must be pre-filled with data from the database. The URL for the route must include the TT of the movie being updated, so that the URL is clean and associated with editing that movie. See strict api below.
If the director
value is not null, the director name must be looked
up and displayed.
In this assignment, you can update the title
, the release
year,
the addedby
or the director
NM. Nothing else is required. In
particular, you don't have to allow the user to update the TT.
If the director
is updated, the new value is the NM of the
director. (Allowing name entry would be nice, but raises issues of
uniqueness, spelling and the like, so I'm not asking for that.)
Ideally, you should check that the director NM exists before updating
that value. However, I will allow you to skip that check; I'll use
valid NM values. But, you need to look up the name of the director and
put the correct sub-document in the movie document.
Modifying addedby
is similar to updating the director; the staff
name has to be looked up and the correct subdocument stored in the
movie.
Deleting A Movie¶
There's a separate form for deleting a movie; it appears on the same page as the update form. The form just posts to the correct URL. If the deletion is successful, redirect to the main page.
Strict API¶
Real-world APIs have clear, well-articulated interfaces and so the routes and forms need to have precise names and interfaces.
Someday, not this semester, I will write a validation script that will test your app. You can use the script to check that your app works, and I will use it to check that your app works.
Therefore, your app must support the following endpoints and methods:
/
(GET) which shows the basic navigation. You can redirect to here when you have no place better to go, such as when a movie is deleted./insert
(GET and POST). On GET, it renders the blank insert form. On POST, it processes the insertion and then redirects to the/update/nnn
page./select
(GET) which shows a menu of movies with incomplete information, either null value for eitherrelease
ordirector
/do-select
(GET) takes the menu selection and redirects to the/update/nnn
page for that movie./update/123
(GET and POST). On GET, it renders a filled-out form for updating a particular movie, with the TT of the movie in the URL on GET. On POST, it does the update and shows the form again./delete/123
(POST) deletes the given movie
Furthermore, the form controls (inputs) need to have precise names:
- The names for the insert form elements are:
movieTt
movieTitle
movieRelease
- The name for the input in the select menu is
menuTt
- The names for your update form elements are:
movieTitle
movieRelease
movieAddedby
movieDirector
This API won't affect your coding much, if at all. Please, be creative in your appearance and the internals of your web app, not in the interface.
Getting Started¶
Before you start implementing, please make sure that you are working in a copy of the WMDB tables in your personal database. The following command will do the trick:
~cs304node/public_html/bin/copy-wmdb
I suggest starting with the crud-starter
code. You can copy it like this:
cd ~/cs304/apps/
cp -rd ~cs304node/apps/crud-starter crud
Note that the starter code includes the HTML code for the navigation menu and flashing. You are welcome to use that code. Consider whether either chunk of code should be made into a partial.
Redirect versus Re-render¶
Many students have asked why redirect versus re-rendering the page. The redirect is simple and easier (less coding), so why not always do that? It's a good question, and indeed, I can see an argument that simplicity of coding might be more valuable than efficiency. But let's at least address the other side of the argument.
Why is re-rendering more efficient than redirect? There are two main reasons:
- The redirect requires an extra network round-trip. That's an additional delay (albeit probably small, depending on your network connection), because your node server returns the redirect to the browser, and the browser has to turn around and ask for the new URL. We've done this in other cases, because we want the URL to look a certain way, so this is not a decisive argument. Still, it has some weight. Avoiding network delay is faster for the user. Avoiding network congestion is good for all users.
- After the redirect, your server has to look up all the movie data that your current handler function already has. That means an extra database connection and an extra query, just to look up data that you have. Avoiding unnecessary database interactions is faster for the user and good for all users.
I think reasonable people might disagree on the relative value of these, but I've decided to make you re-render the page unless the URL changes. At the very least, you should be aware of these considerations, even if you disagree with me.
XSS Attacks¶
We talked about XSS
attacks a
little while ago, but a short synopsis is that an XSS attack is when a
malicious user (Mallory) puts something in the database that will be
displayed to other users (victims) such that the victim is
attacked. Commonly, this is malicious JavaScript code. Here, we will
try to put a <script>
tag in the victim's browser by making the name
of a movie <script>
. But that should not succeed. Instead, the page
displays normally.
WAVE¶
Your forms must be accessible and pass the WAVE test, so that all form inputs are properly labeled.
Error Handling¶
Handling errors and bad input is a pain and doesn't always enhance our understanding of the core material for this course. Therefore, I'm not going to emphasize error handling in your apps.
I want it to be realistic but not burdensome. Some guidelines:
- You may assume that numeric input (e.g. a TT value) is indeed numeric, a non-negative integer of reasonable size.
- As mentioned above, you may assume that a director NM is a
valid value, so you don't have to check the
person
table to ensure that it's valid. - Similarly, you may assume that an addedby value is valid, so you
don't have to worry that the lookup in the
staff
collection will fail. - You may not assume that TT values are valid: neither when inserting a movie nor when showing a form to update a movie; give an appropriate error message. If the user tries to insert a movie with at TT that is in use, it should fail.
- The
/update
route must check that the TT is valid (is the TT of an existing movie). If not, flash an error message and redirect to the home page. - If any other error-checking situations arise, please ask me for clarification.
Grading¶
My policy for programming assignments is something like:
- 70 percent for functionality: does it work? do all the features work?
- 10 percent for modularity and coding style
- 10 percent for documentation
- 10 percent for efficiency, security and other stuff
Specifically, I will look for:
- Accessible forms
- No global variables (constants are ok)
- Good modularity, function/variable names and parameter passing
- Good documentation
- reasonable programming style. lines shouldn't go beyond 80 characters
- static CSS file and some use of CSS rules
Please don't forget all the good programming techniques that you've learned in JavaScript and other languages: look for appropriate abstractions and useful modules to reduce the redundancy and complexity of your code.
Don't forget to put your name(s) in a comment at the top of each file.
Turnin¶
The turn-in procedure for this will be very similar to the Lookup Assignment. See that page for details. The essential checklist is:
- tar up the
crud
folder intocrud.tar
- drop the
crud.tar
file to the course account - Upload your code files to Gradescope, so that I can comment on them.
server.js
is the most important one, but your.ejs
files could be useful, too.
Final Checklist¶
- Make sure you've tarred and dropped the
crud.tar
file - Make sure you've uploaded files to Gradescope, and included your partner's name, if you worked with a partner.
Thanks!