Build FastAPI and Django Apps Faster with Windsurf 2026

Use Windsurf Cascade to scaffold FastAPI routes, Django models, and async endpoints. Boost backend productivity with AI-assisted Python development. Python 3.12 + uv.

Problem: Writing Boilerplate Backend Code Takes Too Long

Windsurf for backend development transforms how you scaffold FastAPI routes, Django models, and async database layers — cutting setup time from hours to minutes.

If you've spent 40 minutes writing CRUD endpoints that all look the same, or manually wiring Pydantic schemas to SQLAlchemy models, Windsurf's Cascade agent handles the repetitive scaffolding while you focus on business logic.

You'll learn:

  • How to use Windsurf Cascade to generate production-ready FastAPI endpoints with Pydantic v2
  • How to scaffold Django models, serializers, and views in one Cascade prompt
  • How to configure Windsurf for Python 3.12 + uv projects for accurate completions
  • When to use Cascade's agentic mode vs inline completions for backend tasks

Time: 20 min | Difficulty: Intermediate


Why Windsurf Outperforms Plain Autocomplete for Backend Work

Most AI coding tools give you one-line completions. Windsurf's Cascade agent reads your entire project — your models.py, existing routes, your pyproject.toml — and generates code that fits.

Where this matters in backend development:

  • It sees your existing User model before writing an auth endpoint, so it doesn't invent a second one
  • It reads your Alembic migration history before generating a new model, avoiding column conflicts
  • It follows your existing error-handling patterns (HTTPException vs custom exception classes) automatically

Common symptoms that Windsurf solves:

  • Scaffolded endpoints that don't match the response schema of your existing ones
  • Generated Django serializers that ignore Meta.fields patterns already in your codebase
  • Autocomplete that ignores your custom middleware and generates raw request.user calls

Windsurf Cascade backend development workflow for FastAPI and Django Cascade's agentic loop for backend work: read codebase → understand models → generate routes → verify types → self-correct


Setup: Configure Windsurf for a Python Backend Project

Step 1: Install Windsurf and open your project

Download Windsurf from codeium.com/windsurf. It's free to start — the paid Wave tier starts at $15/month USD and adds priority Cascade access.

Open your project folder. Windsurf indexes your codebase on first open. For a mid-size FastAPI or Django project (under 50k lines), indexing takes under 30 seconds.

# Start a new FastAPI project with uv (recommended over pip for Python 3.12 projects)
uv init my-api
cd my-api
uv add fastapi "uvicorn[standard]" sqlalchemy pydantic

Step 2: Set Python interpreter to your uv environment

Press Ctrl+Shift+PPython: Select Interpreter → choose the .venv path created by uv.

Windsurf's Cascade uses the active interpreter to resolve imports. If you skip this, Cascade hallucinates stdlib paths instead of reading your actual installed packages.

# Verify uv created the venv correctly
ls .venv/lib/python3.12/site-packages/ | grep fastapi

Expected output: fastapi-0.115.x.dist-info

If it fails:

  • No module named fastapi → Run uv sync to install from pyproject.toml
  • Wrong Python version → Set python = ">=3.12" in pyproject.toml and re-run uv sync

Step 3: Open Cascade and set your backend context

Open Cascade with Ctrl+L. Before generating any code, send one context message. This is the most important step — it prevents Cascade from guessing your stack.

Context: FastAPI 0.115, Python 3.12, SQLAlchemy 2.0 async, PostgreSQL.
Auth via JWT in Authorization header. Error handling uses HTTPException.
Follow existing patterns in /app/routers/users.py for all new endpoints.

You only need to do this once per session. Cascade retains the context for the full conversation.


Using Cascade for FastAPI Development

Step 4: Scaffold a full CRUD router

With context set, ask Cascade to generate a router. Be specific about what already exists.

Cascade prompt:

Create a FastAPI router for a `products` resource.
- Model: id (UUID), name (str), price (Decimal), stock (int), created_at (datetime)
- Endpoints: GET /products, GET /products/{id}, POST /products, PATCH /products/{id}, DELETE /products/{id}
- Use the async SQLAlchemy session pattern from /app/db/session.py
- Pydantic schemas in /app/schemas/products.py (separate Request and Response models)
- Follow the error handling pattern in /app/routers/users.py

Cascade reads session.py and users.py before writing a line. The output matches your existing patterns — same Depends(get_db) injection, same HTTPException(status_code=404) shape.

# app/routers/products.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from uuid import UUID

from app.db.session import get_db
from app.schemas.products import ProductCreate, ProductUpdate, ProductResponse
from app.models.product import Product

router = APIRouter(prefix="/products", tags=["products"])


@router.get("/{product_id}", response_model=ProductResponse)
async def get_product(product_id: UUID, db: AsyncSession = Depends(get_db)):
    # WHY: async get avoids blocking the event loop on I/O-heavy endpoints
    result = await db.get(Product, product_id)
    if not result:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
    return result

Expected output: Full router file with all 5 endpoints, matching your existing patterns.

If Cascade ignores your existing patterns:

  • Add @codebase to your prompt: @codebase Create a products router following the users pattern
  • This forces Cascade to explicitly re-scan before generating

Step 5: Generate Pydantic v2 schemas

Cascade prompt:

Generate Pydantic v2 schemas for the products router:
ProductBase, ProductCreate (inherits Base), ProductUpdate (all fields Optional),
ProductResponse (adds id, created_at). Use model_config = ConfigDict(from_attributes=True)
# app/schemas/products.py
from pydantic import BaseModel, ConfigDict, condecimal
from decimal import Decimal
from datetime import datetime
from uuid import UUID


class ProductBase(BaseModel):
    name: str
    # WHY: condecimal enforces 2 decimal places — prevents float rounding on price fields
    price: condecimal(max_digits=10, decimal_places=2)
    stock: int


class ProductCreate(ProductBase):
    pass


class ProductUpdate(ProductBase):
    name: str | None = None
    price: Decimal | None = None
    stock: int | None = None


class ProductResponse(ProductBase):
    id: UUID
    created_at: datetime
    model_config = ConfigDict(from_attributes=True)

Step 6: Run and verify

# WHY: --reload watches file changes in dev; remove in production
uvicorn app.main:app --reload --port 8000

You should see:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process

Visit http://localhost:8000/docs — your /products endpoints appear in Swagger UI automatically.


Using Cascade for Django Development

Step 7: Scaffold Django models and serializers

Open a new Cascade session and set Django context first.

Cascade prompt:

Context: Django 5.1, Django REST Framework 3.15, PostgreSQL, Python 3.12.
JWT auth via djangorestframework-simplejwt. Follow patterns in /api/users/serializers.py.

Create a Django app for `orders`:
- Model: Order with fields: id (UUID pk), user (FK to User), total (DecimalField),
  status (choices: pending/paid/shipped/cancelled), created_at (auto_now_add)
- DRF ModelSerializer with nested user display (read-only)
- ViewSet: list, retrieve, create, partial_update
- Register on router in urls.py

Cascade generates models.py, serializers.py, views.py, and urls.py in one pass — reading your users/serializers.py to match field naming conventions.

# api/orders/models.py
import uuid
from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()


class Order(models.Model):
    class Status(models.TextChoices):
        PENDING = "pending", "Pending"
        PAID = "paid", "Paid"
        SHIPPED = "shipped", "Shipped"
        CANCELLED = "cancelled", "Cancelled"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="orders")
    # WHY: DecimalField not FloatField — avoids IEEE 754 rounding on currency values
    total = models.DecimalField(max_digits=10, decimal_places=2)
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

Step 8: Generate and run migrations

python manage.py makemigrations orders
# WHY: always migrate immediately in dev — schema drift causes confusing IntegrityErrors later
python manage.py migrate

Expected output:

Migrations for 'orders':
  api/orders/migrations/0001_initial.py
    - Create model Order

Cascade vs Inline Completions: When to Use Which

TaskUse
Writing a single function bodyInline completion (Tab)
Scaffolding a full router or Django appCascade
Fixing a multi-file bugCascade
Generating pytest tests for existing codeCascade with @codebase
Renaming a field across models + serializers + viewsCascade (multi-file edit)
Writing a one-off migrationInline completion

Cascade shines when a change touches 3 or more files. For single-file edits under 30 lines, inline completions are faster.


Verification

# FastAPI — create a product
curl -X POST http://localhost:8000/products \
  -H "Content-Type: application/json" \
  -d '{"name": "Widget", "price": "9.99", "stock": 100}'

# Django — list orders
curl -X GET http://localhost:8000/api/orders/ \
  -H "Authorization: Bearer <your_jwt_token>"

You should see: A 201 Created response with a UUID id field and the resource data.


What You Learned

  • Windsurf Cascade reads your codebase before generating — always set context at the start of a session
  • Use @codebase when you need Cascade to explicitly re-scan before writing
  • Cascade handles multi-file Django scaffolding (models + serializers + views + urls) in a single prompt
  • Use DecimalField / condecimal over float for currency fields — Cascade follows the pattern once you establish it in context
  • Inline completions are faster for single functions; Cascade is better for anything spanning 3+ files

Tested on Windsurf 1.x, FastAPI 0.115, Django 5.1, DRF 3.15, Python 3.12, macOS Sequoia & Ubuntu 24.04


FAQ

Q: Does Windsurf Cascade work with async SQLAlchemy 2.0? A: Yes. Include AsyncSession in your context message and Cascade generates correct await db.execute() patterns. It reads your session.py to match your engine setup.

Q: What is the difference between Windsurf's free plan and the Wave paid tier? A: The free plan includes limited daily Cascade flows. Wave at $15/month USD gives priority model access and unlimited Cascade sessions — worth it for daily backend development use.

Q: What is the minimum Python version for accurate Windsurf type inference? A: Python 3.10+ for union type syntax (str | None). Python 3.12 is recommended — Cascade generates more accurate completions with the newer type system.

Q: Can Windsurf generate pytest tests for FastAPI endpoints? A: Yes. Prompt Cascade: @codebase Generate pytest tests for /app/routers/products.py using httpx AsyncClient. It reads your conftest.py and matches your existing fixture patterns.

Q: Does Cascade run Django migrations automatically? A: No. Cascade generates the model and tells you to run makemigrations. It does not execute terminal commands unless you enable agentic terminal mode in Windsurf settings. Always review generated models before migrating.