FastAPI CLI Library
Project description
MakeFast - FastAPI CLI Manager
Welcome to MakeFast, a FastAPI CLI library designed to streamline your development workflow — inspired by Laravel's elegant developer experience. With MakeFast, you can scaffold routes, models, requests, resources, migrations, and more, so you can focus on writing high-quality business logic.
Table of Contents
- Installation
- Commands
- Form Requests
- API Resources
- Database Configuration
- Project Structure
- Contributing
- License
Installation
To install MakeFast, simply run the following command in your terminal:
pip install makefast
After installation, run this command to generate the project template:
makefast init
Install the project dependencies:
pip install -r requirements.txt
Run the development server:
uvicorn main:app --port 8000 --reload
Commands
Project Creation
| Command | Description | Options |
|---|---|---|
makefast init |
Initializes a new project |
Route Generation
| Command | Description | Options |
|---|---|---|
makefast create-route ROUTE_NAME |
Generates a new route | --model MODEL_NAME, --request_scheme REQUEST_NAME, --response_scheme RESPONSE_NAME |
Model Generation
| Command | Description | Options |
|---|---|---|
makefast create-model MODEL_NAME |
Generates a new model | --table TABLE_NAME, --collection COLLECTION_NAME |
Schema Generation
| Command | Description | Options |
|---|---|---|
makefast create-schema SCHEMA_NAME |
Generates a new schema |
Enum Generation
| Command | Description | Options |
|---|---|---|
makefast create-enum ENUM_NAME |
Generates a new enum | --type str |
Request Generation
Generates a Laravel-style FormRequest validation class under app/requests/.
| Command | Description | Options |
|---|---|---|
makefast create_request REQUEST_NAME |
Generates a new form request class |
makefast create_request StoreUserRequest
# → app/requests/store_user_request.py
Resource Generation
Generates a Laravel-style Resource or ResourceCollection class under app/resources/.
| Command | Description | Options |
|---|---|---|
makefast create_resource RESOURCE_NAME |
Generates a new API resource class | |
makefast create_resource RESOURCE_NAME -c |
Also generates a ResourceCollection | --collection / -c |
makefast create_resource User
# → app/resources/user.py
makefast create_resource User --collection
# → app/resources/user.py + app/resources/user_collection.py
Migration Generation
| Command | Description |
|---|---|
makefast create_migration MIGRATION_NAME |
Generates a new migration |
makefast migrate |
Runs all pending migrations |
Form Requests
Form Requests provide a clean, reusable way to validate incoming HTTP data — just like Laravel's FormRequest.
Defining Rules
# app/requests/store_user_request.py
from makefast.http import FormRequest
from typing import Any, Dict
class StoreUserRequest(FormRequest):
def authorize(self) -> bool:
# Return False to reject unauthorized requests (raises 403)
return True
def rules(self) -> Dict[str, Any]:
return {
"name": ["required", "string", "min:2", "max:100"],
"email": ["required", "email"],
"password": ["required", "string", "min:8", "confirmed"],
"age": ["nullable", "integer", "min:0"],
"role": ["required", "in:admin,user,guest"],
}
Available Rules
| Rule | Example | Description |
|---|---|---|
required |
"required" |
Field must be present and non-empty |
nullable |
"nullable" |
Field may be absent or None |
string |
"string" |
Must be a str |
integer / int |
"integer" |
Must be an int |
numeric |
"numeric" |
Must be int or float |
boolean |
"boolean" |
Must be bool |
email |
"email" |
Must match a valid email pattern |
min:<n> |
"min:3" |
Min length (strings) or min value (numbers) |
max:<n> |
"max:255" |
Max length (strings) or max value (numbers) |
in:<a,b,c> |
"in:admin,user" |
Value must be one of the listed options |
not_in:<a,b,c> |
"not_in:banned" |
Value must NOT be one of the listed options |
regex:<pattern> |
"regex:^[A-Z]" |
Must match the given regex pattern |
confirmed |
"confirmed" |
Must equal {field}_confirmation in the request body |
Custom Messages
def messages(self) -> Dict[str, str]:
return {
"name.required": "Please tell us your name.",
"email.email": "Please use a valid email address.",
"password.min": "Password must be at least 8 characters.",
}
Using in Routes
Use the request class as a FastAPI dependency via Depends:
from fastapi import APIRouter, Depends
from app.requests.store_user_request import StoreUserRequest
router = APIRouter()
@router.post("/users")
async def store(req: StoreUserRequest = Depends(StoreUserRequest.from_request)):
# req.validated() → only fields that passed validation
data = req.validated()
user = await User.create(**data)
return {"message": "User created", "user": user}
If validation fails, a 422 Unprocessable Entity response is returned automatically with detailed error messages. If authorize() returns False, a 403 Forbidden is raised.
API Resources
Resources provide a consistent way to transform your model data before returning it from your routes — like Laravel's API Resources.
Single Resource
# app/resources/user.py
from makefast.http import Resource
from typing import Any, Dict
class UserResource(Resource):
def to_dict(self) -> Dict[str, Any]:
return {
"id": self.data["id"],
"name": self.data["name"],
"email": self.data["email"],
"created_at": str(self.data.get("created_at", "")),
}
Use in a route:
from app.resources.user import UserResource
@router.get("/users/{id}")
async def show(id: int):
user = await User.find(id)
return UserResource(user).response()
# → {"data": {"id": 1, "name": "Alice", "email": "..."}}
Fluent modifiers:
# Remove the "data" wrapper
UserResource(user).without_wrapping().response()
# → {"id": 1, "name": "Alice", ...}
# Use a custom wrapper key
UserResource(user).wrap("user").response()
# → {"user": {"id": 1, ...}}
# Attach extra metadata
UserResource(user).with_meta({"token": jwt_token}).response()
# → {"data": {...}, "token": "..."}
Resource Collection
# app/resources/user_collection.py
from makefast.http import ResourceCollection
from app.resources.user import UserResource
class UserCollection(ResourceCollection):
resource_class = UserResource
Use in a route:
from app.resources.user_collection import UserCollection
@router.get("/users")
async def index():
users = await User.all()
return UserCollection(users).response()
# → {"data": [{...}, {...}]}
Inline factory (no subclassing needed):
from makefast.http import ResourceCollection
from app.resources.user import UserResource
return ResourceCollection.of(UserResource, users).response()
Pagination with Resources
@router.get("/users")
async def index(page: int = 1):
# Paginate with conditions using QueryBuilder
result = await User.query().where("active", 1).order_by("name").paginate(page=page, per_page=15)
return UserCollection(result["data"]).with_pagination(result).response()
Response shape:
{
"data": [...],
"pagination": {
"total": 42,
"per_page": 15,
"current_page": 1,
"last_page": 3,
"from": 1,
"to": 15
}
}
Database Configuration
MakeFast provides the easiest way to configure and use a database. By default, MakeFast supports MySQL and MongoDB.
MySQL
Add the following lines to main.py:
from fastapi import FastAPI
from makefast.database import MySQLDatabaseInit
app = FastAPI()
MySQLDatabaseInit.init(app)
Configure your .env file:
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_username
DB_PASSWORD=your_password
MongoDB
Add the following lines to main.py:
from fastapi import FastAPI
from makefast.database import MongoDBDatabaseInit
app = FastAPI()
MongoDBDatabaseInit.init(app)
Database CRUD operations
MakeFast offers built-in methods for CRUD operations. First, create a model that corresponds to your MySQL table or MongoDB collection.
Create
from app.models import User
user = await User.create(**{
"username": "usertest",
"email": "test@example.com",
"password": "test123",
})
Update
await User.update(45, **{
"name": "New name"
})
Find one
await User.find(45)
await User.find_or_fail(45) # raises 404 if not found
Find all
await User.all()
Delete
await User.delete(45)
Advanced Query Builder
MakeFast's MySQL integration includes a powerful QueryBuilder for building advanced queries with full validation and SQL injection protection.
Filtering
# WHERE username = 'john'
users = await User.query().where("username", "john").get()
# WHERE age > 18 AND status = 'active'
users = await User.query().where("age", ">", 18).where("status", "active").get()
# WHERE IN / NOT IN
users = await User.query().where_in("status", ["active", "pending"]).get()
# WHERE IS NULL / IS NOT NULL
users = await User.query().where_null("deleted_at").get()
# WHERE BETWEEN
users = await User.query().where_between("age", 18, 65).get()
# WHERE LIKE
users = await User.query().where_like("email", "%@gmail.com").get()
# OR WHERE
users = await User.query().where("role", "admin").or_where("role", "moderator").get()
Joins
# INNER JOIN
results = await User.query().join("profiles", "users.id", "profiles.user_id").get()
# LEFT JOIN
results = await User.query().left_join("orders", "users.id", "orders.user_id").get()
# RIGHT JOIN
results = await User.query().right_join("departments", "users.dept_id", "departments.id").get()
Select Specific Columns
users = await User.query().select("id", "username", "email").get()
# With alias
users = await User.query().select_raw("users.id as user_id", "profiles.bio as profile_bio").get()
Ordering, Limit & Offset
users = await User.query().order_by("created_at", "DESC").limit(10).offset(20).get()
Group By
results = await User.query().select("role").group_by("role").get()
First / First Or Fail
user = await User.query().where("username", "john").first()
user = await User.query().where("username", "john").first_or_fail() # raises 404
Pagination (Chained)
Paginate any query, including those with joins, filters, and ordering:
result = await User.query() \
.where("active", 1) \
.order_by("created_at", "DESC") \
.paginate(page=1, per_page=15)
# result = {
# "data": [...],
# "total": 42,
# "per_page": 15,
# "current_page": 1,
# "last_page": 3,
# "from": 1,
# "to": 15
# }
Simple Pagination (Class-level)
result = await User.paginate(page=2, per_page=20)
Exists Check
already_taken = await User.query().where("email", email).exists()
if already_taken:
raise HTTPException(400, "Email already registered")
Chained Update & Delete
# Update matching records
rows_updated = await User.query().where("status", "inactive").update(status="archived")
# Delete matching records (WHERE is required for safety)
rows_deleted = await User.query().where("deleted_at", "<", cutoff_date).delete()
Aggregations
Built-in aggregation helpers on both the model and the query builder:
total_users = await User.count()
max_age = await User.max("age")
min_age = await User.min("age")
avg_age = await User.avg("age")
total_bal = await User.sum("balance")
# With conditions
active_count = await User.query().where("active", 1).count()
Bulk Operations
Bulk Create
users = await User.bulk_create([
{"username": "alice", "email": "alice@example.com"},
{"username": "bob", "email": "bob@example.com"},
])
Get or Create
user, created = await User.get_or_create(
username="john",
defaults={"email": "john@example.com"}
)
Update or Create
user, created = await User.update_or_create(
username="john",
defaults={"email": "newjohn@example.com"}
)
Safe Raw Queries
MakeFast allows raw SQL execution with strict validation and safety:
results = await User.safe_raw_query(
"SELECT id, username FROM users WHERE status = %s",
params=("active",)
)
By default, only SELECT queries are allowed unless you explicitly pass allowed_operations:
results = await User.safe_raw_query(
"UPDATE users SET status = %s WHERE id = %s",
params=("active", 1),
allowed_operations={"UPDATE"}
)
Project Structure
After running makefast init, your project will have the following structure:
app/
├── dependencies/
│ └── response_handler.py
├── enums/
├── migrations/
├── models/
├── requests/ ← Form Request classes
├── resources/ ← API Resource & Collection classes
├── routes/
└── schemas/
.makefast/
└── executed_migrations.txt
main.py
.env
requirements.txt
Contributing
Contributions are welcome! To contribute to MakeFast, follow these steps:
- Fork the repository
- Create a new branch
- Make changes and commit them
- Create a pull request
License
MakeFast is licensed under the MIT License. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file makefast-2.3.0.tar.gz.
File metadata
- Download URL: makefast-2.3.0.tar.gz
- Upload date:
- Size: 86.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b957ac74bb921fcb30cb2d7d8c4dd9f8fe349e8cc91a594eef4dc841b43339a2
|
|
| MD5 |
4f4fa07ddd5b540635c5c96c1e35695b
|
|
| BLAKE2b-256 |
17534869b96d0db1ee5606bed999c4dce52cc68fa6bd9dfab1df572c488ce5dd
|
File details
Details for the file makefast-2.3.0-py3-none-any.whl.
File metadata
- Download URL: makefast-2.3.0-py3-none-any.whl
- Upload date:
- Size: 87.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7bdd55cfa5b4655b54f8c086f5d3a64e86eaa32c92c02f2c111fb95bb70312c
|
|
| MD5 |
2e6b30014f2c25c32bfa9f84be3e9b2e
|
|
| BLAKE2b-256 |
ec7c11a28b58df9aae75b045f9683f0acae8a31580aece46d6c98bc167c2631c
|