Getting Started#

This guide walks from zero to a working CRUD API with FastAPI-Restly.

Requirements: Python 3.10 or later.

1. Install#

From the repository root:

uv sync

If you want example project dependencies too:

make install-dev

2. Create an App#

Create main.py:

import fastapi_restly as fr
from fastapi import FastAPI
from sqlalchemy import create_engine
from sqlalchemy.orm import Mapped

fr.configure(async_database_url="sqlite+aiosqlite:///app.db")

# Create tables — for dev/SQLite only; use Alembic migrations in production
fr.DataclassBase.metadata.create_all(create_engine("sqlite:///app.db"))

app = FastAPI()


class User(fr.IDBase):
    name: Mapped[str]
    email: Mapped[str]


@fr.include_view(app)
class UserView(fr.AsyncRestView):
    prefix = "/users"
    model = User

A few things to note:

  • The table name is derived automatically from the class name (Useruser table).

  • fr.DataclassBase is the explicit dataclass-oriented declarative base.

  • fr.IDBase is the convenience alias that combines DataclassBase with an auto-incrementing integer id primary key.

  • If you prefer standard SQLAlchemy declarative style (without dataclass semantics), use fr.PlainIDBase instead — both work with the rest of the framework.

  • With no manual schema, FastAPI-Restly auto-generates one from your model.

When auto-generated schemas are a good fit:

  • Internal tools and backoffice APIs

  • Early project scaffolding or prototypes

  • Straightforward models with minimal validation rules

3. Run#

uv run fastapi dev main.py

Note: fastapi dev requires the fastapi[standard] extras (pip install "fastapi[standard]" or add it to your dependencies). It is not needed for production — only for the development server.

Open:

  • http://127.0.0.1:8000/docs

  • http://127.0.0.1:8000/openapi.json

4. Use the Generated Endpoints#

For prefix = "/users", generated endpoints are:

  • GET /users/

  • POST /users/

  • GET /users/{id}

  • PATCH /users/{id}

  • DELETE /users/{id}

Update semantics are PATCH (partial update).

You can filter results using query parameters. For example, GET /users/?filter[name]=Jane returns only users named Jane (V1 syntax; see How-To: Filter, Sort, and Paginate Lists for the full V1 and V2 syntax reference).

5. Add an Explicit Schema (Optional)#

Replace the UserView definition from Section 2 with:

class UserSchema(fr.IDSchema):
    name: str
    email: str


@fr.include_view(app)
class UserView(fr.AsyncRestView):
    prefix = "/users"
    model = User
    schema = UserSchema

fr.IDSchema already includes the id field as fr.ReadOnly (excluded from create/update requests, present in responses). You can apply the same marker to your own fields: fr.ReadOnly[str] keeps a field out of write operations. fr.WriteOnly[T] does the opposite — accepted on input, omitted from responses (useful for passwords).

Choose explicit schemas when you need:

  • Public API contracts you want to keep stable

  • Custom validation logic

  • Field aliases and strict response shaping

  • Extra clarity for teams that prefer less implicit behavior

6. Test Quickly#

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#