Skip to content

Add OpenResty guestbook example: Shen web app on nginx+LuaJIT#28

Merged
pyrex41 merged 2 commits into
mainfrom
claude/shen-openresty-app-i1v5ao
Jun 29, 2026
Merged

Add OpenResty guestbook example: Shen web app on nginx+LuaJIT#28
pyrex41 merged 2 commits into
mainfrom
claude/shen-openresty-app-i1v5ao

Conversation

@pyrex41

@pyrex41 pyrex41 commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Summary

This PR adds a complete, production-shaped web application example demonstrating how to run Shen code on OpenResty (nginx + LuaJIT). The example is a guestbook API with typed validation rules, a plain HTML+fetch frontend, and can run both under OpenResty and standalone under plain LuaJIT for testing.

Key Changes

  • examples/openresty/ — New example directory containing:

    • validate.shen — Typed core with field validation rules, loaded with the typechecker ON. Type errors abort at startup, before the server handles any requests.
    • app.shen — Untyped routing and storage orchestration layer. Dispatches HTTP requests, calls typed validators, and orchestrates storage via Lua interop.
    • app.lua — The Lua glue layer that boots the Shen kernel once per worker (in init_worker_by_lua), marshals JSON ↔ Shen values, and implements the nginx content handler.
    • nginx.conf — OpenResty configuration with two locations: /api/ for the JSON API and / for the static frontend.
    • public/index.html — Plain HTML+fetch frontend (no build step) that demonstrates the ShenScript angle: hand-written JS that could be replaced by Shen compiled to JavaScript.
    • selftest.lua — Standalone test harness that runs the entire app under plain LuaJIT with an in-memory store, exercising typed validation and routing without nginx.
    • json_shim.lua — Minimal JSON codec (encode/decode) used only when cjson is unavailable (off-nginx). OpenResty bundles cjson, so this is never loaded in production.
  • Updated examples/README.md — Documents the new example and its purpose.

  • Updated root README.md — Adds reference to the OpenResty example.

  • .gitignore — Ignores examples/openresty/logs/ (nginx log directory).

Notable Implementation Details

  • Boot-once-per-worker pattern: The expensive kernel boot and typechecking of validate.shen happens in init_worker_by_lua, not per-request. This is the critical rule for running shen-lua under nginx.
  • Layered design: Typed core (validate.shen under tc +) proves field rules sound at load time. Untyped shell (app.shen under tc -) handles I/O and routing. Both load into the same environment.
  • Value marshaling: Request bodies are decoded from JSON and tagged into a val datatype ([s "string"], [n 42], [obj [[k v] ...]]) that Shen patterns match directly. Responses are marshaled back to JSON.
  • Storage via Lua interop: The Shen router calls (lua.call "host.store_add" ...) against plain Lua functions backed by an nginx lua_shared_dict, demonstrating non-blocking I/O patterns.
  • Testable off-nginx: selftest.lua swaps in an in-memory store and runs the same app.lua dispatch logic under plain LuaJIT, verifying typed validation and routing without nginx.

https://claude.ai/code/session_014PiuPBGphzddJoh5qLjogD

claude and others added 2 commits June 29, 2026 12:38
A complete guestbook web app whose server logic is written in Shen and runs
on OpenResty (nginx + LuaJIT). Demonstrates the architecture for a real
shen-lua web app:

- validate.shen: typed request validators, loaded under (tc +) so a type
  error aborts startup before any request is served
- app.shen: a Shen router (dispatch + storage orchestration), loaded untyped
- app.lua: the glue — boots Shen once per worker, marshals JSON <-> the
  tagged `val` shape, exposes the content handler; degrades to a bundled JSON
  shim off-nginx so the example is testable under plain luajit
- nginx.conf: boot-per-worker via init_worker_by_lua, lua_shared_dict storage
- public/index.html: plain HTML+fetch front end (the ShenScript slot)
- selftest.lua: drives the full app under luajit, no nginx required (8 cases)

Registered in both README example tables.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014PiuPBGphzddJoh5qLjogD
…aken for fast client load

Make the OpenResty guestbook's validation rules a single source of truth that
runs on both ends, and wire up the ShenScript client path the example only
gestured at before.

- rules.shen: the field rules as one pure, typed, portable Shen file. The
  server loads it under (tc +) (replacing validate.shen — same guarantees, now
  built with cn/str so it needs no Lua bridges and stays portable).
- Browser validation now runs the SAME rules: scripts/build-client.sh shakes
  rules.shen (+ a 4-line marshaling glue) with Ratatoskr down to ~100 reachable
  kernel defuns (eval/reader/typechecker stripped, needs-eval=false), and
  ShenScript compiles that slice to a self-contained ES module
  (public/vendor/shen-rules.client.js, ~140 KB, ~15 ms init vs ~660 KB / ~2.3 s
  for the full kernel bundle). Committed so the demo runs with no extra checkout.
- index.html: imports the shaken validator, checks the form in-browser before
  POSTing (invalid never hits the network), and gained a self-documenting
  "what this demonstrates" panel, a flow diagram, and an inline rules.shen view.
- nginx.conf: serve /rules.shen, and add a minimal MIME map so the ES module is
  served as text/javascript (browsers enforce JS MIME for module scripts).
- Verified end-to-end under real OpenResty + a real browser; selftest still green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pyrex41 pyrex41 merged commit 904c75f into main Jun 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants