Flask – a quick way to configure Rest API service

Working in IT, we sometimes want the customer to have an API to perform pre-defined activities on our servers. We are hoping to manage the activities in a script to control what can be done, allow customer or partner to perform the activities on their own. Restful API is a great way to simplify and automate this. Python3 and flask provide a perfect way to quickly set up an API service.

If we decide to develop Rest API for automation, we want the script (let’s call it dapi.py) to be portable and the configuration done outside of the script itself. YAML file is a good place to store configurations. For example, a file dapi.yml looks like this:

service:
    api_port: 7999
localtool:
    bin_path: /usr/bin/tool
    worker_node: 192.168.37.7
output:
    local_path: /ftp/output/

Here is an example of python script:

#!/usr/bin/env python3
from flask import Flask
from flask import request
from flask import jsonify
from datetime import datetime
from pathlib import Path
import yaml
import os
import io
import json
import re

app = Flask(__name__)

@app.route('/')
def home():
    return "Default Page."
@app.route('/dapi/task1', methods=['GET'])
def task1():
    # implementation of task1 in python3
    msg="all good with task1!"
    return jsonify(result=msg),200,{'Content-Type': 'application/json'}

@app.route('/dapi/task2', methods=['GET'])
def task2():
    # implementation of task2 in python3
    args=request.args
    fileid=args.get('fileid') or None
    filename=args.get('filename') or None
    msg="all good with task1!"
    code=__subtask(filid,filename)
    return jsonify(result=msg),200,{'Content-Type': 'application/json'}

def __subtask(id,name):
    # implementation of sub function
    print(name)
    return 200

if __name__ == "__main__":
    cfg_file_path=Path(__file__).with_suffix('.yml').as_posix()
    with open(cfg_file_path,'r') as ymlfile:
        cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)
    # Load up and normalize configurations
    api_port=cfg["service"]["api_port"]
    local_path=cfg["output"]["local_path"]
    bin_path=cfg["localtool"]["bin_path"]
    worker_node=cfg["localtool"]["worker_node"]
    app.run(host="0.0.0.0",port=api_port,debug=True)

This script provide two end points, and exemplified how to parse out argument from URL, how to load up configurations from yml file. To start the service, simply run:

python3 dapi.py

or ./dapi.py since the shebang line already specifed python3 as interpreter.

The port is defined in YAML file. This can be configured as a systemd service in Linux by creating /etc/systemd/system/dapi.service with appropriate entries. The file can look as simple as this:

[Unit]
Description = API for D
After = network.target

[Service]
Type=simple
ExecStart = /home/ghunch/dapi/dapi_handler.py

[Install]
WantedBy = multi-user.target

We can also use Nginx to front the API in order to add security, caching capabilities, for example:

proxy_cache_path /home/ghunch/dapi/cache levels=1:2 keys_zone=dapicache:5m;
server {
    listen      5000;
    server_name _;
    proxy_cache dapicache;
    location / {
        proxy_pass http://localhost:4999;
    }
}