FastAPI - Beginner to Pro / Authentication & Authorization

Routers Scale Authentication File

🧠 Why We Need Routers

Right now:

  • main.py has app = FastAPI()

  • auth.py also had app = FastAPI()

That creates two separate applications.

What we want instead:

One FastAPI app πŸ‘‰ (main.py)
Multiple routers πŸ‘‰ (auth.py, todos.py, etc.)

Routers allow us to:

  • Keep files separated

  • Keep logic organized

  • Share the same app instance

  • Run everything on one port

This is how real backend systems scale.


πŸ—οΈ Step 1 β€” Create a Routers Package

Inside your Todo project folder:

Create a new folder called:

routers/

<aside> πŸ’‘

you can also create via the terminal using the command

mkdir routers

</aside>

Inside routers/, move:

auth.py

Your structure should now look like:

TodoApp/
└── routers/
    └── auth.py
β”œβ”€β”€ main.py
β”œβ”€β”€ models.py
β”œβ”€β”€ database.py
β”œβ”€β”€ todos.db

This creates modular separation.

<aside> πŸ’‘

Create a init.py inside routers/ incase your IDE doesn’t automatically generate one.

cd routers

ni init.py

</aside>


🧩 Step 2 β€” Convert auth.py into a Router

Open:

routers/auth.py

❌ Remove FastAPI App Instance

Delete this (if it exists):

from fastapi import FastAPI

app = FastAPI()

We no longer want a new application instance.

βœ… Import APIRouter Instead

Now import APIRouter.

from fastapi import APIRouter

βœ… Create a Router Instance

router = APIRouter()

βœ… Replace @app with @router

Update your endpoint:’

@router.get("/auth/")
async def get_user():
    return {"user": "authenticated"}

πŸ‘‰ Final Code should look like this

from fastapi import APIRouter
router = APIRouter()

@router.get("/auth/")
async def get_user():
    return {"user": "authenticated"}

This router will later be attached to the main app.


πŸ—οΈ Step 3 β€” Update main.py

Now open:

main.py

Then import your router:

from routers import auth

πŸ”— Step 4 β€” Include the Router

Now attach the router to your application:

app.include_router(auth.router)

Your main.py should now look like this (simplified):

from typing import Annotated
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from fastapi import FastAPI, Depends, HTTPException, Path
from starlette import status
import models
from models import Todos
from database import engine, SessionLocal
from routers import auth

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

app.include_router(auth.router)

... REST OF THE CODE ...

πŸ–₯️ Step 5 β€” Run the Application

In your terminal:

uvicorn main:app --reload

Now open:

/docs

🌐 What You Will See

You will now see:

βœ” All Todo endpoints

βœ” /auth/ endpoint

All running:

βœ” On the same port

βœ” Inside one FastAPI application

βœ” Under a clean modular architecture


🧠 What Is Happening Internally?

When you call:

app.include_router(auth.router)

FastAPI:

  1. Takes all endpoints defined in auth.router

  2. Registers them inside the main app

  3. Adds them to the OpenAPI schema

  4. Makes them available on the same ASGI app

You now have:

One ASGI app
Multiple modular route groups

This is production-grade architecture.


πŸ—οΈ Scalable Architecture Concept

As your app grows, you can add:

routers/
β”‚
β”œβ”€β”€ auth.py
β”œβ”€β”€ todos.py
β”œβ”€β”€ admin.py
β”œβ”€β”€ users.py

Then include them in main.py:

app.include_router(auth.router)
app.include_router(todos.router)
app.include_router(admin.router)

Clean. Scalable. Maintainable.


πŸ—‚οΈ main.py

from typing import Annotated
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from fastapi import FastAPI, Depends, HTTPException, Path
from starlette import status
import models
from models import Todos
from database import engine, SessionLocal
from routers import auth

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

app.include_router(auth.router)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

db_dependency = Annotated[Session, Depends(get_db)]

class TodoRequest(BaseModel):
    title: str = Field(min_length=3)
    description: str = Field(min_length=3, max_length=100)
    priority: int = Field(gt=0, lt=6)
    complete: bool

@app.get("/", status_code=status.HTTP_200_OK)
async def read_all(db: db_dependency):
    return db.query(Todos).all()

@app.get("/todo/{todo_id}", status_code=status.HTTP_200_OK)
async def read_todo(db: db_dependency, todo_id: int = Path(gt=0)):
    todo_model = db.query(Todos).filter(Todos.id == todo_id).first()
    if todo_model is not None:
        return todo_model
    else:
        raise HTTPException(status_code=404, detail='Todo Not Found')

@app.post("/todo/", status_code=status.HTTP_201_CREATED)
async def create_todo(db: db_dependency, todo_request: TodoRequest):
    todo_model = Todos(**todo_request.model_dump())
    db.add(todo_model)
    db.commit()
    
@app.put("/todo/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
async def update_todo(
    db: db_dependency,
    todo_request: TodoRequest,
    todo_id: int = Path(gt=0)):
    todo_model = db.query(Todos).filter(Todos.id == todo_id).first()

    if todo_model is None:
        raise HTTPException(status_code=404, detail="Todo not found.")

    todo_model.title = todo_request.title
    todo_model.description = todo_request.description
    todo_model.priority = todo_request.priority
    todo_model.complete = todo_request.complete

    db.commit()
    
@app.delete("/todo/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_todo(
    db: db_dependency,
    todo_id: int = Path(gt=0)):
    todo_model = db.query(Todos).filter(Todos.id == todo_id).first()

    if todo_model is None:
        raise HTTPException(status_code=404, detail="Todo not found.")

    db.delete(todo_model)
    db.commit()

πŸ—‚οΈ routers/auth.py

from fastapi import APIRouter

router = APIRouter()

@router.get("/auth/")
async def get_user():
    return {"user": "authenticated"}

No Changes in Other files

Current Folder Structure

TodoApp/
└── routers/
    └── auth.py
β”œβ”€β”€ main.py
β”œβ”€β”€ models.py
β”œβ”€β”€ database.py
β”œβ”€β”€ todos.db