Astra Trails

June 8, 2026 ยท View on GitHub

Examples, hacks, and tricks for Astra - a web server runtime for Lua.

  • Middleware: Introduces patterns for middleware.
  • Routes Grouping: A DSL for registering a group of routes in one place.
  • Templating with Lustache: A full overview of Mustache templates in pure Lua.
  • LugSQL: Converts parameterized SQL into Lua functions.
  • Preman: A simple API client embedded within the app.

Confirmed to work with Astra v0.48.0

Check out main.lua for an example app.

$ git clone https://github.com/0riginaln0/astra-trails.git
$ cd astra-trails
$ astra export
$ astra run main.lua 
Generated sql/queries.lua with 9 queries.
        GET /
        GET /hello
        GET /tier-list
        GET /hype
       POST /hype/like
        GET /guestbook
       POST /guestbook
        GET /api/guestbook
       POST /api/guestbook
        GET /health
STATIC_FILE /preman
 STATIC_DIR /static
        GET /today
        GET /games/guess_number
       POST /games/guess_number
Server uses:    Lua 5.5
Running on http://127.0.0.1:8080

A (not so) short snippet:

local today_app = Routes {
  { GET, "/today", function() return "Today is "..os.date() end}
}

local app = Routes {
  middleware = chain { ctx, logger },

  { GET, "/", html(homepage) },
  { GET, "/hello", function() return "hello world" end },

  scope {
    middleware = html,
    { GET,  "/tier-list", tier_list },
    { GET,  "/hype",      hype_handler },
    { POST, "/hype/like", hype_like }
  },

  { GET,  "/guestbook", html(guestbook_page) },
  { POST, "/guestbook", post_guestbook_form },

  scope "/api" {
    { GET,  "/guestbook", get_api_guestbook },
    { POST, "/guestbook", post_api_guestbook },
  },

  { GET, "/health", function() return { status = "UP" } end },

  { STATIC_FILE, "/preman", "vendor/preman.html" },
  { STATIC_DIR,  "/static", "static" },

  today_app,

  scope "/games" {
    scope "/guess_number" {
      require("guess_number_game")
    }
  },

  fallback = chain { html } (function() return "Page not Found" end)
}

app(server)

require("print-server-info")(server)
server:run()

The same snippet but in YueScript

today_app = Routes
  - [ GET, "/today", () -> "Today is #{os.date()}"]

app = Routes {
  middleware: chain [ ctx, logger ]

  [ GET, "/", html homepate ]
  [ GET, "/hello", () -> "Hello world" ]

  scope {
    middleware: html
    [ GET,  "/tier-list", tier_list ]
    [ GET,  "/hype",      hype_handler ]
    [ POST, "/hype/like", hype_like ]
  }

  [ GET,  "/guestbook", html guestbook_page ]
  [ POST, "/guestbook", post_guestbook_form ]

  scope("api") {
    [ GET,  "/guestbook", get_api_guestbook ]
    [ POST, "/guestbook", post_api_guestbook ]
  }

  [ GET, "/health", () -> status: "UP" ]
  
  [ STATIC_FILE, "/preman", "vendor/preman.html" ]
  [ STATIC_DIR,  "/static", "static" ]

  today_app

  scope("/games")
    - scope("/guess_number")
      - require "guess_number_page"

  fallback: html () -> "Page not found"
}

app server

require("print-server-info") server
server\run!