Implementing HTTP actions
This page explains how to configure and implement an HTTP action.When you configure a reaction definition with an HTTP action, Serialized will send a HTTP request to the configured URL when the reaction is triggered. The request will contain the event data and metadata. The receiving service can then use the event data to, for example, continue a long-running process or send notifications.
Configuring an HTTP action is simple. You specify the following in the action
for the reaction definition:
- actionType - Should be set to
HTTP_POST
. - targetUri - The URL for the endpoint of the handler implementation.
- headers - An (optional) map of headers to send with the request.
- signingSecret - A secret that is used to sign the request. See Request signing for more information.
Request payload
Serialized HTTP reaction types trigger a configured URL with an HTTP POST
request. The request body is a JSON object
containing the following properties:
Field | Data type | Description |
---|---|---|
event | Object | The event structure |
metadata | Object | The event metadata |
The event
object will contain the following properties:
Field | Data type | Description | |
---|---|---|---|
eventType | Required | String | The event type |
eventId | Required | String | The event ID |
data | Object | The event data, consisting of the entire event data structure | |
encryptedData | String | The encrypted data, if saved |
The metadata
object will contain the following properties:
Field | Data type | Description | |
---|---|---|---|
aggregateId | Required | String | The aggregate ID |
timestamp | Required | Number | The timestamp of when the event was stored |
sequenceNumber | Required | Number | The sequence number that the event was part of in the feed |
tenantId | String | The tenantId for the projection |
Below is an example of an HTTP request payload for an OrderShipped
event:
{ "event": { "eventType": "OrderShipped", "eventId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "data": { "orderId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "orderItems": [ { "productId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "quantity": 1 } ] } }, "metadata": { "aggregateId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "timestamp": 1589388800000, "sequenceNumber": 1 }}
Endpoint response
The receiving service should respond with an HTTP
status code of 200
if the request was handled successfully. If any
other status code is returned, Serialized will retry the request. Any response body that is returned from the endpoint
will be ignored.
Configuring an HTTP action
To configure an HTTP action you create a projection definition pointing out the URI
of the endpoint that should
process the event. The endpoint must use HTTPS
and be publicly available. Serialized will send a POST
request to
that endpoint when an event that matches the handler configuration is processed.
Below is an example of a projection definition with an HTTP action that will send a request to the configured endpoint
on the URL https://example.com/reactions/notify-order-shipped
when an event of type OrderShipped
is processed:
var functionUri = URI.create("https://example.com/reactions/notify-order-shipped");var request = ReactionDefinition.newReactionDefinition("notify-on-order-shipped") .feed("order") .reactOnEventType("OrderShipped") .action(httpAction(functionUri).build()) .build();reactionClient.createDefinition(request);
Implementing an HTTP action endpoint
To implement an API that receives HTTP
reaction requests, you need to implement an HTTPS
endpoint for POST
that
accepts application/json
body. The endpoint must be internet-accessible. Since the endpoint is a regular HTTP API
endpoint, it is possible to implement it using any programming language, framework or platform.
Below is an example of an implementation of a reaction endpoint that handles an OrderShipped
event in Java (
Dropwizard) and TypeScript (AWS Lambda):
package io.serialized.saas.console.api;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.ws.rs.Consumes;import javax.ws.rs.POST;import javax.ws.rs.Path;import javax.ws.rs.Produces;import javax.ws.rs.core.Response;import static javax.ws.rs.core.MediaType.APPLICATION_JSON;@Path("order-placed-reaction")@Produces(APPLICATION_JSON)@Consumes(APPLICATION_JSON)public class ReactionResource { private final Logger log = LoggerFactory.getLogger(getClass()); public record ReactionMetadata(String aggregateId, long createdAt, long updatedAt, long sequenceNumber) { } public record Event<T>(String eventType, T data) { } public record OrderShipped(String orderId, long orderAmount, String customerId) { } public record OrderReactionRequest<E>(Event<E> event, ReactionMetadata metadata) { } @POST @Path("notify-order-shipped") public Response handleOrderShipped(OrderReactionRequest<OrderShipped> request) { try { Event<OrderShipped> event = request.event(); log.info("Order was shipped: {}", event); // Implement your logic here return Response.ok().build(); } catch (Exception e) { return Response.serverError().build(); } }}
Securing the endpoint
The endpoint should be secured with a HTTPS
certificate. To further secure request to the endpoint, you can verify the
request signature. To learn more about request signing, see our section
on request signing.
It is also possible to further secure the endpoint
using Basic Authentication by including the Authorization
HTTP header in the reaction definition.
Setting additional headers
If you want to more information to the endpoint, regardless of which event is processed, you can set additional headers in the projection definition. These headers will be included in all request to the endpoint.