A web framework for MoonBit.
moon run src/example --target jsmoon run src/example --target nativeapp.get("/hello/:name", event => {
let name = event.params.get("name").or("World")
Text("Hello, \{name}!")
})// Single wildcard - matches one path segment
app.get("/hello/*", event => {
let name = event.params.get("_").or("World")
Text("Hello, \{name}!")
})
// Double wildcard - matches multiple path segments
app.get("/hello/**", event => {
let path = event.params.get("_").or("")
Text("Hello, \{path}!")
})// async json data example
app.get("/async_data", async fn(event) {
Json({ "name": "John Doe", "age": 30, "city": "New York" })
})app.group("/api", group => {
// Add group-level middleware
group.use_middleware(event => println(
"🔒 API Group Middleware: \{event.req.reqMethod} \{event.req.url}",
))
// Routes under /api prefix
group.get("/hello", _ => Text("Hello from API!"))
group.get("/users", _ => Json({ "users": ["Alice", "Bob"] }))
group.post("/data", e => e.req.body)
})app.ws("/ws", event => match event {
Open(peer) => println("WS open: " + peer.to_string())
Message(peer, body) => {
let msg = match body {
Text(s) => s.to_string()
_ => ""
}
println("WS message: " + msg)
peer.send(msg)
}
Close(peer) => println("WS close: " + peer.to_string())
})let app = @mocket.new(logger=@mocket.new_debug_logger())
// Register global middleware
app
..use_middleware(event => println(
"📝 Request: \{event.req.http_method} \{event.req.url}",
))
// Text Response
..get("/", _event => Text("⚡️ Tadaa!"))
// Hello World
..on("GET", "/hello", _ => Text("Hello world!"))
..group("/api", group => {
// 添加组级中间件
group.use_middleware(event => println(
"🔒 API Group Middleware: \{event.req.http_method} \{event.req.url}",
))
group.get("/hello", _ => Text("Hello world!"))
group.get("/json", _ => Json({
"name": "John Doe",
"age": 30,
"city": "New York",
}))
})
// JSON Response
..get("/json", _event => Json({
"name": "John Doe",
"age": 30,
"city": "New York",
}))
// Async Response
..get("/async_data", async fn(_event) noraise {
Json({ "name": "John Doe", "age": 30, "city": "New York" })
})
// Dynamic Routes
// /hello2/World = Hello, World!
..get("/hello/:name", event => {
let name = event.params.get("name").unwrap_or("World")
Text("Hello, \{name}!")
})
// /hello2/World = Hello, World!
..get("/hello2/*", event => {
let name = event.params.get("_").unwrap_or("World")
Text("Hello, \{name}!")
})
// Wildcard Routes
// /hello3/World/World = Hello, World/World!
..get("/hello3/**", event => {
let name = event.params.get("_").unwrap_or("World")
Text("Hello, \{name}!")
})
// Echo Server
..post("/echo", e => e.req.body)
// 404 Page
..get("/404", e => {
e.res.status_code = 404
HTML(
(
#|<html>
#|<body>
#| <h1>404</h1>
#|</body>
#|</html>
),
)
})
// Serve
..serve(port=4000)
// Print Server URL
for path in app.mappings.keys() {
println("\{path.0} http://localhost:4000\{path.1}")
}| Route Pattern | URL | Parameters |
|---|---|---|
| /hello/:name | /hello/world | name: "world" |
| /hello/* | /hello/world | _: "world" |
| /hello/** | /hello/foo/bar | _: "foo/bar" |
| /users/:id/posts/:postId | /users/123/posts/456 | id: "123", postId: "456" |
| /api/** | /api/v1/users/123 | _: "v1/users/123" |
| group("/api", ...).get("/hello") | /api/hello | - |
| group("/users", ...).get("/:id") | /users/123 | id: "123" |

impl BodyReader for Stringimpl BodyReader for FixedArray[Byte]impl BodyReader for Bytesimpl BodyReader for Array[Byte]impl BodyReader for Jsonpub(open) trait Responder {
options(Self, res : HttpResponse) -> Unit
output(Self, buf : Buffer) -> Unit
}pub(open) trait ServeStaticProvider {
get_meta(Self, id : String) -> StaticAssetMeta?
get_contents(Self, id : String) -> &Responder
get_type(Self, ext : String) -> String?
get_encodings(Self) -> Map[String, String]
get_index_names(Self) -> Array[String]
get_fallthrough(Self) -> Bool
}pub suberror ExecError
pub suberror IOError
pub(all) struct CookieItem {
name : String
value : String
max_age : Int?
path : String?
domain : String?
secure : Bool?
http_only : Bool?
same_site : SameSiteOption?
}type Htmlpub(all) struct HttpRequest {
http_method : String
url : String
headers : Map[StringView, StringView]
raw_body : Bytes
}#external
pub type HttpRequestInternalfn HttpRequestInternal::on_headers(self : HttpRequestInternal, cb : FuncRef[(CStr) -> Unit]) -> Unitpub(all) struct HttpResponse {
status_code : StatusCode
headers : Map[StringView, StringView]
cookies : Map[String, CookieItem]
raw_body : Bytes
}impl Responder for HttpResponsefn HttpResponse::new(status_code : StatusCode, headers? : Map[StringView, StringView], cookies? : Map[String, CookieItem], raw_body? : Bytes) -> HttpResponsefn HttpResponse::set_cookie(self : HttpResponse, name : String, value : String, max_age? : Int, path? : String, domain? : String, secure? : Bool, http_only? : Bool, same_site? : SameSiteOption) -> Unit#alias(T)
pub(all) struct Mocket {
base_path : String
mappings : Map[(String, String), async (MocketEvent) -> &Responder noraise]
middlewares : Array[(String, async (MocketEvent, async () -> &Responder noraise) -> &Responder noraise)]
static_routes : Map[String, Map[String, async (MocketEvent) -> &Responder noraise]]
dynamic_routes : Map[String, Array[(String, async (MocketEvent) -> &Responder noraise)]]
ws_static_routes : Map[String, (WebSocketEvent) -> Unit]
ws_dynamic_routes : Array[(String, (WebSocketEvent) -> Unit)]
ws_clients : Map[String, Unit]
ws_channels : Map[String, Map[String, Unit]]
ws_client_port : Map[String, Int]
}fn Mocket::all(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::connect(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::delete(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::get(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::head(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::on(self : Mocket, event : String, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::options(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::patch(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::post(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::put(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::trace(self : Mocket, path : String, handler : async (MocketEvent) -> &Responder noraise) -> Unitfn Mocket::use_middleware(self : Mocket, middleware : async (MocketEvent, async () -> &Responder noraise) -> &Responder noraise, base_path? : String) -> Unitpub(all) struct MocketEvent {
req : HttpRequest
res : HttpResponse
params : Map[String, StringView]
}pub(all) struct MultipartFormValue {
filename : String?
content_type : String?
data : BytesView
}pub(all) enum SameSiteOption {
Lax
Strict
SameSiteNone
}impl Eq for SameSiteOptionimpl Show for SameSiteOptionimpl ToJson for SameSiteOptionpub(all) struct StaticAssetMeta {
asset_type : String?
etag : String?
mtime : Int64?
path : String?
size : Int64?
encoding : String?
}fn StaticAssetMeta::new(asset_type? : String, etag? : String, mtime? : Int64, path? : String, size? : Int64, encoding? : String) -> StaticAssetMetapub(all) enum StatusCode {
Continue
SwitchingProtocols
Processing
EarlyHints
OK
Created
Accepted
NonAuthoritativeInfo
NoContent
ResetContent
PartialContent
MultiStatus
AlreadyReported
IMUsed
MultipleChoices
MovedPermanently
Found
SeeOther
NotModified
UseProxy
TemporaryRedirect
PermanentRedirect
BadRequest
Unauthorized
PaymentRequired
Forbidden
NotFound
MethodNotAllowed
NotAcceptable
ProxyAuthRequired
RequestTimeout
Conflict
Gone
LengthRequired
PreconditionFailed
RequestEntityTooLarge
RequestUriTooLong
UnsupportedMediaType
RequestedRangeNotSatisfiable
ExpectationFailed
Teapot
MisdirectedRequest
UnprocessableEntity
Locked
FailedDependency
TooEarly
UpgradeRequired
PreconditionRequired
TooManyRequests
RequestHeaderFieldsTooLarge
UnavailableForLegalReasons
InternalServerError
NotImplemented
BadGateway
ServiceUnavailable
GatewayTimeout
HttpVersionNotSupported
VariantAlsoNegotiates
InsufficientStorage
LoopDetected
NotExtended
NetworkAuthenticationRequired
Custom(Int)
}impl Show for StatusCodeimpl ToJson for StatusCodepub enum WebSocketAggregatedMessage {
Text(String)
Binary(Bytes)
Ping
}pub enum WebSocketEvent {
Open(WebSocketPeer)
Message(WebSocketPeer, WebSocketAggregatedMessage)
Close(WebSocketPeer)
}async fn execute_middlewares(middlewares : Array[(String, async (MocketEvent, async () -> &Responder noraise) -> &Responder noraise)], event : MocketEvent, final_handler : async (MocketEvent) -> &Responder noraise) -> &Responder noraiseasync fn[T, E : Error] suspend(f : ((T) -> Unit, (E) -> Unit) -> Unit) -> T raise E