FasORM
Laravel Eloquent-style ORM for FastAPI — expressive models, clean migrations, and an artisan-style CLI, powered by SQLAlchemy 2.0 & Alembic.
pip install fasorm
Quick Start
1. Initialise your project
fasorm init
Creates:
app/
models/
migrations/
seeders/
.env
fasorm.config.py
2. Create a model
fasorm make:model User -m
Edit app/models/user.py:
from fasorm import Model, String, Boolean
class User(Model):
name = String()
email = String().unique()
password = String()
is_active = Boolean().default(True)
3. Edit the migration
app/migrations/2026_07_25_143000_create_users_table.py:
from fasorm.schema import Blueprint
def up(table: Blueprint):
table.id()
table.string("name")
table.string("email").unique()
table.string("password")
table.boolean("is_active").default(True)
table.timestamps()
def down(table: Blueprint):
table.drop()
4. Run migrations
fasorm migrate
FasORM · Running migrations…
3 migration(s) pending…
✅ 2026_07_25_143000_create_users_table
✓ Migration successful. 1 migration(s) applied.
Models
Define a model
from fasorm import Model, String, Boolean, Timestamp, HasMany
class User(Model):
name = String()
email = String().unique()
is_active = Boolean().default(True)
posts = HasMany("Post")
Features:
- Auto-generated
id(BigInteger, autoincrement) - Auto-generated
created_atandupdated_attimestamps - Auto-generated
__tablename__from class name (User→users,BlogPost→blog_posts)
CRUD Operations
# Create
user = await User.create(name="Sandy", email="sandy@example.com")
# Find by ID
user = await User.find(1)
user = await User.find_or_fail(1) # raises ModelNotFoundError
# Query
users = await User.where(is_active=True).get()
user = await User.where(email="sandy@example.com").first()
users = await User.where("age", ">=", 18).order_by("name").get()
# All
users = await User.all()
# Paginate
page = await User.paginate(per_page=20, page=1)
# Update
await user.update(name="New Name")
# Delete
await user.delete()
# First or create
user = await User.first_or_create(
{"email": "sandy@example.com"},
defaults={"name": "Sandy"}
)
# Update or create
user = await User.update_or_create(
{"email": "sandy@example.com"},
defaults={"name": "Updated Sandy"}
)
Relationships
from fasorm import Model, String, HasMany, BelongsTo
class User(Model):
name = String()
posts = HasMany("Post")
class Post(Model):
title = String()
user = BelongsTo("User")
# Eager loading
users = await User.with_("posts").get()
Serialisation
user.to_dict() # → {"id": 1, "name": "Sandy", ...}
user.to_json() # → '{"id": 1, "name": "Sandy", ...}'
page.to_dict() # → {"data": [...], "meta": {"total": 100, ...}}
Field Types
| Field | SQL Type | Notes |
|---|---|---|
String(length=255) |
VARCHAR(n) |
|
Text() |
TEXT |
|
Integer() |
INTEGER |
|
BigInteger() |
BIGINT |
|
SmallInteger() |
SMALLINT |
|
Float(precision) |
FLOAT |
|
Decimal(precision, scale) |
NUMERIC(p,s) |
|
Boolean() |
BOOLEAN |
|
Date() |
DATE |
|
Time() |
TIME |
|
Timestamp() |
TIMESTAMP WITH TZ |
|
Json() |
JSON / JSONB |
|
Uuid() |
UUID / CHAR(36) |
Native on PG |
Enum(values) |
ENUM |
|
Vector(dimension) |
VECTOR(n) / TEXT |
pgvector on PG |
Array(item_type) |
ARRAY / JSON |
Native on PG |
Binary(length) |
LARGEBINARY |
All fields support chaining: .nullable(), .unique(), .default(value), .index(), .comment(text)
CLI Commands
| Command | Description |
|---|---|
fasorm init |
Scaffold project structure |
fasorm make:model Name [-m] |
Create a model (optionally with migration) |
fasorm make:migration name |
Create a migration file |
fasorm make:seeder Name |
Create a seeder file |
fasorm migrate |
Run pending migrations |
fasorm rollback [--steps N] |
Rollback last N batches |
fasorm refresh |
Rollback all + re-migrate |
fasorm fresh [--seed] |
Drop all + migrate + seed |
fasorm seed [--class Name] |
Run seeders |
fasorm status |
Show migration status |
FastAPI Integration
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from fasorm import get_session, init_db
from fasorm.config import load_config
app = FastAPI()
@app.on_event("startup")
async def startup():
config = load_config()
init_db(config)
@app.get("/users")
async def list_users():
users = await User.all()
return [u.to_dict() for u in users]
@app.get("/users/{user_id}")
async def get_user(user_id: int):
user = await User.find_or_fail(user_id)
return user.to_dict()
Architecture
FastAPI
│
FasORM
│
─────────┼─────────
│ │
SQLAlchemy Alembic
│ │
PostgreSQL SQLite
MySQL MariaDB
FasORM is an opinionated layer on top of SQLAlchemy 2.0 and Alembic — not a replacement.
License
MIT
Metadata
Release files for fasorm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fasorm-0.1.0.tar.gz | 36.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fasorm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 76.2 kB
Release files / fasorm-0.1.0.tar.gz
| Download URL | fasorm-0.1.0.tar.gz |
|---|---|
| Size | 36.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f76cb1643280fec18efa64d3dec126e16891c2ea21b6fd591d2be2209139ff23
|
|
BLAKE2b-256 checksum How to use checksums |
2e197d930f8dd04924eb3917c90995a3cbe0f4bc530d3c97f4b9f9b15410b4db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / fasorm-0.1.0-py3-none-any.whl
| Download URL | fasorm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 40.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dbc01b76c2d53c9711169d13aedabcbb5c177def5573c2ab49ec918881a081b6
|
|
BLAKE2b-256 checksum How to use checksums |
30c93f52684065a30cef5a2d53185b661ebe39c3c772ab7c092446f4f0074807
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|