Skip to content

LilGuy Examples

This guide provides practical examples of building applications with LilGuy. Each example demonstrates different features of the framework and how they work together.

Basic Examples

Hello World

The simplest possible LilGuy application:

-- app.lua
routes["/"] = function(req, res)
    res:render("index.html", {
        message = "Hello from LilGuy!"
    })
end
<!-- templates/index.html -->
{% extends "layout.html" %}

{% block content %}
<h1>{{ message }}</h1>
{% endblock %}

Working with Forms

A simple form that handles both GET and POST requests:

-- app.lua
routes["/contact"] = function(req, res)
    if req.method == "POST" then
        -- Access form data
        local name = req.form.name
        local email = req.form.email
        local message = req.form.message

        -- Store in database
        database:execute([[
            INSERT INTO messages (name, email, message) 
            VALUES (?, ?, ?)
        ]], name, email, message)

        res:redirect("/contact?success=true")
    else
        res:render("contact.html", {
            success = req.query.success
        })
    end
end
<!-- templates/contact.html -->
{% extends "layout.html" %}

{% block content %}
<article>
    {% if success %}
    <div class="alert alert-success">
        Message sent successfully!
    </div>
    {% endif %}

    <form method="POST">
        <label for="name">Name</label>
        <input type="text" id="name" name="name" required>

        <label for="email">Email</label>
        <input type="email" id="email" name="email" required>

        <label for="message">Message</label>
        <textarea id="message" name="message" required></textarea>

        <button type="submit">Send Message</button>
    </form>
</article>
{% endblock %}

Database Examples

Todo List Application

A complete todo list showing database operations:

-- app.lua

-- Initialize database table
database:execute([[
    CREATE TABLE IF NOT EXISTS todos (
        id INTEGER PRIMARY KEY,
        task TEXT NOT NULL,
        completed BOOLEAN DEFAULT 0,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
]])

-- List todos
routes["/todos"] = function(req, res)
    local todos = database:query([[
        SELECT * FROM todos ORDER BY created_at DESC
    ]])

    res:render("todos.html", { todos = todos })
end

-- Add new todo
routes["/todos/add"] = function(req, res)
    if req.method == "POST" then
        local task = req.form.task
        database:execute(
            "INSERT INTO todos (task) VALUES (?)",
            task
        )
        res:redirect("/todos")
    end
end

-- Toggle todo completion
routes["/todos/toggle/:id"] = function(req, res)
    database:execute([[
        UPDATE todos 
        SET completed = NOT completed 
        WHERE id = ?
    ]], req.params.id)

    res:redirect("/todos")
end
<!-- templates/todos.html -->
{% extends "layout.html" %}

{% block content %}
<article>
    <header>
        <h1>Todo List</h1>
    </header>

    <form action="/todos/add" method="POST" class="grid">
        <input type="text" name="task" placeholder="What needs to be done?" required>
        <button type="submit">Add Todo</button>
    </form>

    <ul>
        {% for todo in todos %}
        <li>
            <a href="/todos/toggle/{{ todo.id }}" 
               class="{% if todo.completed %}completed{% endif %}">
                {{ todo.task }}
            </a>
        </li>
        {% endfor %}
    </ul>
</article>
{% endblock %}

Dynamic UI with HTMX

Live Search Example

Demonstrates real-time search using HTMX:

-- app.lua
routes["/search"] = function(req, res)
    res:render("search.html")
end

routes["/search/results"] = function(req, res)
    local query = req.query.q or ""
    local results = database:query([[
        SELECT * FROM items 
        WHERE name LIKE ? 
        LIMIT 10
    ]], "%" .. query .. "%")

    res:render("search_results.html", { results = results })
end
<!-- templates/search.html -->
{% extends "layout.html" %}

{% block content %}
<article>
    <input type="search" 
           name="q" 
           placeholder="Search..."
           hx-get="/search/results"
           hx-trigger="input changed delay:500ms"
           hx-target="#results">

    <div id="results">
        <!-- Results will be loaded here -->
    </div>
</article>
{% endblock %}
<!-- templates/search_results.html -->
{% if results|length > 0 %}
    <ul>
        {% for item in results %}
        <li>{{ item.name }}</li>
        {% endfor %}
    </ul>
{% else %}
    <p>No results found</p>
{% endif %}

Global State Management

Example using LilGuy's global state feature:

-- app.lua

-- Initialize counter in global state
local counters = global.counters
counters[1] = { count = 0 }

routes["/counter"] = function(req, res)
    if req.method == "POST" then
        local current = counters[1].count
        counters[1] = { count = current + 1 }

        -- If this is an HTMX request, just return the counter value
        if req.headers["HX-Request"] then
            res:render("counter_value.html", { count = current + 1 })
            return
        end
    end

    res:render("counter.html", { count = counters[1].count })
end
<!-- templates/counter.html -->
{% extends "layout.html" %}

{% block content %}
<article>
    <h1>Counter Example</h1>

    <div id="counter">
        {% include "counter_value.html" %}
    </div>

    <button hx-post="/counter"
            hx-target="#counter">
        Increment
    </button>
</article>
{% endblock %}
<!-- templates/counter_value.html -->
<p>Current count: {{ count }}</p>

Custom Error Pages

Customizing the 404 page:

-- app.lua
function not_found(req, res)
    res.status = 404
    res:render("404.html", {
        path = req.path
    })
end
<!-- templates/404.html -->
{% extends "layout.html" %}

{% block content %}
<article>
    <header>
        <h1>Page Not Found</h1>
    </header>

    <p>Sorry, the page <code>{{ path }}</code> doesn't exist.</p>
    <p><a href="/">Return to homepage</a></p>
</article>
{% endblock %}

Working with JSON APIs

Creating a simple JSON API:

-- app.lua
routes["/api/items"] = function(req, res)
    if req.method == "POST" then
        local item = json.decode(req.body)

        database:execute([[
            INSERT INTO items (name, description) 
            VALUES (?, ?)
        ]], item.name, item.description)

        res:json({ status = "success" })
    else
        local items = database:query("SELECT * FROM items")
        res:json(items)
    end
end

File Upload Example

Handling file uploads:

-- app.lua
routes["/upload"] = function(req, res)
    if req.method == "POST" then
        local file = req.files.photo

        if file then
            -- Save file info to database
            database:execute([[
                INSERT INTO uploads (
                    filename, 
                    content_type, 
                    size
                ) VALUES (?, ?, ?)
            ]], file.filename, file.content_type, file.size)

            -- Move file to permanent storage
            file:save("uploads/" .. file.filename)

            res:redirect("/upload?success=true")
        end
    end

    res:render("upload.html", {
        success = req.query.success
    })
end
<!-- templates/upload.html -->
{% extends "layout.html" %}

{% block content %}
<article>
    <h1>File Upload</h1>

    {% if success %}
    <div role="alert">File uploaded successfully!</div>
    {% endif %}

    <form method="POST" enctype="multipart/form-data">
        <label for="photo">Choose a photo:</label>
        <input type="file" 
               id="photo" 
               name="photo" 
               accept="image/*" 
               required>

        <button type="submit">Upload</button>
    </form>
</article>
{% endblock %}

These examples demonstrate many of LilGuy's core features, including:

  • Routing and request handling
  • Template rendering
  • Database operations
  • Form processing
  • HTMX integration
  • Global state management
  • File uploads
  • JSON APIs
  • Error handling

Each example is designed to be practical and reusable, serving as a starting point for your own applications.