Files
2025-10-19 22:09:35 +03:00

594 lines
14 KiB
Markdown

# 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!