Your own database
api.dataset is Wapka's built-in NoSQL document store. Think MongoDB or Firebase — without any setup. Collections auto-create on first insert. Every document is a JSON object.
Function reference
| Function | Returns | Description |
|---|---|---|
api.dataset.collections() |
string[] | List all collection names |
api.dataset.get(id) |
table or nil | Get a document by ID |
api.dataset.find(collection, filter?) |
{ items, total } |
Query documents |
api.dataset.create(collection, data) |
table | Create a document |
api.dataset.update(id, data, replace?) |
table | Update. replace=true for full replace |
api.dataset.delete(id, permanent?) |
boolean | Delete. permanent=true for hard delete |
api.dataset.restore(id) |
table | Restore soft-deleted document |
api.dataset.purge_collection(name) |
number | Delete all documents in a collection |
Your first document
-- Creates "products" collection automatically
api.dataset.create("products", {
name = "Widget",
price = 9.99,
stock = 100,
tags = { "new", "sale" }
})
No schema definition. No table creation. Just insert.
Finding documents
-- All products
local result = api.dataset.find("products")
-- Filtered
local result = api.dataset.find("products", {
filter = { status = "active" }
})
-- With pagination and sort
local result = api.dataset.find("products", {
filter = { status = "active" },
order = "price_asc", -- cheapest first
page = 1,
limit = 20
})
for _, doc in ipairs(result.items) do
print(doc.data.name, doc.data.price)
end
All query operators
Filters use operators for comparisons. No $ prefix — just the operator name.
| Operator | Meaning | Example |
|---|---|---|
eq |
Equal (default, can omit) | { status = "active" } |
neq |
Not equal | { status = { neq = "deleted" } } |
gt |
Greater than | { price = { gt = 100 } } |
gte |
Greater or equal | { stock = { gte = 1 } } |
lt |
Less than | { age = { lt = 18 } } |
lte |
Less or equal | { score = { lte = 100 } } |
in |
In array | { role = { in = { "admin", "editor" } } } |
nin |
Not in array | { status = { nin = { "banned" } } } |
regex |
Pattern match | { name = { regex = "^A" } } |
like |
SQL LIKE (%) |
{ title = { like = "%lua%" } } |
contains |
JSON_CONTAINS | { tags = { contains = "sale" } } |
null |
Field is null | { deleted_at = { null = true } } |
exists |
Field exists | { email = { exists = true } } |
Real-world query examples
Products: active, in stock, priced $10-$100
local result = api.dataset.find("products", {
filter = {
status = "active",
stock = { gt = 0 },
price = { gte = 10, lte = 100 }
},
order = "price_asc",
limit = 20
})
Users: search by name pattern
local result = api.dataset.find("users", {
filter = {
name = { like = "%" .. search_term .. "%" }
}
})
Posts: newest featured content
local result = api.dataset.find("posts", {
filter = {
status = "published",
featured = true
},
order = "id_desc",
limit = 10
})
Products with multiple tags
local result = api.dataset.find("products", {
filter = {
tags = { contains = "sale" },
category = { in = { "electronics", "computers" } }
}
})
Update: merge vs replace
-- Merge (default): only changes specified fields
api.dataset.update(500, {
data = { price = 14.99, stock = 50 }
})
-- Other fields (name, tags) stay unchanged
-- Full replace: entire document is replaced
api.dataset.update(500, {
data = { name = "New Widget", price = 19.99 }
}, true)
-- All other fields are gone
Soft vs hard delete
-- Soft delete (can be restored)
api.dataset.delete(500)
-- Hard delete (permanent)
api.dataset.delete(500, true)
-- Restore
api.dataset.restore(500)
Purge a collection
-- Delete everything in a collection
api.dataset.purge_collection("temp_logs")
Warning:
purge_collectionis instant and permanent. There is no undo.
Building a product catalog
app:get("/products", function(ctx)
local page = tonumber(req.query.page) or 1
local search = req.query.q
local category = req.query.cat
local filter = { status = "active" }
if search then
filter.name = { like = "%" .. search .. "%" }
end
if category then
filter.category = category
end
local result = api.dataset.find("products", {
filter = filter,
order = "id_desc",
page = page,
limit = 12
})
return ctx:render("products", {
products = result.items,
total = result.total,
page = page
})
end)
app:get("/products/:id", function(ctx)
local result = api.dataset.find("products", {
filter = { id = { eq = tonumber(ctx.params.id) } },
limit = 1
})
if result.total == 0 then
return ctx:error("Product not found", 404)
end
return ctx:render("product", { product = result.items[1] })
end)
Or use the OR condition
-- Find posts that are either featured OR have high views
local result = api.dataset.find("posts", {
filter = {
or = {
{ featured = true },
{ views = { gte = 1000 } }
},
status = "published" -- AND with the OR group
}
})
Note:
api.datasetis the Lua wrapper around the REST API dataset endpoints. For even more advanced querying, see the Dataset Advanced Querying guide.
Next: Explore the Standard Library — 108 utility functions for HTTP, hashing, encoding, and strings.