Creating a REST API - aatishnn/it350_sample GitHub Wiki

Creating a REST API - Part A

Background

In this tutorial, we are going to create a REST API for a sample blogging platform. The ER diagram, relational model, and initial SQL statements are provided below. Go through this and understand the design before diving into the rest of the tutorial.

The next part builds upon this tutorial and adds authentication and authorization so don't worry about it right now.

ER Diagram

2021-02-08_08-36.png

Relational Model

2021-02-08_08-37.png

Create SQL statements

And the following SQL query was used to create a database:

CREATE TABLE users (
   userId SERIAL,
   email VARCHAR(256) NOT NULL,
   password_hash VARCHAR NOT NULL,
   name VARCHAR(100) NOT NULL,
   PRIMARY KEY (userId),
   UNIQUE (email)
);

CREATE TABLE tags (
   tagId SERIAL,
   name VARCHAR(50) NOT NULL CHECK (name NOT IN ('badword1', 'badword2', 'badword3')),
   PRIMARY KEY (tagId),
   UNIQUE (name)
);

CREATE TABLE articles (
   articleId SERIAL,
   title VARCHAR(500) NOT NULL,
   content TEXT NOT NULL,
   userId INT NOT NULL,
   isPublished BOOLEAN DEFAULT FALSE NOT NULL,
   createdAt TIMESTAMP NOT NULL,
   updatedAt TIMESTAMP NOT NULL,
   PRIMARY KEY (articleId),
   FOREIGN KEY (userId) REFERENCES users(userId)
);

CREATE TABLE is_tagged_with (
   articleId INT NOT NULL,
   tagId INT NOT NULL,
   PRIMARY KEY (articleId, tagId),
   FOREIGN KEY (articleId) REFERENCES articles(articleId),
   FOREIGN KEY (tagId) REFERENCES tags(tagId)
);

User Requirements

We identified that this platform will need the following set of pages and functions:

  • Homepage
    • List latest 10 articles sorted by createdAt date in descending order
  • Article Detail Page
    • Display a single article with all the details about it
  • List of Tag pages (think tag cloud)
    • List all the tags and number of articles each tag is associated with
  • Tag Detail Page
    • List all the articles that have that particular tag assigned to them

Apart from the public views, we also need an admin interface for authors to create articles and edit articles they have created. So we need the following features on the admin interface as well:

  • Admin Section
    • Login / Signup as authors
    • CRUD (Create, Read, Update, Delete) articles
    • CRUD (Create, Read, Update, Delete) tags
    • Change published status of articles
    • Assign tags to articles

Identifying Queries and Views

You did this last week. You looked through your requirements and identified types of queries and views that might be necessary for your app. For this example, we're going to use the following views:

  • Homepage

    • List latest 10 articles sorted by createdAt date in descending order
    SELECT * FROM articles
    WHERE isPublished IS TRUE
    ORDER BY createdAt DESC
    LIMIT 10;

    but wait, for the homepage, we do not require all the fields and the whole content of the articles. Bandwidth is not cheap so let's improve this view so it only returns required data. If we look at the prototype of the homepage below, we can see that we require 4 things: (1) title of the page, (2) First few lines of the content (3) Name of the author, and (4) Date it was posted.

    2021-02-08_11-00.png

    So, our updated view will look something like:

    DROP VIEW IF EXISTS homepage_articles;
    CREATE OR REPLACE VIEW homepage_articles AS
        SELECT articleId, title, LEFT(content, 200) as "short_content"
        FROM articles
        WHERE isPublished IS TRUE
        ORDER BY createdAt DESC
        LIMIT 10;
  • Article Detail Page

    • Display a single article with all the details about it. We do not require a dedicated view for this as this is pretty simple with standard SQL statements.
    SELECT * from articles WHERE id=1;

    Similarly, we can identify queries and views for other requirements in this same fashion.

Creating a REST API with PostgREST

Now that we have the database together with views, let's use PostgREST to create a REST API on top of it.

Download and Install PostgREST inside your group VM

wget https://github.com/PostgREST/postgrest/releases/download/v7.0.1/postgrest-v7.0.1-linux-x64-static.tar.xz
tar xf postgrest-v7.0.1-linux-x64-static.tar.xz
mv postgrest /bin/
chmod +x /bin/postgrest

Create a PostgREST configuration file

Create a file named postgrest.conf with the following contents:

db-uri = "postgres://<username>:<password>@127.0.0.1/<dbname>"
db-schema = "public"
db-anon-role = "<username>"
server-port=8000

Replace <username> and <password> with the credentials you used to connect through PgAdmin. Also replace <dbname> with your database name.

Start the API Server

You can now start the API using the following command from the directory where you created the postgrest.conf file:

postgrest ./postrest.conf

Your REST API should be running at http://localhost:8000 . If you are running this on a server and want to access it from a different machine, replace localhost with the IP address of the server.

How does PostgREST generate API URLs?

and since we have views such as homepage_articles, they are also exposed in similar ways. For example:

and many more. You can do filtering, limits, and ordering all through these APIs. Refer to the PostgREST docs for more examples.

Testing your API

Using Insomnia

Insomnia is a GUI tool to try out HTTP and REST requests and test their functionalities. We are going to use it to test some of the endpoints that PostgREST created.Download and Install Insomnia from the official website

Create a GET request

Let's test the endpoint to list all articles. You can do that by creating a new GET request in Insomnia: list_articles.gif

Create a POST request

We can also create POST requests to modify the contents of the database. For example, we can create a POST request to add a new article in the following way: post_article.gif

Updating resources

An author made a typo in the article with id 9. Let's fix that. We can update existing resources using PATCH request:

patch_article.gif

Deleting resources

Turns out that article had a copyright issue and we need to take it down. Let's issue a DELETE request and remove that article: delete_article.gif

Triggers and Stored Procedures

When we were creating an article with the following data:

{
  "title": "Hello Universe",
  "content": "Universe tried calling you but it went to voicemail",
  "userid": 1,
  "ispublished": true,
  "createdat": "2021-02-02T15:20:26",
  "updatedat": "2021-02-03T15:20:26"
}

We were passing in createdAt and updatedAt fields ourselves. But these are timestamps that can be automatically calculated in the backend. So, let's fix that by using triggers and stored procedures.

First define functions (or stored procedures) to generate timestamps:

CREATE OR REPLACE FUNCTION trigger_set_update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updatedAt = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION trigger_set_insert_timestamp()
RETURNS TRIGGER AS $$
BEGIN
  NEW.createdAt = NOW();
  NEW.updatedAt = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Now create triggers that will call these functions whenever an article is created or updated:

CREATE TRIGGER set_update_timestamp
BEFORE UPDATE ON articles
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_update_timestamp();


CREATE TRIGGER set_insert_timestamp
BEFORE INSERT ON articles
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_insert_timestamp();

Now, all we have to do to create a new article is pass the following data:

{
  "title": "Hello Universe",
  "content": "Universe tried calling you but it went to voicemail",
  "userid": 1,
  "ispublished": true
}

and PostgreSQL will take care of updating those timestamp values for us. In your projects, you should find similar use cases and implement at least one trigger that calls a stored procedure.

Stored Procedures as API endpoints

Now, let's implement some backend logic. What if we need an API that takes in id of the article and publishes it (i.e. set isPublished to true). We can implement that using stored procedures and expose it through PostgREST.

Let's create a stored procedure first:

CREATE OR REPLACE FUNCTION publish(id integer)
RETURNS BOOLEAN AS $$
BEGIN
  UPDATE articles SET isPublished = TRUE WHERE articleId = id;
  RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

We can now access this through our API using: publish.gif

Note that all stored procedures and functions are available through PostgREST at URLs like: http://localhost:8000/rpc/<function_name>.

Assignment Submission

  • Create Insomnia requests for each API endpoints that you need for the app (queries and views)
  • Export the workspace and submit it on Canvas
  • Create a simple text file and put all the new triggers and stored prodecures that you have created in it. Upload it on Canvas as well. You should have at least one trigger and a stored procedure for your project.

exportf66439a96455e201.gif

References

APIs and REST

PostgREST Docs

⚠️ **GitHub.com Fallback** ⚠️