Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
== Schematra A modern web framework for CHICKEN Scheme inspired by Sinatra that combines simplicity with power. === Description Schematra is a lightweight, expressive web framework for CHICKEN Scheme built on top of Spiffy that brings the elegance of Lisp to web development. It features minimal boilerplate, Chiccup HTML rendering, flexible routing, built-in sessions, and extensible middleware support. === Requirements * CHICKEN Scheme 5.0 or later * Dependencies: spiffy, format, openssl, message-digest, hmac, sha2, http-client, medea, nrepl === Installation <enscript highlight="bash"> chicken-install schematra </enscript> '''SRFI-1 Compatibility:''' If you're using SRFI-1 and need its {{delete}} function alongside Schematra's HTTP DELETE verb, rename one on import: <enscript highlight="scheme"> ;; Rename SRFI-1's delete (recommended) (import (rename srfi-1 (delete srfi1:delete)) schematra chiccup) (delete "/users/:id" ...) ; HTTP DELETE route (srfi1:delete 'x my-list) ; Remove items from list </enscript> === Basic Usage <enscript highlight="scheme"> (import schematra chiccup) ;; Define routes (get ("/") (ccup->html `[h1 "Hello, Schematra!"])) (post ("/submit") "Form submitted!") ;; Start server (schematra-install) (schematra-start) </enscript> === API ==== Route Definition <syntax>(get (route) body ...)</syntax> <syntax>(post (route) body ...)</syntax> <syntax>(put (route) body ...)</syntax> <syntax>(delete (route) body ...)</syntax> Define HTTP route handlers for the given path. Route can contain URL parameters using {{:param}} syntax. <enscript highlight="scheme"> ;; Simple routes (get ("/") "Home page") (get ("/about") (ccup->html `[h1 "About Us"])) ;; URL parameters (get ("/user/:id") (let ((id (alist-ref "id" (current-params) equal?))) (ccup->html `[h1 ,(string-append "User " id)]))) </enscript> <parameter>(current-params)</parameter> Returns an association list containing the current request parameters: * '''Path parameters''' (string keys): URL segments starting with ':' * '''Query parameters''' (symbol keys): URL query string parameters * '''Form data''' (symbol keys): POST form fields (requires body-parser-middleware) This parameter only contains meaningful data inside a route. Outside it will default to {{#f}}. <enscript highlight="scheme"> ;; For route /users/:id and request /users/123?format=json (current-params) ; => '(("id" . "123") (format . "json")) ;; Access path parameter (alist-ref "id" (current-params) equal?) ; => "123" ;; Access query parameter (alist-ref 'format (current-params)) ; => "json" </enscript> <procedure>(halt status [body] [headers])</procedure> Immediately stop request processing and send an HTTP response. <enscript highlight="scheme"> (halt 'not-found "Page not found") (halt 'bad-request "{\"error\": \"Invalid input\"}" '((content-type application/json))) </enscript> <procedure>(redirect location [status])</procedure> Redirect the client to a different URL. Default status is {{found}} (302). <enscript highlight="scheme"> (redirect "/login") (redirect "/new-location" 'moved-permanently) </enscript> <procedure>(send-json-response datum [status])</procedure> Send a JSON response with proper content-type headers. Default status is {{ok}} (200). <enscript highlight="scheme"> (send-json-response '((status . "healthy") (version . "1.0"))) (send-json-response '((error . "Not found")) 'not-found) </enscript> <procedure>(static path directory)</procedure> Serve static files from {{directory}} at URL {{path}} prefix. <procedure>(schematra-install)</procedure> Install the schematra route handlers. <procedure>(schematra-start #!key development? nrepl?)</procedure> Start the web server. * {{development?}}: Enable development mode (default: #f) * {{nrepl?}}: Start NREPL server in dev mode (default: #t) ==== Response Format Route handlers can return: '''String Response:''' Returns 200 OK with the string as body <enscript highlight="scheme"> (get ("/hello") "Hello, World!") </enscript> '''Response Tuple:''' Format {{(status body [headers])}} for full control <enscript highlight="scheme"> '(created "User created successfully") '(ok "{\"message\": \"success\"}" ((content-type application/json))) </enscript> === Chiccup Rendering <procedure>(ccup->html s-html)</procedure> <procedure>(ccup->sxml s-html)</procedure> Convert Chiccup list to HTML string or SXML. Chiccup allows writing HTML using Lisp syntax with CSS class integration. Behind the scenes, chiccup converts the simplified chiccup-style lists to SXML and then to HTML using sxml-transforms. You can use {{ccup->sxml}} to get the intermediate SXML representation and leverage the full power of the sxml-transforms ecosystem for advanced transformations. <enscript highlight="scheme"> ;; Basic elements `[h1 "Hello World"] ;; CSS classes and IDs - default element is "div" ;; Note: ID (#id) must come after classes `[.container.mx-auto#main "Content"] ;; => <div class="container mx-auto" id="main">Content</div> ;; Attributes with @ syntax `[a (@ (href "/page")) "Link"] `[input (@ (type "text") (name "username"))] ;; Attribute values are automatically HTML-escaped (like React/Vue) `[div (@ (data-config "{\"theme\":\"dark\"}")) "Content"] ;; => <div data-config="{"theme":"dark"}">Content</div> ;; Boolean attributes (no values) `[input (@ (type "checkbox") (checked) (disabled))] ;; => <input type="checkbox" checked disabled> ;; Conditional attributes (let ((is-disabled #t)) `[button (@ (type "submit") ,@(if is-disabled '((disabled)) '())) "Submit"]) ;; => <button type="submit" disabled>Submit</button> ;; Dynamic content `[ul ,@(map (lambda (item) `[li ,item]) items)] ;; Raw HTML (unescaped) `[div (raw "<em>emphasized</em>")] </enscript> === Middleware System <procedure>(use-middleware! middleware-function)</procedure> Install middleware functions that process requests before route handlers. Middleware signature: <enscript highlight="scheme"> (define (my-middleware next) ;; Process request before handler (let ((response (next))) ; Call next middleware or handler ;; Process response after handler response)) </enscript> Middleware can access and modify request parameters using {{(current-params)}} and {{(current-params new-params)}}. ==== Session Middleware <enscript highlight="scheme"> (import sessions) (use-middleware! (session-middleware "secret-key")) ;; Session functions (session-get "key" [default]) (session-set! "key" value) (session-delete! "key") (session-destroy!) </enscript> ==== Body Parser Middleware <enscript highlight="scheme"> (import schematra-body-parser) (use-middleware! (body-parser-middleware)) ;; Automatically parses form data and adds to (current-params) ;; Form fields become symbol keys (post ("/login") (let ((username (alist-ref 'username (current-params)))) ;; Handle login... )) </enscript> === OAuth2 Authentication (Oauthtoothy) Oauthtoothy provides complete OAuth2 authentication integration. <procedure>(oauthtoothy-middleware providers #!key success-redirect save-proc load-proc)</procedure> Creates OAuth2 authentication middleware. '''Parameters:''' * {{providers}}: List of OAuth2 provider configurations * {{success-redirect}}: URL to redirect after successful auth (default: "/") * {{save-proc}}: Function to save user data (optional) * {{load-proc}}: Function to load user data (optional) <parameter>(auth-base-url [url])</parameter> Base URL for OAuth2 callback URLs. <parameter>(current-auth)</parameter> Current user's authentication state. Returns association list with: * {{authenticated?}}: Boolean indicating if user is logged in * {{user-id}}: Unique identifier for the user * Additional user data from provider ==== Provider Configuration Each provider is an association list with required keys: <enscript highlight="scheme"> (define (google-provider #!key client-id client-secret) `((name . "google") (client-id . ,client-id) (client-secret . ,client-secret) (auth-url . "https://accounts.google.com/o/oauth2/auth") (token-url . "https://oauth2.googleapis.com/token") (user-info-url . "https://www.googleapis.com/oauth2/v2/userinfo") (scopes . "profile email") (user-info-parser . ,parse-google-user))) </enscript> ==== Complete OAuth2 Example <enscript highlight="scheme"> (import schematra schematra-session oauthtoothy chiccup) ;; User data parser (define (parse-google-user json-response) `((id . ,(alist-ref 'id json-response)) (name . ,(alist-ref 'name json-response)) (email . ,(alist-ref 'email json-response)))) ;; Install middleware (use-middleware! (session-middleware "secret-key")) (use-middleware! (oauthtoothy-middleware (list (google-provider client-id: (get-environment-variable "GOOGLE_CLIENT_ID") client-secret: (get-environment-variable "GOOGLE_CLIENT_SECRET"))) success-redirect: "/profile")) ;; Protected route (get ("/profile") (let ((auth (current-auth))) (if (alist-ref 'authenticated? auth) (ccup->html `[h1 ,(string-append "Welcome, " (alist-ref 'name auth))]) (redirect "/auth/google")))) ;; Logout (get ("/logout") (session-destroy!) (redirect "/")) (schematra-install) (schematra-start) </enscript> === Examples ==== Simple Web App with Sessions <enscript highlight="scheme"> (import schematra chiccup sessions) (use-middleware! (session-middleware "secret-key")) (get ("/") (let ((user (session-get "username"))) (if user (ccup->html `[h1 ,(format "Welcome back, ~a!" user)]) (redirect "/login")))) (get ("/login") (ccup->html `[form (@ (method "POST") (action "/login")) [input (@ (type "text") (name "username") (placeholder "Username"))] [button "Login"]])) (post ("/login") (let ((username (alist-ref 'username (current-params)))) (session-set! "username" username) (redirect "/"))) (schematra-install) (schematra-start) </enscript> ==== JSON API <enscript highlight="scheme"> (get ("/api/users") (send-json-response `((users . ,(map user->alist (get-all-users))) (count . ,(length (get-all-users)))))) (post ("/api/users") (let ((name (alist-ref 'name (current-params))) (email (alist-ref 'email (current-params)))) (if (and name email) (let ((user-id (create-user! name email))) (send-json-response `((id . ,user-id) (created . #t)) 'created)) (send-json-response '((error . "Invalid input")) 'bad-request)))) </enscript> === License Copyright © 2025 Rolando Abarca. Released under the GNU General Public License v3.0. === Repository & full docs [[https://github.com/schematra/schematra|GitHub Repository]] [[https://github.com/schematra/schematra/blob/main/docs/docs.md|Full docs]]
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you add 24 to 13?