# submillisecond A [lunatic] web framework for the Rust language. Submillisecond is a **backend** web framework around the Rust language, [WebAssembly's][wasm] security and the [lunatic scheduler][lunatic_gh]. > This is an early stage project, probably has bugs and the API is still changing. It's also > important to point out that many Rust crates don't compile to WebAssembly yet and can't be used > with submillisecond. If you would like to ask for help or just follow the discussions around Lunatic & submillisecond, [join our discord server][discord]. # Features - Fast compilation times - async-free - All preemption and scheduling is done by [lunatic][lunatic_gh] - strong security - Each request is handled in a separate _lunatic_ process - Batteries included - Cookies - Json - Logging - Websockets - [Submillisecond LiveView] - Frontend web framework [submillisecond liveview]: https://github.com/lunatic-solutions/submillisecond-live-view # Code example ```rust use submillisecond::{router, Application}; fn index() -> &'static str { "Hello :)" } fn main() -> std::io::Result<()> { Application::new(router! { GET "/" => index }) .serve("0.0.0.0:3000") } ``` ## Getting started with lunatic To run the example you will first need to download the lunatic runtime by following the installation steps in [this repository][lunatic_gh]. The runtime is just a single executable and runs on Windows, macOS and Linux. If you have already Rust installed, you can get it with: ```bash cargo install lunatic-runtime ``` [Lunatic][lunatic_gh] applications need to be compiled to [WebAssembly][wasm] before they can be executed by the runtime. Rust has great support for WebAssembly and you can build a lunatic compatible application just by passing the `--target=wasm32-wasi` flag to cargo, e.g: ```bash # Add the WebAssembly target rustup target add wasm32-wasi # Build the app cargo build --release --target=wasm32-wasi ``` This will generate a .wasm file in the `target/wasm32-wasi/release/` folder inside your project. You can now run your application by passing the generated .wasm file to Lunatic, e.g: ``` lunatic target/wasm32-wasi/release/.wasm ``` #### Better developer experience To simplify developing, testing and running lunatic applications with cargo, you can add a `.cargo/config.toml` file to your project with the following content: ```toml [build] target = "wasm32-wasi" [target.wasm32-wasi] runner = "lunatic" ``` Now you can just use the commands you are already familiar with, such as `cargo run`, `cargo test` and cargo is going to automatically build your project as a WebAssembly module and run it inside `lunatic`. ## Getting started with submillisecond Add it as a dependency ```toml submillisecond = "0.3.0" ``` ## Handlers Handlers are functions which return a response which implements [`IntoResponse`][intoresponse]. They can have any number of arguments, where each argument is an [extractor]. ```rust fn index(body: Vec, cookies: Cookies) -> String { // ... } ``` ## Routers Submillisecond provides a [`router!`][router] macro for defining routes in your app. ```rust #[derive(NamedParam)] struct User { first_name: String, last_name: String, } fn hi(user: User) -> String { format!("Hi {} {}!", user.first_name, user.last_name) } fn main() -> std::io::Result<()> { Application::new(router! { GET "/hi/:first_name/:last_name" => hi POST "/update_data" => update_age }) .serve("0.0.0.0:3000") } ``` The router macro supports: - [Nested routes](#nested-routes) - [Url parameters](#url-parameters) - [Catch-all](#catch-all) - [Guards](#guards) - [Middleware](#middleware) ### Nested routes Routes can be nested. ```rust router! { "/foo" => { GET "/bar" => bar } } ``` ### Url parameters Uri parameters can be captured with the [Path] extractor. ```rust router! { GET "/users/:first/:last/:age" => greet } fn greet(Path((first, last, age)): Path<(String, String, u32)>) -> String { format!("Welcome {first} {last}. You are {age} years old.") } ``` You can use the [NamedParam] derive macro to define named parameters. ```rust router! { GET "/users/:first/:last/:age" => greet } #[derive(NamedParam)] struct GreetInfo { first: String, last: String, age: u32, } fn greet(GreetInfo { first, last, age }: GreetInfo) -> String { format!("Welcome {first} {last}. You are {age} years old.") } ``` Alternatively, you can access the params directly with the [Params] extractor. ### Catch-all The `_` syntax can be used to catch-all routes. ```rust router! { "/foo" => { GET "/bar" => bar _ => matches_foo_but_not_bar } _ => not_found } ``` ### Guards Routes can be protected by guards. ```rust struct ContentLengthLimit(u64); impl Guard for ContentLengthLimit { fn check(&self, req: &RequestContext) -> bool { // ... } } router! { "/short_requests" if ContentLengthGuard(128) => { POST "/super" if ContentLengthGuard(64) => super_short POST "/" => short } } ``` Guards can be chained with the `&&` and `||` syntax. ### Middleware Middleware is any handler which calls [`next_handler`][next_handler] on the request context. Like handlers, it can use extractors. ```rust fn logger(req: RequestContext) -> Response { println!("Before"); let result = req.next_handler(); println!("After"); result } fn main() -> std::io::Result<()> { Application::new(router! { with logger; GET "/" => hi }) .serve("0.0.0.0:3000") } ``` Middleware can be chained together, and placed within sub-routes. ```rust router! { with [mid1, mid2]; "/foo" => { with [foo_mid1, foo_mid2]; } } ``` They can also be specific to a single route. ```rust router! { GET "/" with mid1 => home } ``` ## Testing Lunatic provides a macro `#[lunatic::test]` to turn your tests into processes. Check out the [`tests`][tests] folder for examples. # License Licensed under either of - Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0) - MIT license (http://opensource.org/licenses/MIT) at your option. [lunatic]: https://lunatic.solutions [lunatic_gh]: https://github.com/lunatic-solutions/lunatic [wasm]: https://webassembly.org [discord]: https://discord.gg/b7zDqpXpB4 [tests]: /tests [router]: https://docs.rs/submillisecond/latest/submillisecond/macro.router.html [intoresponse]: https://docs.rs/submillisecond/latest/submillisecond/response/trait.IntoResponse.html [params]: https://docs.rs/submillisecond/latest/submillisecond/params/struct.Params.html [path]: https://docs.rs/submillisecond/latest/submillisecond/extract/path/struct.Path.html [namedparam]: https://docs.rs/submillisecond/latest/submillisecond/derive.NamedParam.html [extractor]: https://docs.rs/submillisecond/latest/submillisecond/extract/trait.FromRequest.html [next_handler]: https://docs.rs/submillisecond/latest/submillisecond/struct.RequestContext.html#method.next_handler