๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Back-end & Server/FastAPI

[FastAPI] Metadata Docs, Testing, Debugging

728x90
๋ฐ˜์‘ํ˜•

 

๐Ÿฐ FastAPI ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด์„œ ๊ฐœ์ธ์ ์œผ๋กœ ์ •๋ฆฌํ•œ ๊ธ€ ์ž…๋‹ˆ๋‹ค.

 

Metadata Docs

/docs๋กœ ์ ‘์†ํ–ˆ์„ ๋•Œ ๋‚˜์˜ค๋Š” Swagger UI ํŽ˜์ด์ง€์— Metadata๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

FastAPI()์— ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋œ๋‹ค.

Parameter Type Description
title str API์˜ ์ œ๋ชฉ
summary str API ์š”์•ฝ
description str API ์„ค๋ช…
version string API ๋ฒ„์ „
terms_of_service str API์˜ ์•ฝ๊ด€์˜ URL
contact dict API ๊ด€๋ จ ์—ฐ๋ฝ์ •๋ณด, fields -> (name:str, url:str, email:str)
license_info dict API ๊ด€๋ จ ๋ผ์ด์„ผ์Šค ์ •๋ณด, fields -> (name:str, identifier:str, url:str)

 

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

description = """
Jinja2 Template & Static Test. ๐ŸŽˆ

## Template 
Use -> **Jinja2Templates()**

## Static
Use -> StaticFiles()

Thank You..!
"""

app = FastAPI(
    title="Jinja2Template & Static Test App",
    description=description,
    summary="I like FastAPI...",
    version="0.0.1",
    terms_of_service="http://example.com/terms/",
    contact={
        "name": "Pupba",
        "url": "http://x-force.example.com/contact/",
        "email": "pupba13@x-force.example.com"
    },
    license_info={
        "name": "MIT",
        "url": "https://opensource.org/license/mit"
    }
)

app.mount("/static", StaticFiles(directory="static"), name="static")


templates = Jinja2Templates(directory="templates")


@app.get("/items/{id}", response_class=HTMLResponse)
async def read_item(request: Request, id: str):
    return templates.TemplateResponse(
        request=request, name="index.html", context={"id": id}
    )

 

 

๋งค๊ฐœ๋ณ€์ˆ˜๋กœ url์„ ์ „๋‹ฌํ•ด openapi.json์— ๋Œ€ํ•œ ๊ฒฝ๋กœ๋ฅผ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

app = FastAPI(openapi_url="/api/v1/openapi.json")

 

/docs์™€ /redoc ํŽ˜์ด์ง€๋ฅผ ์—†์• ๊ฑฐ๋‚˜ ์ž„์˜์˜ ์ฃผ์†Œ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

app = FastAPI(docs_url="/documentation", redoc_url=None)
# None์˜ ๊ฒฝ์šฐ ํŽ˜์ด์ง€ ๋น„ํ™œ์„ฑํ™”

 

 

Testing

fastapi์˜ TestClient๋ฅผ ์‚ฌ์šฉํ•ด ์—”๋“œํฌ์ธํŠธ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

TestClient๋Š” httpx๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— httpx๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.

pip install httpx

 

main.py

from typing import Dict
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles


app = FastAPI()


@app.get("/items/{id}", response_model=Dict[str, str])
async def read_item(id: str):
    return {"id": id}


@app.get("/item/{item}", response_model=Dict[str, str])
async def get_item(item: str):
    return {"Item": item}

 

Testing file - /tests/test_main.py

from fastapi.testclient import TestClient

from ..main import app

client = TestClient(app)


def test_read_item():
    response = client.get('/items/foo')
    assert response.status_code == 200
    assert response.json() == {"id": "foo"}


def test_read_item_bad():
    response = client.get('/items/')
    assert response.status_code == 404
    assert response.json() == {"detail": "Not Found"}


def test_get_item():
    response = client.get('/item/foo')
    assert response.status_code == 200
    assert response.json() == {"Item": "foo"}


def test_get_item_bad():
    response = client.get('/item/')
    assert response.status_code == 404
    assert response.json() == {"detail": "Not Found"}

 

์ด์ œ pytest๋ฅผ ์„ค์น˜ํ•˜๊ณ  test๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

pip install pytest

 

* pytest๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋Š” /app/... ๊ฐ™์ด ํŒŒ์ผ๋“ค์ด ํŒจํ‚ค์ง€๋กœ ๋ฌถ์—ฌ์žˆ์–ด์•ผ ํ•จ, (ํ…Œ์ŠคํŠธ ์ •์˜ ํŒŒ์ผ ์ œ์™ธ)ํŒŒ์ผ์ด๋‚˜ ํŒจํ‚ค์ง€ ์ด๋ฆ„์— test ๊ด€๋ จ๋˜ ์ด๋ฆ„์ด ์•ˆ๋“ค์–ด๊ฐ€๊ฒŒ ์กฐ์‹ฌ!

pytest

 

์ด๋ ‡๊ฒŒ API๋“ค์„ ํ…Œ์ŠคํŠธ ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

 

Debugging

uvicorn์„ ์‚ฌ์šฉํ•ด์„œ FastAPI App์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹คํ–‰ ํฌํŠธ, ์ฃผ์†Œ ๋“ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

from typing import Dict
from fastapi import FastAPI
import uvicorn


app = FastAPI()


@app.get("/items/{id}", response_model=Dict[str, str])
async def read_item(id: str):
    return {"id": id}


@app.get("/item/{item}", response_model=Dict[str, str])
async def get_item(item: str):
    return {"Item": item}

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8080)

 

IDE์˜ Debug ๊ธฐ๋Šฅ์œผ๋กœ Debugging์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

728x90
๋ฐ˜์‘ํ˜•

'Back-end & Server > FastAPI' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[FastAPI] ๋ฐฐํฌ  (0) 2024.02.24
[FastAPI] Template  (0) 2024.02.23
[FastAPI] Background Task  (0) 2024.02.23
[FastAPI] ํŒŒ์ผ ๋ถ„ํ•   (0) 2024.02.23
[FastAPI] Relational Database  (0) 2024.02.22