Usage#
Quickstart#
from muffin import Application
app = Application()
@app.route("/")
async def hello_world(request):
return "<p>Hello, World!</p>"
Save this as hello.py
.
Run with:
uvicorn hello:app
Visit http://127.0.0.1:8000/ to see your greeting.
Request Object#
Every view receives a Request
object:
@app.route('/login', methods=['POST'])
async def login(request):
form = await request.form()
username = form.get('username')
password = form.get('password')
if username == 'admin':
return f"Welcome, {username}"
return "Invalid credentials"
Query parameters:
search = request.query.get('search', '')
Cookies:
session = request.cookies.get('session')
File uploads:
form = await request.form()
file = form['file']
content = await file.read()
Routing#
Define routes:
@app.route('/')
async def index(request):
return 'Index Page'
Dynamic routes:
@app.route('/user/{username}')
async def user_profile(request):
username = request.path_params['username']
return f"Hello, {username}"
Type converters:
|
(default) accepts any text without a slash |
|
accepts positive integers |
|
accepts positive floating point values |
|
like string but also accepts slashes |
|
accepts UUID strings |
Example:
@app.route('/post/{post_id:int}')
async def post(request):
post_id = request.path_params['post_id']
return f"Post #{post_id}"
Regex routes:
import re
@app.route(re.compile(r'/item/(a|b|c)/'))
async def item(request):
return request.path
Static Files#
Configure static files:
app = Application(static_url_prefix='/assets', static_folders=['static'])
Files are served at /assets/{file}.
Redirects and Errors#
Redirect example:
from muffin import ResponseRedirect
@app.route('/')
async def index(request):
raise ResponseRedirect('/login')
Raise HTTP errors:
from muffin import ResponseError
@app.route('/secure')
async def secure(request):
if not request.cookies.get('auth'):
raise ResponseError.UNAUTHORIZED()
return "Secret data"
Custom error handler:
@app.on_error(404)
async def not_found(request, error):
return "Custom 404 Page"
Middlewares#
External ASGI middleware:
from sentry_asgi import SentryMiddleware
app.middleware(SentryMiddleware)
Internal middleware:
@app.middleware
async def simple_md(app, request, receive, send):
response = await app(request, receive, send)
response.headers['x-custom-md'] = 'passed'
return response
Nested Applications#
Mount sub-applications for modular design:
subapp = Application()
@subapp.route('/route')
async def subroute(request):
return "From subapp"
app.route('/sub')(subapp)
Accessing /sub/route calls the sub-application route.
Run Tasks in Background#
Schedule background tasks after response is sent:
import asyncio
async def background_task(param):
await asyncio.sleep(1)
print(f"Done: {param}")
@app.route('/bg')
async def bg(request):
app.run_after_response(background_task("task"))
return "Scheduled"
Debug Mode#
Enable debug mode for detailed error output:
app = Application(debug=True)
This prints tracebacks to console during development.
About Responses#
Return values from views are automatically converted:
Response
is returned as-isstr → HTML response
dict, list, bool, None → JSON response
(status, content) tuple → overrides status
(status, content, headers) tuple → adds headers
Example:
@app.route('/json')
async def json_view(request):
return {'key': 'value'}
@app.route('/html')
async def html_view(request):
return '<h1>Hello</h1>'
@app.route('/tuple')
async def tuple_view(request):
return 201, 'Created'