Documentation
Welcome to docs|Reactions|Implementing HTTP actions

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:

FieldData typeDescription
eventObjectThe event structure
metadataObjectThe event metadata

The event object will contain the following properties:

FieldData typeDescription
eventType
Required
StringThe event type
eventId
Required
StringThe event ID
dataObjectThe event data, consisting of the entire event data structure
encryptedDataStringThe encrypted data, if saved

The metadata object will contain the following properties:

FieldData typeDescription
aggregateId
Required
StringThe aggregate ID
timestamp
Required
NumberThe timestamp of when the event was stored
sequenceNumber
Required
NumberThe sequence number that the event was part of in the feed
tenantIdStringThe 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);
const request = {      reactionName: 'notify-on-order-shipped',      feedName: 'order',      action: {        actionType: "HTTP_POST",        targetUri: 'https://example.com/reactions/notify-order-shipped'      }    }await reactionsClient.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();    }  }}
import {APIGatewayEvent, Handler} from "aws-lambda";interface OrderShipped {  orderId: string,  orderAmount: string,  customerId: string,}interface OrderEventRequest {  event: {    eventType: string,    eventId: string,    data: OrderShipped  }}export const handler: Handler<APIGatewayEvent> = async (e) => {  try {    const request = JSON.parse(e.body!) as OrderEventRequest;    console.info('Order was shipped', {event: request.event})    // Implement your logic here    return {      statusCode: 200,      body: JSON.stringify({status: 'OK'})    }  } catch (e) {    return {      statusCode: 500,    }  }}

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.