본문으로 건너뛰기

1) FastAPI Tutorial

Chapter Preview


목표

  1. FastAPI 의 공식 문서를 참고하여 간단한 API 를 만들어봅니다.
  2. Path Parameter 와 Query Parameter 를 이해하고 이를 이용하여 API 를 만들어봅니다.

스펙 명세서

FastAPI 의 공식 문서를 참고하여 튜토리얼을 진행합니다.

  1. 필요한 패키지를 설치합니다.
  2. First Steps 를 따라 FastAPI 를 이용해 간단한 API 를 만들어봅니다.
  3. Path Parameter 에 대해 학습합니다.
  4. Query Parameter 에 대해 학습합니다.
  5. Path Parameter 와 Query Parameter 를 모두 사용하여 API 를 만들어봅니다. [Multiple Path and Query Parameters]
https://github.com/mlops-for-mle/mlops-for-mle/tree/main/part5

해당 파트의 전체 코드는 mlops-for-mle/part5/ 에서 확인할 수 있습니다.

part5
├── Dockerfile
├── Makefile
├── crud_path.py
├── crud_pydantic.py
├── crud_query.py
├── main.py
├── multi_param.py
├── path_param.py
└── query_param.py

0. 패키지 설치

필요한 패키지를 설치합니다. [Tutorial - User Guide - Intro]

pip install "fastapi[all]"

1. FastAPI 를 이용해 간단한 API 만들어보기

1.1 main.py

다음과 같이 main.py 를 작성합니다. [First Steps]

main.py
# main.py
from fastapi import FastAPI

# Create a FastAPI instance
app = FastAPI()


@app.get("/")
def read_root():
return {"Hello": "World"}

1.2 실행

터미널에 다음 명령어를 입력하여 작성한 파일을 실행합니다. 파일을 실행하는 법은 아래와 같습니다.

uvicorn main:app --reload
  • uvicorn :
    • FastAPI 를 실행하는 웹 서버 실행 Command Line Tool 입니다.
  • main :
    • 위에서 작성한 Python 모듈 main.py 를 의미합니다.
  • app :
    • main.py 에서 app = FastAPI() 를 통해 생성된 객체를 의미합니다.
  • --reload :
    • 코드가 바뀌었을 때 서버가 재시작할 수 있도록 해주는 옵션입니다.

1.3 실행 확인

이제 http://localhost:8000 에 접속해보면 다음 화면과 같이 {"Hello": "World"} 가 표시되는 것을 확인할 수 있습니다.

API Running Screen [그림 5-2] main.py 실행 화면

이번에는 http://localhost:8000/docs 에 접속해 봅시다. 다음과 같은 형태로 된 화면을 볼 수 있습니다.

Swagger UI Screen [그림 5-3] Swagger UI 화면

이는 Swagger UI 에 의해 제공되는 interactive API documentation 입니다. 이를 통해 작성한 API 를 다양하게 실험을 해보면서 잘 작성되었는지 테스트해 볼 수 있습니다.

2. Step by Step 으로 이해하기

이번에는 First Steps 에 있는 코드를 한 줄씩 살펴보겠습니다.

2.1 Step 1: Import FastAPI

API 를 만들 수 있도록 도와주는 Python 클래스 FastAPI 를 import 합니다.

from fastapi import FastAPI

2.2 Step 2: Create a FastAPI Instance

FastAPI 클래스의 인스턴스를 생성합니다. 여기에서 생성하는 인스턴스의 이름에 따라 uvicorn main:app --reload 과 같은 형태로 실행 명령어가 달라지게 됩니다.

app = FastAPI()

2.3 Step 3: Create a Path Operation

여기에서 말하는 path 는 URL 에서 첫 번째 / 부터 시작되는 마지막 부분을 의미합니다. 예를 들어 URL 이 https://example.com/items/foo 와 같이 되어 있다면, path 는 /items/foo 라고 할 수 있습니다.

Operation 은 POST, GET, PUT, DELETE 등과 같은 HTTP Method 를 의미합니다. 이러한 operation 을 수행하기 위해 @app.get("/") 와 같은 Path Operation Decorator 를 사용합니다.

@app.get("/") 은 FastAPI 로 하여금 path / 로 가서 GET operation 을 수행하라는 의미로 사용할 수 있습니다.

2.4 Step 4: Define the Path Operation Function

Path Operation Function 은 Path Operation 이 수행되었을 때 호출될 Python 함수를 말합니다. 다음과 같은 형태로 사용되었습니다.

@app.get("/")
def read_root():
return {"Hello": "World"}

2.5 Step 5: Return the Content

Path Operation Function 을 통해 return 하는 값으로는 dict , list , str , int 등이 가능합니다. 또한, 뒤에서 나올 Pydantic Model 의 형태로도 return 할 수 있습니다.

3. Path Parameter 이해하기

Path Parameter 는 Path Operation 에 포함된 변수로 사용자에게 입력받아 function 의 argument 로 사용되는 parameter 를 의미합니다. [Path Parameters]

다음과 같이 path_param.py 를 작성하고 uvicorn path_param:app --reload 를 입력하여 실행합니다.

path_param.py
# path_param.py
from fastapi import FastAPI

# Create a FastAPI instance
app = FastAPI()


@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}

위와 같이 path 에 parameter 를 입력하도록 할 수 있습니다. 여기에서 item_id 와 같은 parameter 를 Path Parameter 라고 합니다. 입력된 Path Parameter 의 값은 function 에 argument 로 전달되어 함수가 호출됩니다.

또한, def read_item(item_id: int) 와 같이 type (여기에서는 int)을 제공할 수 있는데 이 때 제공된 것과 다른 type (예를 들어 str) 의 데이터가 입력되면 다음과 같은 형태로 HTTP Error 를 return 하게 됩니다.

{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}

4. Query Parameter 이해하기

Query Parameter 는 function parameter 로는 사용되지만 Path Operation 에 포함되지 않아 Path Parameter 라고 할 수 없는 parameter 를 의미합니다. [Query Parameters]

다음과 같이 query_param.py 를 작성하고 uvicorn query_param:app --reload 를 입력하여 실행합니다.

query_param.py
# query_param.py
from fastapi import FastAPI

# Create a FastAPI instance
app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


@app.get("/items/")
def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]

Path Parameter 와는 다르게, function 에 parameter 로 들어있는 skiplimit 이 Path Operation 인 @app.get("/items/") 에는 들어있지 않습니다.

Query 는 URL 에서 ? 뒤에 key-value 쌍의 형태로 나타나고, & 로 구분되어 사용됩니다.
예를 들면, 위와 같은 경우 http://localhost:8000/items/?skip=0&limit=10 과 같은 형태로 사용할 수 있습니다.
Query Parameter 는 path 의 고정된 부분이 아니기 때문에 optional 로 사용될 수 있고, 따라서 기본값을 가질 수 있습니다.
위의 예시에서는 skip=0limit=10 의 기본값을 가지고 있습니다.

하지만 항상 기본값을 가지는 것은 아니고, 값을 입력받아야만 하는 Query Parameter 도 존재합니다. 이를 Required Query Parameter 라고 하고 다음과 같은 형태로 사용할 수 있습니다.

@app.get("/items/{item_id}")
def read_user_item(item_id: str, needy: str):
item = {"item_id": item_id, "needy": needy}
return item

위의 예시에서 needy 는 Path Operation @app.get("/items/{item_id}") 에 포함되어 있지 않으므로 Query Parameter 이고, function read_user_item() 에서 기본값이 존재하지 않기 때문에 Required Query Parameter 임을 알 수 있습니다.
이러한 경우 http://localhost:8000/items/foo-item 와 같이 needy 를 입력해주지 않으면 에러가 발생합니다.
따라서 http://localhost:8000/items/foo-item?needy=someneedy 와 같은 형태로 ? 뒤에 입력을 해 주어야 에러가 발생하지 않고 함수가 제대로 동작합니다.

5. Multiple Path and Query Parameters 사용해보기

Path Parameter 와 Query Parameter 를 모두 사용하여 Path Operation Function 을 작성해봅니다. [Multiple Path and Query Parameters]

다음과 같이 multi_param.py 를 작성합니다.

multi_param.py
# multi_param.py
from typing import Union

from fastapi import FastAPI

# Create a FastAPI instance
app = FastAPI()


@app.get("/users/{user_id}/items/{item_id}")
def read_user_item(user_id: int, item_id: str, q: Union[str, None] = None, short: bool = False):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"},
)
return item

먼저 Path Operation 을 보면, @app.get("/users/{user_id}/items/{item_id}") 로 되어 있습니다. 이를 통해 user_iditem_id 라는 Path Parameter 가 있음을 알 수 있습니다.

이번에는 Path Operation Function 의 parameter 를 살펴 보면, user_id, item_id, q, short 가 있음을 알 수 있습니다. 이 중 Path Parameter 가 아닌 qshort 는 Query Parameter 임을 알 수 있고, 기본값이 각각 NoneFalse 임을 알 수 있습니다.

다음과 같은 형태로 URL 을 줄 수 있을 것입니다.

  • http://localhost:8000/users/3/items/foo-item?q=hello&short=True
  • http://localhost:8000/users/3/items/foo-item?short=True
  • http://localhost:8000/users/3/items/foo-item?q=hello
  • http://localhost:8000/users/3/items/foo-item