[PYTHON]

Intro

Einführung von FastAPI + TypeScript + OpenAPI als Technologie-Stack zum schnellen Erstellen von Webanwendungen mit maschinellem Lernen und Bildverarbeitung als Back-End in Python. Motivation - Ich möchte mit Python schnell einen Webserver (API-Server) einrichten - Verwenden Sie wie früher Flask - "Mit Python" ――Weil es sich um einen maschinellen Lern- / Bildverarbeitungsdienst handelt ―― "Knusprig" ――Ich möchte die Validierung genießen

Entscheidungen berücksichtigt

Web-Framework

Typgarantie

Fazit zuerst

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

Demo

Von den oben genannten Motivationen

―― Richten Sie schnell eine API ein

Demo davon

Klicken Sie hier für ein Beispiel-Repository: https://github.com/tuttieee/fastapi-typescript-openapi-example

Fahren Sie grundsätzlich gemäß dem Fast API-Tutorial fort, das jedoch für ↑ leicht geändert wurde.

Starten Sie mit Docker

Gemäß https://fastapi.tiangolo.com/deployment/.

Versuchen Sie nach docker-compose build, docker-compose up curl localhost: 8000

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

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

Entwicklungseinstellungen

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

Versuchen Sie, eine mit SQL verbundene API zu erstellen

Grundsätzlich nach ↓ vorgehen. Der Teil, der MySQL verwendet, wird unabhängig hinzugefügt.

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

SQL Alchemy-Installation

Verwenden Sie vorerst direkt die Docker-Datei, um "pip install sqlalchemy" auszuführen

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

Erste SQLite

--Erstellen Sie "app / app / __ init __. Py", "app / app / database.py", "app / app / models.py"

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()

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

MySQL

Installieren Sie die zugehörige Software im 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

Stichprobe:

Fügen Sie einen MySQL-Container hinzu

-Verwenden Sie Offizielles Bild. Grundsätzlich schreiben Sie die Einstellungen in "docker-compose.yml" gemäß README

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

Alembic

Tipps: Sie können die Starlette-Klasse unverändert verwenden

Betreten Sie dann die Shell und gehen Sie wie folgt vor:

Initialisieren

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

Aufbau

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

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
Migrationsgenerierung

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

Ausführung der Migration
Ausprobieren
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-Erstellung

Pydantische Modellbildung

See https://fastapi.tiangolo.com/tutorial/sql-databases/#create-initial-pydantic-models-schemas Erstellen Sie app / schemas.py

Erstellen Sie CRUD-Utils

See https://fastapi.tiangolo.com/tutorial/sql-databases/#crud-utils Erstellen Sie app / crud.py

Haupt-API-Erstellung

See https://fastapi.tiangolo.com/tutorial/sql-databases/#main-fastapi-app app / main.py Update

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

Funktionsprüfung

cURL Beispiel:

Von der offenen API 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

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

(Vorerst) API-Zugriff

--CORS-Einstellungen

# 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;

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

Probleme mit der aktuellen Codebasis

Automatische Client-Generierung durch OpenAPI

Intro OpenAPI: Ehemaliger Swagger FastAPI generiert eine JSON-Definition für OpenAPI: <http: // localhost: 8000 / openapi.json> Open API Generator, der diesen JSON frisst und Clients in verschiedenen Sprachen generiert

Führen Sie Open API Generator mit Docker aus

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

--Erstelle 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>
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

Schreiben Sie das Frontend neu, um automatisch generierten Code zu verwenden

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;

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

Andere

Was ich in dieser Demo nicht behandelt habe

Im eigentlichen Projekt werden Unittest, CI, Linter usw. entsprechend übernommen.

Weitere Vorteile von FastAPI, warum wir FastAPI im Vergleich zu anderen Technologien eingeführt haben

Recommended Posts