CSCI 344: Fall 2024

Advanced Web Technology

CSCI 344: Fall 2024

UNCA Logo

Assignments > HW5: Build a REST API

Due on Thu, 12/12 @ 11:59PM. 22 Points.

Video Walkthroughs for Homework 5

The following video walkthroughs go over the concepts needed to complete Homework 5.

  1. 07:55 Introduction
  2. 07:23 Create a database
  3. 04:24 Database connection string
  4. 11:02 Populating your database
  5. 04:34 Verify your installation / configuration
  6. 19:25 SQLAlchemy - Part 1
  7. 11:28 SQLAlchemy - Part 2
  8. 10:57 Flask RESTful
  9. 06:38 Business Rules
  10. 10:03 Postman
  11. 14:17 Get a list of Post objects (/api/post)
  12. 13:34 Get a single Post object (e.g., /api/posts/3)
  13. 21:07 Create a Post object (POST method)
  14. 23:11 Modify a Post object (PATCH method)
  15. 5:51 Delete a Post object (DELETE method)
  16. 11:15 Running the test suite

The folder of videos can also be found here.

Modified Scope (12/11/2024)

  • All of the Post endpoints are required (listed below):
    • /api/posts (GET and POST)
    • /api/posts/ (GET, PATCH, DELETE)
  • The homework assignment is worth 22 points.
  • Everything else is extra credit (you may earn up to 20 points EC applied to the homework category). I strongly encourage you to finish, as I think this activity is valuable to your learning.

Introduction

For homework 5, you are going to create a REST API using Flask and PostgreSQL. To do this, you will:

  1. Create and populate a local database
  2. Configure your python code to interact with the database using SQL Alchemy
  3. Use Flask RESTful to implement a few API endpoints
  4. Run the tests to see that your API requirements have been met

Set Up

Before you get into the concepts and code, let’s first download and configure the Homework 5 files. Please complete the following steps:

1. Download the HW5 files and save them inside of your csci344 folder

Download hw05.zip and unzip it.

hw05.zip

You should see a directory structure that looks like this:

hw05
├── README.md
├── app.py
├── models
├── populate.py
├── requirements.txt
├── static
├── templates
├── tests
├── users.csv
├── utilities
└── views

2. Create a virtual environment and install the dependencies

Navigate to the hw05 folder on your command line. Then create a virtual environment and install the dependencies.

For Mac, Unix, Linux

# creates the virtual environment
python3 -m venv env 

# activates your virtual environment
source env/bin/activate

# install any new Python dependencies
python -m pip install -r requirements.txt 

For Windows Powershell or Command Prompt

# creates the virtual environment
py -m venv env 

# run the activate.bat script as follows:
env\Scripts\activate

# install any new Python dependencies
py -m pip install -r requirements.txt

3. Create a new database and populate it

1. Create a new database

Create a new database called photo-app. In my opinion, the easiest way to do this is via the command line:

If psql is not recognized on your command line, see the Tutorial 10 instructions for configuring your path.

2. Add DB_URL to your .env file

Create an environment variable called DB_URL in your .env file. This variable will store the path to your database in a format that SQLAlchemy can understand. Your database connection information is represented as follows…

postgresql://{user}:{password}@{host}/{database}

Each parameter of the connection string is described in the table below:

user The database user (postgres)
password The password associated with the postgres user (that you set in Tutorial 10)
host The address of the computer where your database is stored. For now, it will be localhost (since it’s stored on your local computer). When we set up your database using Heroku Postgres, your host will be in the cloud.
database The name of the database to which you want ot connect (photo-app)

Here is Sarah’s .env file (your postgres password will be different):

FLASK_APP=app.py
# postgresql://{user}:{password}@{host}/{database}
DB_URL=postgresql://postgres:12345@localhost/photo-app

3. Populate your database

When you’re done, populate your database by running the following command on the command line (from the photo-app directory):

# run from within the photo-app directory on the command line (with your venv activated):
python populate.py

Each time the populate.py script is run, it will delete all of the tables and recreate them with some fake data using the SQL Alchemy library. Feel free to take a look at how this script works if you’re curious! As you’re testing, feel free to run the populate.py script to get a fresh copy of your database.

4. Verify Your Installation

To verify your installation…

1. Run Flask

Run flask with your virtual environment activated as follows:

python app.py

You should see output that looks similar to this:

 * Serving Flask app 'app.py' (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 802-745-136

2. Verify that the API Tester Loads

Next, navigate to http://127.0.0.1:5000/api and you should see a screen that looks like this:

This UI can help you test your API (as can the automated tests and Postman).

3. Run the automated test suite

From a new terminal / shell window, navigate into the hw05/tests and run the test suite while Flask is running. In other words, you will have 2 terminals open: one that’s running flask and one that’s for the test suite:

cd tests
python run_tests.py 

The test suite should run 68 tests. You should see a bunch of error messages output to the screen with a summary at the bottom telling you that there were a bunch of failures and errors

F.FEFFFFFEEFF.FEFFFFF.............F..EFFFFEF.FFF.FFFF.FFFFFF..FFEF..

A bunch of error messages....

Ran 68 tests in 2.697s

FAILED (failures=37, errors=7)

Your goal is to get all of the tests to pass.

Background Readings and Concepts

Before you start coding, let’s spend a few minutes reviewing some of the libraries and principles that we’ll be using for this assignment. Specifically:

  1. SQL Alchemy
  2. Flask RESTful

1. SQL Alchemy

Please see the SQL Alchemy activity (to be done in class) to get guidance re: how to interact with the database. There are many examples in the activity that are directly related to completing your homework.

2. Flask RESTful

Now that you’ve had some practice interacting with your database using SQL Alchemy, you’re ready to create some REST API endpoints. As a reminder, REST is an architectural style that allows other clients to interact with some subset of your database over the web (using HTTP). To help us build these endpoints and keep them organized, we’re going to use a convenience library called Flask RESTful. You can learn more about this library by reading the documentation.

You will be using the Resource class from Flask RESTful to build our various endpoints. I have already created a bunch of starter endpoints for you in the views directory that you will be editing for this assignment. Note that each file in the views directory has a List and Detail resource defined:

Examine the views/posts.py file

Open the views/posts.py file so that you can take a look at the code for creating, reading, updating, and deleting Post models.

Verify the Postman can interact with your various endpoints

When you’re done inspecting this file, please open Postman to test one of the Post endpoints. You will have to download “Postman Agent” in order for Postman to be able to access endpoints on your local computer.

  1. Paste http://127.0.0.1:5000/api/posts/ into the address bar
  2. Ensure that the GET method is selected
  3. Click the “Send” button.

If it worked, you will see an empty list [] in the output panel. This is because the /api/posts/ endpoint for the GET method has not yet been implemented:

Requirements

1. Resources

The API should make 8 resources available to Photo App. Note that the to_dict() methods in each Model definition should already generate the data structures shown below. This section just describes what these resources look like:

  1. Post
  2. User
  3. Comment
  4. Bookmark
  5. LikePost
  6. Following
  7. Follower
  8. Story

1. Post

Post resources should contain data about the post, the user, and any associated comments (order within the json object doesn’t matter). It should also contain some convenience data fields such as whether the current user has liked or bookmarked the post (e.g. current_user_like_id and current_user_bookmark_id). The to_dict() method in each of the models already does this for you, so you will invoke the model’s to_dict() method when returning resources.

{
   "id": 99,
   "image_url": "https://picsum.photos/600/430?id=605",
   "user": {
      "id": 11,
      "first_name": "Jason",
      "last_name": "Lopez",
      "username": "jason_lopez",
      "email": "jason_lopez@yahoo.com",
      "image_url": "https://picsum.photos/300/200?id=368",
      "thumb_url": "https://picsum.photos/30/30?id=582"
   },
   "caption": "Sometimes degree food never sit probably remember main education race machine.",
   "alt_text": "Descriptive text",
   "display_time": "7 days ago",
   "current_user_like_id": 56,
   "current_user_bookmark_id": 20,
   "likes": [
      {
         "id": 263,
         "user_id": 2,
         "post_id": 99
      },
      {
         "id": 264,
         "user_id": 20,
         "post_id": 99
      },
      ...
   ],
   "comments": [
      {
         "id": 256,
         "text": "Night ability such already study make bed there total tonight military democratic expect our serious second perform interesting modern send table window kid dinner message although degree law town standard head special image.",
         "post_id": 99,
         "user": {
            "id": 15,
            "first_name": "Lisa",
            "last_name": "Parrish",
            "username": "lisa_parrish",
            "email": "lisa_parrish@yahoo.com",
            "image_url": "https://picsum.photos/300/200?id=982",
            "thumb_url": "https://picsum.photos/30/30?id=999"
         }
      },
      {
         "id": 257,
         "text": "Start difference news gas administration hot deal support anyone explain task water anything more street better herself yourself its guess sport fall collection war natural foreign stage training example act eat television over happy dark bring character foreign low black establish skill rock science food close people help thought garden task test option help agency.",
         "post_id": 99,
         "user": {
            "id": 12,
            "first_name": "Samantha",
            "last_name": "Acosta",
            "username": "samantha_acosta",
            "email": "samantha_acosta@yahoo.com",
            "image_url": "https://picsum.photos/300/200?id=220",
            "thumb_url": "https://picsum.photos/30/30?id=79"
         }
      },
      ...
   ]
}

2. User

The User resource should be structured like this:

{
    "id": 1,
    "first_name": "Roger",
    "last_name": "Graves",
    "username": "roger_graves",
    "email": "roger_graves@gmail.com",
    "image_url": "https://picsum.photos/300/200?id=994",
    "thumb_url": "https://picsum.photos/30/30?id=953"
}

3. Comment

The Comment resource should be structured like this:

{
    "id": 2,
    "text": "Than join finish special force according heart beautiful actually him candidate down site when or threat much commercial own suddenly food investment finally class into base friend still understand.",
    "post_id": 1,
    "user": {
        "id": 24,
        "first_name": "Joseph",
        "last_name": "Mclaughlin",
        "username": "joseph_mclaughlin",
        "email": "joseph_mclaughlin@yahoo.com",
        "image_url": "https://picsum.photos/300/200?id=474",
        "thumb_url": "https://picsum.photos/30/30?id=987"
    }
}

4. Bookmark

The Bookmark resource should just be an id and a post object without the comments:

{
    "id:": 20,
    "post": {
        "id": 1,
        "image_url": "https://picsum.photos/600/430?id=625",
        "caption": "Many down a reveal woman key center technology citizen truth glass data food arm continue head cup career fear life write talk strong build hospital painting city rest wrong thought give light marriage something trial produce whom total cut third series same personal stage ahead move face personal gas tend religious wish.",
        "alt_text": "Some alt text",
        "user": {
            "id": 1,
            "first_name": "Roger",
            "last_name": "Graves",
            "username": "roger_graves",
            "email": "roger_graves@gmail.com",
            "image_url": "https://picsum.photos/300/200?id=994",
            "thumb_url": "https://picsum.photos/30/30?id=953"
        }
    }
}

5. LikePost

The LikePost resource should be structured like this:

{
    "id": 662,
    "user_id": 12,
    "post_id": 3
}

6. Following

The Following resource should just be an id and a user object structured like this:

{
    "id": 302,
    "following": {
        "id": 11,
        "first_name": "Ashley",
        "last_name": "Thornton",
        "username": "ashley_thornton",
        "email": "ashley_thornton@hotmail.com",
        "image_url": "https://picsum.photos/300/200?id=939",
        "thumb_url": "https://picsum.photos/30/30?id=465"
    }
}

7. Follower

The Follower resource (which is created using the “Following” model)resource should just be an id and a user object structured like this:

{
    "id": 302,
    "follower": {
        "id": 11,
        "first_name": "Ashley",
        "last_name": "Thornton",
        "username": "ashley_thornton",
        "email": "ashley_thornton@hotmail.com",
        "image_url": "https://picsum.photos/300/200?id=939",
        "thumb_url": "https://picsum.photos/30/30?id=465"
    }
}

8. Story

The Story resource should be structured like this (mostly just a placeholder for now):

{
    "id": 7,
    "text": "Produce Democrat what city professor young though since southern history deep mother.",
    "user": {
        "id": 11,
        "first_name": "Lindsey",
        "last_name": "Shepard",
        "username": "lindsey_shepard",
        "email": "lindsey_shepard@gmail.com",
        "image_url": "https://picsum.photos/300/200?id=159",
        "thumb_url": "https://picsum.photos/30/30?id=689"
    }
}

2. Security

Access to data is contextual, based on the user who is logged into the system. For now, the person logged into the system is represented by the current_user variable, which is set in app.py.

Who is currently logged in?

The current user is hardcoded as user_id=12

Read Permissions

Our photo sharing app is more private than Instagram, and the rules are simple:

Write Permissions

3. HTTP Status Codes

All HTTP responses have an attached status code, which represents additional information about the request. A list of all valid HTTP status codes can be found here. For this assignment:

Your Tasks

Implement the following GET routes (20 Points)

The links included below point to a deployment of the hw05 solutions.

Method/Route Description and Examples Parameters Response Type Points
1. GET /api/posts All posts in the current users' feed. This includes the current user's posts, as well as the people that the current user is following. You will need to modify this endpoint to get it to work as specified.
  • limit (int, optional): Limits the number of posts returned (defaults to 20, maximum is 50)
List of Post objects 4
2. GET /api/posts/<int:id> The post associated with the id (already started for you). Post object 4
3. GET /api/profile The current user's profile. Please use self.current_user (which is holding an instance of the User model) to get this information. User object 4
4. GET /api/stories List of stories of users you're following as well as your own story (if you have one). Please use the Story data model to get this information. List of Story objects 4
5. GET /api/bookmarks/ Should display all of the current user's bookmarks (saved posts). Please use the Bookmark data model to get this information. List of Bookmark objects 4

Implement POST, PATCH, and DELETE routes (26 points)

The next set of routes involves storing and manipulating data in your PostgreSQL. A few notes:

Method/Route Description and Examples Parameters Response Type Points
1. POST /api/posts Should create a new post (already started for you).
  • image_url: string (required)
  • caption: string
  • alt_text: string
Post Object 4
2. PATCH /api/posts/<int:id> Should update the post (already started for you).
  • image_url: string (optional)
  • caption: string (optional)
  • alt_text: string (optional)
Post Object 4
3. DELETE /api/posts/<int:id> Should delete the post (already started for you). Note that the delete is configured to cascade (all associated comments, likes, bookmarks, etc. will also be deleted). Some JSON message 2
4. POST /api/comments Should create a new comment associated with the specified post.
  • post_id: int (required)
  • text: string (required)
Comment Object 2
5. DELETE /api/comments/<int:id> Should delete a comment. Remember, you can only delete a comment that you created. Some JSON message 2
6. POST /api/bookmarks/ Should create a bookmark (saves a post to bookmarks).
  • post_id: int (required)
Bookmark object 2
7. DELETE /api/bookmarks/<int:id> Should delete a bookmark. Remember, you can only delete a bookmark that you created. Some JSON message 2
8. POST /api/posts/likes Should create a like.
  • post_id: int (required)
LikePost object 2
9. DELETE /api/posts/likes/<int:id> Should delete a like. Remember, you can only delete a "like" that you created. Some JSON message 2
10. POST /api/following Should create a following record.
  • user_id: int (required)
Following object 2
11. DELETE /api/following/<int:id> Should delete a following record. Remember, you can only delete a "following" that you created. Some JSON message 2

What to Turn In

Please review the requirements above and ensure you have met them. Specifically:

When you’re done, please submit the following to the Moodle: