A web backend framework for MoonBit
Dependencies
import {
"moonbitlang/async@0.21.0",
"moonbitlang/moonback@0.8.2",
}preferred_target = "native"import {
"moonbitlang/moonback@0.8.2",
}async fn main {
let app = @moonback.App(ctx => {
ctx.get("/", (_req, res) => {
res.send_text("Hello, MoonBack!")
})
})
defer app.close()
let server = @moonback.listen(port=3000)
println("Server listening on \{server.addr}")
app.serve(server)
}moon run .http://127.0.0.1:3000/let posts_module : @moonback.Module = @moonback.Module(ctx => {
ctx.get("/posts", list_posts)
ctx.get("/posts/:id", show_post)
})
let admin_module : @moonback.Module = @moonback.Module(ctx => {
ctx.add_middleware(require_admin)
ctx.get("/admin/users", list_users)
})
async fn main {
let app = @moonback.App(ctx => {
ctx.use_(posts_module)
ctx.use_(admin_module)
})
defer app.close()
let server = @moonback.listen(port=3000)
app.serve(server)
}ctx.get("/", (_req, res) => {
res.send_text("home")
})
ctx.get("/posts/:id", (req, res) => {
let id = req.params["id"]
res.send_text("post \{id}")
})
ctx.add_route(@http.Put, "/posts/:id", update_post)ctx.get(path, handler)
ctx.post(path, handler)
ctx.add_route(method, path, handler)ctx.get("/search", (req, res) => {
let q = match req.query().get("q") {
Some(q) => q
None => ""
}
let cookies = req.cookie()
let has_session = match cookies.get("session") {
Some(_) => true
None => false
}
let ip = match req.ip() {
Some(ip) => ip.to_string()
None => ""
}
res.send_json({
"query": q,
"has_session": has_session,
"ip": ip,
})
})res.send_text("ok")
res.send_html(@moonback.Html::raw("<h1>Hello</h1>"))
res.send_json({ "ok": true })
res.send_void(status=204)let page = @moonback.Html(builder => {
let title = "Hello, <MoonBack>"
builder "<h1>\{title}</h1>"
})
res.send_html(page)ctx.get("/stream", (_req, res) => {
let w = res.respond(status=200, headers={ "Content-Type": "text/plain" })
w.write("first line\n")
w.flush()
w.write("second line\n")
})import {
"moonbitlang/async/websocket",
"moonbitlang/moonback",
}async fn serve_websocket(
req : @moonback.Request,
res : @moonback.Responder,
) -> Unit {
let ws = res.upgrade(req)
defer ws.close()
try {
for ;; {
let msg = ws.recv()
match msg.kind {
Text => {
let text = msg.read_all().text()
ws.send_text("echo: \{text}")
}
Binary => {
let data = msg.read_all().binary()
ws.send_binary(data)
}
}
}
} catch {
@websocket.ConnectionClosed(_, _) => ()
err => raise err
}
}
let app = @moonback.App(ctx => {
ctx.get("/ws", serve_websocket)
})let log_requests = @moonback.Middleware(next => {
(req, res) => {
println("\{req.meth} \{req.path}")
next(req, res)
}
})
let app = @moonback.App(ctx => {
ctx.add_middleware(log_requests)
ctx.get("/", (_req, res) => res.send_text("ok"))
})struct Greeter {
prefix : String
}
@moonback.TypedBox {
GreeterDependency(Greeter)
}
let greeter_key : @moonback.TypedKey[Greeter] = @moonback.TypedKey(
name="greeter",
box=greeter => GreeterDependency(greeter),
unbox=err => {
match err {
GreeterDependency(greeter) => Some(greeter)
_ => None
}
},
)let greeter_module : @moonback.Module = @moonback.Module(ctx => {
let greeter = ctx.require(greeter_key)
ctx.get("/", (_req, res) => {
res.send_text("\{greeter.prefix}, MoonBack!")
})
})
let app = @moonback.App(ctx => {
ctx.provide(greeter_key, { prefix: "Hello" })
ctx.use_(greeter_module)
})ctx.provide(key, value)
ctx.resolve(key)
ctx.require(key)req.ctx.set_userdata(key, value)
req.ctx.get_userdata(key)let app = @moonback.App(
root_module(),
config=@moonback.Config(
trust_proxy=true,
max_connections=1024,
stop_timeout=5.0,
),
) @moonback.TypedBox {
UploadRoot(String)
}
let upload_root_key : @moonback.TypedKey[String] = @moonback.TypedKey(
name="upload_root",
box=root => UploadRoot(root),
unbox=boxed => {
match boxed {
UploadRoot(root) => Some(root)
_ => None
}
},
)
let app = @moonback.App(
root_module(),
config=@moonback.Config(
custom=[
@moonback.Config::custom(upload_root_key, "/srv/uploads"),
],
),
)
let upload_root = app.config().get_custom(upload_root_key)let app = @moonback.App(ctx => {
let db = connect_db()
ctx.on_close(() => db.close())
})
defer app.close()let api = @moonback.App(ctx => {
ctx.get("/health", (_req, res) => res.send_text("ok"))
})
let app = @moonback.App(ctx => {
ctx.mount("/api", api)
})ctx.mount("/api", api, error_mode=@moonback.Isolate)moon update
moon check
moon test
moon fmt
moon infopub(open) trait Flusher {
async fn flush(self : Self) -> Unit
}pub(open) trait Listener {
async fn accept(self : Self) -> (ServerConnection, Addr)
fn close(self : Self) -> Unit
}impl WriteToHtmlBuilder for Stringpub suberror DependencyError {
MissingDependency(name~ : String)
DuplicateDependency(name~ : String)
}pub struct App {
// private fields
}let app = @moonback.App((ctx) => { ... })
defer app.close()
app.serve(@moonback.listen())pub struct Config {
max_connections : Int?
stop_timeout : Double
trust_proxy : Bool
// private fields
}fn Config::Config(max_connections? : Int, stop_timeout? : Double, trust_proxy? : Bool, custom? : ArrayView[CustomConfigEntry]) -> Configtype Contextctx.get(
"/",
ctx.with_middlewares([middleware1, middleware2]) ((req, res) => {
...
})
)type Headerstype Htmltest {
let title = "Hello, <MoonBack>"
let badge = @moonback.Html::raw("<span class=\"badge\">new</span>")
let _card = @moonback.Html(b => {
b "<article class=\"card\">"
b "<h1>\{title}</h1>"
b "\{badge}"
b "</article>"
})
}impl WriteToHtmlBuilder for Htmltest {
let title = "Hello, <MoonBack>"
let badge = @moonback.Html::raw("<span class=\"badge\">new</span>")
let _card = @moonback.Html(b => {
b "<article class=\"card\">"
b "<h1>\{title}</h1>"
b "\{badge}"
b "</article>"
})
}test {
let _html = @moonback.Html::escape("Tom & <Jerry>")
}test {
let _html = @moonback.Html::raw("<strong>safe</strong>")
}type HtmlBuilderasync fn[T : WriteToHtmlBuilder] HtmlBuilder::write_string_interpolation(self : HtmlBuilder, value : T) -> Unittest {
let _page = @moonback.Html(b => {
let str = "<MoonBack>"
let br = @moonback.Html::raw("<br>")
b "\{str}"
b "\{br}"
})
}let auth_middleware = Middleware(next => {
(req, res) => {
if is_authenticated(req) {
next(req, res)
} else {
res.send_void(status=401)
}
}
})let posts_module = Module(ctx => {
ctx.get("/posts", list_posts)
ctx.get("/posts/:id", show_post)
})
let admin_module = Module(ctx => {
ctx.add_middleware(require_admin)
ctx.get("/users", list_users)
})
let root = Module(ctx => {
ctx.use_(posts_module)
ctx.use_(admin_module)
})
let app = @moonback.App(root)type ModuleContextfn ModuleContext::add_route(self : ModuleContext, meth : RequestMethod, path : String, handler : async (Request, Responder) -> Unit) -> Unit raise RouterErrorfn ModuleContext::get(self : ModuleContext, path : String, handler : async (Request, Responder) -> Unit) -> Unit raise RouterErrorfn ModuleContext::mount(self : ModuleContext, path : String, app : App, take_ownership? : Bool, error_mode? : MountErrorMode) -> Unit raise RouterErrorfn ModuleContext::post(self : ModuleContext, path : String, handler : async (Request, Responder) -> Unit) -> Unit raise RouterErrorfn[T] ModuleContext::provide(self : ModuleContext, key : TypedKey[T], value : T) -> Unit raise DependencyErrorlet api = Module(ctx => ctx.get("/health", health_handler))
let root = Module(ctx => ctx.use_(api))pub(all) enum MountErrorMode {
Propagate
Isolate
}type MutHeadersimpl ToHeaders for MutHeaderspub struct QueryParams {
// private fields
}pub(all) struct Request {
ctx : Context
meth : RequestMethod
path : StringView
search : StringView?
headers : Headers
params : Map[StringView, StringView]
body : RequestBody
}type RequestBodyasync fn Responder::respond(self : Responder, status? : Int, headers? : &ToHeaders) -> &WriteFlushertype TypedKey[T]Install
Download zipA web backend framework for MoonBit
Dependencies