API Reference#
This page documents:
Generated HTTP endpoints and query behavior.
Key public symbols with brief descriptions.
Full Python API reference generated via Sphinx autodoc.
Generated CRUD Endpoints#
When you register a view with fr.include_view(app, ViewClass) (or the small-app decorator shortcut @fr.include_view(app)), both fr.AsyncRestView and fr.RestView expose the same default CRUD surface:
Method |
Path |
Purpose |
Default Status |
|---|---|---|---|
|
|
List resources |
|
|
|
Create resource |
|
|
|
Get resource by ID |
|
|
|
Partial update |
|
|
|
Delete resource |
|
Notes:
Update semantics are
PATCH(partial update), notPUT. (AsyncReactAdminView/ReactAdminViewadditionally exposePUT /{id}to matchra-data-simple-rest; see How-To: React Admin Integration.)GET /{id}andDELETE /{id}return404when the object is not found.Read-only schema fields are ignored on create/update.
*_id: IDRef[Model]inputs are resolved to SQLAlchemy objects and validated against the database. The scalar id accepts the related primary-key type, such asintorUUID.
Query Parameters (List Endpoint)#
GET /{prefix}/ exposes a stable URL parameter dialect — the list parameters — derived from the response schema. This contract is part of the public API: parameter keys follow the response schema’s public field names (aliases when set, Python names otherwise), end-to-end, including dotted relation paths.
Filtering:
?name=John&created_at__gte=2024-01-01Suffixes:
__in,__gte,__lte,__gt,__lt,__ne,__isnull,__contains,__icontains(contains operators are string fields only)OR-values (IN):
?id=1,2,3(comma-separated values are OR-combined foreq)Explicit IN:
?status__in=active,pendingNOT-IN:
?status__ne=archived,deleted(comma-separated values are AND-combined for__ne)Aliased fields use only the alias as the URL key; the Python field name is not accepted (
populate_by_nameonly affects body parsing, not the URL surface).
Contains:
?name__contains=John(case-sensitive where the SQL backend supports that distinction)IContains:
?name__icontains=john(case-insensitive)Repeat the parameter to AND multiple terms — this is the precise form:
?name__contains=john&name__contains=doe.As a convenience, whitespace inside one value is also AND-split:
?name__contains=john%20doeis equivalent.%,_, and\\are escaped before building the SQLLIKE/ILIKE.
Sorting:
?sort=name,-created_atPagination:
?page=2&page_size=10Opt-in. Omitting
page_sizereturns every matching row (no implicit cap).For public/production endpoints, set
default_page_sizeandmax_page_sizeexplicitly on the view class.
Unknown query keys are rejected. Generated list endpoints validate the request’s query string against the schema’s declared parameters. Any key that isn’t part of the generated schema — a typoed filter, a Python field name on an aliased field, an operator suffix that wasn’t emitted for the field’s type (e.g. __gte on a boolean) — produces a 422 response with a FastAPI-style validation envelope. This prevents typos from silently widening the result set. To allow extra query keys that a view consumes outside the schema (e.g. an ?include_deleted=true escape hatch on a soft-delete mixin), declare them on the view class:
class UserView(fr.AsyncRestView):
extra_query_params = ("include_deleted",)
Relation filtering uses dot notation, and aliases apply to every segment of the path. If ArticleRead.author has Field(alias="writer") and AuthorRead.name has Field(alias="authorName"), the URL key is writer.authorName. Canonical Python names are not exposed.
Low-level helpers#
fr.create_list_params_schema(schema_cls, *, default_page_size=None, max_page_size=1000) and fr.apply_list_params(params, query, model, schema_cls) are the primitives behind the generated endpoints. The happy path is to define a RestView / AsyncRestView and let the framework wire them up — the generated FastAPI endpoint validates incoming requests against the params schema before the SQL clauses are applied. Reach for these helpers directly only when you need to apply list parameters to a custom (non-RestView) endpoint, and pass a validated create_list_params_schema(...) instance rather than a raw QueryParams so pagination/filter bounds are enforced.
Optional Pagination Metadata#
List endpoints return a JSON array by default. Set include_pagination_metadata = True on a view to return metadata together with the list items:
{
"items": [],
"total": 123,
"page": 2,
"page_size": 50,
"total_pages": 3
}
page, page_size, and total_pages are populated when the request was actually paginated — that is, when the client sent ?page= / ?page_size=, or when the view sets a non-None default_page_size. When pagination is not engaged the fields stay null and only total reflects the full result count.
Endpoint Decorators#
Use these decorators on methods in a view class:
Decorator |
HTTP Method |
Default Status |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Custom |
As configured |
The shorthand decorators explicitly set the default status code shown. Pass status_code= to override it.
All other keyword arguments are passed through to FastAPI’s route registration, so class-based routes use the same configuration surface as regular FastAPI routes: response_model=, dependencies=, responses=, tags=, and other APIRouter.add_api_route() options.
@fr.put(...) is available for custom endpoints, but default generated update endpoints use PATCH.
Route Exclusion#
To disable generated endpoints on a view, use:
@fr.include_view(app)
class UserView(fr.AsyncRestView):
prefix = "/users"
model = User
exclude_routes = [fr.ViewRoute.DELETE, fr.ViewRoute.UPDATE]
Valid route values for exclusion: fr.ViewRoute.LIST, fr.ViewRoute.GET, fr.ViewRoute.CREATE, fr.ViewRoute.UPDATE, fr.ViewRoute.DELETE.
exclude_routes accepts any iterable of ViewRoute values. Current route-name strings such as "delete" are also accepted.
Response Modeling#
For generated CRUD endpoints:
Response schema defaults to
schema(or an auto-generated*Readschema when omitted).Input schema for
POSTdefaults to schema without read-only fields (creation_schema, generated as*Create).Input schema for
PATCHdefaults to optionalized schema (update_schema, generated as*Update).Alias-aware serialization is applied so response payload keys follow schema aliases.
Key Public Symbols#
Model Base Classes#
Symbol |
Description |
|---|---|
|
SQLAlchemy declarative base with dataclass semantics and auto snake_case table names. |
|
Convenience alias combining |
|
Dataclass mixin adding |
|
Dataclass mixin adding integer |
|
Cascade string for use with |
|
Like |
FastAPI-Restly also works with ordinary SQLAlchemy declarative models that inherit from your own sqlalchemy.orm.DeclarativeBase. Use fr.IDBase when you want Restly’s dataclass-oriented convenience base; bring your own SQLAlchemy base when you prefer standard declarative constructor semantics or are adding Restly to an existing model layer.
RestView and AsyncRestView assume a single resource identifier: one primary key column addressable as /{id}. That column does not have to be named id when you provide explicit schemas and id_type, but the default CRUD routes, IDSchema[Model], IDRef[Model], React Admin integration, and OpenAPI identity shape are all scalar-id contracts. Composite primary keys are therefore not supported by the generated CRUD views. For composite-key tables, use fr.View and declare explicit routes such as @fr.get("/{tenant_id}/{slug}"), then write the SQLAlchemy query that matches that identity.
Schema Classes and Utilities#
Symbol |
Description |
|---|---|
|
Thin Pydantic base equivalent to |
|
Response-schema base class that adds the resource’s own read-only |
|
Scalar FK reference type. Wire format is the raw id ( |
|
Nested relationship-object field type. Wire format is |
|
Pydantic mixin adding read-only |
|
Type annotation marker. Fields annotated |
|
Type annotation marker. Fields annotated |
|
Auto-generate a Pydantic schema from a SQLAlchemy model. Useful for scaffolding, prototypes, and internal tools; prefer explicit schemas for stable public API contracts. Import from |
View Classes#
Symbol |
Description |
|---|---|
|
Base class for all class-based views. Subclass this directly when you do not need CRUD — add endpoints with |
|
Supported advanced base class for custom CRUD foundations shared by sync and async views. Import from |
|
Async CRUD view. Use with async SQLAlchemy sessions. |
|
Sync CRUD view. Use with sync SQLAlchemy sessions. |
|
Value object returned by |
|
Async CRUD view that speaks the |
|
Sync variant of |
View Method Surface#
Methods on RestView / AsyncRestView fall into three categories:
Route methods define the HTTP contract and are decorated as FastAPI endpoints. Override these only when you need to change status codes, response shape, headers, or request parameters.
Handler hooks contain the default CRUD business logic and are the normal customization point.
Public helpers are utility methods intended for use inside handlers or custom routes.
Category |
Method |
Signature |
Return |
Purpose |
|---|---|---|---|---|
Route |
|
|
response schema list or pagination envelope |
|
Route |
|
|
response schema |
|
Route |
|
|
response schema |
|
Route |
|
|
response schema |
|
Route |
|
|
|
|
Handler hook |
|
|
|
Fetch list rows and total count before pagination; override for list business logic after query construction. Use |
Handler hook |
|
|
|
Fetch one row through |
Handler hook |
|
|
|
Build and save a new row; override for create-time business rules. |
Handler hook |
|
|
|
Fetch, mutate, and save an existing row; override for update rules. |
Handler hook |
|
|
|
Fetch and delete a row; override for delete rules while keeping the HTTP contract. |
Public helper |
|
|
|
Base query shared by listing and single-row retrieve. |
Public helper |
|
|
|
Count an already-built list query after removing ordering and pagination. |
Public helper |
|
|
response schema list or pagination envelope |
Serialize a |
Public helper |
|
|
pagination envelope |
Serialize a |
Public helper |
|
|
response schema |
Validate and serialize an ORM object with Restly’s alias/reference/write-only handling. |
Public helper |
|
|
|
Build and stage a new object without flushing. |
Public helper |
|
|
|
Apply writable fields without flushing. |
Public helper |
|
|
|
Flush and refresh a staged object. Shared by the default create and update flows; deletes use |
Public helper |
|
|
|
Delete and flush an existing object. Override alongside |
Internal methods prefixed with _, including _reject_unknown_query_params, are implementation details even though they are visible on instances.
See Class-Based Views for the class hierarchy and How-To: Override Endpoints for examples of choosing between handler hooks and route replacement.
fr.View class attributes:
Attribute |
Type |
Description |
|---|---|---|
|
|
URL prefix for all routes in the view (e.g. |
|
|
OpenAPI tags. The view class name is always added automatically; set this to add extra tags. |
|
|
FastAPI dependencies applied to every route in the view. |
|
|
OpenAPI response overrides. Defaults to |
View Class Attributes#
Attribute |
Type |
Description |
|---|---|---|
|
|
The read/response schema. If omitted, auto-generated from |
|
|
Schema for |
|
|
Schema for |
|
|
The SQLAlchemy model class. |
|
|
Scalar primary-key type used in generated |
|
|
Set |
|
|
Route names to suppress. |
|
|
Query keys to allow on the listing endpoint in addition to those derived from the response schema. Use for view-specific parameters consumed outside |
|
|
Default |
|
|
Upper bound for |
Advanced Object Helpers#
These advanced helpers live in fastapi_restly.objects. They are the primitive surface for building, updating, deleting, and explicitly saving ORM objects from schemas. Use them when you need the framework’s schema-to-object mapping outside the view instance methods, for example in custom routes, services, or test setup. Each variant exists in both sync and async form, matching the session type you have on hand.
Symbol |
Description |
|---|---|
|
Build a new |
|
Apply the schema’s writable fields onto an existing ORM |
|
Flush the session and refresh |
|
Delete |
|
Async equivalent of |
|
Async equivalent of |
|
Async equivalent of |
|
Async equivalent of |
View Instance Methods#
Every AsyncRestView / RestView instance exposes ergonomic wrappers around the object helpers above. The wrappers bind self.session, self.model, and self.schema so the dominant case (self.build_from_schema(schema_obj)) doesn’t have to thread them explicitly. The async/sync split is implicit: AsyncRestView.build_from_schema calls async_build_from_schema under the hood, RestView.build_from_schema calls the sync version.
Use these inside perform_* handlers or custom route methods. When you need to work with a model that isn’t self.model (e.g. creating a sibling row in a custom endpoint) reach for the fastapi_restly.objects helpers instead.
Method |
Description |
|---|---|
|
Serialise an ORM object to the configured response schema, applying alias rules, stripping |
|
Wraps |
|
Wraps |
|
Wraps |
|
Wraps |
|
Return the base SQLAlchemy |
|
Return the total row count for an already-built list query. The default |
|
Convert a |
|
Convert a |
Database#
Symbol |
Description |
|---|---|
|
FastAPI |
|
FastAPI |
|
Open an async SQLAlchemy session context manager for use outside request handling, for example in background jobs or scripts. |
|
Open a sync SQLAlchemy session context manager for use outside request handling, for example in background jobs or scripts. |
|
Configure the framework. Accepts async/sync URLs, engines, session makers, custom session generators, and |
|
Return the configured |
|
Return the configured sync |
Restly has one public process-wide configuration. Configure it once during application startup:
fr.configure(async_database_url="sqlite+aiosqlite:///app.db")
fr.configure(...) must receive at least one meaningful setup option, such as an app for default exception-handler registration, a database URL, an engine, a session maker, a custom session generator, or an explicit commit_session_on_response policy. A bare fr.configure() call raises TypeError.
Applications that need more than one database can still use FastAPI and SQLAlchemy directly: provide a custom dependency on a view, or pass a custom session generator to fr.configure(...). Restly does not currently provide a public multi-context or multi-engine API. See Use a custom session dependency on one view for per-view session wiring.
By default, Restly commits sessions created by AsyncSessionDep / SessionDep when an endpoint successfully produces a response. On FastAPI versions that support dependency scopes, Restly requests function scope so this commit runs before the response is sent. On older FastAPI versions, commit timing follows FastAPI’s default yield dependency cleanup timing and may run after the response has been sent.
Set commit_session_on_response=False if your handlers should call commit() / rollback() explicitly. If you pass session_generator or sync_session_generator, Restly does not add commit/rollback behavior; that custom generator owns the transaction lifecycle.
Exceptions#
Symbol |
Description |
|---|---|
|
Base class for FastAPI-Restly framework errors. |
|
Raised when a public Restly helper needs configuration that has not been set up yet, such as calling |
Testing#
Symbol |
Description |
|---|---|
|
Sync test client wrapper around FastAPI’s |
|
Intended for tests. Wraps a session factory in savepoint-only mode so test data never commits to the database. Requires the session maker as argument. |
|
Restore normal session behavior after testing. |
Default Exception Handling#
FastAPI-Restly installs a default handler for SQLAlchemy IntegrityError on FastAPI apps. The handler translates database integrity conflicts — unique constraint, foreign-key, not-null, and check-constraint violations — into HTTP 409 Conflict responses using FastAPI’s normal error body shape:
{
"detail": "Unique constraint violated on user.email"
}
The exact detail text is best-effort and depends on the database driver. The handler recognizes common PostgreSQL SQLSTATE integrity codes and SQLite constraint messages; unknown dialects fall back to a generic conflict message.
Registration is automatic in either of these cases:
fr.configure(app=app, ...)is called with the defaultinstall_default_exception_handlers=True.A view is registered directly on a
FastAPIapp withfr.include_view(app). This fallback covers apps that configure database sessions separately.
Restly only skips this default when the app already has a handler registered specifically for sqlalchemy.exc.IntegrityError. Other handlers, such as a generic Exception handler, do not prevent Restly from registering its IntegrityError handler.
To opt out:
fr.configure(
app=app,
async_database_url="sqlite+aiosqlite:///app.db",
install_default_exception_handlers=False,
)
To use your own response format, register your IntegrityError handler before Restly installs its defaults:
from fastapi.responses import JSONResponse
from sqlalchemy.exc import IntegrityError
@app.exception_handler(IntegrityError)
async def integrity_error_handler(request, exc):
return JSONResponse(
status_code=409,
content={"error": {"code": "constraint_conflict"}},
)
fr.configure(app=app, async_database_url="sqlite+aiosqlite:///app.db")
Important Limitations and Capabilities#
Nested schemas are supported for responses and relation filtering, including nested aliases
Full nested schemas are not supported for create/update payloads by the default CRUD flow; write payloads must map directly to model fields, or use model-aware reference fields such as
*_id: IDRef[Model]and relationship fields typed asIDSchema[Model]Ordinary SQLAlchemy
DeclarativeBasemodels work with generated CRUD viewsUUID and other non-
intscalar primary keys are supported throughid_type,IDRef[Model], andIDSchema[Model]Composite primary keys are not supported by generated
RestView/AsyncRestViewCRUD routes; usefr.Viewfor custom route shapes
Minimal Example#
import asyncio
import fastapi_restly as fr
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import Mapped
engine = create_async_engine("sqlite+aiosqlite:///app.db")
fr.configure(async_engine=engine)
app = FastAPI()
class User(fr.IDBase):
name: Mapped[str]
@fr.include_view(app)
class UserView(fr.AsyncRestView):
prefix = "/users"
model = User
async def init_models() -> None:
async with engine.begin() as conn:
await conn.run_sync(fr.DataclassBase.metadata.create_all)
asyncio.run(init_models())
Generated endpoints:
GET /users/POST /users/GET /users/{id}PATCH /users/{id}DELETE /users/{id}