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:
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.DataclassBaseandfr.IDBaseconvenience bases.RestViewandAsyncRestViewexpect a single primary-key column; for composite-key tables, see the view hierarchy.With no manual schema, FastAPI-Restly auto-generates
UserRead,UserCreate, andUserUpdatefrom 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:
Class-Based Views: what makes the views subclassable, and when to use
View,RestView, or a plain FastAPI route.Already have a FastAPI app? Use Restly in an Existing Project shows how Restly adopts per resource, beside your current routes.
Deploying: production engine config, Alembic, and a
main.pytemplate.