An Express-compatible web framework for native MoonBit, served by µWebSockets (uWebSockets/uSockets bound through a C ABI shim).
///|
fn main {
let app = @express.express()
app.use(@express.logger())
app.use(@express.express_json())
app.get("/users/:id", fn(req, res, _next) {
res.json_string("{\"id\":\"" + req.param("id") + "\"}")
})
app.post("/echo", fn(req, res, _next) {
match req.json_body() {
Some(value) => res.json(value)
None => res.send("body=" + req.body())
}
})
app.get("/boom", fn(_req, _res, next) { next.fail("boom!") })
app.use_error(fn(message, _req, res, _next) {
res.status(500).send("caught: " + message)
})
app.listen(8080, fn(ok) { println(ok.to_string()) })
}import {
"kupas/express" @lib,
}
supported_targets = "native"
pkgtype(kind: "executable")
options(
"link": {
"native": {
"cc-link-flags": "-lstdc++ -lz -lssl -lcrypto",
},
},
)moon run example/main # demo server on :8080
moon test # router/query unit tests| MoonBit | Express.js | Notes |
|---|---|---|
| express() | express() | Creates a new app |
| app.get/post/put/patch/delete/options/head/all(path, handler) | app.METHOD(path, handler) | Route registration |
| app.add_route(method, path, handler) | app.METHOD(path, handler) | Programmatic route registration (renamed from route) |
| app.route(path) | app.route(path) | Chainable builder returning a RouteBuilder |
| app.use(mw) | app.use(mw) | Global middleware |
| app.use_at(path, mw) | app.use(path, mw) | Path-scoped middleware |
| app.use_error(mw) | app.use((err, req, res, next) => ...) | Error middleware |
| app.use_error_at(path, mw) | app.use(path, (err, req, res, next) => ...) | Path-scoped error middleware |
| app.use_router(path, router) | app.use(path, router) | Mount a sub-router |
| app.listen(port, cb) | app.listen(port, cb) | Blocks in event loop |
| app.listen_host(host, port, cb) | app.listen(port, host, cb) | Specific host, blocks |
| app.listen_async(host, port, cb) | — | Non-blocking; call app.run() after |
| app.enable_tls(cert, key) / enable_tls_with(options) | — | Switch the app to HTTPS |
| app.listen_tls(cert, key, port, cb) | https.createServer(...).listen(...) | TLS, blocks |
| app.listen_tls_host(cert, key, host, port, cb) | — | TLS on a specific host, blocks |
| app.listen_tls_async(cert, key, host, port, cb) | — | TLS, non-blocking |
| app.run() | — | Runs the event loop (after listen_async) |
| app.close() | — | Closes all sockets, stops event loop |
| app.set(key, value) / app.get_setting(key) | app.set(key, value) / app.get(key) | App settings |
| app.enable(name) / app.disable(name) | app.enable(name) / app.disable(name) | Toggle boolean settings |
| app.enabled(name) / app.disabled(name) | app.enabled(name) / app.disabled(name) | Read boolean settings |
| app.engine(ext, render) | app.engine(ext, callback) | Register a view engine |
| app.render(view, locals?) | app.render(view, locals?, cb) | Render a view through an engine |
| app.ws(path, behavior) | app.ws(path, { open, message, close }) | WebSocket route |
| app.param(name, callback) | app.param(name, callback) | Param middleware |
| app.locals | app.locals | App-scoped local variables |
| MoonBit | Express.js | Notes |
|---|---|---|
| req.method() / req.http_method() | req.method | HTTP method (uppercase) |
| req.path() | req.path | URL path |
| req.original_url() | req.originalUrl | URL before router mounting |
| req.base_url() | req.baseUrl | Accumulated mount prefix |
| req.url() | req.url | URL relative to the current mount |
| req.param(name) | req.params[name] | Route parameter |
| req.params() | req.params | All route parameters |
| req.has_param(name) | — | Whether a param exists |
| req.query_param(name) | req.query[name] | Parsed query parameter |
| req.query_map() | req.query | All query parameters |
| req.raw_query() / req.query() | req.query (raw) | Raw query string |
| req.header(name) / req.get(name) | req.get(name) | Request header (case-insensitive) |
| req.xhr() | req.xhr | Whether X-Requested-With is XMLHttpRequest |
| req.body() | req.body | Raw body text |
| req.body_bytes() | req.body (Buffer) | Raw body bytes |
| req.json_body() | req.body (parsed) | Parsed JSON body |
| req.form(name) | req.body[name] | Form field value |
| req.form_map() | req.body | All form fields |
| req.form_json() | req.body | Nested form body (extended mode) |
| req.cookie(name) | req.cookies[name] | Cookie value |
| req.cookies() | req.cookies | All cookies |
| req.signed_cookie(name) | req.signedCookies[name] | Verified signed cookie |
| req.signed_cookies() | req.signedCookies | All verified signed cookies |
| req.ip() | req.ip | Remote IP (trust-proxy aware) |
| req.ips() | req.ips | IP chain from X-Forwarded-For |
| req.protocol() | req.protocol | Protocol (trust-proxy aware) |
| req.secure() | req.secure | Whether the request is TLS |
| req.host() | req.get('host') | Host header (trust-proxy aware) |
| req.hostname() | req.hostname | Host without port |
| req.subdomains() | req.subdomains | Subdomain list |
| req.remote_port() | — | Remote TCP port |
| req.route() | req.route | The matched RouteInfo |
| req.app() | req.app | The owning application |
| req.accepts(types) | req.accepts(types) | Content negotiation (media) |
| req.accepts_charsets(types) | req.acceptsCharsets(types) | Charset negotiation |
| req.accepts_encodings(types) | req.acceptsEncodings(types) | Encoding negotiation |
| req.accepts_languages(types) | req.acceptsLanguages(types) | Language negotiation |
| req.range(size) | req.range(size) | Parse a Range header |
| req.fresh() | req.fresh | Conditional request matches |
| req.stale() | req.stale | Inverse of fresh |
| req.is(type) | req.is(type) | Content-Type check |
| req.is_aborted() | — | Whether the request was aborted |
| req.locals | req.locals | Request-scoped local variables |
| req.setting(key) | req.app.get(key) | Read an app setting |
| MoonBit | Express.js | Notes |
|---|---|---|
| res.status(code) | res.status(code) | Set status code |
| res.status_code() | res.statusCode | Get status code |
| res.send_status(code) | res.sendStatus(code) | Send status with its reason phrase |
| res.set(name, value) | res.set(field, value) | Set response header |
| res.set_header(name, value) | res.setHeader(name, value) | Alias for set |
| res.get(name) | res.get(name) | Get response header |
| res.headers_sent() | res.headersSent | Whether headers were flushed |
| res.type_(content_type) | res.type(type) | Set Content-Type (normalised) |
| res.send(text) | res.send(text) | Send text response |
| res.send_bytes(bytes) | res.send(Buffer) | Send binary response |
| res.send_empty() | res.send() | Send empty response |
| res.json(value) | res.json(value) | Send JSON response |
| res.json_string(raw) | — | Send pre-serialized JSON |
| res.jsonp(value) | res.jsonp(value) | JSONP response |
| res.redirect(url, status?) | res.redirect(url, status?) | Redirect (302 by default) |
| res.render(view, locals?) | res.render(view, locals?, cb) | Render a view through an engine |
| res.format(handlers) | res.format(obj) | Content-negotiated response |
| res.cookie(name, value) | res.cookie(name, value) | Set cookie |
| res.cookie_with(name, value, path?, max_age?, http_only?, domain?, expires?, secure?, same_site?, signed?) | res.cookie(name, value, options) | Cookie with options |
| res.clear_cookie(name, path?) | res.clearCookie(name, options?) | Expire cookie |
| res.write(chunk) | res.write(chunk) | Stream a text chunk |
| res.write_bytes(chunk) | res.write(chunk) | Stream a binary chunk |
| res.end_stream() | res.end() | End a streamed response |
| res.flush_headers() | res.flushHeaders() | Flush headers without ending |
| res.is_ended() | — | Whether response was sent |
| res.is_aborted() | — | Whether response was aborted |
| res.send_file(path, options?) | res.sendFile(path, options?) | Send file (ETag, Range, 304) |
| res.download(path, filename?) | res.download(path, filename?) | Download file as attachment |
| res.attachment(filename?) | res.attachment(filename?) | Set Content-Disposition |
| res.append(field, value) | res.append(field, value) | Append response header |
| res.links(links) | res.links(links) | Set Link header |
| res.location(url) | res.location(url) | Set Location header |
| res.vary(field) | res.vary(field) | Add Vary header |
| res.locals | res.locals | Response-scoped local variables |
fn(req, res, next) {
println("before: " + req.path())
next.proceed()
println("after: " + req.path())
}| MoonBit | Express.js | Description |
|---|---|---|
| express_json(limit?) | express.json(limit?) | Parse application/json bodies |
| express_urlencoded(limit?, extended?) | express.urlencoded({ extended }) | Parse form bodies (flat or nested) |
| express_text(limit?) | express.text(limit?) | Parse text/* bodies |
| express_raw(limit?) | express.raw(limit?) | Parse raw bodies into bytes |
| express_static(root, options?) | express.static(root, options) | Serve static files (ETag, Range, ...) |
| cors() | cors() | Permissive CORS with preflight |
| logger() | morgan('dev') | Minimal request logger |
app.ws("/chat/:room", @express.ws_behavior(
open=fn(conn) { conn.send("welcome") },
message=fn(conn, message) {
match message {
@express.WsMessage::Text(text) => conn.send("echo:" + text)
@express.WsMessage::Binary(data) => conn.send_bytes(data)
}
},
close=fn(_conn, _code, _reason) { () },
))///|
fn main {
let app = @express.express()
app.get("/", fn(req, res, _next) { res.send("hello over " + req.protocol()) })
// Switches the app to TLS, then blocks in the µWebSockets event loop.
app.listen_tls("cert.pem", "key.pem", 8443, fn(ok) {
println("TLS listen: " + ok.to_string())
})
}///|
fn main {
let app = @express.express()
app.get("/", fn(_req, res, _next) { res.send("hello over TLS") })
app.listen_tls_async("cert.pem", "key.pem", "0.0.0.0", 8443, fn(ok) {
println("TLS listen: " + ok.to_string())
})
app.run() // blocks
}///|
fn main {
let app = @express.express()
app.enable_tls_with(@express.UwsSslOptions::{
cert_file: "cert.pem",
key_file: "key.pem",
passphrase: Some("secret"),
dh_params_file: None,
ca_file: None,
ssl_ciphers: Some("ECDHE+AESGCM"),
})
app.get("/", fn(_req, res, _next) { res.send("hello over TLS") })
app.listen(8443, fn(ok) { println("TLS listen: " + ok.to_string()) })
}openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
-keyout key.pem -out cert.pem -subj "/CN=localhost"
moon run example/tls # HTTPS demo on :8443moon run example/main
curl 'http://localhost:8080/users/42?tab=info'
curl -X POST -H 'Content-Type: application/json' -d '{"a":1}' http://localhost:8080/echopub struct App {
// private fields
}pub struct FileOptions {
dotfiles : String
index : String
etag : Bool
last_modified : Bool
max_age : Int
immutable : Bool
fallthrough : Bool
extensions : Array[String]
redirect : Bool
}pub struct Next {
// private fields
}pub struct PathPattern {
// private fields
}fn PathPattern::match_path(self : PathPattern, path : String, prefix : Bool) -> Array[(String, String)]?pub struct Req {
// private fields
}pub struct Res {
// private fields
}pub struct RouteBuilder {
// private fields
}pub struct RouteInfo {
http_method : String
path : String
}pub struct Router {
// private fields
}pub struct UwsSslOptions {
cert_file : String
key_file : String
passphrase : String?
dh_params_file : String?
ca_file : String?
ssl_ciphers : String?
}pub struct WsConn {
// private fields
}fn base64_encode(data : Bytes) -> Stringfn constant_time_equal(a : Bytes, b : Bytes) -> Boolfn dispatch_abort(request_id : UInt64) -> Unitfn dispatch_body(request_id : UInt64, body : Bytes, _len : Int, status : Int) -> Unitfn escape_html(value : String) -> Stringfn format_http_date(epoch_seconds : Int64) -> Stringfn hmac_sha256(key : Bytes, message : Bytes) -> Bytesfn mime_by_extension(extension : String) -> Stringfn mime_type(path : String) -> Stringfn parse_http_date(value : String) -> Int64?fn percent_decode(input : String) -> Stringfn percent_decode_path(input : String) -> Stringfn percent_encode(input : String) -> Stringfn sha256_bytes(data : Bytes) -> Bytesfn sign_cookie(secret : String, value : String) -> Stringfn status_text(code : Int) -> Stringfn unsign_cookie(secret : String, signed : String) -> String?Install
Download zipAn Express-compatible web framework for native MoonBit, served by µWebSockets (uWebSockets/uSockets bound through a C ABI shim).