Add OpenResty guestbook example: Shen web app on nginx+LuaJIT#28
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 (ininit_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— Ignoresexamples/openresty/logs/(nginx log directory).Notable Implementation Details
validate.shenhappens ininit_worker_by_lua, not per-request. This is the critical rule for running shen-lua under nginx.validate.shenundertc +) proves field rules sound at load time. Untyped shell (app.shenundertc -) handles I/O and routing. Both load into the same environment.valdatatype ([s "string"],[n 42],[obj [[k v] ...]]) that Shen patterns match directly. Responses are marshaled back to JSON.(lua.call "host.store_add" ...)against plain Lua functions backed by an nginxlua_shared_dict, demonstrating non-blocking I/O patterns.selftest.luaswaps in an in-memory store and runs the sameapp.luadispatch logic under plain LuaJIT, verifying typed validation and routing without nginx.https://claude.ai/code/session_014PiuPBGphzddJoh5qLjogD