# FastAPI React Toolkit

A set of extensions for [FastAPI](https://github.com/tiangolo/fastapi) and a React component library for building modern web applications with [Mantine](https://mantine.dev/), [Zustand](https://zustand.docs.pmnd.rs/), [TanStack Query](https://tanstack.com/query/latest), and [JsonForms](https://jsonforms.io/).

## Concept

FastAPI React Toolkit bootstraps a web API with [FastAPI](https://github.com/tiangolo/fastapi) and provides a React component library for building a SPA frontend. It supports automatic CRUD API generation, RBAC, OAuth2/JWT authentication, database migrations, and i18n.

## Features

- Automatic CRUD API generation from SQLAlchemy models
- Role-Based Access Control (RBAC)
- Database migrations (Alembic)
- OAuth2/JWT authentication
- Modular backend and frontend
- React hooks and components for API, Auth, Language, DataGrid, UserMenu, etc.
- Built-in i18n for backend and frontend

## Getting Started

You can use `fastapi-rtk create-app` command to quickly set up a new project with the recommended structure and example code.

### Recommended Project Structure

```
project/
├── app/
│   ├── __init__.py          # Configuration loading
│   ├── app.py               # FastAPI app initialization
│   ├── config.py            # Settings
│   ├── models.py            # Database models
│   └── apis.py              # API endpoints
├── webapp/
│   ├── src/
│   │   ├── main.jsx         # React app entry point
│   │   ├── App.jsx          # Main app component
│   │   ├── constants.js     # Constants for the frontend, like BASE_PATH
│   │   └── ...              # Other React components and hooks
│   ├── index.html           # HTML template
└── run.py                   # Entry point for development server
```

### Backend

1. Install FastAPI React Toolkit:

   ```bash
   pip install fastapi-rtk
   mkdir -p app
   touch run.py app/__init__.py app/app.py app/config.py app/models.py app/apis.py
   ```

2. Project files:

   **app/\_\_init\_\_.py**

   ```python
   from fastapi_rtk import g

   g.config.from_pyfile("./app/config.py")
   ```

   **app/app.py**

   ```python
   from fastapi import FastAPI
   from fastapi.middleware.cors import CORSMiddleware
   from fastapi_rtk import FastAPIReactToolkit

   app = FastAPI(docs_url="/openapi/v1")

   app.add_middleware(
       CORSMiddleware,
       allow_origins=["http://localhost:5173"],
       allow_credentials=True,
       allow_methods=["*"],
       allow_headers=["*"],
   )

   toolkit = FastAPIReactToolkit(
       app,
       create_tables=True,      # Dev: auto-create tables
       upgrade_db=False,        # Prod: run migrations
   )

   from .apis import *  # noqa: E402, F403
   ```

   **app/config.py**

   See more configuration [here](https://codeberg.org/datatactics/fastapi-rtk/wiki/03-Configuration).

   ```python
   import os

   basedir = os.path.abspath(os.path.dirname(__file__))

   # Required settings
   SECRET_KEY = "your-secure-secret-key"
   SQLALCHEMY_DATABASE_URI = "sqlite+aiosqlite:///" + os.path.join(basedir, "app.db")

   # Optional settings
   APP_NAME = "My FastAPI-RTK App"
   ```

   **app/models.py**

   ```python
   from fastapi_rtk import Model, Mapped, mapped_column, relationship
   from sqlalchemy import String, ForeignKey

   class Category(Model):
       __tablename__ = "categories"

       id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
       name: Mapped[str] # Automatically set the column to String

       items: Mapped[list["Item"]] = relationship(back_populates="category")

       def __repr__(self):
           return self.name

   class Item(Model):
       __tablename__ = "items"

       id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
       name: Mapped[str] = mapped_column(String(100)) # Can also be explicitly given
       description: Mapped[str | None]

       category_id: Mapped[int | None] = mapped_column(ForeignKey("categories.id"))
       category: Mapped[Category | None] = relationship(back_populates="items")

       def __repr__(self):
           return self.name
   ```

   **app/apis.py**

   ```python
   from fastapi_rtk import ModelRestApi, SQLAInterface, g
   from .models import Item, Category

   class ItemApi(ModelRestApi):
       resource_name = "items"
       datamodel = SQLAInterface(Item)

   class CategoryApi(ModelRestApi):
       resource_name = "categories"
       datamodel = SQLAInterface(Category)

   g.current_app.add_api(ItemApi)
   g.current_app.add_api(CategoryApi)
   ```

   It will create the following CRUD endpoints automatically, all under the resource prefix `/api/v1/items`:

   - `GET /api/v1/items/_image/{filename}` - Serve image files (If image column is present)
   - `GET /api/v1/items/_file/{filename}` - Serve file downloads (If file column is present)
   - `GET /api/v1/items/_info` - Get metadata about the model, including which columns can be added, edited, filtered, etc.
   - `POST /api/v1/items/bulk/{handler}` - Bulk operations, if set on the API class
   - `GET /api/v1/items/download` - Download items as CSV
   - `GET /api/v1/items/` - List items
   - `POST /api/v1/items/` - Create item
   - `GET /api/v1/items/{id}` - Get item by ID
   - `PUT /api/v1/items/{id}` - Update item by ID
   - `DELETE /api/v1/items/{id}` - Delete item by ID

### Frontend

1. Install React dependencies:

   ```bash
   pnpm install @mantine/core @mantine/dates @mantine/form @mantine/hooks dayjs react react-dom react-router fastapi-rtk
   ```

2. Project files:

   **webapp/index.html**

   ```html
   <!DOCTYPE html>
   <html lang="en">
     <head>
       <meta charset="UTF-8" />
       <base href="%VITE_BASE_PATH%" />
       <link rel="icon" type="image/svg+xml" href="" />
       <meta
         name="viewport"
         content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
       />
       <title>YOUR_APP_NAME_HERE</title>
       <script src="%VITE_BASE_PATH%server-config.js"></script>
       <script nonce="{{nonce}}">
         window.nonce = "{{nonce}}";
       </script>
     </head>
     <body>
       <div id="root"></div>
       <script type="module" src="/src/main.jsx"></script>
     </body>
   </html>
   ```

   **src/main.jsx**

   ```jsx
   import "@mantine/core/styles.css";
   import "@mantine/dates/styles.css";
   // Other Mantine styles can be imported here if needed

   import "fastapi-rtk/styles.css";
   import "./index.css";

   import { MantineProvider } from "@mantine/core";
   import { Provider } from "fastapi-rtk";
   import { StrictMode } from "react";
   import { createRoot } from "react-dom/client";
   import { BrowserRouter } from "react-router";
   import App from "./App.jsx";
   import { BASE_PATH } from "./constants.js";

   createRoot(document.getElementById("root")).render(
     <StrictMode>
       <MantineProvider>
         <Provider baseUrl={BASE_PATH + "api/v1"}>
           <BrowserRouter basename={BASE_PATH}>
             <App />
           </BrowserRouter>
         </Provider>
       </MantineProvider>
     </StrictMode>,
   );
   ```

   **src/constants.js**

   ```javascript
   export const BASE_PATH = new URL(document.baseURI).pathname;
   ```

## Platform Adapters (React Native)

The frontend package is split in two: a Mantine/DOM-free package published as the `fastapi-rtk/api` subpath, and the Mantine-based web package (`fastapi-rtk` / `fastapi-rtk/core`). Every place the library touches the platform (storage, HTTP, cookies/tokens, file downloads, OAuth popups, `FormData`) goes through six small adapter interfaces (plus an optional localization adapter), grouped into an `Adapters` object. `fastapi-rtk` ships web defaults built on the DOM (cookie auth, `localStorage`, `fetch`, ...), so existing web apps need no changes.

An optional `fastapi-rtk/react-native-adapters` package provides factory functions (`createReactNativeAdapters`, plus per-adapter factories) that build the same six adapters on top of React Native / Expo primitives, using bearer JWT auth (`auth/jwt/login`) instead of the web's cookie auth (`auth/login`):

```tsx
import { ApiProvider, Provider, useAuth } from "fastapi-rtk/api";
import { useApi } from "fastapi-rtk/contexts";
import { createReactNativeAdapters } from "fastapi-rtk/react-native-adapters";

const rnAdapters = createReactNativeAdapters({
  asyncStorage,
  fileSystem,
  sharing,
  webBrowser,
});

<Provider baseUrl="https://api.example.com/api/v1" adapters={rnAdapters}>
  <ApiProvider resource_name="items">
    {/* your own RN UI, driven by useApi()/useAuth() */}
  </ApiProvider>
</Provider>;
```

A single adapter can also be swapped on the web `Provider` via `adapters={{ storage: mine }}` (merged per-key over the web defaults; the rest stay web defaults).

See the wiki page [React Native and Adapters](https://codeberg.org/datatactics/fastapi-rtk/wiki/06.04-React-Native-and-Adapters) for the full adapter reference (interfaces, per-adapter web/RN table, auth transports).

## License

FastAPI-RTK is licensed under the [MIT license](https://codeberg.org/datatactics/fastapi-rtk/src/branch/main/LICENSE).

## Contributing

Contributions are welcome! Please open an issue or submit a pull request.

---

For more details, see the [Wiki](https://codeberg.org/datatactics/fastapi-rtk/wiki) and the [example app](https://codeberg.org/datatactics/fastapi-rtk/src/branch/main/py/example/app/readme.md).
