Get the FREE Ultimate OpenClaw Setup Guide →

backend

npx machina-cli add skill mrsknetwork/supernova/backend --openclaw
Files (1)
SKILL.md
6.2 KB

Backend Engineering

Default Stack (Ask First)

Before applying anything, ask:

"Can I use the Supernova backend stack? Python 3.12 + FastAPI + SQLAlchemy 2.0 async + PostgreSQL. Or do you have an existing backend stack I should match?"

If an existing requirements.txt, pyproject.toml, or setup.py is detected, read it and match the existing stack.

Progressive Disclosure

  • Load references/fastapi-patterns.md when setting up dependency injection, middleware, or lifespan events.
  • Load references/sqlalchemy-async.md when implementing async DB sessions or complex ORM queries.

SOP: Backend Implementation

Step 1 - Project Scaffold (New Projects Only)

For greenfield projects, structure the project as follows. Do not deviate:

src/
├── main.py              # FastAPI app instance, lifespan, router inclusion
├── config.py            # pydantic-settings BaseSettings
├── database.py          # Async engine + session factory
├── models/              # SQLAlchemy ORM models (one file per domain entity)
├── schemas/             # Pydantic request/response models
├── repositories/        # Data access layer (all DB queries live here)
├── services/            # Business logic (calls repositories, never raw DB)
├── api/
│   └── v1/              # FastAPI routers (thin - delegate to services)
├── middleware/          # Custom ASGI middleware
└── tests/               # pytest-asyncio test files mirroring src/ structure

pyproject.toml is the only dependency file. Use uv as the package manager.

Step 2 - Dependency Management

# pyproject.toml
[project]
requires-python = ">=3.12"
dependencies = [
    "fastapi>=0.111.0",
    "uvicorn[standard]>=0.29.0",
    "sqlalchemy[asyncio]>=2.0.0",
    "asyncpg>=0.29.0",
    "pydantic>=2.7.0",
    "pydantic-settings>=2.2.0",
    "alembic>=1.13.0",
    "structlog>=24.1.0",
]

[project.optional-dependencies]
dev = ["pytest>=8.0", "pytest-asyncio>=0.23", "httpx>=0.27", "ruff>=0.4"]

Install with: uv pip install -e ".[dev]"

Step 3 - Application Layering (Strict Separation)

The request lifecycle must flow exactly as follows. Do not skip layers:

HTTP Request
  -> FastAPI Router (validates request schema via Pydantic)
  -> Service Layer (applies business logic, raises domain exceptions)
  -> Repository Layer (executes SQLAlchemy queries)
  -> Database

Router rules: Routers only receive validated Pydantic inputs and return Pydantic outputs. No SQL, no business logic.

Service rules: Services call repositories and other services. They raise HTTPException or custom domain exceptions. Never return raw ORM models - always map to schemas.

Repository rules: Repositories are the only place that imports AsyncSession. Every method is async def. Return ORM model instances, not dicts.

Step 4 - Pydantic v2 Model Patterns

from pydantic import BaseModel, EmailStr, ConfigDict
from uuid import UUID
from datetime import datetime

class UserBase(BaseModel):
    email: EmailStr
    display_name: str

class UserCreate(UserBase):
    password: str  # plain text in; hashed in service

class UserOut(UserBase):
    model_config = ConfigDict(from_attributes=True)
    id: UUID
    created_at: datetime

Always define three Pydantic models per entity: Base (shared fields), Create (input), Out (response). Never expose ORM models directly.

Step 5 - Async SQLAlchemy Session Pattern

# database.py
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession

engine = create_async_engine(settings.DATABASE_URL, echo=settings.DEBUG)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)

async def get_db() -> AsyncSession:
    async with AsyncSessionLocal() as session:
        yield session

Inject with db: AsyncSession = Depends(get_db) in services via the router. Never create sessions manually outside of this factory.

Step 6 - Structured Logging

import structlog

log = structlog.get_logger()

async def create_user(user_in: UserCreate, db: AsyncSession) -> UserOut:
    log.info("creating_user", email=user_in.email)
    # ...
    log.info("user_created", user_id=str(user.id))
    return UserOut.model_validate(user)

Log with structured key=value pairs, never f-strings in log calls. Include user_id, request_id, and relevant entity IDs in every log line.

Step 7 - Error Handling

# In service layer
from fastapi import HTTPException

async def get_user_by_id(user_id: UUID, db: AsyncSession) -> UserOut:
    user = await user_repo.get_by_id(db, user_id)
    if not user:
        raise HTTPException(status_code=404, detail=f"User {user_id} not found")
    return UserOut.model_validate(user)

Define a global exception handler in main.py for unhandled exceptions: log the full traceback with structlog, return a generic 500 response. Never expose stack traces to API consumers.

Step 8 - Environment Configuration

# config.py
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")
    DATABASE_URL: str
    SECRET_KEY: str
    DEBUG: bool = False
    ENVIRONMENT: str = "development"

settings = Settings()

.env is git-ignored. Provide .env.example with all keys and no values.

Step 9 - Testing Pattern

# tests/test_user_service.py
import pytest
from httpx import AsyncClient, ASGITransport
from src.main import app

@pytest.mark.asyncio
async def test_create_user():
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post("/api/v1/users", json={"email": "test@test.com", ...})
    assert response.status_code == 201
    assert "id" in response.json()["data"]

Test via the HTTP layer, not by calling services directly. This ensures routers, validation, and middleware are also tested.

Source

git clone https://github.com/mrsknetwork/supernova/blob/main/skills/backend/SKILL.mdView on GitHub

Overview

Backend Engineering focuses on server-side logic, data access layers, and background services using Python 3.12 and FastAPI. It emphasizes clean layering (routers, services, repositories), Pydantic v2 models, and async SQLAlchemy with PostgreSQL, enabling scalable web services. The practice is to align with an existing stack or the Supernova defaults while applying structured patterns.

How This Skill Works

Technically, you scaffold a project under src with modules for main, config, database, models, schemas, repositories, services, and API routers. The HTTP request flows through the FastAPI router (input validated by Pydantic) to the service layer (business logic) then to the repository layer (async SQL queries) before reaching the database; responses map to Pydantic schemas. Repositories are the only place importing AsyncSession, and services raise HTTPException or domain exceptions instead of returning raw ORM models.

When to Use It

  • Launching a new FastAPI service with Python 3.12 + async PostgreSQL (SQLAlchemy) stack
  • Building data access via a dedicated repository layer and async DB sessions
  • Adding dependency injection, middleware, or lifespan events per FastAPI patterns references
  • Defining API input/output with Pydantic v2 models for strict validation
  • Migrating an existing API to the async SQLAlchemy + PostgreSQL workflow while matching an existing stack

Quick Start

  1. Step 1: Scaffold the project under src with main.py, config.py, database.py, models, schemas, repositories, services, api, middleware, and tests
  2. Step 2: Add dependencies in pyproject.toml for Python 3.12, FastAPI, SQLAlchemy async, asyncpg, pydantic, and Alembic; install with uv pip install -e .[dev]
  3. Step 3: Implement the HTTP request lifecycle: HTTP Request -> Router (validated) -> Service (business logic) -> Repository (async SQL) -> Database; ensure routers return validated schemas and services raise HTTPException

Best Practices

  • Always ask about the existing backend stack before applying defaults
  • Enforce strict layering: Router -> Service -> Repository; avoid business logic in routers
  • Map all ORM models to Pydantic schemas; never expose ORM models directly
  • Keep Repository methods async and return ORM model instances, not dicts
  • Consult references/fastapi-patterns.md and references/sqlalchemy-async.md for DI, middleware, and async sessions

Example Use Cases

  • User management service with create/read/update/delete endpoints and hashed passwords
  • Order processing service with background tasks for email or notification delivery
  • Middleware implementing tracing, logging, or request metrics across the app
  • Background job runner handling long-running tasks via FastAPI lifespans or workers
  • API router layer delegating all requests to services with strict schema validation

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers