Tutorial: Building a CRUD API
Learn how to build a complete REST API for managing products using Lua scripts. This tutorial covers folder-based routing, HTTP methods, validation, and error handling.
Prerequisites
- 1. SoliDB server running on localhost:6745
- 2. A database created (e.g., "myapp")
- 3. The SoliDB CLI installed
# Initialize your scripts directory
$ solidb scripts init --db myapp --host localhost --port 6745
# Login to save authentication
$ solidb scripts login
Project Structure
SoliDB uses folder-based routing. The file path determines the API endpoint URL.
scripts/ ├── solidb-scripts.toml # Configuration ├── .env # Environment variables ├── products.lua # GET/POST /api/default/myapp/products ├── products/ │ └── _id.lua # GET/PUT/DELETE /api/default/myapp/products/:id └── tests/ # Test files (not synced) └── products_test.lua
Routing Rules:
- •
products.lua→/api/default/myapp/products - •
products/_id.lua→/api/default/myapp/products/:id(underscore = URL parameter) - • Nested folders create nested routes
Step 1: List & Create Products
Create products.lua to handle listing and creating products.
products.lua
-- @methods GET, POST -- @description List or create products -- @collection products local products = db:collection("products") -- Handle GET: List all products if request.method == "GET" then -- Optional filtering by category local category = request.query.category local results if category then results = products:find({ category = category }) else results = products:find({}) end return { products = results, count = #results } end -- Handle POST: Create a new product if request.method == "POST" then local body = request.body -- Validate required fields if not body.name or body.name == "" then solidb.error("Name is required", 400) end if not body.price or body.price <= 0 then solidb.error("Price must be a positive number", 400) end -- Create the product local product = products:insert({ name = body.name, price = body.price, category = body.category or "uncategorized", description = body.description or "", in_stock = body.in_stock ~= false, created_at = solidb.now() }) -- Return 201 Created solidb.status(201) return { message = "Product created", product = product } end
Step 2: Get, Update & Delete
Create products/_id.lua for single product operations.
products/_id.lua
-- @methods GET, PUT, DELETE -- @description Get, update or delete a product -- @collection products local products = db:collection("products") local id = request.params.id -- Find the product first local product = products:get(id) if not product then solidb.error("Product not found", 404) end -- Handle GET: Return the product if request.method == "GET" then return product end -- Handle PUT: Update the product if request.method == "PUT" then local body = request.body -- Build update object with only provided fields local updates = { updated_at = solidb.now() } if body.name then updates.name = body.name end if body.price then if body.price <= 0 then solidb.error("Price must be positive", 400) end updates.price = body.price end if body.category then updates.category = body.category end if body.description then updates.description = body.description end if body.in_stock ~= nil then updates.in_stock = body.in_stock end local updated = products:update(id, updates) return { message = "Product updated", product = updated } end -- Handle DELETE: Remove the product if request.method == "DELETE" then products:delete(id) return { message = "Product deleted", id = id } end
Step 3: Deploy & Test
Push your scripts to the server and test the API.
Deploy Scripts
# Push all scripts to server
$ solidb scripts push
Created: products.lua -> products [GET, POST]
Created: products/_id.lua -> products/:id [GET, PUT, DELETE]
Created: products/_id.lua -> products/:id [GET, PUT, DELETE]
Test with cURL
# Create a product
$ curl -X POST http://localhost:6745/api/default/myapp/products \
-H "Content-Type: application/json" \
-d '{"name": "Widget", "price": 29.99, "category": "gadgets"}'
# List all products
$ curl http://localhost:6745/api/default/myapp/products
# Get a specific product
$ curl http://localhost:6745/api/default/myapp/products/abc123
# Update a product
$ curl -X PUT http://localhost:6745/api/default/myapp/products/abc123 \
-H "Content-Type: application/json" \
-d '{"price": 24.99}'
# Delete a product
$ curl -X DELETE http://localhost:6745/api/default/myapp/products/abc123
Watch Mode
During development, use watch mode to auto-sync changes:
$ solidb scripts watch
Watching /path/to/scripts for changes...
Step 4: Enhanced Validation
Use SoliDB's built-in validation helpers for cleaner code.
-- Using solidb.validate for cleaner validation local body = request.body -- Validate all fields at once solidb.validate(body, { name = { required = true, type = "string", min_length = 1 }, price = { required = true, type = "number", min = 0.01 }, category = { type = "string" }, in_stock = { type = "boolean" } }) -- Sanitize user input local clean_name = solidb.sanitize.trim(body.name) local slug = solidb.sanitize.slug(body.name)