Static copy
Copied from docs/en/packages/aipo-http.md in https://github.com/poppy-team/aipo-lang (MIT). Pinned to revision ce47c4e070404fc41722ccd8e2b7b808b99062a8, blob e1dd134f93733b4a66fcf2f3be57963a9c1fe2e4. The source repository remains canonical; this copy is refreshed through a sync pull request, not live.
aipo.http is the official backend framework for the Aipo programming language, designed for high-performance REST APIs, microservices, and web servers.
It is built following Aipo's core minimalist and deterministic philosophy:
:param), and wildcards (*wildcard) without runtime regular expression parsing.next() calls, enabling response transformations, audit trails, and short-circuit controls.cors: Cross-Origin Resource Sharing with automatic preflight OPTIONS (204).logger: Structured logging for request methods, paths, and status codes.recover: Traps unhandled runtime faults, returning 500 JSON without crashing the runtime.Context): High-ergonomics access to query strings, route parameters, headers, JSON body decoding, and immediate response builders (json, text, html, status).group): Nested prefixes (/api/v1) with group-scoped middlewares.app.handle_request(req_dict), allowing deterministic unit tests without binding OS sockets.In your project's aipo.toml:
[dependencies]
"aipo.http" = { path = "packages/aipo-http" }import aipo.http as http
let app = http.create()
# Plain text response
app.get("/", fn (c) {
c.text("Welcome to Aipo HTTP!")
})
# JSON response
app.get("/api/health", fn (c) {
c.json({
"status": "healthy",
"uptime": 3600
})
})
# HTML response
app.get("/welcome", fn (c) {
c.html("<h1>Aipo HTTP Portal</h1>")
})# Single parameter
app.get("/users/:id", fn (c) {
let user_id = c.param("id")
c.json({ "id": user_id, "name": "Developer" })
})
# Nested parameters
app.get("/orgs/:org/repos/:repo", fn (c) {
let org = c.param("org")
let repo = c.param("repo")
c.text(f"Repository: {org}/{repo}")
})
# Wildcard catch-all for static assets
app.get("/static/*filepath", fn (c) {
let file = c.param("filepath")
c.text(f"Serving file: {file}")
})# Query parameters (?q=search&page=2)
app.get("/search", fn (c) {
let query = c.query_param("q")
let page = c.query_param("page")
c.json({ "query": query, "page": page })
})
# POST request with JSON payload
app.post("/users", fn (c) {
let payload = c.body_json()
c.status(201)
c.set_header("x-created-by", "aipo-http")
c.json({
"created": true,
"username": payload["username"]
})
})Middlewares follow the fn (ctx, next) signature:
let app = http.create()
# Audit & custom header middleware
app.use(fn (c, next) {
c.set_header("x-server", "aipo-engine")
let res = next()
return res
})
# Auth guard with short-circuit
app.use(fn (c, next) {
let token = c.header("Authorization")
if token != "Bearer secret-token" {
c.status(401)
c.json({ "error": "Unauthorized" })
return none # Halts pipeline execution
}
return next()
})# CORS configuration
app.use(http.cors({
"origin": "https://example.com",
"methods": "GET, POST, PUT, DELETE"
}))
# Request logger
app.use(http.logger())
# Panic / runtime fault recovery
app.use(http.recover())group) let api_v1 = app.group("/api/v1")
# Registered as /api/v1/status
api_v1.get("/status", fn (c) {
c.json({ "version": "1.0.0" })
})
# Registered as /api/v1/users
api_v1.get("/users", fn (c) {
c.json([])
})Execute tests in memory with pure dictionaries:
let app = http.create()
app.get("/ping", fn (c) {
c.text("pong")
})
let response = app.handle_request({
"method": "GET",
"path": "/ping"
})
expect.equal(response["status"], 200)
expect.equal(response["body"], "pong")