OpenAPI#
Restly adds each route of a view to FastAPI as a normal path operation. So FastAPI builds the OpenAPI spec, and FastAPI’s options work on every route, the CRUD routes included. Restly fills in a few things for you:
the tag of each route: the view’s
tags, or else a tag made from the last segment of the prefix, so/order-itemsgivesOrder Items;the name, the summary and the operation ID of each CRUD route;
the request and response types of the CRUD routes: the classes that Restly builds from the view’s schema, named as in The names of the generated classes;
the filter, sort and page parameters of each list route;
an
x-resource-refon each field that points to another resource.
When the app builds its spec, Restly also warns when two classes have the same name; see Name your schemas.
To change a default, set a view attribute, or override the endpoint method with a decorator of your own. To document a custom route, pass keyword arguments to its decorator, as in FastAPI.
The names and operation IDs of the CRUD routes#
The CRUD routes get these FastAPI route names, operation IDs and summaries:
Endpoint method |
Route name |
Operation ID at |
Summary |
|---|---|---|---|
|
|
|
List |
|
|
|
Get |
|
|
|
Create |
|
|
|
Update |
|
|
|
Delete |
React Admin |
|
|
Update (PUT) |
An operation ID joins the full path of the resource and the action. It leaves
out the final {id} parameter and the HTTP method. So a view mounted under
/v1 gets v1_items_list instead of items_list, and the same view mounted
under /v1 and /v2 gets different IDs. The class name and the tags of the
view do not change the IDs.
Details
A path parameter of a parent stays in the ID as by_<parameter>. For
/projects/{project_id}/items, the list ID is
projects_by_project_id_items_list. Punctuation becomes an underscore, so
/order-items gives order_items_list. Restly keeps the spelling of the
resource: /people gives people_get, not a singular form. Two paths that
give the same name, such as /order-items and /order_items, need explicit
operation IDs when they are in one spec.
The route name is the action part of the ID. So name="search" on the list
route gives items_search. Custom routes, and the routes of a plain
View, get FastAPI’s normal operation IDs.
An explicit operation_id always wins. You can also give FastAPI a
generate_unique_id_function
on a route, a router, an include_router() call or the app. It replaces
Restly’s IDs, with FastAPI’s normal order of precedence. It receives the route
names above, so a function based on the tag and the name can give IDs such as
items-get_many.
If you generate clients from the spec, generate them again when you move to
Restly’s IDs. To keep an existing operation ID, pass operation_id to the
decorator of the endpoint method; see the next section.
Change the name, summary or operation ID of a CRUD route#
Override the endpoint method, and pass the keyword arguments to its decorator, as in FastAPI. The method calls the method of the base class, so the route works as before:
from fastapi_restly.views import ViewListResponse
class InvoiceView(fr.AsyncRestView):
prefix = "/invoices"
model = Invoice
schema = InvoiceSchema
@fr.get("/", summary="List invoices", operation_id="billing_list_invoices")
async def get_many_endpoint(self, list_params) -> ViewListResponse:
return await super().get_many_endpoint(list_params)
ViewListResponse keeps the view’s list response as the response model; see
Name the view’s classes in a custom route.
Restly still fills in the rest of the route: the list params, and the name
and the summary that you do not pass. A keyword argument
that you pass wins over Restly’s default. The table in
The names and operation IDs of the CRUD routes
lists the endpoint methods.
Details
The decorator passes its keyword arguments to FastAPI, so every option of a
FastAPI route works. For example, dependencies adds a check to one route:
@fr.post("/", dependencies=[Depends(require_admin)])
async def create_endpoint(self, data: ViewCreate) -> ViewResponse:
return await super().create_endpoint(data)
An item route that you declare, such as GET /{id}, keeps the path that you
give it. Restly adds the int or uuid converter only to a path parameter
annotated ViewID; see Item paths and static routes.
A subclass inherits the method and its decorator, so it also inherits an
explicit operation_id. Each operation_id must be unique in the app’s
spec.
The route names repeat across views. If you use app.url_path_for() to build
the URLs of routes in several views, give them names of their own.
Document a custom route#
To document a custom route, pass keyword arguments to its route decorator.
The decorators pass them to FastAPI’s add_api_route(), so a custom route
documents itself like any FastAPI route:
@fr.post(
"/{id}/publish",
status_code=200,
summary="Publish an article",
responses={409: {"description": "Already published"}},
)
async def publish(self, id: int): ...
Endpoint decorators lists the decorators and their default status codes.
Document another response for a CRUD route#
The response_model of a CRUD route, and so its schema in the spec, comes
from the classes that Restly builds from the view’s
schema, such as
UserResponse and UserCreate. To change the fields of every route, set
the view’s response class; see
Set your own response class. To document and return
another shape on one route, replace its endpoint method with your own
decorator and response_model; see
Change the shape of one route and
Use a different schema for the list route.
A route that the view leaves out with
include_routes or
exclude_routes is
not in the spec at all; see Exclude CRUD routes.
See which resource an id points to#
Restly adds the vendor extension x-resource-ref to each field that points to
another resource. A client or a code generator can read it. Its value is the
prefix of the view that serves the target model, without the leading slash:
{
"author_id": {"type": "integer", "title": "Author Id", "x-resource-ref": "authors"}
}
Two kinds of fields get it:
a field named after a foreign key column, whatever its type, such as
author_id: fr.MustExist[int, Author]orauthor_id: int;a field named after a relationship, with the type
fr.IDRef[Model]orfr.IDSchema[Model], or a list of them.
Details
A field gets it only when a view for the target model is included on the same
app. A nested object, such as author: AuthorSchema, gets none. The
reference types themselves are on
Relationships.
There is a known limit: views that you include on an APIRouter, not on the
app, do not get these annotations. The check for two classes with the same
name still covers them when you pass the app to
fr.configure(app, ...).
Restly adds the annotations in the app’s app.openapi(), and checks the
class names there too. FastAPI’s function get_openapi() does neither. So to
write the spec to a file, call app.openapi(). Restly wraps app.openapi
when the first view is included on the app, or when you pass the app to
fr.configure. An app.openapi of your own that you set after that replaces
Restly’s. Set it before, and Restly wraps it.
See also#
Views: the class attributes that every view has.
Endpoint decorators: the decorators and the keyword arguments that they pass to FastAPI.
Schemas & Validation: the classes that the spec shows, and their names.