Typed request extraction, responses, and OpenAPI export for MoonBack.
import {
"moonbit-community/tyroute",
"moonbitlang/moonback",
"moonbitlang/async",
"moonbitlang/core/json",
}
options("is-main": true)///|
#derive(FromJson, ToJson)
struct User {
id : Int32
name : String
}
///|
#derive(FromJson)
struct UserQuery {
name : String?
}
///|
fn get_user(
id : Int32,
query : @tyroute.ViaJson[UserQuery],
_headers : Unit,
) -> @tyroute.ViaJson[User] {
let name = query.0.name.unwrap_or("MoonBit")
@tyroute.ViaJson({ id, name, })
}
///|
fn create_user(
_params : Unit,
_query : Unit,
_headers : Unit,
body : @tyroute.ViaJson[User],
) -> (@tyroute.StatusCode, @tyroute.ViaJson[User]) {
(@tyroute.StatusCode(201), body)
}
///|
async fn serve() -> Unit {
let router = @tyroute.Router()
..openapi("/openapi.json", title="Users API", version="1.0.0")
..get("/users/:id", get_user)
..post("/users", create_user)
let app = @moonback.App(ctx => {
ctx.use_(router)
})
defer app.close()
let server = @moonback.listen(host="127.0.0.1", port=3000)
println("Listening on http://127.0.0.1:3000")
app.serve(server)
}moon run examplecurl 'http://127.0.0.1:3000/users/42?name=MoonBit'
curl -i -X POST http://127.0.0.1:3000/users \
-H 'Content-Type: application/json' \
-d '{"id":42,"name":"MoonBit"}'
curl http://127.0.0.1:3000/openapi.jsonmoon check
moon test
moon fmt
moon infopub(open) trait FromHeaders {
fn schema() -> Json = _
fn from_headers(Headers) -> Self raise HandlerError
}impl FromHeaders for Unitimpl FromHeaders for Array[Tuple2[String, T]]impl FromHeaders for Map[String, T]impl FromHeaders for Headerspub(open) trait FromParam {
fn schema() -> Json = _
fn from_param(Array[(StringView, StringView)]) -> Self raise HandlerError
}fn[A : FromString, B : FromString] from_param(arr : Array[(StringView, StringView)]) -> (A, B) raise HandlerErrorfn[A : FromString, B : FromString, C : FromString] from_param(arr : Array[(StringView, StringView)]) -> (A, B, C) raise HandlerErrorfn[A : FromString, B : FromString, C : FromString, D : FromString] from_param(arr : Array[(StringView, StringView)]) -> (A, B, C, D) raise HandlerErrorfn[A : FromString, B : FromString, C : FromString, D : FromString, E : FromString] from_param(arr : Array[(StringView, StringView)]) -> (A, B, C, D, E) raise HandlerErrorfn[A : FromString, B : FromString, C : FromString, D : FromString, E : FromString, F : FromString] from_param(arr : Array[(StringView, StringView)]) -> (A, B, C, D, E, F) raise HandlerErrorfn[A : FromString, B : FromString, C : FromString, D : FromString, E : FromString, F : FromString] schema() -> Jsonimpl FromParam for StringViewpub(open) trait FromQuery {
fn schema() -> Json = _
fn from_query(QueryParams) -> Self raise HandlerError
}pub(open) trait FromString {
fn schema() -> Json = _
fn from_string(StringView) -> Self raise HandlerError
}impl FromString for Boolimpl FromString for Charimpl FromString for Intimpl FromString for Int64impl FromString for UIntimpl FromString for UInt64impl FromString for Floatimpl FromString for Doubleimpl FromString for Stringimpl FromString for StringViewpub(open) trait ToResponse {
fn schema() -> Json = _
fn to_response(Self) -> Response raise HandlerError
}impl ToResponse for Unitimpl ToResponse for Boolimpl ToResponse for Byteimpl ToResponse for Charimpl ToResponse for Intimpl ToResponse for Int16impl ToResponse for Int64impl ToResponse for UIntimpl ToResponse for UInt16impl ToResponse for UInt64impl ToResponse for Floatimpl ToResponse for Doubleimpl ToResponse for Stringimpl ToResponse for Bytesimpl ToResponse for Jsonimpl ToResponse for Tuple2[StatusCode, A]impl ToResponse for BytesViewimpl ToResponse for StringViewpub(all) suberror HandlerError {
StringMismatch(String)
ParamMismatch(String)
QueryMismatch(String)
HeaderMismatch(String)
BodyMismatch(String)
ResponseMismatch(String)
}impl Show for HandlerErrorpub suberror OpenApiError {
InvalidPath(String)
InvalidSchema(String)
DuplicateOperation(String)
}type Routerfn[P : FromParam, Q : FromQuery, H : FromHeaders, B : FromBody, R : ToResponse] Router::add(self : Router, path : String, meth : RequestMethod, handler : (P, Q, H, B) -> R) -> Unitfn[P : FromParam, Q : FromQuery, H : FromHeaders, B : FromBody, R : ToResponse] Router::delete(self : Router, path : String, handler : (P, Q, H, B) -> R) -> Unitfn Router::generate_openapi(self : Router, title~ : String, version~ : String, components? : Json) -> Json raise OpenApiErrorfn[P : FromParam, Q : FromQuery, H : FromHeaders, R : ToResponse] Router::get(self : Router, path : String, handler : (P, Q, H) -> R) -> Unitfn[P : FromParam, Q : FromQuery, H : FromHeaders, R : ToResponse] Router::head(self : Router, path : String, handler : (P, Q, H) -> R) -> Unitfn[P : FromParam, Q : FromQuery, H : FromHeaders, R : ToResponse] Router::options(self : Router, path : String, handler : (P, Q, H) -> R) -> Unitfn[P : FromParam, Q : FromQuery, H : FromHeaders, B : FromBody, R : ToResponse] Router::patch(self : Router, path : String, handler : (P, Q, H, B) -> R) -> Unitfn[P : FromParam, Q : FromQuery, H : FromHeaders, B : FromBody, R : ToResponse] Router::post(self : Router, path : String, handler : (P, Q, H, B) -> R) -> Unitfn[P : FromParam, Q : FromQuery, H : FromHeaders, B : FromBody, R : ToResponse] Router::put(self : Router, path : String, handler : (P, Q, H, B) -> R) -> Unitpub(all) struct ViaJson[T](T)