String manipulation made safe
The str library handles common string operations. All functions are UTF-8 safe — they work correctly with international text, emoji, and special characters.
Function reference
| Function | Example | Result |
|---|---|---|
str.split(s, sep) |
str.split("a,b,c", ",") |
{ "a", "b", "c" } |
str.trim(s) |
str.trim(" hello ") |
"hello" |
str.contains(s, sub) |
str.contains("hello world", "ell") |
true |
str.starts_with(s, p) |
str.starts_with("/api/users", "/api") |
true |
str.ends_with(s, suf) |
str.ends_with("file.lua", ".lua") |
true |
str.limit(s, n, end?) |
str.limit("hello world", 8) |
"hello wo..." |
str.replace(s, old, new) |
str.replace("hello world", "world", "Lua") |
"hello Lua" |
str.lcfirst(s) |
str.lcfirst("Hello") |
"hello" |
str.ucfirst(s) |
str.ucfirst("hello") |
"Hello" |
str.ucwords(s) |
str.ucwords("hello world") |
"Hello World" |
str.split — Parse lists
-- Parse comma-separated tags
local tags = str.split(req.post.tags, ",")
for _, tag in ipairs(tags) do
tag = str.trim(tag) -- clean whitespace
table.insert(clean_tags, str.lcfirst(tag))
end
-- Split a path
local parts = str.split(req.path, "/")
-- "/blog/2026/hello" → { "", "blog", "2026", "hello" }
str.contains — Quick checks
-- Check if a URL is an API path
if str.contains(req.path, "/api/") then
-- apply rate limiting
end
-- Check user agent
if str.contains(req.headers["user-agent"] or "", "bot") then
log.info("Bot detected: " .. env.remote_ip)
end
str.starts_with / str.ends_with — Pattern matching
-- Route guards
if str.starts_with(req.path, "/admin") and not env.is_admin then
return ctx:error("Forbidden", 403)
end
-- File type check
if str.ends_with(filename, ".lua") then
-- handle Lua file
elseif str.ends_with(filename, ".md") then
-- handle markdown
end
str.replace — Find and replace
-- Clean user input
local clean = str.replace(raw_input, "<script>", "")
clean = str.replace(clean, "</script>", "")
-- Template-like substitution
local template = "Hello, {name}! Your order #{id} is ready."
local message = str.replace(template, "{name}", user.name)
message = str.replace(message, "{id}", tostring(order.id))
str.limit — Truncate for previews
-- Blog post excerpt
local excerpt = str.limit(post.content, 200)
-- → first 200 chars + "..."
-- Custom ending
local title = str.limit(long_title, 50, "…")
str.ucfirst / str.lcfirst — Case normalization
-- Normalize user input
local name = str.ucfirst(str.trim(req.post.name))
-- "john" → "John"
-- " ALICE " → "Alice"
-- Generate display labels
local label = str.ucwords(str.replace(status, "_", " "))
-- "order_shipped" → "Order Shipped"
Practical: parsing a tag input field
-- " Lua, Wapka, API, tutorial " → { "lua", "wapka", "api", "tutorial" }
function parseTags(input)
local tags = {}
for _, tag in ipairs(str.split(input, ",")) do
local clean = str.trim(str.lcfirst(tag))
if clean ~= "" then
table.insert(tags, clean)
end
end
return tags
end
app:post("/posts/create", function(ctx)
local tags = parseTags(req.post.tags)
-- tags = { "lua", "wapka", "api", "tutorial" }
end)
Next: Logging — debug and monitor your app.