# First Run Guide Congratulations on setting up the FastAPI Boilerplate! This guide will walk you through testing your installation, understanding the basics, and making your first customizations. ## Verification Checklist Before diving deeper, let's verify everything is working correctly. ### 1. Check All Services Ensure all services are running: ```bash # For Docker Compose users docker compose ps # Expected output: # NAME COMMAND SERVICE STATUS # fastapi-boilerplate-web-1 "uvicorn app.main:app…" web running # fastapi-boilerplate-db-1 "docker-entrypoint.s…" db running # fastapi-boilerplate-redis-1 "docker-entrypoint.s…" redis running # fastapi-boilerplate-worker-1 "arq src.app.core.wo…" worker running ``` ### 2. Test API Endpoints Visit these URLs to confirm your API is working: **API Documentation:** - **Swagger UI**: [http://localhost:8000/docs](http://localhost:8000/docs) - **ReDoc**: [http://localhost:8000/redoc](http://localhost:8000/redoc) **Health Check:** ```bash curl http://localhost:8000/api/v1/health ``` Expected response: ```json { "status": "healthy", "timestamp": "2024-01-01T12:00:00Z" } ``` ### 3. Database Connection Check if the database tables were created: ```bash # For Docker Compose docker compose exec db psql -U postgres -d myapp -c "\dt" # You should see tables like: # public | users | table | postgres # public | posts | table | postgres # public | tiers | table | postgres # public | rate_limits | table | postgres ``` ### 4. Redis Connection Test Redis connectivity: ```bash # For Docker Compose docker compose exec redis redis-cli ping # Expected response: PONG ``` ## Initial Setup Before testing features, you need to create the first superuser and tier. ### Creating the First Superuser !!! warning "Prerequisites" Make sure the database and tables are created before running create_superuser. The database should be running and the API should have started at least once. #### Using Docker Compose If using Docker Compose, uncomment this section in your `docker-compose.yml`: ```yaml #-------- uncomment to create first superuser -------- create_superuser: build: context: . dockerfile: Dockerfile env_file: - ./src/.env depends_on: - db command: python -m src.scripts.create_first_superuser volumes: - ./src:/code/src ``` Then run: ```bash # Start services and run create_superuser automatically docker compose up -d # Or run it manually docker compose run --rm create_superuser # Stop the create_superuser service when done docker compose stop create_superuser ``` #### From Scratch If running manually, use: ```bash # Make sure you're in the root folder uv run python -m src.scripts.create_first_superuser ``` ### Creating the First Tier !!! warning "Prerequisites" Make sure the database and tables are created before running create_tier. #### Using Docker Compose Uncomment the `create_tier` service in `docker-compose.yml` and run: ```bash docker compose run --rm create_tier ``` #### From Scratch ```bash # Make sure you're in the root folder uv run python -m src.scripts.create_first_tier ``` ## Testing Core Features Let's test the main features of your API. ### Authentication Flow #### 1. Login with Admin User Use the admin credentials you set in your `.env` file: ```bash curl -X POST "http://localhost:8000/api/v1/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=admin&password=your_admin_password" ``` You should receive a response like: ```json { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", "token_type": "bearer", "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." } ``` #### 2. Create a New User ```bash curl -X POST "http://localhost:8000/api/v1/users" \ -H "Content-Type: application/json" \ -d '{ "name": "John Doe", "username": "johndoe", "email": "john@example.com", "password": "securepassword123" }' ``` #### 3. Test Protected Endpoint Use the access token from step 1: ```bash curl -X GET "http://localhost:8000/api/v1/users/me" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` ### CRUD Operations #### 1. Create a Post ```bash curl -X POST "http://localhost:8000/api/v1/posts" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" \ -d '{ "title": "My First Post", "content": "This is the content of my first post!" }' ``` #### 2. Get All Posts ```bash curl -X GET "http://localhost:8000/api/v1/posts" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` #### 3. Get Posts with Pagination ```bash curl -X GET "http://localhost:8000/api/v1/posts?page=1&items_per_page=5" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` ### Background Tasks Test the job queue system: #### 1. Submit a Background Task ```bash curl -X POST "http://localhost:8000/api/v1/tasks/task?message=hello" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` Response: ```json { "id": "550e8400-e29b-41d4-a716-446655440000" } ``` #### 2. Check Task Status ```bash curl -X GET "http://localhost:8000/api/v1/tasks/task/550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` ### Caching Test the caching system: #### 1. Make a Cached Request ```bash # First request (cache miss) curl -X GET "http://localhost:8000/api/v1/users/johndoe" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" \ -w "Time: %{time_total}s\n" # Second request (cache hit - should be faster) curl -X GET "http://localhost:8000/api/v1/users/johndoe" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" \ -w "Time: %{time_total}s\n" ``` ## Your First Customization Let's create a simple custom endpoint to see how easy it is to extend the boilerplate. ### 1. Create a Simple Model Create `src/app/models/item.py`: ```python from sqlalchemy import String from sqlalchemy.orm import Mapped, mapped_column from app.core.db.database import Base class Item(Base): __tablename__ = "items" id: Mapped[int] = mapped_column("id", autoincrement=True, nullable=False, unique=True, primary_key=True, init=False) name: Mapped[str] = mapped_column(String(100)) description: Mapped[str] = mapped_column(String(500), default="") ``` ### 2. Create Pydantic Schemas Create `src/app/schemas/item.py`: ```python from pydantic import BaseModel, Field class ItemBase(BaseModel): name: str = Field(..., min_length=1, max_length=100) description: str = Field("", max_length=500) class ItemCreate(ItemBase): pass class ItemCreateInternal(ItemCreate): pass class ItemRead(ItemBase): id: int class ItemUpdate(BaseModel): name: str | None = None description: str | None = None class ItemUpdateInternal(ItemUpdate): pass class ItemDelete(BaseModel): is_deleted: bool = True ``` ### 3. Create CRUD Operations Create `src/app/crud/crud_items.py`: ```python from fastcrud import FastCRUD from app.models.item import Item from app.schemas.item import ItemCreateInternal, ItemUpdate, ItemUpdateInternal, ItemDelete CRUDItem = FastCRUD[Item, ItemCreateInternal, ItemUpdate, ItemUpdateInternal, ItemDelete] crud_items = CRUDItem(Item) ``` ### 4. Create API Endpoints Create `src/app/api/v1/items.py`: ```python from typing import Annotated from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.api.dependencies import get_current_user from app.core.db.database import async_get_db from app.crud.crud_items import crud_items from app.schemas.item import ItemCreate, ItemRead, ItemUpdate from app.schemas.user import UserRead router = APIRouter(tags=["items"]) @router.post("/", response_model=ItemRead, status_code=201) async def create_item( item: ItemCreate, db: Annotated[AsyncSession, Depends(async_get_db)], current_user: Annotated[UserRead, Depends(get_current_user)] ): """Create a new item.""" db_item = await crud_items.create(db=db, object=item) return db_item @router.get("/{item_id}", response_model=ItemRead) async def get_item( item_id: int, db: Annotated[AsyncSession, Depends(async_get_db)] ): """Get an item by ID.""" db_item = await crud_items.get(db=db, id=item_id) if not db_item: raise HTTPException(status_code=404, detail="Item not found") return db_item @router.get("/", response_model=list[ItemRead]) async def get_items( db: Annotated[AsyncSession, Depends(async_get_db)], skip: int = 0, limit: int = 100 ): """Get all items.""" items = await crud_items.get_multi(db=db, offset=skip, limit=limit) return items["data"] @router.patch("/{item_id}", response_model=ItemRead) async def update_item( item_id: int, item_update: ItemUpdate, db: Annotated[AsyncSession, Depends(async_get_db)], current_user: Annotated[UserRead, Depends(get_current_user)] ): """Update an item.""" db_item = await crud_items.get(db=db, id=item_id) if not db_item: raise HTTPException(status_code=404, detail="Item not found") updated_item = await crud_items.update(db=db, object=item_update, id=item_id) return updated_item @router.delete("/{item_id}") async def delete_item( item_id: int, db: Annotated[AsyncSession, Depends(async_get_db)], current_user: Annotated[UserRead, Depends(get_current_user)] ): """Delete an item.""" db_item = await crud_items.get(db=db, id=item_id) if not db_item: raise HTTPException(status_code=404, detail="Item not found") await crud_items.delete(db=db, id=item_id) return {"message": "Item deleted successfully"} ``` ### 5. Register the Router Add your new router to `src/app/api/v1/__init__.py`: ```python from fastapi import APIRouter from app.api.v1.login import router as login_router from app.api.v1.logout import router as logout_router from app.api.v1.posts import router as posts_router from app.api.v1.rate_limits import router as rate_limits_router from app.api.v1.tasks import router as tasks_router from app.api.v1.tiers import router as tiers_router from app.api.v1.users import router as users_router from app.api.v1.items import router as items_router # Add this line router = APIRouter(prefix="/v1") router.include_router(login_router, prefix="/login") router.include_router(logout_router, prefix="/logout") router.include_router(users_router, prefix="/users") router.include_router(posts_router, prefix="/posts") router.include_router(tasks_router, prefix="/tasks") router.include_router(tiers_router, prefix="/tiers") router.include_router(rate_limits_router, prefix="/rate_limits") router.include_router(items_router, prefix="/items") # Add this line ``` ### 6. Create and Run Migration Import your new model in `src/app/models/__init__.py`: ```python from .user import User from .post import Post from .tier import Tier from .rate_limit import RateLimit from .item import Item # Add this line ``` Create and run the migration: ```bash # For Docker Compose docker compose exec web alembic revision --autogenerate -m "Add items table" docker compose exec web alembic upgrade head # For manual installation cd src uv run alembic revision --autogenerate -m "Add items table" uv run alembic upgrade head ``` ### 7. Test Your New Endpoint Restart your application and test the new endpoints: ```bash # Create an item curl -X POST "http://localhost:8000/api/v1/items/" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" \ -d '{ "name": "My First Item", "description": "This is a test item" }' # Get all items curl -X GET "http://localhost:8000/api/v1/items/" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` ## Debugging Common Issues ### Logs and Monitoring #### Check Application Logs ```bash # For Docker Compose docker compose logs web # For manual installation tail -f src/app/logs/app.log ``` #### Check Database Logs ```bash # For Docker Compose docker compose logs db ``` #### Check Worker Logs ```bash # For Docker Compose docker compose logs worker ``` ### Performance Testing #### Test API Response Times ```bash # Test endpoint performance curl -w "Time: %{time_total}s\n" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" \ http://localhost:8000/api/v1/users/me ``` #### Test Database Performance ```bash # Check active connections docker compose exec db psql -U postgres -d myapp -c "SELECT count(*) FROM pg_stat_activity;" ``` ## Monitoring Dashboard ### Redis Monitor ```bash # Monitor Redis operations docker compose exec redis redis-cli monitor ``` ### Database Activity ```bash # Check database activity docker compose exec db psql -U postgres -d myapp -c "SELECT * FROM pg_stat_activity;" ``` ## Next Steps Now that you've verified everything works and created your first custom endpoint, you're ready to dive deeper: ### Essential Learning 1. **[Project Structure](../user-guide/project-structure.md)** - Understand how the code is organized 2. **[Database Guide](../user-guide/database/index.md)** - Learn about models, schemas, and CRUD operations 3. **[Authentication](../user-guide/authentication/index.md)** - Deep dive into JWT and user management ### Advanced Features 1. **[Caching](../user-guide/caching/index.md)** - Speed up your API with Redis caching 2. **[Background Tasks](../user-guide/background-tasks/index.md)** - Process long-running tasks asynchronously 3. **[Rate Limiting](../user-guide/rate-limiting/index.md)** - Protect your API from abuse ### Development Workflow 1. **[Development Guide](../user-guide/development.md)** - Best practices for extending the boilerplate 2. **[Testing](../user-guide/testing.md)** - Write tests for your new features 3. **[Production](../user-guide/production.md)** - Deploy your API to production ## Getting Help If you encounter any issues: 1. **Check the logs** for error messages 2. **Verify your configuration** in the `.env` file 3. **Review the [GitHub Issues](https://github.com/benavlabs/fastapi-boilerplate/issues)** for common solutions 4. **Search [existing issues](https://github.com/benavlabs/fastapi-boilerplate/issues)** on GitHub 5. **Create a [new issue](https://github.com/benavlabs/fastapi-boilerplate/issues/new)** with detailed information ## Congratulations! You've successfully: - Verified your FastAPI Boilerplate installation - Tested core API functionality - Created your first custom endpoint - Run database migrations - Tested authentication and CRUD operations You're now ready to build amazing APIs with FastAPI!