How-To: React Admin Integration#
React-admin with
ra-data-simple-rest
expects a specific REST wire contract that differs from the default Restly
contract in several ways: JSON-encoded sort and range parameters, a plain
array response body, and a Content-Range header for pagination.
AsyncReactAdminView and ReactAdminView implement this contract. Switch
to one of these view classes and ra-data-simple-rest works without a custom
data provider.
Quick start#
Replace AsyncRestView with AsyncReactAdminView (or RestView with
ReactAdminView for sync sessions):
import fastapi_restly as fr
from fastapi import FastAPI
from sqlalchemy.orm import Mapped
app = FastAPI()
fr.configure(async_database_url="sqlite+aiosqlite:///app.db")
class Product(fr.IDBase):
name: Mapped[str]
price: Mapped[float]
@fr.include_view(app)
class ProductView(fr.AsyncReactAdminView):
prefix = "/products"
model = Product
On the frontend, point ra-data-simple-rest at your API:
import { Admin, Resource } from "react-admin";
import simpleRestProvider from "ra-data-simple-rest";
const dataProvider = simpleRestProvider("http://localhost:8000");
export default () => (
<Admin dataProvider={dataProvider}>
<Resource name="products" />
</Admin>
);
No custom data provider, no adapter layer.
Wire contract#
AsyncReactAdminView translates the ra-data-simple-rest query format to SQL
and returns responses the provider expects.
List — GET /resource/#
Query parameter |
Format |
Example |
|---|---|---|
|
JSON |
|
|
JSON |
|
|
JSON object |
|
Response: a plain JSON array. The Content-Range header carries the total:
Content-Range: items 0-24/315
The id array form of filter ({"id": [1, 2, 3]}) is used by react-admin
for getMany calls. It translates to WHERE id IN (1, 2, 3).
Other operations#
Method |
Path |
Purpose |
Source |
|---|---|---|---|
|
|
Get one — react-admin |
inherited from |
|
|
Create — react-admin |
inherited from |
|
|
Full update — react-admin |
added by |
|
|
Partial update |
inherited from |
|
|
Delete — react-admin |
inherited from |
AsyncReactAdminView and ReactAdminView add a PUT /{id} endpoint because
ra-data-simple-rest’s default update method issues a PUT request. The
default PATCH /{id} is also kept available, so clients that prefer partial
updates continue to work.
The PUT route delegates to the same perform_update handler as PATCH and accepts the
view’s standard update_schema payload. Override perform_update (or replace the
PUT route directly) if you need different write semantics for the two methods.
CORS setup#
Browsers block non-standard response headers by default. The Content-Range
header must be explicitly exposed in your CORS configuration, otherwise the
frontend cannot read the total and pagination breaks.
Add CORSMiddleware to your FastAPI app with Content-Range in
expose_headers:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"], # your frontend origin
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["Content-Range"],
)
AsyncReactAdminView also sets Access-Control-Expose-Headers: Content-Range
on every list response as a per-response fallback, but the middleware approach
is more reliable and is the recommended setup for production.
Customization#
Set the default page size#
When the frontend does not send a range query parameter, Restly returns the
first 25 rows. Set default_page_size on the view to choose a different
default:
@fr.include_view(app)
class ProductView(fr.AsyncReactAdminView):
prefix = "/products"
model = Product
default_page_size = 50
Change the Content-Range unit#
ra-data-simple-rest ignores the unit part of the header (it only parses the
numbers), but if another consumer cares, override get_react_admin_range_unit:
class ProductView(fr.AsyncReactAdminView):
prefix = "/products"
model = Product
def get_react_admin_range_unit(self) -> str:
return "products"
Under the hood#
AsyncReactAdminView is a thin subclass of AsyncRestView built with the
route replacement
pattern. It replaces the listing route to change the list contract and adds a
PUT /{id} route that delegates to the standard perform_update handler. All other
generated routes (GET /{id}, POST /, PATCH /{id}, DELETE /{id}) and all
perform_* handlers are inherited unchanged.
The shared parsing and response logic is an internal implementation detail of the concrete React Admin view classes.