p3-insta485-clientside

REST API Tools

This tutorial shows how to use command line tools to interact with a REST API.

Install

Make sure you have curl and HTTPie installed.

Linux and Windows Subsystem for Linux

$ sudo apt-get install curl
$ pip install httpie

Your virtual environment should be activated when installing HTTPie.

MacOS

$ brew install curl httpie coreutils

Sanity check

Your versions might be different.

$ curl --version
curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
$ http --version
1.0.3

Basic usage

A REST API sends JSON data over HTTP. First, try it in your browser. For this example, we’ll use the GitHub REST API. Navigate to https://api.github.com/users/awdeorio. You should see something like:

{
  "login": "awdeorio",
  "name": "Andrew DeOrio",
  "public_repos": 5,
  ...
}

Using curl, we can do the same thing at the command line.

$ curl https://api.github.com/users/awdeorio
{
  "login": "awdeorio",
  "name": "Andrew DeOrio",
  "public_repos": 5,
  ...
}

Better yet, HTTPie (the http command) provides color-coded results. It also makes complex requests easier to type. This is the recommended utility.

$ http https://api.github.com/users/awdeorio
HTTP/1.1 200 OK
...
{
  "login": "awdeorio",
  "name": "Andrew DeOrio",
  "public_repos": 5,
  ...
}

REST APIs and HTTP Basic Access Authentication

HTTP Basic Access Authentication includes a username and password in the headers of every request.

Send HTTP basic auth credentials using HTTPie (http command). In this example, the username is awdeorio and the password is password.

The response below is a hardcoded version of the post detail route (/api/v1/posts/1/) described in the Flask REST API Tutorial. This is a simplified version of what the spec describes for the route.

$ http \
  -a awdeorio:password \
  "http://localhost:8000/api/v1/posts/1/"
HTTP/1.0 200 OK
...
{
  "created": "2017-09-28 04:33:28",
  "imgUrl": "/uploads/122a7d27ca1d7420a1072f695d9290fad4501a41.jpg",
  "owner": "awdeorio",
  "ownerImgUrl": "/uploads/e1a7c5c32973862ee15173b0259e3efdb6a391af.jpg",
  "ownerShowUrl": "/users/awdeorio/",
  "postShowUrl": "/posts/1/",
  "postid": 1,
  "url": "/api/v1/posts/1/"
}

REST API POST requests

We can also make POST requests with HTTPie. Note that HTTPie implicitly uses JSON as the content type for all requests so we don’t need to specify the content-type in the POST header:

$ http \
  -a awdeorio:password \
  POST \
  "http://localhost:8000/api/v1/comments/?postid=3" \
  text='Comment sent from httpie'

Note: this endpoint will only work once the /api/v1/comments/ API endpoint is complete.

REST APIs and sessions

Some REST APIs require a login and session cookies. These examples show how to use sessions with HTTPie. This tutorial section is optional in EECS 485.

This section assumes functional /accounts/ and /api/v1/posts/1/ routes.

To try these examples, start your EECS 485 Project 3 development server.

$ ./bin/insta485run

Request to the REST API without a login results in a 403 error. Note: this assumes a complete project 3 implementation.

$ http "http://localhost:8000/api/v1/posts/1/"
HTTP/1.0 403 FORBIDDEN
...

Login using HTTPie to fill out the login form. This will save a file called session.json containing a cookie set by the server.

$ http \
  --session=./session.json \
  --form POST \
  "http://localhost:8000/accounts/" \
  username=awdeorio \
  password=password \
  operation=login
HTTP/1.0 302 FOUND
...
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
...

HTTPie will use the cookie in session.json to authenticate to the insta485 API.

$ http \
  --session=./session.json \
  "http://localhost:8000/api/v1/posts/1/"
HTTP/1.0 200 OK
...
{
  "comments": [
    {
      "commentid": 5,
      "lognameOwnsThis": false,
      "owner": "jflinn",
      "ownerShowUrl": "/users/jflinn/",
      "text": "Walking the plank #chickensofinstagram",
      "url": "/api/v1/comments/5/"
    },
    {
      "commentid": 6,
      "lognameOwnsThis": true,
      "owner": "awdeorio",
      "ownerShowUrl": "/users/awdeorio/",
      "text": "This was after trying to teach them to do a #crossword",
      "url": "/api/v1/comments/6/"
    }
  ],
  "comments_url": "/api/v1/comments/?postid=1", 
  "created": "2021-05-06 19:52:44",
  "imgUrl": "/uploads/122a7d27ca1d7420a1072f695d9290fad4501a41.jpg",
  "likes": {
    "lognameLikesThis": true,
    "numLikes": 3,
    "url": "/api/v1/likes/1/"
  },
  "owner": "awdeorio",
  "ownerImgUrl": "/uploads/e1a7c5c32973862ee15173b0259e3efdb6a391af.jpg",
  "ownerShowUrl": "/users/awdeorio/",
  "postShowUrl": "/posts/1/",
  "postid": 1,
  "url": "/api/v1/posts/1/"
}

Pro-tip: shell script shortcuts

Save some typing by writing a short script that logs in and make a request. Here is shell script code for a sample resttest.sh.

#!/bin/bash
set -Eeuo pipefail
set -x

# Log in
http \
  --session=./session.json \
  --form POST \
  "http://localhost:8000/accounts/" \
  username=awdeorio \
  password=password \
  operation=login

# REST API request
http \
  --session=./session.json \
  "http://localhost:8000/api/v1/posts/1/"

Don’t forget to make the script executable.

$ chmod +x resttest.sh

Run.

$ ./resttest.sh
+ http --session=./session.json --form POST http://localhost:8000/accounts/ username=awdeorio password=password operation=login
HTTP/1.0 302 FOUND
...

+ http --session=./session.json http://localhost:8000/api/v1/posts/1/
HTTP/1.0 200 OK
...
{
  "comments": [
    {
      "commentid": 5,
      "lognameOwnsThis": false,
      "owner": "jflinn",
      "ownerShowUrl": "/users/jflinn/",
      "text": "Walking the plank #chickensofinstagram",
      "url": "/api/v1/comments/5/"
    },
    {
      "commentid": 6,
      "lognameOwnsThis": true,
      "owner": "awdeorio",
      "ownerShowUrl": "/users/awdeorio/",
      "text": "This was after trying to teach them to do a #crossword",
      "url": "/api/v1/comments/6/"
    }
  ],
  "created": "2021-05-06 19:52:44",
  "imgUrl": "/uploads/122a7d27ca1d7420a1072f695d9290fad4501a41.jpg",
  "likes": {
    "lognameLikesThis": true,
    "numLikes": 3,
    "url": "/api/v1/likes/1/"
  },
  "owner": "awdeorio",
  "ownerImgUrl": "/uploads/e1a7c5c32973862ee15173b0259e3efdb6a391af.jpg",
  "ownerShowUrl": "/users/awdeorio/",
  "postShowUrl": "/posts/1/",
  "url": "/api/v1/posts/1/"
}

Update .gitignore

Be sure to add session.json and cookies.txt to your .gitignore. These files shouldn’t be in version control.

Acknowledgments

Original document written by Andrew DeOrio awdeorio@umich.edu.

This document is licensed under a Creative Commons Attribution-NonCommercial 4.0 License. You’re free to copy and share this document, but not to sell it. You may not share source code provided with this document.