Getting Started
Installation
- Set environment variables:
export SIMPLE_WEB=/path/to/simple_web export SIMPLE_JSON=/path/to/simple_json export SIMPLE_PROCESS=/path/to/simple_process - Add to your ECF file:
<library name="simple_web" location="$SIMPLE_WEB/simple_web.ecf"/>
Dependencies
- EiffelStudio 25.02+
curl_http_clientlibrary (included with EiffelStudio)curl.exeon PATH (for hybrid client)- EWF libraries (wsf, httpd, default_standalone)
simple_jsonlibrarysimple_processlibrary
HTTP Client
Basic Requests
local
client: SIMPLE_WEB_CLIENT
response: SIMPLE_WEB_RESPONSE
do
create client.make
-- GET request
response := client.get ("https://api.example.com/users")
-- POST with body
response := client.post ("https://api.example.com/users", "name=Alice")
-- POST with JSON
response := client.post_json ("https://api.example.com/users",
'{"name": "Alice", "email": "alice@example.com"}')
-- PUT request
response := client.put ("https://api.example.com/users/1", '{"name": "Bob"}')
-- DELETE request
response := client.delete ("https://api.example.com/users/1")
end
Handling Responses
local
response: SIMPLE_WEB_RESPONSE
do
response := client.get ("https://api.example.com/users")
-- Check status
if response.is_success then
print ("Status: " + response.status_code.out)
print ("Body: " + response.body)
elseif response.is_client_error then
print ("Client error: " + response.status_code.out)
elseif response.is_server_error then
print ("Server error: " + response.status_code.out)
end
-- Access headers
if attached response.header ("Content-Type") as ct then
print ("Content-Type: " + ct)
end
end
Fluent Request Builder
local
request: SIMPLE_WEB_REQUEST
response: SIMPLE_WEB_RESPONSE
do
-- Build complex request
create request.make_post ("https://api.example.com/data")
request
.with_bearer_token ("your-jwt-token")
.with_json_body ('{"key": "value"}')
.with_header ("X-Custom-Header", "custom-value")
.with_timeout (30000)
.do_nothing
response := client.execute (request)
end
Hybrid Client (for Localhost)
Use the hybrid client when making POST requests to localhost services. It works around a known issue in libcurl.
local
client: SIMPLE_WEB_HYBRID_CLIENT
do
create client.make
-- Uses curl.exe process for POST (reliable)
response := client.post_json ("http://localhost:11434/api/generate",
'{"model": "llama3", "prompt": "Hello", "stream": false}')
-- Uses libcurl for GET (fast)
response := client.get ("http://localhost:11434/api/tags")
end
AI Clients
Ollama Client
local
ollama: SIMPLE_WEB_OLLAMA_CLIENT
response: SIMPLE_WEB_RESPONSE
do
create ollama
-- Generate completion
response := ollama.generate ("llama3", "Why is the sky blue?")
print (response.body)
-- Chat conversation
response := ollama.chat ("llama3",
<<["user", "Hello"],
["assistant", "Hi! How can I help?"],
["user", "What's the weather?"]>>)
-- List available models
response := ollama.list_models
-- Pull a model
response := ollama.pull ("mistral")
end
Claude Client
local
claude: SIMPLE_WEB_CLAUDE_CLIENT
do
create claude.make ("your-anthropic-api-key")
response := claude.message ("claude-3-sonnet-20240229",
"Explain quantum computing in simple terms")
print (response.body)
end
OpenAI Client
local
openai: SIMPLE_WEB_OPENAI_CLIENT
do
create openai.make ("your-openai-api-key")
response := openai.chat ("gpt-4",
<<["user", "Hello, GPT!"]>>)
end
Grok Client
local
grok: SIMPLE_WEB_GROK_CLIENT
do
create grok.make ("your-xai-api-key")
response := grok.chat ("grok-1",
<<["user", "What's trending today?"]>>)
end
HTTP Server
Basic Server Setup
class
MY_API
create
make
feature {NONE}
make
local
server: SIMPLE_WEB_SERVER
do
create server.make (8080)
-- Register routes
server.on_get ("/", agent handle_root)
server.on_get ("/api/users", agent handle_users)
server.on_get ("/api/users/{id}", agent handle_user)
server.on_post ("/api/users", agent create_user)
server.on_put ("/api/users/{id}", agent update_user)
server.on_delete ("/api/users/{id}", agent delete_user)
-- Start (blocking)
server.start
end
feature -- Handlers
handle_root (req: SIMPLE_WEB_SERVER_REQUEST; res: SIMPLE_WEB_SERVER_RESPONSE)
do
res.send_text ("Welcome to My API")
end
handle_users (req: SIMPLE_WEB_SERVER_REQUEST; res: SIMPLE_WEB_SERVER_RESPONSE)
do
res.send_json ('[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]')
end
handle_user (req: SIMPLE_WEB_SERVER_REQUEST; res: SIMPLE_WEB_SERVER_RESPONSE)
do
-- Access path parameter
if attached req.path_parameter ("id") as id then
res.send_json ('{"id": ' + id + ', "name": "Alice"}')
else
res.set_status (404)
res.send_json ('{"error": "User not found"}')
end
end
create_user (req: SIMPLE_WEB_SERVER_REQUEST; res: SIMPLE_WEB_SERVER_RESPONSE)
do
-- Access request body
print ("Body: " + req.body)
res.set_status (201)
res.send_json ('{"id": 3, "created": true}')
end
end
Request Object
-- Path parameters: /users/{id} -> req.path_parameter ("id")
if attached req.path_parameter ("id") as id then
-- Use id
end
-- Query parameters: /search?q=test -> req.query_parameter ("q")
if attached req.query_parameter ("q") as query then
-- Use query
end
-- Request body
body := req.body
-- Headers
if attached req.header ("Authorization") as auth then
-- Use auth
end
-- HTTP method
method := req.method -- "GET", "POST", etc.
Response Object
-- Set status code
res.set_status (200)
res.set_status (201) -- Created
res.set_status (404) -- Not Found
-- Send responses
res.send_text ("Plain text response")
res.send_json ('{"key": "value"}')
res.send_json_object (my_json_object)
res.send_html ("<h1>Hello</h1>")
-- Set headers
res.set_header ("X-Custom", "value")
-- Redirect
res.redirect ("/new-location")
Middleware
Using Middleware
local
server: SIMPLE_WEB_SERVER
do
create server.make (8080)
-- Add middleware (order matters)
server.use (create {SIMPLE_WEB_LOGGING_MIDDLEWARE}.make)
server.use (create {SIMPLE_WEB_CORS_MIDDLEWARE}.make)
server.use (create {SIMPLE_WEB_AUTH_MIDDLEWARE}.make_bearer ("secret"))
-- Register routes
server.on_get ("/api/data", agent handle_data)
server.start
end
CORS Middleware
-- Allow all origins
server.use (create {SIMPLE_WEB_CORS_MIDDLEWARE}.make)
-- Allow specific origins
server.use (create {SIMPLE_WEB_CORS_MIDDLEWARE}.make_with_origins (
<<"http://localhost:3000", "https://myapp.com">>))
Authentication Middleware
-- Bearer token auth
server.use (create {SIMPLE_WEB_AUTH_MIDDLEWARE}.make_bearer ("your-secret"))
-- API key auth (header: X-API-Key)
server.use (create {SIMPLE_WEB_AUTH_MIDDLEWARE}.make_api_key ("your-api-key"))
-- Basic auth
server.use (create {SIMPLE_WEB_AUTH_MIDDLEWARE}.make_basic ("user", "pass"))
Custom Middleware
class
MY_MIDDLEWARE
inherit
SIMPLE_WEB_MIDDLEWARE
feature
process (req: SIMPLE_WEB_SERVER_REQUEST; res: SIMPLE_WEB_SERVER_RESPONSE): BOOLEAN
-- Return True to continue, False to stop
do
-- Pre-processing
print ("Request: " + req.method + " " + req.path)
-- Continue to next middleware/handler
Result := True
-- Or stop the chain:
-- res.set_status (403)
-- res.send_json ('{"error": "Forbidden"}')
-- Result := False
end
end
Security
Input Sanitization
local
sanitizer: SIMPLE_WEB_INPUT_SANITIZER
do
create sanitizer
-- XSS prevention
safe_html := sanitizer.escape_html (user_input)
-- Path traversal prevention
if sanitizer.is_safe_path (file_path) then
-- Safe to use
end
-- Header injection prevention
safe_header := sanitizer.sanitize_header (header_value)
-- SQL injection prevention (basic)
safe_value := sanitizer.escape_sql (user_value)
end
JSON Handling
local
json: SIMPLE_JSON
do
create json
-- Parse request body as JSON
if attached json.parse (req.body) as v then
if v.is_object then
if attached v.as_object.string_item ("name") as name then
-- Use name
end
end
end
-- Build JSON response
response_obj := json.new_object
.put_string ("status", "success")
.put_integer ("id", new_id)
res.send_json_object (response_obj)
end