У цій лекції ми зберемо всі раніше вивчені концепції і реалізуємо повноцінний API на FastAPI з інтеграцією зовнішнього сервісу. Ми організуємо проєкт, створимо ендпоінти, підключимось до зовнішнього API, обробимо отримані дані та додамо валідацію, а також напишемо прості тести. Щоб зробити проєкт цікавішим, будемо працювати з OpenWeatherMap API для надання даних про погоду.
Реалізуємо невеликий API-додаток, який дозволяє клієнтам:
- Запитувати поточну інформацію про погоду по місту.
- Валідація даних (наприклад, переконаємось, що місто дійсно вказане).
- Обробляти помилки у випадку проблем із зовнішнім API (наприклад, неправильний API-ключ або мережеві помилки).
Такі навички знадобляться при створенні реальних проєктів, де потрібно взаємодіяти з іншими сервісами: інтеграція з платіжними системами, соцмережами, аналітичними інструментами тощо. Ця вміння також часто перевіряється на співбесідах.
Створення структури проєкту
Спочатку організуємо каталог проєкту. Найбільш базова структура виглядатиме так:
weather_app/
├── app/
│ ├── main.py
│ ├── api/
│ │ ├── __init__.py
│ │ ├── weather.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── schemas.py
│ ├── services/
│ ├── __init__.py
│ ├── weather_service.py
│ ├── settings.py
├── requirements.txt
- app/main.py — основний файл для запуску додатка.
- app/api/weather.py — тут описуватимемо маршрути.
- app/models/schemas.py — зберігаємо Pydantic-схеми.
- app/services/weather_service.py — реалізуємо логіку інтеграції з зовнішнім API.
- app/settings.py — налаштування проєкту, наприклад API-ключі.
Встановлення залежностей
Почнемо з встановлення бібліотек. Нам знадобляться FastAPI, httpx і Pydantic:
pip install fastapi uvicorn httpx
Оновіть requirements.txt:
fastapi
httpx
uvicorn
Налаштування основного файлу додатка
Створимо мінімальний файл main.py:
from fastapi import FastAPI
from app.api.weather import router as weather_router
app = FastAPI(
title="Weather API",
description="A simple API to fetch weather information from OpenWeatherMap",
version="1.0.0"
)
app.include_router(weather_router, prefix="/weather")
Тут ми створюємо екземпляр FastAPI і підключаємо маршрути з модуля weather.
Реалізація ендпоінта для отримання погоди
Додамо файл app/api/weather.py:
from fastapi import APIRouter, HTTPException, Query
from app.models.schemas import WeatherResponse
from app.services.weather_service import get_weather_data
router = APIRouter()
@router.get("/", response_model=WeatherResponse)
async def get_weather(city: str = Query(..., description="City name to fetch weather data")):
"""
Fetch weather data for a given city.
"""
weather_data = await get_weather_data(city)
if not weather_data:
raise HTTPException(status_code=404, detail="Weather data not found")
return weather_data
- Маршрут: Створюємо маршрут
GET /weather/, який приймає параметрcity. - Опис: Використовуємо
Queryдля опису параметра міста. - Відповідь: Повертаємо JSON у вигляді, описаному в схемі
WeatherResponse.
Опис Pydantic-схеми
Тепер створимо файл app/models/schemas.py, де опишемо схему відповіді:
from pydantic import BaseModel
class WeatherResponse(BaseModel):
city: str
temperature: float
description: str
Цей клас задає структуру повернених даних: місто, температура і опис погоди.
Написання сервісу для роботи з зовнішнім API
Додамо файл app/services/weather_service.py:
import httpx
from app.settings import OPENWEATHERMAP_API_KEY
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
async def get_weather_data(city: str):
"""
Fetch weather data from OpenWeatherMap API.
"""
params = {
"q": city,
"appid": OPENWEATHERMAP_API_KEY,
"units": "metric" # Get temperature in Celsius
}
async with httpx.AsyncClient() as client:
response = await client.get(BASE_URL, params=params)
if response.status_code != 200:
return None
data = response.json()
return {
"city": data["name"],
"temperature": data["main"]["temp"],
"description": data["weather"][0]["description"]
}
- Функція відправляє асинхронний GET-запит до OpenWeatherMap API.
- Парсить відповідь і витягує потрібні дані: назву міста, температуру і опис.
Налаштування API-ключа
Створимо файл app/settings.py:
import os
OPENWEATHERMAP_API_KEY = os.getenv("OPENWEATHERMAP_API_KEY", "your_api_key_here")
Не забудьте замінити your_api_key_here на ваш реальний API-ключ. Або встановіть змінну оточення OPENWEATHERMAP_API_KEY.
Обробка помилок
Якщо в сервісу OpenWeatherMap виникне помилка (наприклад, неправильний API-ключ або неіснуюче місто), ми повертаємо None. У маршруті це перетворюється на HTTP-помилку 404 через HTTPException.
Можна покращити обробку помилок:
if response.status_code == 404:
return {"error": "City not found"}
elif response.status_code == 401:
return {"error": "Invalid API key"}
response.raise_for_status()
Тестування ендпоінта
Простий клієнт для перевірки:
створіть файл test_weather.py:
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
@pytest.mark.parametrize("city", ["London", "Paris", "Tokyo"])
def test_get_weather(city):
response = client.get(f"/weather/?city={city}")
assert response.status_code == 200
assert "temperature" in response.json()
- Тести перевіряють кілька міст.
- Переконайтесь, що відповідь містить поле
temperature.
Запуск додатка
Запустіть сервер за допомогою команди:
uvicorn app.main:app --reload
Перейдіть за адресою http://127.0.0.1:8000/docs, щоб протестувати ендпоінт через Swagger UI.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ