Thunder is an OCaml-first edge framework for Cloudflare Workers.
Note: Thunder is still in active development and may break without warning until it reaches its first major version.
Thunder gives you:
- a typed request/response API
- a router and middleware model
- a Dune-driven build flow
- preview upload and production deploy through Wrangler
If you want to build an app with Thunder, start with KICKSTART.md.
thunder new my-app
cd my-app
npm install
dune buildIf you have a problem, thunder doctor reports how the Thunder binary resolves its framework home and checks the local tools Thunder expects.
A Thunder app is a generated project with these main places to edit:
app/routes.mlapp/middleware.mlworker/entry.ml
In most apps:
app/routes.mldefines routes and handlersapp/middleware.mlapplies app-wide middlewareworker/entry.mlstays small and only wires the app into the Worker export
- OCaml + opam + dune
- Node.js + npm
- local Wrangler install via
npm install wasm_of_ocaml-compiler- CMake + Ninja
- Linux/CI may also require
binaryenforwasm-merge
Optional:
odocfor local docs builds
Example setup:
opam install dune wasm_of_ocaml-compiler odoc
npm installCreate an app:
thunder new my-app
cd my-app
npm installBuild it:
dune buildThat build does three things:
- compiles the app to Worker runtime artifacts
- stages a deploy-ready Worker tree
- uploads a preview when credentials are present and the artifact hash changed
Useful commands inside a generated app:
# run the normal build flow
dune build
# build runtime artifacts only
dune build @worker-build
# build the Wasm runtime path explicitly
THUNDER_COMPILE_TARGET=wasm dune build @worker-build
# run tests
dune runtest
# explicit production deploy
CONFIRM_PROD_DEPLOY=1 dune build @deploy-prodPreview metadata is written to .thunder/preview.json.
Thunder uses Wrangler for preview uploads and production deploys.
Set a Cloudflare API token:
export CLOUDFLARE_API_TOKEN="<your-token>"Set your Cloudflare account id in wrangler.toml:
account_id = "<your-cloudflare-account-id>"
compatibility_flags = ["nodejs_compat"]Find your account id with:
npx wrangler whoamiIf CLOUDFLARE_API_TOKEN is not set, dune build still succeeds and preview upload is skipped.
The main generated outputs are:
_build/default/dist/worker/thunder_runtime.mjs_build/default/dist/worker/manifest.json_build/default/dist/worker/thunder_runtime.assets/when the selected target iswasm_build/default/dist/worker/thunder_runtime_js.mjsand_build/default/dist/worker/thunder_runtime_wasm.mjsas build intermediates_build/default/deploy/wrangler.toml_build/default/deploy/worker_runtime/index.mjs_build/default/deploy/worker_runtime/app_abi.mjs_build/default/deploy/worker_runtime/compiled_js_runtime_backend.mjs_build/default/deploy/worker_runtime/compiled_runtime_backend.mjs.thunder/preview.json
Thunder compiles your app to a JS or Wasm runtime bundle and deploys it behind a thin Cloudflare Worker host. New apps default to js; set THUNDER_COMPILE_TARGET=wasm when you want the Wasm-backed path.
At runtime:
- the Worker host receives
fetch(request, env, ctx) - Thunder encodes the request through a JSON ABI
- the compiled OCaml app runs the router, middleware, and handlers
- Thunder encodes the response back to the Worker host
- the Worker host returns the final
Response
For the detailed architecture walkthrough, see docs/architecture.md.
Top-level module: Thunder
Submodules:
Thunder.MethodThunder.StatusThunder.HeadersThunder.CookieThunder.QueryThunder.ContextThunder.RequestThunder.ResponseThunder.RouterThunder.Worker
Convenience exports:
- routing:
Thunder.get,Thunder.post,Thunder.put,Thunder.patch,Thunder.delete,Thunder.router - responses:
Thunder.text,Thunder.html,Thunder.json,Thunder.redirect - middleware:
Thunder.logger,Thunder.recover
This repository is the Thunder framework source tree.
Useful commands at repo root:
npm install
dune build
dune runtest
bash scripts/check_mli.sh
bash scripts/verify_generated_app_fixture.shIf you change CLI behavior such as scaffolding and want your installed thunder command to pick up those changes, rebuild and reinstall it from the repo root:
npm install
opam exec -- dune build packages/thunder_cli/main.exe
bash scripts/install_thunder.sh
thunder --version
thunder doctorWhat those commands do:
opam exec -- dune build packages/thunder_cli/main.exerebuilds the Thunder CLI binary at_build/default/packages/thunder_cli/main.exebash scripts/install_thunder.shcopies that rebuilt binary into~/.local/bin/thunderand updates the installed framework home under~/.local/share/thunder/currentthunder doctorverifies that your shell is resolving the installed binary you expect and that Thunder can find its framework files
This matters because changing files in this repo does not automatically update the already-installed thunder binary on your machine.
If you specifically changed app scaffolding, do a quick installed-binary smoke test after reinstalling:
thunder new my-app
cd my-app
npm install
dune build @worker-build
THUNDER_COMPILE_TARGET=wasm dune build @worker-build
dune buildIf you want installable release artifacts without cloning the repo, use the release-artifact workflow in .github/workflows/release-artifacts.yml.
That workflow builds:
- platform CLI binaries for macOS and Linux
- a versioned framework bundle tarball
- a
checksums.txtfile for installer verification
The intended hosted install flow is:
curl -fsSL https://my-website.com/install_thunder.sh | bashThe installer script at scripts/install_thunder.sh now supports both modes:
- local repo install when run from a checked-out Thunder repo with a built CLI binary
- release-asset install when run standalone and pointed at GitHub Release assets
For release work, the workflow assembles everything into an artifacts/ directory before verification and publication.
The app deployed from this repository lives in packages/thunder_worker/wasm_entry.ml.
The examples/ directory contains reference examples for learning the API; those examples are not the app this repository deploys by default.
examples/hello_siteexamples/json_apiexamples/cookiesexamples/paramsexamples/middlewareexamples/env_binding
Program odoc not found: installodocwithopam install odoc- preview upload skipped: export
CLOUDFLARE_API_TOKEN - account/auth errors: verify
account_idinwrangler.toml - Wrangler missing: run
npm install - worker artifacts missing: run
dune build @worker-build - deploy tree missing: run
dune buildand inspect_build/default/deploy/
- buffered body only
- no streaming response API
- no multipart parser
- no WebSockets
- Cloudflare Workers target only
KICKSTART.mddocs/architecture.mddocs/supported_features.mddocs/deployment.mddocs/runtime_parity_matrix.mddocs/examples.md