Getting Started#
This guide walks from zero to a working REST API with FastAPI-Restly. Python 3.10 or later is required.
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#
restly new myapp generates a complete project; see
Generate it. This guide builds one by hand instead, starting from
a single 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. On those bases,Mapped[datetime]represents a UTC instant and maps toDateTime(timezone=True). PostgreSQL enforces that through its column type, while SQLite returns naive datetime values. Declaremapped_column(DateTime())explicitly only for a field that intentionally stores a timezone-free wall-clock value.RestViewandAsyncRestViewexpect a single primary-key column; for composite-key tables, see the view hierarchy.With no manual schema, FastAPI-Restly generates the view’s schema
UserSchemafrom your model; see Auto-Generated Schemas. OpenAPI shows the classes derived from it:UserResponse,UserCreate,UserUpdateandUserListResponse; see Generated Class Names.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 CRUD routes#
UserView inherits five endpoint methods from AsyncRestView. Registering it
with prefix = "/users" exposes these routes:
GET /usersPOST /usersGET /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
Default CRUD Routes 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 UserSchema(fr.IDSchema):
name: str
email: str
@fr.include_view(app)
class UserView(fr.AsyncRestView):
prefix = "/users"
model = User
schema = UserSchema
schema is the view’s schema. Responses follow it, without its WriteOnly fields.
Restly derives the create and update schemas 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.
Grow beyond one file#
Keep the one-file layout while the application is small. Once it holds several
resources, give each one a package with its own models.py, schemas.py, and
views.py, and register them all from a VIEWS tuple in main.py.
Project structure covers the full layout, the
import direction it depends on, and which modules to add only once they earn
their place. Generating a project with restly new and moving this code into it
is a reasonable way to make the move.
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#
Build a Blog API adds a related model, explicit schemas, filtering, and broader tests. Customize the Blog API then adds overrides, custom routes, authorization, and shared base classes.
Use these pages for individual topics:
Using RestView: the default CRUD contract, view configuration, and the map from common changes to their owning guides.
Views: when to use a generic
Viewor a plain FastAPI route, and how view registration and inheritance work.Already have a FastAPI app? Use Restly in an Existing Project shows how Restly adopts per resource, beside your current routes.
Current context: bind the current user for application code and column defaults.
Scopes: row visibility declared once and applied to every read and reference check.
Query Clauses: reusable query fragments with late-bound request values.
Deploying: production engine config, Alembic, and a
main.pytemplate.