ExpressLite Extension V1
ExpressLite is a non-visible extension that brings a lightweight HTTP server to Android, with an Express.js-like workflow: register routes (GET/POST/PUT/DELETE/ALL), capture path params (/hello/:name
), read query, headers, and body (text/JSON), enable CORS, and send responses from Blocks.
Why for Kodular/AI2?
- No external libraries (pure
ServerSocket
) β compiles on Kodular/AI2 without JARs.- Java 7 compatible (no lambdas/Streams).
- Great for LAN/edge debugging, local webhooks, device-to-device APIs.
Features
- Express-style route patterns:
/path/:param
and wildcard*
- Events: OnRouteRequest and OnNotFound
- Respond with Text / JSON / Binary (Base64)
- CORS with preflight OPTIONS
- Properties: Port, LocalURL(), RequestTimeoutMs
- Zero external dependencies
Installation
- Import ExpressLite.aix into your Kodular/AI2 project.
- Drag the ExpressLite non-visible component to the screen.
- Ensure the app has INTERNET permission (enabled by default).
Quick Start
Blocks outline:
-
Set
Port = 8080
-
Call
StartServer()
β readLocalURL()
(e.g.http://192.168.1.5:8080
) -
Register routes:
AddRoute("hello", "GET", "/hello/:name")
AddRoute("echo", "POST", "/echo")
-
Handle
OnRouteRequest
β reply usingSendTextResponse
/SendJsonResponse
.
Test from PC (replace IP with LocalURL()
):
# GET + path param & query
curl "http://192.168.1.5:8080/hello/Rasi?lang=id"
# POST JSON
curl -X POST "http://192.168.1.5:8080/echo" \
-H "Content-Type: application/json" \
-d '{"msg":"Hello world"}'
Simplest Example: return "hello"
at "/"
Blocks (pseudo):
when Screen.Initialize
set ExpressLite.Port to 8080
call ExpressLite.StartServer
call ExpressLite.AddRoute("root", "ALL", "/")
when ExpressLite.OnRouteRequest(routeName, requestId, method, path, paramsJson, queryJson, headersJson, bodyText)
if routeName = "root"
call ExpressLite.SendTextResponse(requestId, 200, "text/plain; charset=utf-8", "hello")
Test:
curl "http://<PHONE-IP>:8080/"
# -> hello
API Reference
Properties
Name | Type | Description |
---|---|---|
Port |
Number | Server port. Set before StartServer() . |
LogEnabled |
Boolean | Enable/disable internal logs (optional). |
RequestTimeoutMs |
Number | Time limit to wait for a Block response (default 30000 ms). |
LocalURL() |
Text | Current base URL (e.g., http://192.168.1.5:8080 ). |
Functions
Function | Parameters | Description |
---|---|---|
StartServer() |
β | Starts the server. Returns base URL (Text). |
StopServer() |
β | Stops the server if running. |
SetCORS(enabled, allowOrigin, allowMethods, allowHeaders) |
Boolean, Text, Text, Text | Enable/configure CORS. Default: enabled with * and OPTIONS preflight. |
AddRoute(routeName, method, pathPattern) |
Text, Text, Text | Add a route. method : GET /POST /PUT /DELETE /ALL . pathPattern supports :param & * . Returns effective routeName . |
RemoveRoute(routeName) |
Text | Remove a route by name. |
ClearRoutes() |
β | Remove all routes. |
SendTextResponse(requestId, status, contentType, body) |
Text, Number, Text, Text | Send a text response. |
SendJsonResponse(requestId, status, jsonText) |
Text, Number, Text | Send a JSON response (Content-Type: application/json; charset=utf-8 ). |
SendBinaryResponse(requestId, status, mimeType, base64Data) |
Text, Number, Text, Text | Send a binary response from Base64. |
Events
Event | Parameters | Description |
---|---|---|
OnRouteRequest |
routeName , requestId , method , path , paramsJson , queryJson , headersJson , bodyText |
Fired when a route matches. Use requestId with a Send...Response function to reply. |
OnNotFound |
requestId , method , path |
Fired when no route matches (404). You may reply manually: SendTextResponse(requestId, 404, "text/plain", "Not Found") . |
Notes about event params
paramsJson
β extracted from:param
inpathPattern
(e.g.,/user/:id
β{"id":"42"}
).queryJson
β query map with array values per key (supports?x=1&x=2
).headersJson
β headers map (single value per key).bodyText
β request body as string (client must sendContent-Length
; chunked is not supported).
Route Pattern Examples
Pattern | Matches | paramsJson |
---|---|---|
/hello/:name |
/hello/Ana |
{"name":"Ana"} |
/file/* |
/file/a/b/c.txt |
{} (use full path if needed) |
/u/:id/profile |
/u/42/profile |
{"id":"42"} |
Security & CORS
Enable CORS if you call the server from a web page:
SetCORS(true, "*", "GET,POST,PUT,DELETE,OPTIONS", "Content-Type, Authorization")
For private LAN tools, *
is convenient. For stricter setups, whitelist origins and headers.
Troubleshooting
-
Cannot access from PC
Ensure phone and PC are on the same Wi-Fi/LAN. Check firewall/router/VPN. -
504 Timeout
OnRouteRequest
fired, but noSend...Response
was called withinRequestTimeoutMs
. Ensure you respond. -
Empty body
Server reads body viaContent-Length
(no chunked support). Make sure clients send the header and proper length. -
Internet/WAN exposure
Not recommended. This is a lightweight LAN server. If you must, put a reverse proxy/TLS in front and implement authentication.
Limitations
- No built-in TLS (HTTPS).
- No keep-alive / chunked transfer (one request per connection β simple and stable).
- Large uploads/multipart not optimized yet.
- Not intended for high traffic or public Internet exposure.
Download
com.rasitech.expresslite.aix (31.6 KB)
Changelog
- v2 β Self-contained server (ServerSocket), CORS preflight, JSON helpers, this EN doc.
- v1 β Initial NanoHTTPD-based prototype (replaced to remove external dependencies).
License
Sample code & docs: Apache-2.0. Feel free to use and modify with reasonable attribution.
FAQ
Q: Can I use it as a local webhook endpoint?
A: Yes. Run the server on the phone, keep clients on the same LAN, or use a tunnel on a separate machine (mind the security).
Q: Can it serve static files?
A: Current focus is replying from Blocks (Text/JSON/Binary). Static files can be added in a future version if thereβs interest.
Q: WebSocket support?
A: Not yet. The scope is a simple HTTP request/response server. If the community needs it, it can be explored.