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
- Routing & Middleware
- Form Requests
- API Resources
- Emails (Mailables)
- 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 |
Controller Generation
Generates a Laravel-style Controller class and automatically registers its routes in app/routes/api.py.
| Command | Description | Options |
|---|---|---|
makefast create_controller CONTROLLER_NAME |
Generates a new controller | --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
Mail Generation
Generates a Laravel-style Mailable class under app/mail/ and its corresponding HTML template under resources/views/emails/.
| Command | Description |
|---|---|
makefast create-mail MAIL_NAME |
Generates a new mailable & HTML template |
makefast create-mail WelcomeEmail
# → app/mail/welcome_email.py
# → resources/views/emails/welcome-email.html
Migration Generation
| Command | Description |
|---|---|
makefast create_migration MIGRATION_NAME |
Generates a new migration |
makefast migrate |
Runs all pending migrations |
Routing & Middleware
Since MakeFast adopts a Laravel-style routing architecture, all routes are registered centrally in app/routes/api.py.
Defining Routes
When you generate a controller via makefast create_controller, the route is automatically registered for you.
# app/routes/api.py
from fastapi import APIRouter
from app.controllers.user_controller import UserController
router = APIRouter()
router.add_api_route(
path="/user",
endpoint=UserController.index,
methods=["GET"]
)
Adding Route Middleware
In FastAPI, route-specific middleware is handled using the Depends dependency injection system. Because the dependencies parameter accepts a list, you can chain multiple middlewares to run in an exact sequence before your controller method is executed.
from fastapi import Depends
from app.dependencies.auth import verify_token
from app.dependencies.roles import require_admin
router.add_api_route(
path="/admin/dashboard",
endpoint=AdminController.dashboard,
methods=["GET"],
dependencies=[
Depends(verify_token), # Runs first
Depends(require_admin) # Runs second (only if first succeeds)
]
)
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
}
}
Emails (Mailables)
MakeFast provides a Laravel-like mailing system using Mailable classes and Jinja2 HTML templates.
Creating Mailables
Generate a Mailable using the CLI:
makefast create-mail WelcomeEmail
This will create app/mail/welcome_email.py and an HTML template in resources/views/emails/welcome-email.html.
In your WelcomeEmail class, define the view, data, and subject:
# app/mail/welcome_email.py
from makefast.mail import Mailable
class WelcomeEmail(Mailable):
def __init__(self, data: dict = None):
super().__init__()
self.mail_data = data or {}
def build(self):
return (
self.view("emails.welcome-email")
.with_data(**self.mail_data)
.subject("Welcome to MakeFast!")
)
In your HTML template (resources/views/emails/welcome-email.html), you can use Jinja2 syntax to output the passed data:
<!DOCTYPE html>
<html>
<body>
<h2>Welcome, {{ name }}!</h2>
<p>Thank you for joining us.</p>
</body>
</html>
Sending Emails
Use the Mail facade to send emails. MakeFast uses SMTP configurations defined in your .env file (e.g., MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_FROM_ADDRESS, MAIL_FROM_NAME).
from makefast.mail import Mail
from app.mail.welcome_email import WelcomeEmail
@router.post("/users")
async def store():
# User creation logic...
mailable = WelcomeEmail({"name": "Alice"})
Mail.to("alice@example.com").send(mailable)
return {"message": "User created and email sent"}
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/
├── controllers/ ← Controller classes
├── dependencies/
│ └── response_handler.py
├── enums/
├── migrations/
├── models/
├── requests/ ← Form Request classes
├── resources/ ← API Resource & Collection classes
├── routes/ ← Route definitions (api.py)
└── 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.4.1.tar.gz.
File metadata
- Download URL: makefast-2.4.1.tar.gz
- Upload date:
- Size: 92.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ce84b9919bbfe9a1ed68391bf0715e64eced15683b89ea6507905525140a776
|
|
| MD5 |
63dc04de7efe9d093c39f94c30a839d6
|
|
| BLAKE2b-256 |
3eb8b2b729aa8d0bcdc4f20c017835de4c9979ec75fa23450290a1b1ace865ca
|
File details
Details for the file makefast-2.4.1-py3-none-any.whl.
File metadata
- Download URL: makefast-2.4.1-py3-none-any.whl
- Upload date:
- Size: 93.6 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 |
b43a026793a7b31114e4eb2fed8dda3ba655afa1d47b1dcb71b8ea1732e4ddcb
|
|
| MD5 |
4db95c9af530ff6df211c05e3bf69b1f
|
|
| BLAKE2b-256 |
721c4d10cf89f3550bcd49e918799ff323e64ac625f5935e27a6cf76176da545
|