REST API Crash Course - Concepts, Python, Flask, Postgres
REST API Crash Course - Concepts, Python, Flask, Postgres
An API (application programming interface) is a setup to allow two pieces of software to communicate.
One of the challenges with software communicating with each other is that every software system is a
little different, so it helps if they’re communicating using some standard notation.
This notation is usually JSON.
https://www.json.org/json-en.html
This communication between software is typically one direction. This means one software system will
do all of the talking, and the other software system will request data. You could say server (backend)
and client (frontend).
A very common setup for this is a data processing server and a website client. The client is going to
make requests to the server.
Let’s say we are building an app to store information about drinks, and we want to get information about
a particular drink. I’ll likely have a server, let’s say written in Python, that manages this data by
connecting to a database. The client can then request this data from the server, the server gets the
information from the database, and then returns the data to the client.
This software doesn’t just open up everything for other software to use. Instead, the developers
carefully choose specific things that should be exposed for other software to consume. These things are
exposed as API endpoints.
So for the backend we might create an API endpoint and it will look like this: /drinks/<id> where the id
could be any ID to grab information about a particular drink.
Now you might be asking, What’s with the slashes? That’s where the REST part of this comes in. REST
(representational state transfer) is a particular type of API that allows the transfer of data (also known as
state, as the acronym would imply) over the internet. So these slashes are actually a URL.
You can see all of this in the developer tools of any modern browser.
To get JSON, there is actually another web address that you can use, api.stackexchange.com, that has
endpoints that return JSON.
POST is another method, which is used when you submit forms on web pages. APIs also support POST
and this is the method you usually use when you want to add or modify data, such as adding a new
answer to a question on Stack Overflow.
There are a few more you should know about, which we will get into later.
Consuming an API
We are going to work with the Stackoverflow API available at api.stackoverflow.com.
We can make a request by following through to an example and copying the URL.
Here’s an example:
https://api.stackexchange.com//2.2/answers?order=desc&sort=activity&site=stackoverflow
Order ,sort, and site are examples of query string parameters. These are sent to the server and are
used to customize the response from the server.
This is a GET request as we can see in the headers of the request in developer tools, but now the
response in JSON.
There are also POST options in this API as well, but these require authentication.
For more elaborate API consumption, I recommend Postman, which is a fairly standard tool in any web
developers toolbox. This allows you to make similar requests to what we just did, but is fully
customizable with the headers and body.
Later on when we start sending data to an API, this will come in handy, because we can build out
objects in the body of the request. This allows us to send data in a similar way to how we had
parameters in the URL, but now we can work with more sophisticated data by using JSON.
import requests
import json
response = requests.get("https://api.stackexchange.com/2.2/questions?
order=desc&sort=activity&site=stackoverflow")
This would be a good time to research what standard JSON looks like.
We are going to start building our application as a module (a single file), but for more sophisticated
applications it’s nice to follow a pattern. Specifically the one from here. It describes the setup to build
your application as a package instead of a module.
Starting in your folder that you want all your files, execute these in the terminal:
python3 -m venv .venv
source .venv/bin/activate
pip3 install flask
pip3 install flask-sqlalchemy
pip3 freeze > requirements.txt
touch application.py
This will create a virtual environment and activate it, install the dependencies we need, list them in a file
requirements.txt, and then create a new file for our code called application.py. We can write code now,
but keep your virtual environment running.
app = Flask(__name__)
@app.route('/')
def index():
return "Hello!"
export FLASK_APP=application.py
export FLASK_ENV=development
flask run
You’ll need to do these exports every time you start your terminal window. So always make sure to
1. Be in the right directory
2. Activate your virtual environment
3. Run your exports
class Drink(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True, nullable=False)
description = db.Column(db.String(120))
def __repr__(self):
return f"{self.name} - {self.description}"
python3
from application import db
db.create_all()
from application import Drink
drink = Drink(name="Grape Soda", description="Tastes like the real thing")
db.session.add(drink)
db.session.add(Drink(name="Cherry", description="Tastes like that one ice
cream"))
db.session.commit()
Drink.query.all()
exit()
flask run
PUT replaces a resource. It is similar to POST but with a minor (but important) difference. PUT requests
are said to be idempotent. This is a fancy word to say that if you do the exact same request with PUT
numerous times, it has the same effect. This is different from POST as POSTing the same thing
numerous times is not guaranteed to not result in duplicates. That’s why if you refresh a page after you
fill out a web form, it may warn you that you’ll be re-submitting the form and can cause side effects.
This description is based on the definition of POST and PUT, but as a developer building an API, you’re
still required to implement the idempotent behavior of PUT.
POST /drinks
Or
PUT /drinks/23
So far these methods correlate to the common actions of working with data known as CRUD (create,
read, update, delete).
PATCH is another method that can be used with the purpose of updating part of a resource, such as
updating the description of a drink. If you’re working with small objects that are in memory, you could
also use PUT and send the whole object back with its new state, replacing the old.
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
db = SQLAlchemy(app)
class Drink(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True, nullable=False)
description = db.Column(db.String(120))
def __repr__(self):
return f"{self.name} - {self.description}"
@app.route('/')
def index():
return 'Hello!'
@app.route('/drinks')
def get_drinks():
drinks = Drink.query.all()
output = []
for drink in drinks:
drink_data = {'name': drink.name, 'description': drink.description}
output.append(drink_data)
@app.route('/drinks/<id>')
def get_drink(id):
drink = Drink.query.get_or_404(id)
return {"name": drink.name, "description": drink.description}
REST API Crash Course - C…
@app.route('/drinks', methods=['POST'])
def add_drink():
drink = Drink(name=request.json['name'],
description=request.json['description'])
db.session.add(drink)
db.session.commit()
return {'id': drink.id}
@app.route('/drinks/<id>', methods=['DELETE'])
def delete_drink(id):
drink = Drink.query.get(id)
if drink is None:
return {"error": "not found"}
db.session.delete(drink)
db.session.commit()
return {"message": "yeet!@"}
Extra Thoughts
Another common construct is building an SDK (software development kit).
This is a wrapper around the SDK in a particular language. So instead of making an HTTP request to
get data, you can invoke a function like get_drinks which returns a list of drink objects.
This does add a layer of complexity, but could make client side development a lot easier to develop.
You should also familiarize yourself with various HTTP response codes.