API Development Summary(1)

🚀 This blog is just a cheetsheet for myself. You can't visit links because the corresponding repositories are private.

Simple API

API is the shortname of Application Program Interface. An API can be viewed as a program which takes some data, processes it and gives back some data.

We use JSON format to store and transmit data over the Internet. The server side cannot return the Python or Java data directly since JavaScript doesn't understand it. 😓 Imagining how JS deals with Python's dictionary or Java's HashTable? Instead, since JSON data is text, a long string, so JS can read it and deals with it.

  • tip: Alway double quote in JSON instead of single quotes.

❤️ LINK: demo of a simple api using flask

❤️ LINK: What is an API endpoint?

RESTful API

REST Principles

"REST" is a way of thinking about how a web server responds to your requests. Resource is the core component when designing RESTful API. A resouces can be anything, like a document, an image, a service, or a collection of other resources. So it is the resources that transfer between cilent and server. JSON, XML or JPG image are just the format of resources.

(When designing RESTful API, theoretically we only use noun and don't use verb)

Another key feature of REST is to be stateless. This means one request cannot depend on other requests. The server doesn't remember any state.

Let's see an example in the real world.

1
2
3
- A user logs in a web application such as Twitter.
- The web server doesn't know the user is logged in since REST api is stateless.
- In each request, the application must send enough data to identify the user, otherwise the server won't associate the request with the user.

Virtual Environment

We develop our api using Python virtual environment.

❤️ Link: virtualenv README

flask-restful

flask_jwt

  • Flask-JWT: (JWT: JSON Web Token) an extension of Flask that implementing token-based authentication

Using JWT authentication is good for scaling. Since the server doesn't need to store any information of the user, so it is stateless.

When we pass the authentication, the server will generate a JWT token and return to the client. Each time, the cilent sends request containing the JSON token, and the server will use this JSON token to identity the user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# security.py

from werkzeug.security import safe_str_cmp
from user import User

# memory database: store registered users
users = [
User(1, 'bob', 'abcd'),
User(2, 'emma', '123456')
]

username_mapping = {u.username: u for u in users}
userid_mapping = {u.id: u for u in users}

def authenticate(name, pw):
"""Authenticate a user."""
user = username_mapping.get(name, None)
if user and safe_str_cmp(user.password, pw):
return user

def identity(payload):
"""Identify a user.

payload: the contents of JWT Token
"""
# 'identity' is one of JWT fields. Flask-JWT use this filed to populate the user's id
user_id = payload['identity']
return userid_mapping.get(user_id, None)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# app.py (snippet)

from flask import Flask
from flask_restful import Api, Resource
from flask_jwt import JWT, jwt_required

app = Flask(__name__)
api = Api(app)

"""
JWT creates a new endpoint('/auth').
When calling this endpoint and passing username and password, the authenticate function will be called.
It will do some authentication(login simulation). If the authentication succeeds, the jwt-extension will return a JWT object.

Later, when send request with this generated JWT to the server, JWT will call the identity function.
By the information from JWT's payload, if it can get the correct user, it means the JWT is valid, and the user is authenticated.
"""
# create the jwt instance
jwt = JWT(app, authenticate, identity)

class Item(Resource):
"""Advanced request parsing"""
parser = reqparse.RequestParser()
parser.add_argument('price', type=float, required=True, help="This filed cannot be left blank!")

# meaning a valid JWT token is required in the request
@jwt_required()
def get(self, name):
item = next(filter(lambda item: item['name'] == name, items), None)
return {'item': item}, 200 if item else 404

Advanced JWT configurations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
"""Advanced JWT Configuration"""

# 1. change authentication url
app.config['JWT_AUTH_URL_RULE'] = '/login'

# 2. Token expiration time(有效期): each access token must have an expiration time for security purpose
app.config['JWT_EXPIRATION_DELTA'] = timedelta(seconds=1800)

# 3. authentication key name: config JWT auth key name to be 'email' instead of default 'username'
app.config['JWT_AUTH_USERNAME_KEY'] = 'email'

# 4. authentication response handler: customize JWT auth response, include user_id in response body
@jwt.auth_response_handler
def customized_response_handler(access_token, identity):
return jsonify({
'access_token': access_token.decode('utf-8'),
'user_id': identity.id
})

# 5. error handler
@jwt.error_handler
def customized_error_handler(error):
return jsonify({
'message': error.description,
'code': error.status_code
}), error.status_code


jwt = JWT(app, authenticate, identity)

Advanced Postman Config

❤️ Link: Advanced Postman —— environment & tests

Resources vs. Models

For any code related to getting data from API users and responding with data, that will go in a resource. The resource will then call the model for any interactions with our system (e.g. databases, saving things, creating objects,...)

Resources and models are used to encapsulate the logic of user-facing and system-facing respectively, which makes it simpler for you — the developer — in the long term.

Persistent Storage: Database

SQLite DB

❤️ Link: interaction with sqlite demo

PostgreSQL with SQLAlchemy

We can use SQLAlchemy to easily replace database from SQLite to PostgreSQL or other databases. SQLAlchemy is a library that provides a nice “Pythonic” way of interacting with databases. So rather than dealing with the differences between specific dialects of traditional SQL such as MySQL or PostgreSQL or Oracle, you can leverage the Pythonic framework of SQLAlchemy to streamline your workflow and more efficiently query your data.

Most of the times, this library is used as an Object Relational Mapper (ORM) tool that translates Python classes to tables on relational databases and automatically converts function calls to SQL statements. Also, it can map objects to rows in a database.

psycopg2

SQLAlchemy is a ORM. psycopg2 is a PostgreSQL database adapter for the Python programming language. These are completely different things: SQLAlchemy generates SQL statements and psycopg2 sends SQL statements to the database. SQLAlchemy depends on psycopg2 or other database drivers to communicate with the database!

Security

Security in REST APIs is extremely important, because often applications that use our REST APIs will be sending us all sorts of data about users: passwords, e-mail addresses, names, postal addresses, security questions, bank details, and more.

In order to prevent people from intercepting the data on the internet and being able to read it, we must use Secure Sockets Layer. This sits on top of HTTP and encrypts all communication between a server and a client.

❤️ Link: Security in REST APIs

Flask Extension: Flask-JWT-Extended