Project 3 — Express and RESTful APIs

CS-332: Web Programming

Fall 2023

For the duration of this course, you will work in teams to develop a microblogging and social networking web application based loosely around X (formerly Twitter). In this third project, you will work in teams of four or five students to create a RESTful API using Node.js, Express, and MongoDB. You will gain experience programming in JavaScript, querying MongoDB, and using asynchronous programming techniques.

SlugFest is said to be the next hit social media site1—you just have to create it first! Members can post short messages ("slimes") for their followers to read. Members can also "favorite" slimes that they like or even "reslime" so that the post shows up on their own profile. Additionally, this is a social site, so it is possible to reply to slimes creating a "slime trail" conversation. If these features sound familiar, that's because they are based off of those supported by a popular microblogging and social media site.

Your task is to build all of the necessary backend functionality to support the pages you developed for Projects 1 and 2. This project spec contains a nontrivial RESTful API specification for you to implement. To receive the highest possible grade on this project, your team must support both reading and writing with this API. Note, however, that it is possible to receive a 3.75 on this project without supporting any API endpoints that update/post data.

ALERT! This assignment spec will likely need several revisions and clarifications. Ask lots of questions and make sure you refresh frequently (to catch any updates). Fixes will generall be backwards-compatible unless we identify any major blunders.

Requirements

Working in teams of up to 4-5 students (found on Canvas), create a collection of JavaScript files that:
  • Implements the entirety of the SlugFest REST API as described in this spec.
  • Uses MongoDB to store all of the data generated by the API.

Individually, you must also submit a short reflection on your contributions to and experiences with your student group.

Checkpoints

There will be two checkpoints as you work on this project to ensure that you are making progress. To complete the checkpoint, your team will have to demonstrate the required functionality in person. Note that these checkpoints do not mean that this is all the work your group should have done by the specified date.

  • Checkpoint 1: The team has set up shared access to a MongoDB instance or each team member has their own configured copy of MongoDB
    • The database should contain all of the initialization data
    • Every team member should be able to programmatically connect to the database
    • /api/login and /api/logout are supported
  • Checkpoint 2: Initial support for the API
    • /api/statuses/show/611fcaf640148d94e2a469c7.json
    • /api/user/get.json
    • /api/user/get.json?screen_name=ltorrey

Starter Code

 Download p3-starter-code.zip

You are provided with an intial skeleton of an npm package to use for this project. After downloading, be sure to run npm install from within the package directory to install all dependencies from the package.json file.

You are also provided with the profile pictures for users of SlugFest. Skeleton HTML files are also provided, but these are not needed for this project.

Make sure to create a .env file with your MongoDB connection details. Recall that you should not be sharing these with anyone. You will lose points if you turn in your .env file. It is possible to create additional users in the MongoDB interface if your group would like to share a database for testing. The format of this file should be as follows (note the lack of spaces around the assignment operator):

              
                CERT_FILE=path_to_certificate
                DB_NAME=name_of_the_db
                DB_HOST=hostname
              
            

Thes files may be downloaded here. The contents of this file is:

              
                unzip -l p3-starter-code.zip
                (out)Archive:  p3-starter-code.zip
                (out)  Length      Date    Time    Name
                (out)---------  ---------- -----   ----
                (out)        0  11-27-2023 12:23   p3-starter-code/
                (out)     6459  11-27-2023 11:37   p3-starter-code/test.mjs
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/favorites/
                (out)      986  11-27-2023 11:41   p3-starter-code/test_data/api/favorites/ltorrey.json
                (out)     2384  11-27-2023 11:41   p3-starter-code/test_data/api/favorites/ed.json
                (out)     2085  11-27-2023 11:41   p3-starter-code/test_data/api/favorites/list.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/favorites/choongsool.json
                (out)     2085  11-27-2023 11:41   p3-starter-code/test_data/api/favorites/kevinaangstadt.json
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/user/
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/user/get/
                (out)      553  11-27-2023 11:41   p3-starter-code/test_data/api/user/get/ltorrey.json
                (out)      474  11-27-2023 11:41   p3-starter-code/test_data/api/user/get/ed.json
                (out)      435  11-27-2023 11:41   p3-starter-code/test_data/api/user/get/choongsool.json
                (out)      579  11-27-2023 11:41   p3-starter-code/test_data/api/user/get/kevinaangstadt.json
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ccd3969d515951edefac.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175cb4a969d515951edefa9.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ccf1969d515951edefaf.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ca7a969d515951edefa7.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ccbe969d515951edefaa.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ccfa969d515951edefb0.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ccde969d515951edefad.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175cce8969d515951edefae.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ccc7969d515951edefab.json
                (out)     1205  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/611fcaf640148d94e2a469c7.json
                (out)     1350  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175c92c969d515951edefa3.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175c9a2969d515951edefa4.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175cb11969d515951edefa8.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175c9e3969d515951edefa5.json
                (out)        3  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/replies/6175ca53969d515951edefa6.json
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/
                (out)      905  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ccd3969d515951edefac.json
                (out)     1228  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175cb4a969d515951edefa9.json
                (out)      906  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ccf1969d515951edefaf.json
                (out)     1985  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ca7a969d515951edefa7.json
                (out)      905  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ccbe969d515951edefaa.json
                (out)      905  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ccfa969d515951edefb0.json
                (out)      906  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ccde969d515951edefad.json
                (out)      905  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175cce8969d515951edefae.json
                (out)      906  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ccc7969d515951edefab.json
                (out)     1062  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/611fcaf640148d94e2a469c7.json
                (out)     1088  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175c92c969d515951edefa3.json
                (out)     1907  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175c9a2969d515951edefa4.json
                (out)     2039  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175cb11969d515951edefa8.json
                (out)      960  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175c9e3969d515951edefa5.json
                (out)     1085  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/show/6175ca53969d515951edefa6.json
                (out)        0  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/user_timeline/
                (out)     3280  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/user_timeline/ltorrey.json
                (out)     4235  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/user_timeline/ed.json
                (out)     7075  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/user_timeline/choongsool.json
                (out)     4633  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/user_timeline/kevinaangstadt.json
                (out)     5322  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/activity.json
                (out)    18944  11-27-2023 11:41   p3-starter-code/test_data/api/statuses/home_timeline.json
                (out)        0  11-27-2023 11:34   p3-starter-code/models/
                (out)     1679  11-27-2023 11:34   p3-starter-code/models/db.js
                (out)    13332  11-27-2023 12:29   p3-starter-code/initdb.mjs
                (out)        0  11-13-2023 10:45   p3-starter-code/public/
                (out)        0  11-13-2023 10:45   p3-starter-code/public/skeletons/
                (out)     3809  11-22-2021 15:59   p3-starter-code/public/skeletons/feed.html
                (out)     5615  11-22-2021 15:59   p3-starter-code/public/skeletons/profile.html
                (out)     2940  11-22-2021 15:59   p3-starter-code/public/skeletons/notifications.html
                (out)     3045  11-22-2021 15:59   p3-starter-code/public/skeletons/slime.html
                (out)        0  11-13-2023 10:45   p3-starter-code/public/assets/
                (out)        0  11-13-2023 10:45   p3-starter-code/public/assets/profile/
                (out)   249750  11-22-2021 15:59   p3-starter-code/public/assets/profile/kevin.jpeg
                (out)    79031  11-22-2021 15:59   p3-starter-code/public/assets/profile/choong-soo.png
                (out)    75662  11-22-2021 15:59   p3-starter-code/public/assets/profile/ed.png
                (out)    18900  11-22-2021 15:59   p3-starter-code/public/assets/profile/lisa.jpeg
                (out)     1850  11-27-2023 12:22   p3-starter-code/.gitignore
                (out)   242732  11-27-2023 12:23   p3-starter-code/package-lock.json
                (out)     1213  11-27-2023 11:36   p3-starter-code/package.json
                (out)     2924  11-27-2023 12:03   p3-starter-code/app.mjs
                (out)        0  11-27-2023 11:37   p3-starter-code/routes/
                (out)      160  11-27-2023 11:37   p3-starter-code/routes/api.js
                (out)---------                     -------
                (out)  782460                     77 files
              
            

Intializing the Database

The starter code also contains a script to initialize the database with data consistent with Project 2. You may run this script with npm run initdb. This script will clear anything in the database and regenerate the collections. It will set the password for each user to password.

You may download the latest initdb script here.

When correctly executed, this command should produce output similar to:

              
                npm run initdb
                (out)
                (out)> project3@1.0.0 initdb
                (out)> cross-env DEBUG=project3:* node ./initdb.mjs
                (out)
                (out)  project3:initdb Verifying environment has correct fields... +0ms
                (out)  project3:initdb Connecting to database... +0ms
                (out)  project3:initdb Connected to database. +535ms
                (out)  project3:initdb Deleting session store... +1ms
                (out)  project3:initdb Deleted session store. +1ms
                (out)  project3:initdb Clearing exiting users... +0ms
                (out)  project3:initdb Cleared 4 users. +163ms
                (out)  project3:initdb Inserting users... +0ms
                (out)  project3:initdb Inserted kevinaangstadt. +93ms
                (out)  project3:initdb Inserted ltorrey. +22ms
                (out)  project3:initdb Inserted choongsool. +112ms
                (out)  project3:initdb Inserted ed. +9ms
                (out)  project3:initdb Inserted all users. +0ms
                (out)  project3:initdb Clearing existing slimes... +1ms
                (out)  project3:initdb Cleared 15 slimes. +34ms
                (out)  project3:initdb Inserting slimes... +0ms
                (out)  project3:initdb Inserted 15 slimes. +65ms
              
            

Testing Your Code

The starter code also contains a test file that will run a series of tests on your API. You may run this script to see how you are progressing through the project. The script may be run with npm test, and it will output diff style explanations for errors.

ALERT! This test file currently only supports the Project2 API spec and not the full spec listed on this page. The script will likely be updated as the project progresses, but it is ultimately your team's responsibility to perform exhaustive testing.

Sample output of a successful test run will look like:

              
                npm test
                (out)
                (out)> project3@1.0.0 test
                (out)> cross-env NODE_OPTIONS='--experimental-vm-modules' jest --test-timeout 10000
                (out)
                (out)(node:9574) ExperimentalWarning: VM Modules is an experimental feature and might change at any time
                (out)(Use `node --trace-warnings ...` to show where the warning was created)
                (out)GET /api/statuses/replies/611fcaf640148d94e2a469c7.json 400 2.886 ms - 35
                (out)POST /api/login 200 87.535 ms - 62
                (out)GET /api/statuses/replies/611fcaf640148d94e2a469c7.json 200 212.672 ms - 882
                (out)GET /api/logout 200 24.343 ms - 22
                (out)POST /api/login 200 209.252 ms - 62
                (out)GET /api/favorites/list.json 200 117.247 ms - 1560
                (out)GET /api/favorites/list.json?screen_name=kevinaangstadt 200 142.044 ms - 1560
                (out)GET /api/favorites/list.json?screen_name=ed 200 148.075 ms - 1822
                (out)GET /api/favorites/list.json?screen_name=choongsool 200 69.500 ms - 2
                (out)GET /api/favorites/list.json?screen_name=ltorrey 200 131.108 ms - 740
                (out)POST /api/login 200 22.059 ms - 62
                (out)GET /api/statuses/home_timeline.json 200 846.682 ms - 14148
                (out)GET /api/statuses/activity.json 200 380.484 ms - 3919
                (out)GET /api/statuses/replies/611fcaf640148d94e2a469c7.json 200 257.307 ms - 882
                (out)GET /api/statuses/replies/6175c92c969d515951edefa3.json 200 248.395 ms - 1013
                (out)GET /api/statuses/replies/6175c9a2969d515951edefa4.json 200 107.243 ms - 2
                (out)GET /api/statuses/replies/6175c9e3969d515951edefa5.json 200 86.589 ms - 2
                (out)GET /api/statuses/replies/6175ca53969d515951edefa6.json 200 89.977 ms - 2
                (out)GET /api/statuses/replies/6175ca7a969d515951edefa7.json 200 113.275 ms - 2
                (out)GET /api/statuses/replies/6175cb11969d515951edefa8.json 200 111.087 ms - 2
                (out)GET /api/statuses/replies/6175cb4a969d515951edefa9.json 200 80.506 ms - 2
                (out)GET /api/statuses/replies/6175ccbe969d515951edefaa.json 200 80.365 ms - 2
                (out)GET /api/statuses/replies/6175ccc7969d515951edefab.json 200 82.287 ms - 2
                (out)GET /api/statuses/replies/6175ccd3969d515951edefac.json 200 86.198 ms - 2
                (out)GET /api/statuses/replies/6175ccde969d515951edefad.json 200 79.795 ms - 2
                (out)GET /api/statuses/replies/6175cce8969d515951edefae.json 200 81.586 ms - 2
                (out)GET /api/statuses/replies/6175ccf1969d515951edefaf.json 200 89.041 ms - 2
                (out)GET /api/statuses/replies/6175ccfa969d515951edefb0.json 200 83.357 ms - 2
                (out)GET /api/statuses/show/611fcaf640148d94e2a469c7.json 200 190.660 ms - 853
                (out)GET /api/statuses/show/6175c92c969d515951edefa3.json 200 190.727 ms - 880
                (out)GET /api/statuses/show/6175c9a2969d515951edefa4.json 200 292.203 ms - 1482
                (out)GET /api/statuses/show/6175c9e3969d515951edefa5.json 200 183.352 ms - 772
                (out)GET /api/statuses/show/6175ca53969d515951edefa6.json 200 188.725 ms - 878
                (out)GET /api/statuses/show/6175ca7a969d515951edefa7.json 200 295.184 ms - 1553
                (out)GET /api/statuses/show/6175cb11969d515951edefa8.json 200 304.938 ms - 1604
                (out)GET /api/statuses/show/6175cb4a969d515951edefa9.json 200 186.609 ms - 1011
                (out)GET /api/statuses/show/6175ccbe969d515951edefaa.json 200 199.572 ms - 728
                (out)GET /api/statuses/show/6175ccc7969d515951edefab.json 200 193.200 ms - 729
                (out)GET /api/statuses/show/6175ccd3969d515951edefac.json 200 204.841 ms - 728
                (out)GET /api/statuses/show/6175ccde969d515951edefad.json 200 202.571 ms - 729
                (out)GET /api/statuses/show/6175cce8969d515951edefae.json 200 194.715 ms - 728
                (out)GET /api/statuses/show/6175ccf1969d515951edefaf.json 200 198.909 ms - 729
                (out)GET /api/statuses/show/6175ccfa969d515951edefb0.json 200 201.806 ms - 728
                (out)GET /api/statuses/user_timeline.json 200 417.086 ms - 3472
                (out)GET /api/statuses/user_timeline.json?screen_name=kevinaangstadt 200 383.358 ms - 3472
                (out)GET /api/statuses/user_timeline.json?screen_name=ed 200 383.678 ms - 3138
                (out)GET /api/statuses/user_timeline.json?screen_name=choongsool 200 265.843 ms - 5107
                (out)GET /api/statuses/user_timeline.json?screen_name=ltorrey 200 374.455 ms - 2434
                (out)POST /api/login 200 26.607 ms - 62
                (out)GET /api/user/get.json 200 52.320 ms - 498
                (out)GET /api/user/get.json?screen_name=kevinaangstadt 200 82.241 ms - 498
                (out)GET /api/user/get.json?screen_name=ed 200 79.237 ms - 403
                (out)GET /api/user/get.json?screen_name=choongsool 200 89.826 ms - 368
                (out)GET /api/user/get.json?screen_name=ltorrey 200 79.213 ms - 474
                (out)GET /api/logout 200 27.804 ms - 22
                (out)PASS  ./test.mjs (15.475 s)
                (out)  requests that require authentication
                (out)    ✓ should get 400 when not logged in (18 ms)
                (out)    ✓ should be able to log in and out (384 ms)
                (out)    /api/favorites
                (out)      /list.json
                (out)        ✓ no screen_name should match the authenticated user (122 ms)
                (out)        ✓ kevinaangstadt (155 ms)
                (out)        ✓ ed (153 ms)
                (out)        ✓ choongsool (74 ms)
                (out)        ✓ ltorrey (134 ms)
                (out)    /api/statuses
                (out)      ✓ /api/statuses/home_timeline.json (851 ms)
                (out)      ✓ /api/status/activity.json (385 ms)
                (out)      /replies
                (out)        ✓ /api/statuses/replies/611fcaf640148d94e2a469c7.json (261 ms)
                (out)        ✓ /api/statuses/replies/6175c92c969d515951edefa3.json (253 ms)
                (out)        ✓ /api/statuses/replies/6175c9a2969d515951edefa4.json (113 ms)
                (out)        ✓ /api/statuses/replies/6175c9e3969d515951edefa5.json (91 ms)
                (out)        ✓ /api/statuses/replies/6175ca53969d515951edefa6.json (97 ms)
                (out)        ✓ /api/statuses/replies/6175ca7a969d515951edefa7.json (118 ms)
                (out)        ✓ /api/statuses/replies/6175cb11969d515951edefa8.json (118 ms)
                (out)        ✓ /api/statuses/replies/6175cb4a969d515951edefa9.json (87 ms)
                (out)        ✓ /api/statuses/replies/6175ccbe969d515951edefaa.json (83 ms)
                (out)        ✓ /api/statuses/replies/6175ccc7969d515951edefab.json (86 ms)
                (out)        ✓ /api/statuses/replies/6175ccd3969d515951edefac.json (90 ms)
                (out)        ✓ /api/statuses/replies/6175ccde969d515951edefad.json (85 ms)
                (out)        ✓ /api/statuses/replies/6175cce8969d515951edefae.json (85 ms)
                (out)        ✓ /api/statuses/replies/6175ccf1969d515951edefaf.json (93 ms)
                (out)        ✓ /api/statuses/replies/6175ccfa969d515951edefb0.json (87 ms)
                (out)      /show
                (out)        ✓ /api/statuses/show/611fcaf640148d94e2a469c7.json (194 ms)
                (out)        ✓ /api/statuses/show/6175c92c969d515951edefa3.json (193 ms)
                (out)        ✓ /api/statuses/show/6175c9a2969d515951edefa4.json (296 ms)
                (out)        ✓ /api/statuses/show/6175c9e3969d515951edefa5.json (187 ms)
                (out)        ✓ /api/statuses/show/6175ca53969d515951edefa6.json (192 ms)
                (out)        ✓ /api/statuses/show/6175ca7a969d515951edefa7.json (299 ms)
                (out)        ✓ /api/statuses/show/6175cb11969d515951edefa8.json (307 ms)
                (out)        ✓ /api/statuses/show/6175cb4a969d515951edefa9.json (192 ms)
                (out)        ✓ /api/statuses/show/6175ccbe969d515951edefaa.json (204 ms)
                (out)        ✓ /api/statuses/show/6175ccc7969d515951edefab.json (196 ms)
                (out)        ✓ /api/statuses/show/6175ccd3969d515951edefac.json (208 ms)
                (out)        ✓ /api/statuses/show/6175ccde969d515951edefad.json (206 ms)
                (out)        ✓ /api/statuses/show/6175cce8969d515951edefae.json (198 ms)
                (out)        ✓ /api/statuses/show/6175ccf1969d515951edefaf.json (202 ms)
                (out)        ✓ /api/statuses/show/6175ccfa969d515951edefb0.json (205 ms)
                (out)      /user_timeline
                (out)        ✓ no screen_name should match the authenticated user (420 ms)
                (out)        ✓ kevinaangstadt (386 ms)
                (out)        ✓ ed (387 ms)
                (out)        ✓ choongsool (270 ms)
                (out)        ✓ ltorrey (383 ms)
                (out)    /api/user
                (out)      /get
                (out)        ✓ no screen_name should match the authenticated user (54 ms)
                (out)        ✓ kevinaangstadt (84 ms)
                (out)        ✓ ed (82 ms)
                (out)        ✓ choongsool (93 ms)
                (out)        ✓ ltorrey (82 ms)
                (out)
                (out)Test Suites: 1 passed, 1 total
                (out)Tests:       49 passed, 49 total
                (out)Snapshots:   0 total
                (out)Time:        15.504 s, estimated 16 s
                (out)Ran all test suites.
              
            

Commentary

As befitting a 300-level elective, this assignment is fairly open-ended and expects a lot of you. There is no "one true path" to creating and styling the required pages. Thus, this project requires you to tap your inner creativity and problem-solving.

Under-specification is fairly indicative of the real world. Clients will often expect you to "read their mind" to deliver a product that meets their needs. This can definitely be frustrating at times, but it is also a good lesson to learn early. It will help you be a high-quality developer who gains a positive reputation with clients.

A keen eye for detail is also key here. While there is textual description of each page provided, taking a close look at the provided images for styling and inferring withheld information can help you decide how to proceed.

As this is an upper-level course, you may use outside references to complete this assignment. Be sure to keep a running list of citations as you work on this project. Search engines and AI will likely be your friend here.

If you are sharing code using GitHub, be sure to make your repository private. Sharing your code in a public repository for this project is considered an honor code violation.

DANGER! This is a typical warning, but do not wait to get started on this project. If you underestimated the time needed from Project 2, then you are going to really underestimate the time needed for this project.

Recommended Approach

You will most certainly want to split your code into multiple JavaScript files. There are many opportunities for code reuse across this project. Follow the principles we discussed in class for organizing your site using the Model-View-Controller design pattern. The less you mingle code between aspects of these parts of the backend, the less "speghetti code" you will likely produce.

As you build out the API, consider implementing aspects of the model and controller in pairs. That is, implement the endpoint for getting a slime at the same time you implement the code in your model to generate a slime object. Write this code in such a way that you can reuse it as much as possible. For example, if you need to modify or add to the JSON returned by MongoDB for a slime, consider splitting this into a separate function that can be used for many different operations. There are many different endpoints that generate slimes. Try to only extend/modify the slime object in one function.

The API is large enough that you will likely wish to create sub-routers for the different API subsections. You can create these just like we do the main router. There are examples of this in our ToDoApp and in the starter code for this project.

Be sure to ask lots of questions as you work on this project. Questions about the desired behavior are likely to receive more expressive answers than direct questions about the reference implementation (your clients won't have a reference impelmentation). Similarly, there are likely many mistakes in this specification document. If something does not make sense there is a non-zero possibility that it's a mistake!

You may want to look back at the Project 1 and Project 2 specifications for styling and API hints. Note that the data returned by the API in this assignment may not be quite the same as what was in Project 2. Some URIs and data formats may have changed a bit.

Testing

You will want to test your code as you proceed. The most direct means of doing this is with HTTP requests generated by ThunderClient in Visual Studio Code. You can keep a list of requests and run through these tests as you make additional updates.

You might also consider following a test-driven development approach in which you create your requests first and then implement your code to add support for these requests. This can work particularly well because it allows you to have a request for printing debugging information as you work.

There are more sophisticated testing techniques you could use such as what is used in the test.mjs file, which relies on Jest. Jest is a testing framework for JavaScript that allows you to write tests in JavaScript. You can run these tests with the npm test and extend the code to verify more features of your API.

The most recent testing oracle data is available here.

Security Considerations

As we have discussed on numerous occasions in class, there are many security concerns to consider when developing a web application. These become particularly important when collaborating with others and handling user passwords. First and foremost, do not use any personal passwords for this project (either to authenticate MongoDB) or for user accounts. We are not using all possible security measures (especially for user accounts on SlugFest, where passwords are sent over the network with cleartext). Therefore, it's best to assume that all of the passwords used on this project are compromised.

While passwords are sent in cleartext over the network (http does not encrypt messages), you should not store passwords directly in the database. Instead, store a hash of the paswword in the database using the Argon2 library. We will be covering this in class. If you are getting a head start, you might just hard code an authenticated user to use until we cover this material.

Similarly, you should not be sharing your .env file. If you wish to share a database for development, create additional uses in the MongoDB Atlast user interface for all group members. Note that you will lose points on this assignment if you turn in your .env file.

What to Submit for P3

Be sure that you are submitting to the correct assignment on Gradescope! Further, be sure to make sure that you submit all required files. Don't wait until the last minute to submit (you can resubmit up to the deadline).

Only submit one project per group. You can select your group members after uploading your files to Gradescope.

Submit the following:

  • *.mjs: all JavaScript files needed for your pages
  • *.css: any additional CSS files you may have needed.
  • package.json: the npm configuration file.
  • All asset files used in your project (e.g., profile images, JSON)
  • An HTML, TXT, or PDF file containing a list of citations for resources you used to complete this project. No specific convention is required, but this list should provide sufficient information to find a copy of each resource.
  • Do not submit your .env file.
  • Do not submit your node_modules directory.

Your code should work with version 18 of Node.js. I will view your files by node on your main app.mjs file as we have done in class:

              
                npm run initdb
                npm run dev
              
            

Automated testing may also be run on your code using commands akin to the following:

              
                npm run initdb
                npm test
              
            

Thus, make sure that your files are submitted with a folder structure identical to your development machine and that the commands provided in this specification behave correctly. You will likely want to use relative URIs for assets.

I will be using my own MongoDB instance to test your code. Therefore, be sure to use a .env file, but do not submit this file.

Individual Report

Individually, submit a short (1-2 paragraph) report on your contributions to the project. You will submit this report to a separate Gradescope assignment. This should include:

  • A summary of what you contributed to the project (e.g., "I created the login page"). What do you "own" of the artifacts?
  • How you feel about your contributions. Did you do a similar amount of work to your peers? Was your work successful?
  • A reflection on the process of working in a group. What went well? What changes did you make from the last collaboration, and were these changes beneficial? Are there changes you might make for a future collaboration?

Grading Rubric

P3 Grading (out of 100 points):

  • 5 points: Checkpoint 1
  • 5 points: Checkpoint 2
  • 29 points: API endpoints
    • Each of the GET endpoints is worth 2 points. (There are 12)
    • Each of the POST or PUT endpoints is worth 0.5 points. (There are 10)
    • To receive a point for the endpoint, your implemenation must support all of the functionality specified.
  • 36 points: JavaScript Implementation
    • You will be graded on your proper use of JavaScript, including:
      • Use of the relevant packages and APIs
      • Higher-order functions
      • Asynchronous programming (Promises, Callbacks, async/await)
      • Proper use of event handlers and Express endpoints
    • You will receive more points for increased use of good coding habits. Such habits include:
      • Model-View-Controller design and organization
      • Reducing code reuse by placing common code in functions
      • Splitting JavaScript into several modules to facilitate use across APIs
      • Minimizing the number of global bindings (variables) created in your code
  • 10 points: MongoDB design and querying
    • You will be graded on a maintaining a consistent design for your MongoDB database (consistent with initdb.mjs) and using high-quality queries.
    • You will receive more points for increased use of good habits, including:
      • Storing like data together
      • Projecting fields
      • Sorting/limiting/aggregating as part of the query
  • 5 points: Formatting of HTML and JavaScript
    • 5 — files are well-formatted (proper indentation, no extremely long lines, etc.)
    • 3 — files are generally well-written, but spacing is not uniform (e.g., large gaps, old commented code, etc.).
    • 1 — files are very difficult to follow and contain the bare minimum of formatting.
    • 0 — script contains no comments.
  • 5 points: References
    • 5 — References exist and contain a list of resources used for this assignment. This includes course materials.
    • 2 — References file exists, but might be the wrong format or does not adequately identify resources used for this assignment.
    • 0 — No references file provided.
  • 5 points: Individual Report
    • 5 — Report is well written, addresses all required content, and has minimal spelling/grammar errors.
    • 3 — Report is generally well written, but fails to address one required topic.
    • 1 — Report could benefit from significant revisions or fails to address two or more topics.
    • 0 — No report provided.
  • Security Considerations
    • -10 points — submission contains a .env file
    • -50 points — if your implemenation stores passwords as plaintext, you cannot receive a grade higher than 50%

SlugFest REST API

The API provided by SlugFest is based on Twitter's v1.1 API. One goal of this project is to give you exposure to real software and understanding how applications you might use on a daily basis actually function. We have made certain simplifications or adapations for ease of use.

All APIs are available at the api/ endpoint of the server. For example, the statuses API should be available at api/statuses.

Authenticating

Where indicated, these APIs require that a user be authenticated before use. Authenticating a user requires validating that a user's screen name and password match the information on file. Do not store passwords as plain text. Your project will receive a grade no higher than 50% if you do this.

Once a user is authenticated, the API server should remember the user through a session cookie. As long as this cookie remains valid, the user remains authenticated.

The following API endpoints can be used to log in and log out with user information.

  • POST login
  • GET logout

POST login

Authenticates a user using the username and password provided as parameters with the request.

Returns a welcome message if authentication is successful or an error message in the case of an authentication failure.

Parameters

NameRequiredDescription
username required The username (screen name) of the authenticating user.
password required The password of the authenticating user.

GET logout

Deauthenticates an authenticated user. Returns a farewell message upon success or a description of the error upon failure.

Parameters

NameRequiredDescription
There are no parameters for this request

Data Dictionary

All SlugFest APIs that return slimes provide that data encoded using JavaScript Object Notation (JSON). JSON is based on key-value pairs, with named attributes and associated values. These attributes, and their state are used to describe objects.

At SlugFest we serve many objects as JSON, including slimes and users. These objects all encapsulate core attributes that describe the object. Each slime has an author, a message, a unique ID, a timestamp of when it was posted, and sometimes geo metadata shared by the user. Each User has a SlugFest name, an ID, a number of followers, and most often an account bio.

With each slime we also generate "entity" objects, which are arrays of common slime contents such as hashtags, mentions, media, and links.

Slime Object

              
                {
                  "created_at": <date/time in UTC format>,
                  "id_str": <id from db>,
                  "text": <slime text>,
                  "user": <user object>,
                  "in_reply_to_status_id_str": <id of the replied-to slime>,
                  "in_reply_to_user_id_str": <user id of the replied-to slime>,
                  "reslimed_status_id_str": <id of the reslimed status>
                  "reslimed_status": <slime object>,
                  "reply_count": <integer>,
                  "reslime_count": <integer>,
                  "favorite_count": <integer>,
                  "reslimed": <boolean>,
                  "favorited": <boolean>,
                  "entities": {
                      "hashtags": [],
                      "urls": [],
                      "user_mentions": [],
                      "media": []
                }
                         
            

Slimes are the basic atomic building block of all things SlugFest. Slimes are also known as "status updates." The slime object has a long list of 'root-level' attributes, including fundamental attributes such as id_str, created_at, and text. Slime objects are also the 'parent' object to several child objects. Slime child objects include user, entities.

AttributeTypeDescription
created_at String UTC time when this slime was created.
id_str String The string representation of the unique identifier for this slime.
text String The actual UTF-8 text of the status update.
user User object The user who posted this slime. See User data dictionary for complete list of attributes.
in_reply_to_status_id_str String Nullable. If the represented slime is a reply, this field will contain the string representation of the original slime's ID.
in_reply_to_user_id_str String Nullable. If the represented slime is a reply, this field will contain the string representation of the original slime's author ID. This will not necessarily always be the user directly mentioned in the slime.
reslimed_status_id_str String Nullable. If the represented slime is a reslime, this field will contain the string represenataion of the original slime's ID. Note that this will always be an original slime object (not another reslime).
reslimed_status Slime object Users can amplify the broadcast of slimes authored by other users by resliming . Reslimes can be distinguished from typical slimes by the existence of a reslimeed_status attribute (and the reslimed_status_id_str attribute). This attribute contains a representation of the original slime that was reslimed. Note that reslimes of reslimes do not show representations of the intermediary reslime, but only the original slime. (Users can also unreslime a reslime they created by deleting their reslime.)
reply_count Int Number of times this slime has been replied to.
reslime_count Int Number of times this slime has been reslimed.
favorite_count Int Number of times this slime has been favorited.
reslimed Boolean Nullable. Indicates whether this slime has been reslimed by the authenticating user.
favorited Boolean Nullable. Indicates whether this slime has been favorited by the authenticating user.
entities Entities object Entities which have been parsed out of the text of the slime. Additionally see Entities object.

User Object

              
                {
                  "id_str": <id of user>,
                  "name": <display name>,
                  "screen_name": <the username handle>,
                  "description": <short bio>,
                  "followers_count": <int>,
                  "friends_count": <int>,
                  "favorites_count": <int>,
                  "statuses_count": <int>,
                  "profile_image_url": <url for profile image>
                }
              
            

The User object contains SlugFest User account metadata that describes the SlugFest User referenced. Users can author slimes, reslime, reply to slimes, follow Users, and be @mentioned in slimes.

AttributeTypeDescription
id_str String The string representation of the unique identifier for the user.
name String The name of the user, as they’ve defined it. Not necessarily a person’s name. Typically capped at 50 characters, but subject to change. If the user does not specify a name, then this is their screen_name.
screen_name String The screen name, handle, or alias that this user identifies themselves with. screen_names are unique but subject to change. Use id_str as a user identifier whenever possible.
description String Nullable. The user-defined UTF-8 string describing their account.
followers_count Int The number of followers this account currently has. Under certain conditions of duress, this field will temporarily indicate 0.
friends_count Int The number of users this account is following (AKA their “followings”). Under certain conditions of duress, this field will temporarily indicate 0.
favorites_count Int The number of slimes this user has favorited in the account's lifetime.
statuses_count Int The number of slimes (including reslimes) issued by the user.
profile_image_url String A URL endpoint pointing to the user's profile image. This may be appended to the main host url for the web server to access the image.

Entities Object

              
                {
                  "hashtags": [],
                  "urls": [],
                  "user_mentions": [],
                  "media": []
                }
              
            

Entities provide metadata and additional contextual information about content posted on SlugFest The entities section provides arrays of common things included in slimes: hashtags, user mentions, links, and attached media. These arrays are convenient for developers when ingesting Slimes, since SlugFest has essentially pre-processed, or pre-parsed, the text body. Instead of needing to explicitly search and find these entities in the slime body, your parser can go straight to this JSON section and there they are.

AttributeTypeDescription
hashtags Array of Hashtag Objects Represents hashtags which have been parsed out of the slime text.
urls Array of URL Objects Represents URLs included in the text of a slime.
user_mentions Array of User Mention Objects Represents other SlugFest users mentioned in the text of the slime.
media Array This array is unused and should be left empty.

Hashtag Object

              
                {
                  "indices": [<array of two ints>],
                  "text": <String>
                }
              
            

The entities section will contain a hashtags array containing an object for every hashtag included in the slime body, and include an empty array if no hashtags are present.

AttributeTypeDescription
indices Array of Int An array of integers indicating the offsets within the slime text where the hashtag begins and ends. The first integer represents the location of the # character in the slime text string. The second integer represents the location of the first character after the hashtag. Therefore the difference between the two numbers will be the length of the hashtag name plus one (for the '#' character).
text String Name of the hashtag, minus the leading '#' character.

URL Object

              
                {
                  "indices": [<array of two ints>],
                  "url": <String>
                }
              
            

The entities section will contain a urls array containing an object for every link included in the slime body, and include an empty array if no links are present.

AttributeTypeDescription
indices Array of Int An array of integers representing offsets within the slime text where the URL begins and ends. The first integer represents the location of the first character of the URL in the slime text. The second integer represents the location of the first non-URL character after the end of the URL.
url String Wrapped URL, corresponding to the value embedded directly into the raw Tweet text, and the values for the indices parameter.

User Mention Object

              
                {
                  "indices": [<array of two ints>],
                  "id_str": <String>,
                  "name": <String of the display name>,
                  "screen_name": <String of the username>
                }
              
            

The entities section will contain a user_mentions array containing an object for every user mention included in the Tweet body, and include an empty array if no user mention is present.

AttributeTypeDescription
indices Array of Int An array of integers representing the offsets within the slime text where the user reference begins and ends. The first integer represents the location of the '@' character of the user mention. The second integer represents the location of the first non-screenname character following the user mention.
id_str String ID of the mentioned user, as a string.
name String Display name of the referenced user.
screen_name String Screen name of the referenced user.

Statuses API

The following API endpoints can be used to programmatically create, retrieve and delete slimes and reslimes.

  • POST statuses/update.json full credit
  • GET statuses/show/:id.json
  • POST statuses/destroy/:id.json full credit
  • GET statuses/replies/:id.json full credit
  • GET statuses/reslimes/:id.json
  • POST statuses/reslime/:id.json full credit
  • POST statuses/unreslime/:id.json full credit
  • GET statuses/user_timeline.json
  • GET statuses/home_timeline.json
  • GET statuses/reslimes_of_me.json
  • GET statuses/activity.json

Access to this API requires that the user be authenticated.

POST statuses/update

full credit Updates the authenticating user's current status, also known as composing a Slime. This request responds with the created slime object.

Parameters

NameRequiredDescription
status required The text of the status update.
reply_to optional The ID of an existing status that the update is in reply to.

GET statuses/show/:id.json

Returns a single slime, specified by the ID parameter. The slime's auther will also be embedded withing the slime.

Parameters

NameRequiredDescription
There are no parameters for this request

POST statuses/destroy/:id.json

full credit Destroys the status specified by the required ID parameter. The authenticating user must be the author of the specified status. Returns the destroyed status if successful.

Parameters

NameRequiredDescription
There are no parameters for this request

GET statuses/replies/:id.json

Returns an array of slimes that are replies to the slime specified by the ID parameter. Each slime's author will be embedded within each slime.

Parameters

NameRequiredDescription
count optional The number of most recent replies to return (defaults to all reslimes).

GET statuses/reslimes/:id.json

Returns an array of slimes that are reslimes of the slime specified by the ID parameter.

Parameters

NameRequiredDescription
count optional The number of most recent reslimes to return (defaults to all reslimes).

POST statuses/reslime/:id.json

full credit Reslimes a slime as specified by the ID parameter. Returns the original slime with reslime details embedded.

Parameters

NameRequiredDescription
There are no parameters for this request

POST statuses/unreslime/:id.json

full credit Unreslimes a reslime as specified by the ID parameter. Returns the original slime with reslime details embedded.

Parameters

NameRequiredDescription
There are no parameters for this request

GET statuses/user_timeline.json

Returns a collection of the most recents slimes posted by the user indicated by the screen_name or user_id parameter. If no user data is specified, return the timeline for the authenticated user.

The timeline returned is the equivalent of the one seen as a user's profile on SlugFest.

Parameters

NameRequiredDescription
user_id optional The ID of the user for whom to return results
screen_name optional The screen name of the user for whom to return results
count optional Specifies the number of slimes to try to retrieve. By default, all slimes are returned.

GET statuses/home_timeline.json

Returns a collection of the most recent slimes and reslimes posted by the authenticating user and the users they follow. The home timeline is central to how most users interact with the SlugFest service.

Parameters

NameRequiredDescription
count optional The number of slime objects to return (defaults to all slime objects)

GET statuses/reslimes_of_me.json

Returns the most recent slimes authored by the authenticating user that have been reslimed by others. This timeline is a subset of the user's timeline.

Parameters

NameRequiredDescription
count optional The number of slime objects to return (defaults to all slime objects)

GET statuses/activity.json

Returns the most recent slimes that are in reply to the authenticating user's slimes or are reslimes of the authenticating user's slimes. This is useful as a list of notifications to the authenticating user.

Parameters

NameRequiredDescription
count optional The number of slime objects to return (defaults to all slime objects)

Favorites API

The following API endpoints can be used to programmatically create, retrieve, and delete favorites.

  • GET favorites/list.json
  • POST favorites/create.json full credit
  • POST favorites/destroy.json full credit

Access to this API requires that the user be authenticated.

GET favorites/list.json

Returns a collection of the most recents slimes liced by the user indicated by the screen_name or user_id parameter. If no user data is specified, return the favorites for the authenticated user.

Parameters

NameRequiredDescription
user_id optional The ID of the user for whom to return results
screen_name optional The screen name of the user for whom to return results
count optional Specifies the number of slimes to try to retrieve. By default, all slimes are returned.

POST favorites/create.json

full credit Favorites (likes) the slime specified in the ID parameter as the authenticating user. Returns the favorite slime when successful.

Parameters

NameRequiredDescription
id required The string ID of the slime to like.

POST favorites/destroy.json

full credit Unfavorites (un-likes) the slime specified in the ID parameter as the authenticating user. Returns the unfavorited slime when successful.

Parameters

NameRequiredDescription
id required The string ID of the slime to like.

Friendships API

The following API endpoints can be used to programmatically follow and unfollow users.

  • POST friendships/create.json full credit
  • POST friendships/destroy.json full credit

This API requires that the user be authenticated.

POST friendships/create.json

full credit Allows the authenticating user to follow (friend) the user specified in the ID parameter.

Returns the followed user when successful. Returns a string describing the failure condition when unsuccessful. If the user is already friends with the user a HTTP 403 may be returned, though for performance reasons this method may also return a HTTP 200 OK message even if the follow relationship already exists.

Parameters

NameRequiredDescription
user_id optional The ID of the user for whom to return results
screen_name optional The screen name of the user for whom to return results

POST friendships/destroy.json

full credit Allows the authenticating user to unfollow the user specified in the ID parameter.

Returns the unfollowed user when successful. Returns a string describing the failure condition when unsuccessful.

Parameters

NameRequiredDescription
user_id optional The ID of the user for whom to return results
screen_name optional The screen name of the user for whom to return results

User API

The following API endpoints can be used to programmatically create, get, and update user information.

  • POST user/create.json full credit
  • GET user/get.json
  • PUT user/display-name.json full credit
  • PUT user/description.json full credit
  • PUT user/profile-image.json bonus

Use of this API (except for user/create.json) requires that the user be authenticated.

POST user/create.json

full credit Allows a user to create a new SlugFest user. When creating a user, duplicate screen names are not allowed.

Returns the created user when successful. Returns a string describing the failure condition when unsuccessful.

Note that unlike other enpoints in this API, this endpoint does not require user authentication.

Parameters

NameRequiredDescription
username required The screen name of the user to create.
password required The password for the user being created.

GET user/get.json

Returns information about a user specified by the user_id or screen_name parameter. If no parameter is specified, return the authenticated user.

Parameters

NameRequiredDescription
user_id optional The ID of the user for whom to return results
screen_name optional The screen name of the user for whom to return results

PUT user/display-name.json

full credit Update the "display name" for the authenticating user as specified by the display_name parameter.

Returns the user object with the updated information upon success.

Parameters

NameRequiredDescription
display_name required The new display name for the user.

PUT user/description.json

full credit Update the "description" for the authenticating user as specified by the description parameter. This is the brief bio that shows up on a user's profile.

Returns the user object with the updated information upon success.

Parameters

NameRequiredDescription
description required The new description for the user.

PUT user/profile-image.json

bonus Update the profile image for the authenticated user. Note that this method expects raw multipart data, not a URL to an image. This method processes the uploaed file before updating the user's profile image URL.

Returns the user object with the updated information upon success.

This endpoint is bonus. You will need to research how to encode and upload/save images. Should you choose to implement this feature, your report should describe the process in detail.

Parameters

NameRequiredDescription
image required The avatar image for the profile, base64-encoded. Must be a valid GIF, JPG, or PNG image.

Acknowledgements

The initial concept for this project is borrowed from an assignment designed by Andrew DeOrio at the University of Michigan.

Text from the API section was lifted and adapted directly from the Twitter Developer Platform.

1By your professor