├── env ├── prod │ └── .env ├── dev │ └── .env └── local │ └── .env ├── app ├── __init__.py ├── routers │ ├── __init__.py │ ├── users.py │ └── items.py ├── adapters │ ├── __init__.py │ └── client │ │ ├── nftbank_apispec.py │ │ └── nftbank_api.py ├── internal │ ├── __init__.py │ └── admin.py ├── dependencies.py └── main.py ├── .gitignore ├── Dockerfile ├── requirements.txt ├── .vscode └── launch.json └── README.md /env/prod/.env: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/routers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/adapters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/internal/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .pytest_cache 3 | -------------------------------------------------------------------------------- /env/dev/.env: -------------------------------------------------------------------------------- 1 | NFTBANK_API_KEY = "a31434c04d540107784b4b1b959071fb" -------------------------------------------------------------------------------- /env/local/.env: -------------------------------------------------------------------------------- 1 | PARTNER_API_HOST = "http://fast-partner-api.dev.nftbank.tools" 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | COPY . /app 4 | ENV PYTHONPATH /app 5 | 6 | WORKDIR /app 7 | 8 | RUN pip install -r requirements.txt 9 | 10 | CMD ["uvicorn", "app.main:app"] 11 | -------------------------------------------------------------------------------- /app/internal/admin.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | router = APIRouter() 4 | 5 | 6 | @router.post("/") 7 | async def update_admin(): 8 | return {"message": "Admin getting schwifty"} 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | uvicorn==0.18.2 2 | fastapi==0.79.0 3 | aioredis==2.0.1 4 | dependency_injector==4.39.1 5 | humps~=0.2.2 6 | pydantic~=1.9.1 7 | urllib3~=1.26.10 8 | aiohttp~=3.8.1 9 | sqlalchemy[asyncio]==1.4.37 10 | asyncpg==0.26.0 11 | python-dotenv==0.20.0 12 | -------------------------------------------------------------------------------- /app/dependencies.py: -------------------------------------------------------------------------------- 1 | from fastapi import Header, HTTPException 2 | 3 | 4 | async def get_token_header(x_token: str = Header()): 5 | if x_token != "fake-super-secret-token": 6 | raise HTTPException(status_code=400, detail="X-Token header invalid") 7 | 8 | 9 | async def get_query_token(token: str): 10 | if token != "jessica": 11 | raise HTTPException( 12 | status_code=400, detail="No Jessica token provided") 13 | -------------------------------------------------------------------------------- /app/routers/users.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | router = APIRouter() 4 | 5 | 6 | @router.get("/users/", tags=["users"]) 7 | async def read_users(): 8 | return [{"username": "Rick"}, {"username": "Morty"}] 9 | 10 | 11 | @router.get("/users/me", tags=["users"]) 12 | async def read_user_me(): 13 | return {"username": "fakecurrentuser"} 14 | 15 | 16 | @router.get("/users/{username}", tags=["users"]) 17 | async def read_user(username: str): 18 | return {"username": username} 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요. 3 | // 기존 특성에 대한 설명을 보려면 가리킵니다. 4 | // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요. 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: 현재 파일", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal", 13 | "justMyCode": true, 14 | "env": { 15 | "PYTHONPATH": "${workspaceRoot}" 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /app/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | 3 | from app.dependencies import get_query_token, get_token_header 4 | from app.internal import admin 5 | from app.routers import items, users 6 | 7 | app = FastAPI(dependencies=[Depends(get_query_token)]) 8 | 9 | 10 | app.include_router(users.router) 11 | app.include_router(items.router) 12 | app.include_router( 13 | admin.router, 14 | prefix="/admin", 15 | tags=["admin"], 16 | dependencies=[Depends(get_token_header)], 17 | responses={418: {"description": "I'm a teapot"}}, 18 | ) 19 | 20 | 21 | @app.get("/") 22 | async def root(): 23 | return {"message": "Hello Bigger Applications!"} 24 | 25 | 26 | if __name__ == "__main__": 27 | import uvicorn 28 | uvicorn.run(app, host="localhost", port=8000) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## FastAPI Tutorial 2 | 3 | ``` 4 | . 5 | ├── app # "app" is a Python package 6 | │ ├── __init__.py # this file makes "app" a "Python package" 7 | │ ├── main.py # "main" module, e.g. import app.main 8 | │ ├── dependencies.py # "dependencies" module, e.g. import app.dependencies 9 | │ └── routers # "routers" is a "Python subpackage" 10 | │ │ ├── __init__.py # makes "routers" a "Python subpackage" 11 | │ │ ├── items.py # "items" submodule, e.g. import app.routers.items 12 | │ │ └── users.py # "users" submodule, e.g. import app.routers.users 13 | │ └── internal # "internal" is a "Python subpackage" 14 | │ ├── __init__.py # makes "internal" a "Python subpackage" 15 | │ └── admin.py # "admin" submodule, e.g. import app.internal.admin 16 | ``` 17 | 18 | ### How to start FastAPI app? 19 | 20 | ``` 21 | $ uvicorn app.main:app --reload 22 | ``` 23 | 24 | ### How to call API? 25 | 26 | ```sh 27 | 28 | $ curl -X GET "http://localhost:8000/?token=jessica" 29 | 30 | ``` -------------------------------------------------------------------------------- /app/adapters/client/nftbank_apispec.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Optional, Iterator, List 3 | 4 | from pydantic import BaseModel, Field 5 | from humps import camel 6 | 7 | 8 | class DailyEstimatedPriceItemPrice(BaseModel): 9 | currency_symbol: str 10 | estimate_price: float 11 | 12 | class Config: 13 | alias_generator = camel.case 14 | allow_population_by_field_name = True 15 | 16 | 17 | class DailyEstimatedPriceItem(BaseModel): 18 | id: str = Field(alias='_id') 19 | asset_contract: str 20 | chain_id: str 21 | estimate: List[DailyEstimatedPriceItemPrice] 22 | estimated_at: str 23 | item_id: str 24 | token_id: str 25 | 26 | class Config: 27 | alias_generator = camel.case 28 | allow_population_by_field_name = True 29 | 30 | 31 | class DailyEstimatedPriceResponse(BaseModel): 32 | data: List[DailyEstimatedPriceItem] 33 | response: int 34 | message: str 35 | 36 | class Config: 37 | alias_generator = camel.case 38 | allow_population_by_field_name = True 39 | -------------------------------------------------------------------------------- /app/routers/items.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends, HTTPException 2 | 3 | from app.dependencies import get_token_header 4 | 5 | router = APIRouter( 6 | prefix="/items", 7 | tags=["items"], 8 | dependencies=[Depends(get_token_header)], 9 | responses={404: {"description": "Not found"}}, 10 | ) 11 | 12 | 13 | fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}} 14 | 15 | 16 | @router.get("/") 17 | async def read_items(): 18 | return fake_items_db 19 | 20 | 21 | @router.get("/{item_id}") 22 | async def read_item(item_id: str): 23 | if item_id not in fake_items_db: 24 | raise HTTPException(status_code=404, detail="Item not found") 25 | return {"name": fake_items_db[item_id]["name"], "item_id": item_id} 26 | 27 | 28 | @router.put( 29 | "/{item_id}", 30 | tags=["custom"], 31 | responses={403: {"description": "Operation forbidden"}}, 32 | ) 33 | async def update_item(item_id: str): 34 | if item_id != "plumbus": 35 | raise HTTPException( 36 | status_code=403, detail="You can only update the item: plumbus" 37 | ) 38 | return {"item_id": item_id, "name": "The great Plumbus"} 39 | -------------------------------------------------------------------------------- /app/adapters/client/nftbank_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import aiohttp 3 | 4 | from app.adapters.client.nftbank_apispec import DailyEstimatedPriceResponse 5 | 6 | 7 | class NFTBankApiClient: 8 | def __init__(self, host, api_key): 9 | self.host = host 10 | self.api_key = api_key 11 | 12 | async def get_daily_estimated_price(self, network_id: str, asset_contract: str, token_id: str) -> DailyEstimatedPriceResponse: 13 | url = f'{self.host}/estimates-v2/estimates/{asset_contract}/{token_id}?chain_id={network_id}' 14 | headers = {"X-API-KEY": self.api_key} 15 | async with aiohttp.ClientSession() as session: 16 | async with session.get(url, headers=headers) as resp: 17 | if resp.status != 200: 18 | raise Exception(f'{resp.status} {resp.reason}') 19 | 20 | data = await resp.text() 21 | return DailyEstimatedPriceResponse.parse_raw(data) 22 | 23 | 24 | async def main(): 25 | client = NFTBankApiClient( 26 | 'https://api.nftbank.ai', os.environ['NFTBANK_API_KEY']) 27 | 28 | # when 29 | result = await client.get_daily_estimated_price("ethereum", "0xbce3781ae7ca1a5e050bd9c4c77369867ebc307e", "4714") 30 | print(result) 31 | 32 | 33 | if __name__ == "__main__": 34 | import asyncio 35 | asyncio.run(main()) 36 | --------------------------------------------------------------------------------