Create a typed web app with Python's web framework "Fast API" and TypeScript / OpenAPI-Technology stack for machine learning web apps

Intro Introducing FastAPI + TypeScript + OpenAPI as a technology stack for quickly creating web applications with machine learning and image processing as the back end implemented in Python.

motivation

--I want to set up a web server (API server) quickly with Python ――Use like you used Flask until now --"In Python" ――Because it is a machine learning / image processing service ―― "Crispy" ――I want to enjoy validation --I want type guarantee for both server and client ――Machine learning and image processing apps tend to have many parameters ・ There is no consistent convention, so it is easy to make mistakes - width or w --The range of values is [0, w] or [0, 1]? -→ I want to cover with type annotation

Choices considered

Web framework

Type guarantee

Conclusion first

FastAPI? FastAPI logo https://fastapi.tiangolo.com/

--Starlette base

Demo

Of the above motivations

――Set up an API quickly --Type-guaranteed clients can be used

Demo of that

Click here for sample Repository: https://github.com/tuttieee/fastapi-typescript-openapi-example

Basically, proceed according to the Fast API tutorial, but it has been slightly modified for ↑.

--Including frontend (TypeScript) in the sample to cover up to creating WebApp --Develop and deploy with Docker

Launch with Docker

As per https://fastapi.tiangolo.com/deployment/.

--Digging one level and putting it in / backend to make it a monorepo with frontend. ――In order to add migration etc. later, dig another layer and make it / backend / app / app. / backend / app is mounted in docker container --Manage with docker-compose

After docker-compose build, docker-compose up, try curl localhost: 8000

$ curl localhost:8000
{"Hello":"World"}

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/703af24fff8d39e261e1f1ce5ba859f28e806fb2

Development settings

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/33f6e91bc48c9c6396a9ceaf1e720b88c78a6822

Try to create an API connected to SQL

Basically proceed according to ↓. The part that uses MySQL is added independently.

https://fastapi.tiangolo.com/tutorial/sql-databases/

SQLAlchemy installation

For the time being, use Dockerfile directly RUN pip install sqlalchemy

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/75b7d2d7ccb8bc5a142b86bd3800b5e190732628

First SQLite

--Create ʻapp / app / __ init__.py, ʻapp / app / database.py, ʻapp / app / models.py --Try to see if it works --Enter the shell andpython -i` -Try hitting ↓

from app import models
from app.database import SessionLocal, engine
models.Base.metadata.create_all(bind=engine)
user = models.User(email='[email protected]', hashed_password='')
db = SessionLocal()
db.add(user)
db.commit()
db.close()
db = SessionLocal()
db.query(models.User).all()

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/8f0d85445c33bfddd05466060b11501c2f3748b3

MySQL

Install related software in backend container

RUN apt-get update && apt-get install -y \
    default-mysql-client \ 
&& apt-get clean \ 
&& rm -rf /var/lib/apt/lists/*

RUN pip install sqlalchemy PyMySQL

sample:

Add MySQL container

-Use Official image. Basically, write the settings in docker-compose.yml as per the README --Add Credentials to .env and pass it as an environment variable with docker-compose.yml

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/22d760d963394f340622ea402fdcf72f02cd240f

Alembic --Added pip install alembic to Dockerfile (will it be converted to requirements.txt?)-> Docker-compose build --Update database.py --Update database URL for MySQL --Update create_engine for MySQL (remove unnecessary arguments) --Added naming convention

Tips: You can use the starlette class as it is

Then enter the shell and do the following:

Initialization

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/979f4b3982b3ce3e7631661af3171000d6ea0381

Setting

Ref: https://alembic.sqlalchemy.org/en/latest/autogenerate.html

--Editing ʻalembic.ini --Deletesqlalchemy.url (because it will be taken from database.py) --Edit migrations / env.py` -Added ↓

from app.models import Base
from app.database import SQLALCHEMY_DATABASE_URL

config.set_main_option("sqlalchemy.url", SQLALCHEMY_DATABASE_URL)
target_metadata = Base.metadata
Migration generation

--Define models.py for MySQL (specify the length in String)

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/4ceae799f6f6b83ffc71cfec5ce13a669e137757

Migration execution
Try out

--Run the following with python -i

from app import models
from app.database import SessionLocal
user = models.User(email='[email protected]', hashed_password='')
db = SessionLocal()
db.add(user)
db.commit()
db.close()

db = SessionLocal()
db.query(models.User).all()

API creation

Pydantic model creation

See https://fastapi.tiangolo.com/tutorial/sql-databases/#create-initial-pydantic-models-schemas ʻApp / schemas.py` created

Create CRUD utils

See https://fastapi.tiangolo.com/tutorial/sql-databases/#crud-utils ʻApp / crud.py` created

Main API creation

See https://fastapi.tiangolo.com/tutorial/sql-databases/#main-fastapi-app ʻApp / main.py` update

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/1e7d140c1d7929c15f7815648deb14f831807ddd

Operation check

cURL example:

From OpenAPI http://localhost:8000/docs

MISC

from fastapi.routing import APIRoute

...

def use_route_names_as_operation_ids(app: FastAPI) -> None:
  """
  Simplify operation IDs so that generated API clients have simpler function
  names.

  Should be called only after all routes have been added.
  """
  for route in app.routes:
      if isinstance(route, APIRoute):
          route.operation_id = route.name

use_route_names_as_operation_ids(app)

Frontend Create React App

$ yarn create react-app frontend --template typescript
$ cd frontend
$ yarn start

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/21b1f0f926132fdbf9c1dbef967ef4286fa27279

(For the time being) API access

--CORS settings --Added the following to backend / app / app / main.py

# TODO: This is for development. Remove it for production.
origins = [
    "<http://localhost:3000",>
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
import React, { useState, useEffect } from 'react';
import './App.css';
import { DefaultApi, Configuration, User } from './api-client';

const config = new Configuration({ basePath: 'http://localhost:8000' }); // TODO: This is for dev
export const apiClient = new DefaultApi(config);

const App: React.FC = () => {
  const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    apiClient.readUsers().then((response) => {
      setUsers(response.data);
    })
  })

  return (
    <div className="App">
      <ul>
        {users.map(user =>
          <li key={user.id}>{user.email}</li>
        )}
      </ul>
    </div>
  );
}

export default App;

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/79fc0bb26c6ffa7e6e43473f0ef497dd1c2f53ff

Problems with the current code base

--Must be typed manually --Write yourself ʻinterface User`

Client automatic generation by OpenAPI

Intro OpenAPI: Former Swagger FastAPI will generate a JSON definition for OpenAPI: <http: //localhost: 8000/openapi.json> OpenAPI Generator that eats this JSON and generates clients in various languages

Run OpenAPI Generator with Docker

See https://openapi-generator.tech/docs/installation.html#docker

--Create docker-compose.openapi-generator.yml

version: "3.7"

services:
  openapi-generator:
    image: openapitools/openapi-generator-cli
    volumes:
      - ./frontend:/frontend
    working_dir: /frontend
    command:
      - generate
      - -g
      - typescript-axios
      - -i
      - <http://backend/openapi.json>
      - -o
      - /frontend/src/api-client
      - --additional-properties=supportsES6=true,modelPropertyNaming=original
      # modelPropertyNaming=original is necessary though camelCase is preferred
      # See <https://github.com/OpenAPITools/openapi-generator/issues/2976>

--Add command to Makefile

oapi/gen:
  docker-compose -f docker-compose.yml -f docker-compose.openapi-generator.yml up openapi-generator \
  && docker-compose -f docker-compose.yml -f docker-compose.openapi-generator.yml rm -f openapi-generator

Rewrite Frontend to use auto-generated code

frontend/src/App.tsx

import React, { useState, useEffect } from 'react';
import './App.css';
import { DefaultApi, Configuration, User } from './api-client';

const config = new Configuration({ basePath: '<http://localhost:8000'> }); // TODO: This is for dev
export const apiClient = new DefaultApi(config);

const App: React.FC = () => {
  const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    apiClient.readUsers().then((response) => {
      setUsers(response.data);
    })
  })

  return (
    <div className="App">
      <ul>
        {users.map(user =>
          <li>{user.email}</li>
        )}
      </ul>
    </div>
  );
}

export default App;

Sample: https://github.com/tuttieee/fastapi-typescript-openapi-example/commit/1d38242a63c9a5477fa1cde506f36f68a19060a7

Other

What I didn't deal with in this demo

In the actual project, Unittest, CI, Linter, etc. are adopted as appropriate.

Other advantages of FastAPI, why we chose FastAPI compared to other technologies

Recommended Posts

Create a typed web app with Python's web framework "Fast API" and TypeScript / OpenAPI-Technology stack for machine learning web apps
Easy machine learning with scikit-learn and flask ✕ Web app
Create a machine learning app with ABEJA Platform + LINE Bot
Create a social integration API for smartphone apps with Django
How to create a serverless machine learning API with AWS Lambda
Created a new corona app in Kyoto with Python's web framework Dash
Create a GUI app with Python's Tkinter
Machine Learning x Web App Diagnosis: Recognize CAPTCHA with Cloud Vision API
Create a simple web app with flask
Create a clean DB for testing with FastAPI and unittest the API with pytest
Until you create a machine learning environment with Python on Windows 7 and run it
Create a Todo app with Django REST Framework + Angular
Create a native GUI app with Py2app and Tkinter
Create a Todo app with the Django REST framework
Create a machine learning environment from scratch with Winsows 10
Build a machine learning scikit-learn environment with VirtualBox and Ubuntu
Create a web surveillance camera with Raspberry Pi and OpenCV
Tornado-Let's create a Web API that easily returns JSON with JSON
Create a python machine learning model relearning mechanism with mlflow
Create a web API that can deliver images with Django
(For beginners) Try creating a simple web API with Django
Easy deep learning web app with NNC and Python + Flask
Building a Windows 7 environment for getting started with machine learning with Python
Create a striped illusion with gamma correction for Python3 and openCV3
Web App Development Practice: Create a Shift Creation Page with Django! (Shift creation page)
Create a REST API to operate dynamodb with the Django REST Framework
Create and return a CP932 CSV file for Excel with Chalice