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:

  1. MongoDB Insert, Update and Delete
  2. Parameterized Endpoints
  3. Redirects
  4. POST and Flashing

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

  1. use the VPN to get an on-campus IP address (see the FAQ) or
  2. 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:

  1. Click on the link to insert a movie. It brings you to a page with a form for doing that.
  2. 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.
  3. 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.
  4. You must be brought to a form for updating the movie.
  5. Fill out the form completely and submit. You must be brought to a form for updating the movie.
  6. 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.
  7. You must be able to update the movie title. I changed mine to "dumb movie 2".
  8. You must be able to update the year.
  9. 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.
  10. 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.)
  11. 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 an if statement and then a insert, 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.

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:

  1. / (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.
  2. /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.
  3. /select (GET) which shows a menu of movies with incomplete information, either null value for either release or director
  4. /do-select (GET) takes the menu selection and redirects to the /update/nnn page for that movie.
  5. /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.
  6. /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:

  1. 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.
  2. 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 into crud.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

  1. Make sure you've tarred and dropped the crud.tar file
  2. Make sure you've uploaded files to Gradescope, and included your partner's name, if you worked with a partner.

Thanks!