mars

Hono-inspired MoonBit web framework with trie routing, middleware, SSE, and mizchi/x HTTP backend for native and Node.js

moonbit
http
router
web-framework
middleware
sse
nodejs
api
moon add mizchi/mars@0.3.11
Download zip
Author
Version
0.3.11
License
MIT
Last updated
last month
Downloads
5K

Dependencies

README

#Mars

A Hono-inspired HTTP framework for MoonBit.

#Features

  • Trie-based Router: Fast URL matching with parameter extraction
  • Dynamic Parameters: Support for :id style URL parameters
  • Wildcard Routes: Support for * wildcard patterns
  • Optional Parameters: Support for :param? optional parameters
  • Regex Parameters: Support for :id{[0-9]+} regex-constrained parameters
  • HTTP Methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT, TRACE
  • Middleware Support: Chain multiple handlers
  • JSON/HTML/Text Responses: Built-in response helpers

#Installation

Add to your moon.mod.json:

{ "deps": { "mizchi/mars": "0.1.0" } }

#Usage

async fn main {
let app = @mars.Mars::new()

// Static route
let _ = app.get("/", @mars.handler(async fn(ctx) {
ctx.text("Hello, Mars!")
}))

// Dynamic parameter
let _ = app.get("/users/:id", @mars.handler(async fn(ctx) {
let id = ctx.param("id").unwrap_or("unknown")
ctx.json({ "id": id })
}))

// Wildcard
let _ = app.get("/files/*", @mars.handler(async fn(ctx) {
ctx.text("File path: " + ctx.path())
}))

// Multiple methods
let _ = app.post("/users", @mars.handler(async fn(ctx) {
ctx.json({ "status": "created" }, status=201)
}))

// Start server
app.serve(host="127.0.0.1", port=3000)
}

#JWT Middleware

jwt_bearer_with_key and jwt_bearer_with_jwks are temporarily disabled while cross-target JWT verification is being redesigned. Current verifier behavior is fail-closed (always unauthorized).

jwks_fetch_native, jwks_fetch_node, and jwks_fetch_wasm fetch JWKS documents for applications that need the transport helper directly. Native and Node.js targets use mizchi/x/http.

Claims helper APIs are still available:

  • jwt_claims_json
  • jwt_claims_map
  • jwt_claims_decode
  • jwt_claims_decode_string

#API

#Mars

  • Mars::new() -> Mars - Create new application
  • Mars::get(path, handler) -> Mars - Register GET route
  • Mars::post(path, handler) -> Mars - Register POST route
  • Mars::put(path, handler) -> Mars - Register PUT route
  • Mars::delete(path, handler) -> Mars - Register DELETE route
  • Mars::patch(path, handler) -> Mars - Register PATCH route
  • Mars::all(path, handler) -> Mars - Register route for all methods
  • Mars::use_(middleware) -> Mars - Add middleware
  • Mars::serve(host?, port?) - Start HTTP server

#Context

  • Context::param(name) -> String? - Get URL parameter
  • Context::path() -> String - Get request path
  • Context::request() -> @http.Request - Get raw HTTP request
  • Context::header(name) -> String? - Get request header
  • Context::text(body, status?) - Send text response
  • Context::json(data, status?) - Send JSON response
  • Context::html(body, status?) - Send HTML response
  • Context::redirect(url) - Send redirect response
  • Context::not_found() - Send 404 response

#Router Patterns

  • /users - Static path
  • /users/:id - Dynamic parameter
  • /users/:id{[0-9]+} - Regex-constrained parameter
  • /files/* - Wildcard (matches any path)
  • /api/:type? - Optional parameter

#License

MIT

#
Env

pub(open) trait Env {
fn get_var(Self, String) -> String?
fn get_binding(Self, String) -> Json?
}

Environment trait for platform-specific bindings Implement this trait for your platform (Cloudflare, Deno, etc.)

#
ExecCtx

pub(open) trait ExecCtx {
fn wait_until(Self, async () -> Unit, name~ : String) -> Unit
}

Execution context trait for background tasks Similar to Cloudflare Workers ExecutionContext

#
Var

pub(open) trait Var {
fn to_json(Self) -> Json
fn from_json(Json) -> Self?
}

Trait for types that can be stored in Variables
impl Var for Bool
impl Var for Double
impl Var for String
impl Var for Json

#
BackgroundTask

pub(all) struct BackgroundTask {
task : async () -> Unit
name : String
}

Background task that will run after response is sent

#
Context

pub(all) struct Context {
request :
Request

params :
Params

vars : Variables
exec_ctx : &ExecCtx
// private fields
}

HTTP Context for request/response handling

#
Context::body

async fn Context::body(self : Context) -> String

Read request body as string (native implementation)

#
Context::body_json

async fn Context::body_json(self : Context) -> Json?

Parse request body as JSON

#
Context::conn

Get server connection (native only)

#
Context::cookie

fn Context::cookie(self : Context, name : String) -> String?

Get a cookie by name

#
Context::cookies

fn Context::cookies(self : Context) -> Map[String, String]

Get all cookies
fn Context::delete_cookie(self : Context, name : String) -> Unit

Delete a cookie

#
Context::end_response

async fn Context::end_response(self : Context) -> Unit

End the response (native implementation)

#
Context::env_binding

fn Context::env_binding(self : Context, name : String) -> Json?

Get environment binding as JSON

#
Context::env_var

fn Context::env_var(self : Context, name : String) -> String?

Get environment variable

#
Context::get

fn Context::get(self : Context, key : String) -> String?

Get custom data

#
Context::header

fn Context::header(self : Context, name : String) -> String?

Get a request header

#
Context::html

async fn Context::html(self : Context, body : String, status? : Int) -> Unit

Send an HTML response (native implementation)

#
Context::is_response_sent

fn Context::is_response_sent(self : Context) -> Bool

Check if response has been sent

#
Context::json

async fn Context::json(self : Context, data : Json, status? : Int) -> Unit

Send a JSON response (native implementation)

#
Context::meth

Get request method

#
Context::new

Create a new Context (native-specific constructor)

#
Context::not_found

async fn Context::not_found(self : Context) -> Unit

Send a 404 Not Found response (native implementation)

#
Context::param

fn Context::param(self : Context, name : String) -> String?

Get a URL parameter by name

#
Context::path

fn Context::path(self : Context) -> String

Get request path

#
Context::queries

fn Context::queries(self : Context) -> Map[String, String]

Get all query parameters

#
Context::query

fn Context::query(self : Context, name : String) -> String?

Get a query parameter by name

#
Context::redirect

async fn Context::redirect(self : Context, url : String) -> Unit

Send a redirect response (native implementation)

#
Context::request

Get raw HTTP request

#
Context::set

fn Context::set(self : Context, key : String, value : String) -> Unit

Set custom data
fn Context::set_cookie(self : Context, name : String, value : String, max_age? : Int, path? : String, http_only? : Bool, same_site? : String) -> Unit

Set a cookie

#
Context::set_header

fn Context::set_header(self : Context, name : String, value : String) -> Unit

Set a response header

#
Context::set_status

fn Context::set_status(self : Context, status : Int) -> Unit

Set response status code

#
Context::sse_start

async fn Context::sse_start(self : Context) -> Unit

Start an SSE response (native implementation)

#
Context::text

async fn Context::text(self : Context, body : String, status? : Int) -> Unit

Send a text response (native implementation)

#
Context::wait_until

fn Context::wait_until(self : Context, task : async () -> Unit, name? : String) -> Unit

Schedule a background task (like Cloudflare's waitUntil)

#
Context::with_env

Create a new Context with environment and execution context (native-specific)

#
Context::write_raw

async fn Context::write_raw(self : Context, data : String) -> Unit

Write raw data to the response (native implementation)

#
EmptyEnv

pub(all) struct EmptyEnv {
}

Empty environment (no bindings)
impl Env for EmptyEnv

#
EmptyEnv::new

fn EmptyEnv::new() -> EmptyEnv

#
ExecutionContext

pub(all) struct ExecutionContext {
// private fields
}

ExecutionContext manages background tasks

#
ExecutionContext::deactivate

fn ExecutionContext::deactivate(self : ExecutionContext) -> Unit

Deactivate context (no more tasks can be added)

#
ExecutionContext::get_tasks

Get all pending tasks (for testing)

#
ExecutionContext::is_active

fn ExecutionContext::is_active(self : ExecutionContext) -> Bool

Check if context is still active

#
ExecutionContext::new

Create a new ExecutionContext

#
ExecutionContext::pending_count

fn ExecutionContext::pending_count(self : ExecutionContext) -> Int

Get number of pending tasks

#
ExecutionContext::run_tasks

async fn ExecutionContext::run_tasks(self : ExecutionContext) -> Unit

Execute all pending background tasks Called after response is sent

#
ExecutionContext::wait_until

fn ExecutionContext::wait_until(self : ExecutionContext, task : async () -> Unit, name? : String) -> Unit

Schedule a background task to run after response is sent Similar to Cloudflare's ctx.waitUntil()

#
Handler

pub(all) struct Handler(async (Context) -> Unit)

Handler function signature Handler receives a Context and returns Unit

#
MapEnv

pub(all) struct MapEnv {
vars : Map[String, String]
bindings : Map[String, Json]
}

Map-based environment (for testing and simple use cases)
impl Env for MapEnv

#
MapEnv::new

fn MapEnv::new() -> MapEnv

#
MapEnv::with_binding

fn MapEnv::with_binding(self : MapEnv, name : String, value : Json) -> MapEnv

#
MapEnv::with_var

fn MapEnv::with_var(self : MapEnv, name : String, value : String) -> MapEnv

#
PlatformContext

pub(all) struct PlatformContext {
conn :
ServerConnection

body_reader : &
Reader

}

Native platform-specific context data

#
PlatformContext::new

Create a new PlatformContext for native

#
Server

pub(all) struct Server {
router :
TrieRouter

handlers : Array[Handler]
middlewares : Array[Handler]
routes : Array[(
Method
, String, Int)]
}

Server HTTP Framework A Hono-inspired HTTP framework for MoonBit

#
Server::all

fn Server::all(self : Server, path : String, handler : Handler) -> Unit

Register a route for all HTTP methods

#
Server::delete

fn Server::delete(self : Server, path : String, handler : Handler) -> Unit

Register a DELETE route

#
Server::get

fn Server::get(self : Server, path : String, handler : Handler) -> Unit

Register a GET route

#
Server::middleware

fn Server::middleware(self : Server, handler : Handler) -> Unit

Add a middleware to the application

#
Server::mount

fn Server::mount(self : Server, prefix : String, sub : Server) -> Unit

Mount a sub-application at the given prefix

#
Server::new

fn Server::new() -> Server

Create a new Server application

#
Server::new_debug

fn Server::new_debug() -> Server

Create a new Server application with debug mode enabled

#
Server::patch

fn Server::patch(self : Server, path : String, handler : Handler) -> Unit

Register a PATCH route

#
Server::post

fn Server::post(self : Server, path : String, handler : Handler) -> Unit

Register a POST route

#
Server::put

fn Server::put(self : Server, path : String, handler : Handler) -> Unit

Register a PUT route

#
Server::query

fn Server::query(self : Server, path : String, handler : Handler) -> Unit

Register a QUERY route (RFC 9110)

#
Server::serve

async fn Server::serve(self : Server, host? : String, port? : Int) -> Unit

Start the HTTP server

#
Server::serve_with_env

async fn[E : Env, C : ExecCtx] Server::serve_with_env(self : Server, host? : String, port? : Int, env : E, exec_ctx : C) -> Unit

Start the HTTP server with environment and execution context

#
Server::to_handler

Convert Server app to a platform handler function (native only)

#
Server::to_handler_with_env

fn[E : Env, C : ExecCtx] Server::to_handler_with_env(self : Server, env : E, exec_ctx : C) -> (async (
Request
, &
Reader
,
ServerConnection
) -> Unit)

Convert Server app to a platform handler function with environment and execution context (native only)

#
Variables

pub(all) struct Variables {
data : Map[String, Json]
}

Typed variables storage Uses Json for flexible storage with runtime type checking

#
Variables::get

fn Variables::get(self : Variables, key : String) -> Json?

Get a variable as Json for pattern matching

#
Variables::has

fn Variables::has(self : Variables, key : String) -> Bool

Check if variable exists

#
Variables::keys

fn Variables::keys(self : Variables) -> Array[String]

Get all keys

#
Variables::new

fn Variables::new() -> Variables

#
Variables::remove

fn Variables::remove(self : Variables, key : String) -> Unit

Remove a variable

#
Variables::set

fn[V : Var] Variables::set(self : Variables, key : String, value : V) -> Unit

Set a variable (trait-based)

#
char_to_lower

fn char_to_lower(c : Char) -> Char

Convert character to lowercase

#
compose

fn compose(handlers : Array[Handler]) -> Handler

Compose multiple handlers into a chain Each handler is called in order

#
find_char

fn find_char(s : String, c : Char) -> Int

Find first occurrence of character in string, returns -1 if not found

#
handler

fn handler(f : async (Context) -> Unit) -> Handler

Helper to create a Handler from an async function

#
hex_to_int

fn hex_to_int(c : Char) -> Int

Convert hex character to integer, returns -1 for invalid

#
is_debug

fn is_debug() -> Bool

Check if debug mode is enabled

#
is_whitespace

fn is_whitespace(c : Char) -> Bool

Check if character is whitespace

#
set_debug

fn set_debug(enabled : Bool) -> Unit

Enable or disable debug mode globally

#
split_by_char

fn split_by_char(s : String, sep : Char) -> Array[String]

Split string by separator character

#
strip_query_string

fn strip_query_string(path : String) -> String

Strip query string from path (everything from first '?' onwards)

#
substring

fn substring(s : String, start : Int, end : Int) -> String

Extract substring from start to end index

#
substring_from

fn substring_from(s : String, start : Int) -> String

Extract substring from start to end of string

#
to_lower

fn to_lower(s : String) -> String

Convert string to lowercase

#
trim

fn trim(s : String) -> String

Trim whitespace from both ends of string

#
url_decode

fn url_decode(s : String) -> String

URL decode a string (handles %XX and + encoding)

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io