-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
103 lines (81 loc) · 3.63 KB
/
Copy pathmain.py
File metadata and controls
103 lines (81 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# ============================================================
# main.py — Application entrypoint
#
# Responsibilities:
# 1. Create the FastAPI app
# 2. On startup: create all DB tables (SQLAlchemy does this)
# and seed 3 example tasks if the table is empty
# 3. Mount the task router (/tasks/*)
# 4. Define global endpoints (/, /health, /stats)
# ============================================================
from fastapi import FastAPI, Depends
from contextlib import asynccontextmanager
from app.routes import router as tasks_router
from app.service import TaskService
from app.dependencies import get_service
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Runs once on startup before the server starts accepting requests.
WHAT HAPPENS HERE:
1. Base.metadata.create_all(engine)
SQLAlchemy reads all classes that inherit from Base (only TaskORM
right now) and runs CREATE TABLE IF NOT EXISTS for each one.
This is the ORM equivalent of running init.sql manually.
2. Seed 3 example tasks if the table is empty.
Uses a fresh DB session opened just for this setup work.
The 'yield' hands control to FastAPI — everything after yield
runs on shutdown (nothing to clean up here).
"""
from app.database import engine, SessionLocal
from app.models import Base, TaskORM
# ── Create tables ─────────────────────────────────────────
# checkfirst=True is implied by IF NOT EXISTS — safe to rerun
Base.metadata.create_all(bind=engine)
# ── Seed example data ─────────────────────────────────────
db = SessionLocal()
try:
if db.query(TaskORM).count() == 0:
# Table is empty (first run) — add seed rows
seeds = [
TaskORM(title="Buy groceries", done=False),
TaskORM(title="Read a book", done=True),
TaskORM(title="Go for a walk", done=False),
]
db.add_all(seeds)
db.commit()
finally:
db.close()
yield # server is now running and accepting requests
# ── Create app ────────────────────────────────────────────────
app = FastAPI(
title="Task API",
description=(
"CRUD API for tasks — FastAPI + SQLAlchemy ORM + PostgreSQL + Docker.\n\n"
"Storage: **PostgreSQL** when `DATABASE_URL` is set, "
"**in-memory** otherwise.\n\n"
"Try the full CRUD cycle using the endpoints below."
),
version="3.0",
lifespan=lifespan,
)
app.include_router(tasks_router)
# ── Global endpoints ──────────────────────────────────────────
@app.get("/", tags=["info"], summary="API info")
def root():
from app.database import DATABASE_URL
return {
"name": "Task API",
"version": "3.0",
"storage": "PostgreSQL via SQLAlchemy ORM" if DATABASE_URL else "In-Memory (no DB)",
"docs": "/docs",
}
@app.get("/health", tags=["info"], summary="Health check")
def health():
return {"status": "ok"}
@app.get("/stats", tags=["info"], summary="Task statistics")
def stats(svc: TaskService = Depends(get_service)):
return svc.stats()
# ── Run locally (no Docker) ───────────────────────────────────
# uvicorn main:app --reload
# → http://localhost:8000/docs