Homecooked is a ASGI-compliant web framework similar to Flask and FastAPI. It includes support for dynamic routing of paths and middleware, and also features a static server. Through PyDantic, Homecooked is able to deserialize JSON requests to Python objects and via the Meal Template Engine, Homecooked supports it's own, very special, templating language. It also has support for SubRouters, which is Homecooked's equivalent to Blueprints in Flask.
To get started with Homecooked, read the Homecooked Docs. Then, clone the Homecooked repo, place it in the base directory of your project, and fire away!
Below we will create our first Homecooked application. This application will be a simple "Hello, World!" application that returns on the index route
from homecooked import App, Response
app = App()
@app.get("/")
async def index():
return Response("Hello World!")
From the homecooked module, we import the App and Response classes. We then create an instance of the App class. We then create a route that listens for GET requests on the index route. When a GET request is made to the index route, the index function is called, which returns a Response object with the text "Hello, World!".
We often want to be able to access information associated with the client's request. Via the request object, we can access the client's IP address, the request method, the request headers, and more. The request object is passed as an argument to the route function if the route has a parameter named 'request'. The fields of request come from the Request class in the homecooked/request.py file.
from homecooked import App, Response, Request
app = App()
@app.get("/")
async def index(request : Request):
return Response(f"Hello {request.client_ip}!")
In the above example, we access the client's IP address via the request object. We then return a Response object with the text "Hello {client's IP address}!".
Query parameters are key-value pairs that are appended to the end of a URL. They are separated from the URL by a question mark and are separated from each other by an ampersand. We can access query parameters via the query_params attribute of the request object. The query_params attribute is a dictionary that maps query parameter keys to their values.
from homecooked import App, Response, Request
app = App()
@app.get("/")
async def index(request : Request):
name = request.query_params.get("name", "World")
return Response(f"Hello {name}!")
In the above example, we access the query parameter "name" via the query_params attribute of the request object. If the "name" query parameter is not present, we default to "World". We then return a Response object with the text "Hello {name}!".
We may want to be able to dynamically create URLs for any reason, and then access the values of the dynamic parts of the URL. Homecooked supports this via path parameters. Path parameters are parts of the URL that are enclosed in curly braces.
@app.get("/{name:str}")
async def index(name : str):
return Response(f"Hello {name}!")
In the above example, we create a route that listens for GET requests on the /{name} route, with the type of {name} being string. When a GET request is made to the /{name} route, the index function is called, which returns a Response object with the text "Hello {name}!". Dynamic paths currently support the types:
from homecooked import Converter, ConverterEngine
class HexConverter(Converter):
regex = r"(0x[A-Fa-f0-9]+)"
def convert(self, value : str) -> int:
return int(value, 16)
ConverterEngine.add_converter('hex', HexConverter())
In the above example, we create a custom converter that converts hexadecimal strings to integers. We then add the custom converter to the ConverterEngine of Homecooked. We can then use the custom converter in our routes like so:
@app.get("/{hval:hex}")
async def index(hval : int):
print(f'look, the hex value has been converted to an int: {hval}')
return Response(f"You sent: {hval}")
It is required that all conveters have a regex attribute, used to match with a part of a path, and a convert method.
Middleware is a function that is called before or after a route is called. Middleware can be used to perform tasks such as logging, authentication, and error handling. Middleware is added to the app via the app.middleware decorator. This decorator accepts a path to match with, similar to a route. If the path is not provided, the middleware will be called for all routes. Middleware accepts two arguments, being the request object and the next function. The next function is a coroutine that calls the next middleware or route. Middleware can be used to modify the request object, the response object, or to short-circuit the request and return a response.
import time
from homecooked import App, Response
app = App()
@app.middleware()
async def middleware(request, next):
start = time.time()
response = await next(request)
print(f"Time taken: {time.time() - start}")
return response
In the above example, we create a middleware that logs the time taken to process a request. We then call the next function with the request object, which calls the next middleware or route. We then print the time taken to process the request and return the response object.
Homecooked supports returning TemplateResponse and JSONResponse objects. TemplateResponse objects are used to render templates with a context, and JSONResponse objects are used to return JSON data. TemplateResponse objects are created by passing a template and a context to the TemplateResponse constructor. JSONResponse objects are created by passing a dictionary to the JSONResponse constructor.
from homecooked import App, TemplateResponse, JSONResponse
app = App()
@app.get("/")
async def index():
return TemplateResponse("index.html", {"name": "Homecooked"})
@app.get("/json")
async def json():
data = {"name": "Homecooked"}
return JSONResponse(data)
In the above example, we create a route that listens for GET requests on the / route. When a GET request is made to the / route, the index function is called, which returns a TemplateResponse object that renders the index.html template with the context {"name": "Homecooked"}. We also create a route that listens for GET requests on the /json route. When a GET request is made to the /json route, the json function is called, which returns a JSONResponse object with the data {"name": "Homecooked"}.
Homecooked supports serving static files such as images, CSS files, and JavaScript files. Static files are served from the static directory in the base directory of the project. Static files are not stored in memory, unlike templates.
Homecooked supports deserializing JSON data into parameters of route functions. This is done by adding a parameter to the route function with the type of a Pydantic model. The Pydantic model is used to deserialize the JSON data into a Python object.
from homecooked import App, Request, Response
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
app = App()
@app.post("/")
async def create_item(request : Request, item : Item):
return Response(f"Item {item.name} created with price {item.price}")
In the above example, we create a Pydantic model called Item with the fields name and price. We then create a route that listens for POST requests on the / route. When a POST request is made to the / route, the create_item function is called, which deserializes the JSON data in the request body into an Item object. We then return a Response object with the text "Item {item.name} created with price {item.price}".
SubRouters are used to group routes together. A SubRouter instance can be created via creating a new instance of homecooked.SubRouter. SubRouters are binded by calling the app.add_subrouter function, providing the path to subroute first, and the SubRouter instance second. SubRouters can have their own middleware and routes.
from homecooked import App, SubRouter, Response
subrouter = SubRouter()
@subrouter.get("/")
async def admin_index():
return Response("Admin Index")
app = App()
app.add_subrouter("/admin", subrouter)
In the above example, we create a SubRouter instance called subrouter. We then create a route that listens for GET requests on the / route. When a GET request is made to the /admin route, the admin_index function is called, which returns a Response object with the text "Admin Index".
That's all for the Homecooked Docs! Start working on your own Homecooked-based application today!