Getting Started#

This guide walks from zero to a working REST API with FastAPI-Restly. Python 3.10 or later is required.

To try FastAPI-Restly without installing anything, open the project in GitHub Codespaces, which builds the dev environment and its example projects for you:

Open in GitHub Codespaces

Once the Codespace finishes setup, run cd example-projects/blog && uv run uvicorn blog.main:app --port 8000 in the terminal, then open the forwarded port 8000 and visit /docs for the interactive API.

Installation#

Install the framework and an async driver for your database:

pip install "fastapi-restly[standard]" aiosqlite

The base package intentionally stays small. The standard extra adds FastAPI’s standard server dependencies (the fastapi dev toolchain), mirroring fastapi[standard]. Restly is database-driver-agnostic, so the async driver is a separate, explicit dependency: aiosqlite for the SQLite examples in this guide, or asyncpg/psycopg for PostgreSQL. Test tooling lives in its own extra; see Testing for fastapi-restly[testing].

Create an app#

A first application fits in one file, main.py:

from contextlib import asynccontextmanager

import fastapi_restly as fr
from fastapi import FastAPI
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

fr.configure(async_database_url="sqlite+aiosqlite:///app.db")


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    email: Mapped[str]


@asynccontextmanager
async def lifespan(_app: FastAPI):
    # Dev/demo table creation only; use Alembic migrations in production.
    # Runs after model classes are declared, so metadata contains every table.
    await fr.db.async_create_all(Base)
    yield


app = FastAPI(lifespan=lifespan)


@fr.include_view(app)
class UserView(fr.AsyncRestView):
    prefix = "/users"
    model = User

A few details are worth noting:

  • The model uses standard SQLAlchemy declarative style: your own DeclarativeBase, an explicit __tablename__, and an explicit primary-key column.

  • If you prefer dataclass-oriented SQLAlchemy models, FastAPI-Restly also provides fr.DataclassBase and fr.IDBase convenience bases.

  • RestView and AsyncRestView expect a single primary-key column; for composite-key tables, see the view hierarchy.

  • With no manual schema, FastAPI-Restly auto-generates UserRead, UserCreate, and UserUpdate from your model; see Auto-Generated Schemas.

  • The lifespan hook creates tables with the async engine configured by fr.configure(). Use Alembic migrations in production.

Auto-generated schemas are a good fit for internal tools and backoffice APIs, early project scaffolding or prototypes, and straightforward models with minimal validation rules.

Sync or async?#

Default to AsyncRestView for new services. Use sync RestView for sync-only libraries or sync-first codebases. Mixed projects are fine; choose per view.

Run the app#

With main.py in place, start the development server:

fastapi dev main.py

The fastapi dev command comes from the standard extra; the dev server is not for production. Then open http://127.0.0.1:8000/docs or http://127.0.0.1:8000/openapi.json.

Use the generated endpoints#

Registering UserView with prefix = "/users" generated five endpoints:

  • GET /users/

  • POST /users/

  • GET /users/{id}

  • PATCH /users/{id}

  • DELETE /users/{id}

These endpoints work as soon as the server starts. A POST creates a row:

POST /users/
{"name": "Jane", "email": "jane@example.com"}

The response is 201 Created with the stored record:

{
  "id": 1,
  "name": "Jane",
  "email": "jane@example.com"
}

The database assigned the id, and GET /users/ now returns that row in the default data envelope:

{
  "data": [{"id": 1, "name": "Jane", "email": "jane@example.com"}],
  "total_count": 1,
  "page": 1,
  "page_size": 50,
  "total_pages": 1
}

Update semantics are PATCH (partial update); see Generated REST Endpoints for the full contract. Filter lists with query parameters, for example GET /users/?name=Jane. See Filter, Sort, and Paginate Lists.

Add an explicit schema (optional)#

Auto-generated schemas can be replaced at any point. Replace the UserView definition above with:

class UserRead(fr.IDSchema):
    name: str
    email: str


@fr.include_view(app)
class UserView(fr.AsyncRestView):
    prefix = "/users"
    model = User
    schema = UserRead

schema is the read/response contract. Restly derives UserCreate and UserUpdate from it unless you override schema_create or schema_update. fr.IDSchema includes id as fr.ReadOnly: present in responses, excluded from create/update. Use fr.ReadOnly[T] for other response-only fields and fr.WriteOnly[T] for input-only fields such as passwords (see ReadOnly and WriteOnly).

Choose explicit schemas for public API contracts you want to keep stable, custom validation logic, field aliases and strict response shaping, or extra clarity for teams that prefer less implicit behavior; Auto-Generated vs Explicit Schemas compares the two.

Test quickly#

A quick check with FastAPI’s regular TestClient confirms the API works:

from fastapi.testclient import TestClient
from main import app

with TestClient(app) as client:
    res = client.post("/users/", json={"name": "Jane", "email": "jane@example.com"})
    assert res.status_code == 201

For test isolation (rolling back test data between tests), see the Testing guide.

Next steps#

Continue with the Tutorial, which builds a complete multi-model API step by step. The pages below go deeper into individual topics: