Write your first function¶
Tutorial: build a working composable function from scratch, wire it to HTTP, and call it programmatically.
At a glance
- What — write a
@PreLoad-annotated class, expose it over HTTP inrest.yaml, and call it from another function viaPostOffice.- Why it matters — every higher-layer concept (flow task, graph skill) is still this same atom; mastering Layer 1 is the foundation.
- For developers new to Mercury or building their first composable feature.
A composable function is plain Java — no framework-specific base class, no dependency injection
container, no coupling to other functions. You pick the coding style that suits the problem:
sequential, object-oriented, or reactive. The framework constrains only coupling (functions
communicate through named routes and EventEnvelope, never by direct call).
1. Define the function¶
Create a class that implements TypedLambdaFunction<I, O> and annotate it with @PreLoad.
import org.platformlambda.core.annotations.PreLoad;
import org.platformlambda.core.models.TypedLambdaFunction;
import java.util.Map;
@PreLoad(route = "hello.function", instances = 10)
public class HelloFunction implements TypedLambdaFunction<Map<String, Object>, Map<String, Object>> {
@Override
public Map<String, Object> handleEvent(Map<String, String> headers,
Map<String, Object> input,
int instance) throws Exception {
var name = input.getOrDefault("name", "world").toString();
return Map.of("message", "Hello, " + name + "!");
}
}
Three things to notice:
| Part | What it does |
|---|---|
@PreLoad(route = "hello.function") |
Registers the function on the event bus under this name. Other components reach it only by this string. |
instances = 10 |
How many concurrent workers the platform starts for this function. |
TypedLambdaFunction<I, O> |
Typed input/output. Use Map<String, Object> for flexible key-value data, or a PoJo for a strict interface contract. |
For an untyped function that works directly with the raw EventEnvelope (useful for
pass-through or routing logic), implement LambdaFunction instead:
@PreLoad(route = "hello.passthrough", instances = 5)
public class HelloPassthrough implements LambdaFunction {
@Override
public Object handleEvent(Map<String, String> headers,
Object body,
int instance) throws Exception {
return body; // echo the raw body back
}
}
2. Expose it over HTTP¶
Open (or create) src/main/resources/rest.yaml and add an entry under the rest: section:
The service: value is the route name from @PreLoad. That string is the only link between
the HTTP layer and your function — no import, no wiring code.
Enable REST automation in application.properties:
3. Call it from another function¶
Inside any other function, use PostOffice to send a request to hello.function by route name:
import org.platformlambda.core.system.PostOffice;
import org.platformlambda.core.models.EventEnvelope;
// inside handleEvent(...)
var po = new PostOffice(headers, instance);
EventEnvelope response = po.request(
new EventEnvelope().setTo("hello.function").setBody(Map.of("name", "Mercury")),
5000 // timeout in ms
).get();
var result = response.getBody(); // Map containing {"message": "Hello, Mercury!"}
po.request(...).get() reads as synchronous but never blocks a platform thread — the virtual
thread is parked while waiting, so you get sequential code at reactive throughput.
4. Run and test¶
Build and start the application (the examples are not part of the top-level reactor build, so build this one on its own; adjust the version to match your build):
Note:
x.y.zdenotes the current Mercury version shown in the rootpom.xml.
Test with curl:
# GET — query parameter mapped automatically
curl "http://127.0.0.1:8085/api/hello?name=World"
# POST — JSON body
curl -s -X POST http://127.0.0.1:8085/api/hello \
-H "content-type: application/json" \
-d '{"name": "Mercury"}'
Expected response:
5. Add it to a flow (optional next step)¶
Once the function works standalone, you can orchestrate it as a task in an Event Script flow without changing a single line of its code:
# flows/hello-flow.yml
tasks:
- name: greet
input:
- input.body.name -> name
process: "hello.function"
output:
- result.message -> model.greeting
execution: end
The route name hello.function is the only connection. See
Composable Orchestration for the full flow model.
See also¶
- Function Execution Strategies — virtual vs. kernel threads,
Mono/Flux, authentication functions, serialization. - REST Automation — full
rest.yamlreference: CORS, headers, authentication, static content. - Event Script Syntax — orchestrate this function as a flow task.
- API Overview —
PostOffice,Platform,EventEnvelopeAPI reference.