Awesome
:book: docs: main branch
:warning: On linux --linker=legacy
is necessary for this package because of this Roc issue.
Basic Web Server for Roc
A webserver platform with a simple interface.
:racing_car: basic-webserver uses Rust's high-performance hyper and tokio libraries to execute your Roc function on incoming requests.
Example
Run this example server with $ roc helloweb.roc
(on linux, add --linker=legacy
) and go to http://localhost:8000
in your browser. You can change the port (8000) and the host (localhost) by setting the environment variables ROC_BASIC_WEBSERVER_PORT and ROC_BASIC_WEBSERVER_HOST.
app [Model, server] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.9.0/taU2jQuBf-wB8EJb0hAkrYLYOGacUU5Y9reiHG45IY4.tar.br" }
import pf.Stdout
import pf.Http exposing [Request, Response]
import pf.Utc
# Model is produced by `init`.
Model : {}
# With `init` you can set up a database connection once at server startup,
# generate css by running `tailwindcss`,...
# In this case we don't have anything to initialize, so it is just `Task.ok {}`.
server = { init: Task.ok {}, respond }
respond : Request, Model -> Task Response [ServerErr Str]_
respond = \req, _ ->
# Log request datetime, method and url
datetime = Utc.now! |> Utc.toIso8601Str
Stdout.line! "$(datetime) $(Http.methodToStr req.method) $(req.url)"
Task.ok { status: 200, headers: [], body: Str.toUtf8 "<b>Hello, web!</b></br>" }
Contributing
If you'd like to contribute, check out our group chat and let us know what you're thinking, we're friendly!
Developing / Building Locally
If you have cloned this repository and want to run the examples without using a packaged release (...tar.br), you will need to build the platform first by running roc build.roc
. Run examples with roc examples/hello.roc
(on linux, add --linker=legacy
).
Benchmarking
Basic webserver should have decent performance due to being built on top of Rust's hyper. That said, it has a few known issues that hurt performance:
- We do extra data copying on every request.
- Until roc has effect interpreters, basic-webserver can only do blocking io for effects. To work around this, every request is spawned in a blocking thread.
- Until sqlite improvements land, we never prepare queries.
That said, running benchmarks and debugging performance is still a great idea. It can help improve both Roc and basic-webserver.
Lots of load generators exist. Generally, it is advised to use one that avoids coordinated omission. A trusted generator that fits this criteria is wrk2 (sadly doesn't work on Apple Silicon).
If you are benchmarking on a single machine, you can use the TOKIO_WORKER_THREADS
environment variable to limit parallelism of the webserver.
Note: When benchmarking, it is best to run the load generator and the webserver on different machines.
When benchmarking on a single 8 core machine with wrk2
, these commands could be used (simply tune connections -c
and rate -R
):
- Optimized Build:
roc build --optimize my-webserver.roc
- Launch server with 4 cores:
TOKIO_WORKER_THREADS=4 ./my-webserver
- Generate load with 4 cores:
wrk -t4 -c100 -d30s -R2000 http://127.0.0.1:8000