Behavior and Configuration Reference#
CRUD views define default HTTP routes, schema rules, and transaction behavior. This page lists those defaults, view configuration attributes, and public methods. API Reference lists the public modules. API Modules documents complete signatures.
Default CRUD Routes#
Register a view with fr.include_view(app, ViewClass) or
@fr.include_view(app). fr.AsyncRestView and fr.RestView define the same
default CRUD endpoint methods. include_view registers them as these routes:
Method |
Path |
Purpose |
Default Status |
|---|---|---|---|
|
|
List resources |
|
|
|
Create resource |
|
|
|
Get resource by ID |
|
|
|
Partial update |
|
|
|
Delete resource |
|
OpenAPI uses the collection path without a trailing slash as the canonical
form. Restly also accepts /{prefix}/ as a hidden compatibility alias. Use
/{prefix} in templates, examples, and contract tests.
The default routes share these conventions:
Updates use
PATCH, notPUT. React Admin views also exposePUT /{id}forra-data-simple-rest; see React Admin Integration.GET /{id}andDELETE /{id}return404when the object is not found.Built-in integer and UUID item routes use typed path matching. Values that do not match fall through to another route or return
404.Read-only schema fields are ignored on create/update.
*_id: fr.MustExist[int, Model]inputs are validated against the database: the referenced row must exist. The scalar id is the related primary-key type, such asintorUUID.
List Endpoint Behavior#
GET /{prefix} accepts filter, sort, and pagination parameters derived from
the view’s response class; keys use public field names (aliases included), and
dotted paths filter on relations. The table below gives the grammar in one
line each; the full description, including comma semantics, LIKE escaping,
foreign-key filtering, and alias rules, is in
Filter, Sort, and Paginate Lists:
Kind |
Form |
|---|---|
Equality / OR |
|
Operators |
|
Relation paths |
|
Sorting |
|
Pagination |
|
Unknown keys |
rejected with |
Pagination is on by default, and list responses are wrapped in a data
envelope. Two class attributes on RestView / AsyncRestView tune this
behavior:
Attribute |
Type |
Default |
Purpose |
|---|---|---|---|
|
|
How list endpoints paginate. A |
|
|
|
Query keys to allow beyond those derived from the response class, for view-specific parameters read from the request outside the list grammar (e.g. |
fr.NumberedPagination holds the
settings. Each is a keyword argument, and replace(**changes) returns a
checked copy:
Setting |
Default |
Purpose |
|---|---|---|
|
Page size when the client sends none. Lower it and cap |
|
|
Largest page size a client may ask for; a larger one is rejected with |
|
|
Largest page number a client may ask for; a larger one is rejected with |
|
|
Query parameter for the page number. |
|
|
Query parameter for the page size. |
|
|
|
The list response model: a generic model filled by field name from |
fr.NoPagination has one setting,
envelope, Envelope by
default, filled from data and total_count, the number of rows.
The envelope’s shape and custom alternatives are covered in Response Envelopes and List Metadata.
At a lower level, fr.query.derive_schema_list_params(...) and
fr.query.apply_list_params(...) power the default list endpoint. Use the
view classes for normal CRUD. Call these helpers directly only for custom
endpoints that need the same list grammar, and pass validated list params
instead of raw QueryParams. Pass the view’s settings to
derive_schema_list_params as pagination=self.pagination;
apply_list_params then reads them from the list params model. It takes
(query, list_params, model, schema_response), the same arguments in the same
order as the view method apply_list_params(query, list_params). Pass the
view’s self.schema_response, not self.schema: the list params follow the
response class.
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.
Other keyword arguments pass through to FastAPI route registration: response_model=, dependencies=, responses=, tags=, and other APIRouter.add_api_route() options.
@fr.put(...) is available for custom endpoints, but the default update route
uses PATCH.
Route Exclusion#
To disable default CRUD routes on a view, set exclude_routes:
@fr.include_view(app)
class UserView(fr.AsyncRestView):
prefix = "/users"
model = User
exclude_routes = [fr.ViewRoute.DELETE, fr.ViewRoute.UPDATE]
The valid route values for exclusion are fr.ViewRoute.GET_MANY, fr.ViewRoute.GET_ONE, fr.ViewRoute.CREATE, fr.ViewRoute.UPDATE, and fr.ViewRoute.DELETE.
exclude_routes accepts ViewRoute values or the equivalent endpoint method names, such as "delete_endpoint". Worked examples are in Exclude CRUD routes.
Response Modeling#
The inherited CRUD endpoint methods derive their request and response schemas from the view’s configuration:
The response schema is
schema_response. By default, Restly builds it fromschema: the schema without itsWriteOnlyfields, named likeUserResponse. Without aschema, Restly generates one from the model, named likeUserSchema(see Generated Class Names).The input schema for
POSTdefaults to the schema without read-only fields (schema_create, generated as*Create).The input schema for
PATCHdefaults to the optionalized schema (schema_update, generated as*Update).Alias-aware serialization is applied, so response payload keys follow schema aliases.
The derivation rules are described in Generated Input Schemas.
Key Public Symbols#
The tables below summarize public symbols by module. API Modules documents their complete signatures.
Model Base Classes#
These base classes and mixins form the declarative foundation for SQLAlchemy models:
Symbol |
Description |
|---|---|
SQLAlchemy declarative base with dataclass semantics and auto snake_case table names. Mixes in SQLAlchemy’s |
|
Convenience alias combining |
|
Dataclass mixin adding |
|
Dataclass mixin adding integer |
|
|
Cascade string for use with |
|
Like |
FastAPI-Restly also works with ordinary SQLAlchemy models that inherit from your own DeclarativeBase. Use fr.IDBase for Restly’s dataclass convenience base; bring your own base for standard constructor semantics or existing model layers.
On DataclassBase and IDBase, a plain Mapped[datetime] represents a UTC
instant and maps to DateTime(timezone=True). PostgreSQL enforces that through
its timestamptz column type. SQLite does not preserve timezone metadata and
returns naive datetime values. Use mapped_column(DateTime()) to opt a specific
wall-clock field out of timezone-aware storage.
RestView and AsyncRestView assume one scalar resource identifier at
/{id}. The column can have another name when you provide explicit
schemas, but the default CRUD routes, IDSchema, IDRef, React Admin,
and OpenAPI identity shape all remain scalar-id contracts. A composite key is
addressed from a custom route on the view, which loads with a predicate
(handle_get_one(sa.and_(Model.a == a, Model.b == b)), see
Look a row up by another key); fr.View with explicit
routes such as @fr.get("/{tenant_id}/{slug}") remains the option for a
hand-written group.
Schema Classes and Utilities#
These classes and markers define how model data crosses the wire; the reference-field types (MustExist, IDRef, IDSchema) are treated in depth in Work with Foreign Keys and Relationships:
Symbol |
Description |
|---|---|
Thin Pydantic base equivalent to |
|
Response-schema base class that adds the resource’s own read-only |
|
Existence-checked scalar FK type for a |
|
Existence check with the target named explicitly and a per-field scope: |
|
Relationship reference with a flat-id wire; resolves the id to the related object. 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 are accepted on input and excluded from Pydantic serialization, including CRUD responses and direct |
|
Auto-generate a Pydantic schema from a SQLAlchemy model. With its defaults, it returns the schema that a view generates when it has none. Useful for scaffolding, prototypes, and internal tools; prefer explicit schemas for stable public API contracts. Import from |
|
The response class that a view builds from its schema: the schema without its |
|
|
The list response class of a view: the envelope of its pagination, filled with its response class, named like |
View Classes#
Views are the routing layer; each class below is a registration entry point:
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#
Each CRUD verb on RestView / AsyncRestView is split into three tiers: the
endpoint method (<verb>_endpoint), the handler (handle_<verb>), and
the business method (<verb>). You override the endpoint method or the
business method, whichever owns your change; the handler is final. The model
and the decision table live in Customizing RestView.
Alongside the tiers are the declared read scope (the
scope class attribute) and cross-cutting override points
(apply_list_params, count, authorize,
before_action_commit / after_action_commit, to_response,
get_relationship_loader_options, snapshot). The handlers and the
domain utilities (make_new_object, update_object, save_object) are
final: you call them rather than override them, and a view class that defines
one fails at class definition.
On AsyncRestView, the handlers, the business methods, the domain utilities,
count, authorize and the transaction hooks are async def; the response
methods, apply_list_params, snapshot and get_relationship_loader_options
are plain def, and the two brackets are entered with async with. Argument
names are identical between variants.
Tier / kind |
Method |
Signature |
Return |
Purpose |
|---|---|---|---|---|
Endpoint method |
|
the list response, such as |
|
|
Endpoint method |
|
response schema |
|
|
Endpoint method |
|
response schema |
|
|
Endpoint method |
|
response schema |
|
|
Endpoint method |
|
|
|
|
Handler |
|
|
Run |
|
Handler |
|
|
Load through |
|
Handler |
|
|
Authorize, run |
|
Handler |
|
|
Load, authorize, snapshot, run |
|
Handler |
|
|
Load, authorize, snapshot, run |
|
Custom-action bracket |
|
context manager |
Entered as |
|
Commit bracket |
|
context manager |
Share one commit across write actions on the session. The outermost block commits, then runs their after-hooks. See Commit several writes together. |
|
Business method |
|
|
Scoped and filtered list via |
|
Business method |
|
|
Load one row through |
|
Business method |
|
|
Build a new object and save it. Commit-free: the usual create override point. |
|
Business method |
|
|
Apply the update payload to |
|
Business method |
|
|
Remove |
|
Configuration |
class attribute |
|
The clause every read applies, shared by |
|
Function |
|
|
The scope a read applies, resolved down the ladder: the view’s |
|
Override point |
|
|
Apply URL filter/sort/pagination to |
|
Override point |
|
|
Total for a paginated list: receives the same params-applied query, strips |
|
Override point |
|
|
Gate a verb. A no-op by default; override to enforce policy and raise |
|
Override point |
|
|
In-transaction side effect (outbox/audit rows), atomic with the write. |
|
Override point |
|
|
Post-commit side effect (email, webhook, cache invalidation). |
|
Override point |
|
response payload |
The single wire-level response method, called by the endpoint methods with the wire |
|
Override point |
|
|
Frozen capture of an object’s already-loaded column values, taken after |
|
Override point |
|
|
Loader options ( |
|
Helper |
|
instance of |
Validate and serialize one ORM object with Restly’s alias/reference/write-only handling. Override for custom projections or an intentional |
|
Helper |
|
envelope model instance |
Build the list response body: an instance of the pagination’s envelope, or of |
|
Domain utility |
|
|
Build and stage a new object without flushing, resolving references and skipping read-only fields. Final. |
|
Domain utility |
|
|
Apply writable fields without flushing, resolving references. Final. |
|
Domain utility |
|
|
Flush and refresh a staged object, then eager-load the relationships the response class names (via |
Internal methods prefixed with _, such as _reject_unknown_query_params, are implementation details even though they are visible on instances.
See Views for the class hierarchy, Customizing RestView for examples of choosing which tier to override, and Use Type Annotations for the typed signatures of these methods.
View Class Attributes#
Every View subclass, CRUD or not, honors these class attributes:
Attribute |
Type |
Description |
|---|---|---|
|
URL prefix for all routes in the view (e.g. |
|
|
OpenAPI tags. When unset, a tag derived from the view class name is used; setting this replaces the derived tag. |
|
|
FastAPI dependencies applied to every route in the view. Each class in the hierarchy adds its own, base first. |
|
|
OpenAPI response overrides. |
|
|
Per-route FastAPI keyword arguments. See OpenAPI customization for names, operation IDs, and override rules. |
RestView and AsyncRestView add the following:
Attribute |
Type |
Description |
|---|---|---|
|
The view’s schema. Restly derives the response, create and update schemas from it. If omitted, auto-generated from |
|
|
The class of everything that goes out: the responses, the list items, the fields the list params filter on, and the relationships the view loads. Auto-derived by removing |
|
|
Schema for |
|
|
Schema for |
|
|
The SQLAlchemy mapped class. A class without a mapper raises at class definition. |
|
|
Type of the |
|
|
Route names to suppress. |
|
|
The list params (filter, sort, page) as a Pydantic model, generated from |
The list-tuning attributes (pagination, extra_query_params) are tabulated under List Endpoint Behavior.
Current Request Values#
Current context covers declaration, FastAPI integration, and manual binding.
Symbol |
Description |
|---|---|
Base for the application’s |
|
A context value, declared as |
|
Generate a FastAPI dependency that binds values from existing dependencies per request. |
|
Bind values for a |
|
Show each member’s bound value and origin, or |
Clauses and Scopes#
Reusable query fragments and the visibility rules built from them. Query Clauses and Scopes own the topics; this table names the symbols.
Symbol |
Description |
|---|---|
Declare a predicate clause from a SQLAlchemy boolean expression, or from a function without parameters that returns one. Values come from context members. |
|
The type of every clause. Annotate a |
|
Compose clauses: AND, OR, NOT. |
|
Apply clauses to a plain SQLAlchemy statement. |
|
Group a model’s clauses under one named class. Declaring |
|
|
The explicit unscoped sentinel and its type: the one spelling for reading past a scope on a view, a namespace, a reference, or a single read. |
|
The per-read scope type the handlers take and forward: |
Advanced Object Helpers#
These helpers build, update, delete, and save ORM objects from schemas. Use them outside view instance methods: custom routes, services, workers, or tests. Sync and async variants are available from fastapi_restly.objects.
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 |
The view methods of the same names (in the
method surface) wrap these helpers, binding
self.session, self.model, and self.schema; reach for the fr.objects
forms in custom routes that touch a model other than self.model, and in
services, workers, or tests.
Database#
These symbols cover connection configuration and session access:
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, a |
|
Return the configured |
|
Return the configured sync |
Restly has one public process-wide configuration, described further in Restly Runtime Configuration. Configure it once during application startup:
fr.configure(async_database_url="sqlite+aiosqlite:///app.db")
fr.configure(...) must receive at least one setup option: an app, database URL, engine, session maker, custom session generator, health=, or a warn_on_uncommitted / warn_on_misuse setting. A bare fr.configure() raises TypeError.
Pass warn_on_misuse=True to enable opt-in registration-time misuse warnings (fr.exc.RestlyMisuseWarning): include_view then flags endpoint method overrides, direct session.commit() calls in view methods, CRUD route sets hand-rolled on a bare View, and an IDRef / IDSchema field named like a scalar foreign-key column, each with the idiomatic fix named. It is off by default and intended for development, project templates, and CI.
For multiple databases, use FastAPI and SQLAlchemy directly: add a custom dependency on a view, or pass a custom session generator to fr.configure(...). Restly does not provide a public multi-context or multi-engine API. See Use a custom session dependency on one view.
Restly’s write handlers normally own the commit: each runs before_action_commit, then the commit, then after_action_commit around domain logic. Inside shared_write_action_commit, the outermost block owns that commit and the handlers return before it. Session dependencies do not commit on response; they roll back and close on exit.
A custom write route should use self.write_action(...) or reuse a
handle_<verb>. To group their writes under one commit, wrap them in
self.shared_write_action_commit(). See
Commit several writes together.
Restly warns (RestlyUncommittedChangesWarning) when a request finishes with uncommitted session changes; this is the tell of a custom write route that forgot to commit. Fix the missing commit (write_action(...) or a handle_<verb>), or suppress a deliberate dry run with session.info["_fr_suppress_uncommitted"] = True. The global fr.configure(warn_on_uncommitted=False) opt-out exists but is rarely the right response to the warning.
Exceptions#
There are two families: configuration errors subclass RestlyError, and request-time HTTP errors subclass fastapi.HTTPException via RestlyHTTPError. Typed classes let you target Restly errors with app.add_exception_handler(...); recipes, the app-wide envelope pattern, and the 422-vs-400 boundary are in Shape Error Responses.
Symbol |
Description |
|---|---|
Base class for FastAPI-Restly framework (configuration-time) errors. |
|
Raised when a public Restly helper needs configuration that has not been set up yet, such as calling |
|
Base for Restly’s request-time HTTP errors. Subclass of |
|
HTTP |
|
HTTP |
|
HTTP |
|
HTTP |
Testing#
The testing utilities provide a status-asserting client and savepoint-based isolation:
Symbol |
Description |
|---|---|
Add managed testing to an application that has already been configured for its test database. Accepts the app, models ( |
|
Sync test client wrapper around FastAPI’s |
|
Async HTTPX client with the same default status-code assertions. The |
The pytest fixtures below are auto-loaded by the testing extra; their full
behavior is documented in Testing:
Fixture |
Scope |
One-liner |
|---|---|---|
|
function |
The app passed to |
|
function |
|
|
function |
|
|
function |
The suite’s |
|
function |
The async equivalent, following |
|
function |
|
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. This mapping and the surrounding error-shaping recipes are also covered in Shape Error Responses.
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 skips this default only when the app already has a sqlalchemy.exc.IntegrityError handler. Generic handlers do not block registration.
To opt out, disable the default handlers in fr.configure:
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#
The default CRUD contract has these boundaries:
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: fr.MustExist[int, Model]for FK columns and relationship fields typed asIDRef[Model]orIDSchema[Model].Ordinary SQLAlchemy
DeclarativeBasemodels work with CRUD views.UUID and other non-
intscalar primary keys are supported by the default routes,fr.MustExist[UUID, Model],IDRef[Model], andIDSchema[Model].Composite primary keys are not supported by the default
RestView/AsyncRestViewCRUD routes; a custom route on the view addresses one with a predicate (handle_get_one(sa.and_(...))), or usefr.Viewfor a hand-written route shape.