Python JSON API dev
Tags: python, showdev, webdev, database
Published: 2020-06-01 16:40:31

# Introduction

I created a library that allows you to easily develop an API using SQLAlchemy models: [safrs](https://github.com/thomaxxl/safrs). In this series I will demonstrate how to use the library to implement an API similar to this [live demo](http://thomaxxl.pythonanywhere.com/api). The exposed endpoints are compliant with the [json:api specification](https://jsonapi.org/format/). This means the API provides all functionality you'd expect from a JSON API, such as relationships, resource linkage,  pagination, filtering, sorting, and much more. An OpenAPI (fka swagger) frontend is automatically generated to allow API exploration.

In this first part, I explain how to build a simple API app. The source code of this app can be found [here](https://github.com/thomaxxl/safrs/blob/master/examples/demo_devto.py)

# Installation

The safrs package can be installed by cloning the github repository or by using pip, e.g.:
```bash
python3 -m pip install safrs --user
```
# SQLAlchemy Objects

The objects that will be exposed are almost identical to SQLAlchemy models. The following class for example, describes attributes and a relationship for an object called `User`, based on a `Users` table.

```python
class User(SAFRSBase, db.Model):
    """
        description: User description
    """

    __tablename__ = "Users"
    id = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, default="")
    email = db.Column(db.String, default="")
    books = db.relationship("Book", back_populates="user", lazy="dynamic")
```

Similar to the `User` class, a `Book` class is created:

```python
class Book(SAFRSBase, db.Model):
    """
        description: Book description
    """

    __tablename__ = "Books"
    id = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, default="")
    user_id = db.Column(db.String, db.ForeignKey("Users.id"))
    user = db.relationship("User", back_populates="books")
```
# API Endpoints
To create Flask API endpoints for these objects, all you have to do is create an API and expose the objects:

```python
def create_api(app, HOST="localhost", PORT=5000, API_PREFIX=""):
    api = SAFRSAPI(app, host=HOST, port=PORT, prefix=API_PREFIX)
    api.expose_object(User)
    api.expose_object(Book)
```

# Flask App
All that is left to do now is creating the Flask app and populate the in-memory sqlite database: 

```python
def create_app(config_filename=None, host="localhost"):
    app = Flask("demo_app")
    app.config.update(SQLALCHEMY_DATABASE_URI="sqlite://")
    db.init_app(app)

    with app.app_context():
        db.create_all()
        # Populate the db with users and a books and add the book to the user.books relationship
        for i in range(200):
            user = User(name=f"user{i}", email=f"email{i}@dev.to")
            book = Book(name="test_book")
            user.books.append(book)

        create_api(app, host)
    return app
```

The API app can be started by calling the flask `app.run`:

```python
host = "127.0.0.1" # address where the api will be hosted, change this if you're not running the app on localhost!
app = create_app(host=host)

if __name__ == "__main__":
    app.run(host=host)
```

# Dependencies and SQLAlcheymy DB Instance
The [app script](https://github.com/thomaxxl/safrs/blob/master/examples/demo_devto.py) should start with the imports and SQLAlchemy db:

```python
import sys
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from safrs import SAFRSBase, SAFRSAPI

db = SQLAlchemy()
```

# Running the App

You can start the app from the command line with
```bash
python3 demo_devto.py
```

You can now browse to http://127.0.0.1 to check out the API documentation and use the API. In the swagger UI, you will see the endpoints for the `User` and `Book` classes:

![Swagger](https://dev-to-uploads.s3.amazonaws.com/i/jx8ujavy71d3zxvcyyzl.PNG)

In the following post I will go into more detail explaining the API functionality and customizations.