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.
Individually, you must also submit a short reflection on your contributions to and experiences with your student group.
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.
/api/login
and /api/logout
are supported/api/statuses/show/611fcaf640148d94e2a469c7.json
/api/user/get.json
/api/user/get.json?screen_name=ltorrey
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
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
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.
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.
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.
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.
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.
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.
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.
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.
.env
file.
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.
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:
P3 Grading (out of 100 points):
async
/await
)
initdb.mjs
) and using high-quality queries.
.env
fileThe 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
.
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.
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.
Name | Required | Description |
---|---|---|
username | required | The username (screen name) of the authenticating user. |
password | required | The password of the authenticating user. |
Deauthenticates an authenticated user. Returns a farewell message upon success or a description of the error upon failure.
Name | Required | Description |
---|---|---|
There are no parameters for this request |
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.
{
"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
.
Attribute | Type | Description |
---|---|---|
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. |
{
"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.
Attribute | Type | Description |
---|---|---|
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. |
{
"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.
Attribute | Type | Description |
---|---|---|
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. |
{
"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.
Attribute | Type | Description |
---|---|---|
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. |
{
"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.
Attribute | Type | Description |
---|---|---|
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. |
{
"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.
Attribute | Type | Description |
---|---|---|
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. |
The following API endpoints can be used to programmatically create, retrieve and delete slimes and reslimes.
Access to this API requires that the user be authenticated.
full credit Updates the authenticating user's current status, also known as composing a Slime. This request responds with the created slime object.
Name | Required | Description |
---|---|---|
status | required | The text of the status update. |
reply_to | optional | The ID of an existing status that the update is in reply to. |
Returns a single slime, specified by the ID parameter. The slime's auther will also be embedded withing the slime.
Name | Required | Description |
---|---|---|
There are no parameters for this request |
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.
Name | Required | Description |
---|---|---|
There are no parameters for this request |
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.
Name | Required | Description |
---|---|---|
count | optional | The number of most recent replies to return (defaults to all reslimes). |
Returns an array of slimes that are reslimes of the slime specified by the ID parameter.
Name | Required | Description |
---|---|---|
count | optional | The number of most recent reslimes to return (defaults to all reslimes). |
full credit Reslimes a slime as specified by the ID parameter. Returns the original slime with reslime details embedded.
Name | Required | Description |
---|---|---|
There are no parameters for this request |
full credit Unreslimes a reslime as specified by the ID parameter. Returns the original slime with reslime details embedded.
Name | Required | Description |
---|---|---|
There are no parameters for this request |
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.
Name | Required | Description |
---|---|---|
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. |
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.
Name | Required | Description |
---|---|---|
count | optional | The number of slime objects to return (defaults to all slime objects) |
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.
Name | Required | Description |
---|---|---|
count | optional | The number of slime objects to return (defaults to all slime objects) |
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.
Name | Required | Description |
---|---|---|
count | optional | The number of slime objects to return (defaults to all slime objects) |
The following API endpoints can be used to programmatically create, retrieve, and delete favorites.
Access to this API requires that the user be authenticated.
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.
Name | Required | Description |
---|---|---|
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. |
full credit Favorites (likes) the slime specified in the ID parameter as the authenticating user. Returns the favorite slime when successful.
Name | Required | Description |
---|---|---|
id | required | The string ID of the slime to like. |
full credit Unfavorites (un-likes) the slime specified in the ID parameter as the authenticating user. Returns the unfavorited slime when successful.
Name | Required | Description |
---|---|---|
id | required | The string ID of the slime to like. |
The following API endpoints can be used to programmatically follow and unfollow users.
This API requires that the user be authenticated.
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.
Name | Required | Description |
---|---|---|
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 |
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.
Name | Required | Description |
---|---|---|
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 |
The following API endpoints can be used to programmatically create, get, and update user information.
Use of this API (except for user/create.json) requires that the user be authenticated.
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.
Name | Required | Description |
---|---|---|
username | required | The screen name of the user to create. |
password | required | The password for the user being created. |
Returns information about a user
specified by the user_id
or screen_name
parameter. If no parameter is specified, return the authenticated
user.
Name | Required | Description |
---|---|---|
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 |
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.
Name | Required | Description |
---|---|---|
display_name | required | The new display name for the user. |
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.
Name | Required | Description |
---|---|---|
description | required | The new description for the user. |
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.
Name | Required | Description |
---|---|---|
image | required | The avatar image for the profile, base64-encoded. Must be a valid GIF, JPG, or PNG image. |
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