Files
fastapi-toolsets/docs/migration/v2.md

4.2 KiB

Migrating to v2.0

This page covers every breaking change introduced in v2.0 and the steps required to update your code.


CRUD

schema is now required in offset_paginate() and cursor_paginate()

Calls that omit schema will now raise a TypeError at runtime.

Previously schema was optional; omitting it returned raw SQLAlchemy model instances inside the response. It is now a required keyword argument and the response always contains serialized schema instances.

=== "Before (v1)"

```python
# schema omitted — returned raw model instances
result = await UserCrud.offset_paginate(session, page=1)
result = await UserCrud.cursor_paginate(session, cursor=token)
```

=== "Now (v2)"

```python
result = await UserCrud.offset_paginate(session, page=1, schema=UserRead)
result = await UserCrud.cursor_paginate(session, cursor=token, schema=UserRead)
```

as_response removed from create(), get(), and update()

Passing as_response to these methods will raise a TypeError at runtime.

The as_response=True shorthand is replaced by passing a schema directly. The return value is a Response[schema] when schema is provided, or the raw model instance when it is not.

=== "Before (v1)"

```python
user = await UserCrud.create(session, data, as_response=True)
user = await UserCrud.get(session, filters, as_response=True)
user = await UserCrud.update(session, data, filters, as_response=True)
```

=== "Now (v2)"

```python
user = await UserCrud.create(session, data, schema=UserRead)
user = await UserCrud.get(session, filters, schema=UserRead)
user = await UserCrud.update(session, data, filters, schema=UserRead)
```

delete(): as_response renamed and return type changed

as_response is gone, and the plain (non-response) call no longer returns True.

Two changes were made to delete():

  1. The as_response parameter is renamed to return_response.
  2. When called without return_response=True, the method now returns None on success instead of True.

=== "Before (v1)"

```python
ok = await UserCrud.delete(session, filters)
if ok:  # True on success
    ...

response = await UserCrud.delete(session, filters, as_response=True)
```

=== "Now (v2)"

```python
await UserCrud.delete(session, filters)  # returns None

response = await UserCrud.delete(session, filters, return_response=True)
```

paginate() alias removed

Any call to crud.paginate(...) will raise AttributeError at runtime.

The paginate shorthand was an alias for offset_paginate. It has been removed; call offset_paginate directly.

=== "Before (v1)"

```python
result = await UserCrud.paginate(session, page=2, items_per_page=20, schema=UserRead)
```

=== "Now (v2)"

```python
result = await UserCrud.offset_paginate(session, page=2, items_per_page=20, schema=UserRead)
```

Exceptions

Missing api_error raises TypeError at class definition time

Unfinished or stub exception subclasses that previously compiled fine will now fail on import.

In v1, a subclass without api_error would only fail when the exception was raised. In v2, __init_subclass__ validates this at class definition time.

=== "Before (v1)"

```python
class MyError(ApiException):
    pass  # fine until raised
```

=== "Now (v2)"

```python
class MyError(ApiException):
    pass  # TypeError: MyError must define an 'api_error' class attribute.
```

For shared base classes that are not meant to be raised directly, use abstract=True:

class BillingError(ApiException, abstract=True):
    """Base for all billing-related errors — not raised directly."""

class PaymentRequiredError(BillingError):
    api_error = ApiError(code=402, msg="Payment Required", desc="...", err_code="BILLING-402")

Schemas

Pagination alias removed

Pagination was already deprecated in v1 and is fully removed in v2, you now need to use OffsetPagination or CursorPagination.