Test applications locally

Ask about this Page
Copy for LLM
View as Markdown

Run service, event, and job applications on your machine to confirm they behave as expected before you publish a Connector.

Before you publish a Connector, test each of its Connect applications locally to confirm they behave as expected. Local testing lets you exercise your business logic, catch errors early, and iterate without deploying.
This guide covers the three application types that expose an endpoint: service, event, and job. For guidance on writing automated tests, see Develop a Connect application.

How local testing works

Service, event, and job applications run as HTTP servers that listen on port 8080. Each application exposes a single endpoint whose path matches the endpoint value in connect.yaml, such as /service, /event, or /job. All three endpoints accept POST requests.
In production, Connect triggers each application type through different infrastructure. Locally, that infrastructure is not present, so you simulate a trigger by sending a POST request with a representative payload to the application's endpoint.
Application typeTrigger in productionLocal test
ServiceAn API Extension or webhookPOST an API Extension payload
EventA Subscription message sent through a brokerPOST a message payload
JobA scheduler that runs on a cron expressionPOST to the endpoint with no schedule control

Run your application locally

The starter templates configure each application as an HTTP server that listens on port 8080. Build and run the application you want to test before you send requests to it.
Open the application's folder (service, event, or job) and use the following commands:
  • yarn installs dependencies.
  • yarn build compiles the application, and yarn start runs the compiled output. Alternatively, yarn start:dev runs the application in watch mode and rebuilds automatically when you change the source.
  • yarn test runs the automated tests included with the templates. These tests send requests to the application's routes, so they are a fast way to verify behavior without starting the server.
For the full list of commands, see Develop a Connect application.

Test a service application

Service applications respond to API Extensions. When triggered, the application receives an action and the affected resource. It applies its logic and returns a list of update actions in the API Extension response format.
Consider a service application that applies an automatic 10% discount to a Cart. To test it, send a POST request that contains a sample Cart in the body, then confirm the application returns the expected update actions.

The following example shows a sample request body and the update actions that the application returns.

Sample POST bodyjson
{
  "action": "Create",
  "resource": {
    "typeId": "cart",
    "id": "bcaeb291-5151-434c-80a9-c1ce7c88e6e0",
    "obj": {
      "type": "Cart",
      "id": "bcaeb291-5151-434c-80a9-c1ce7c88e6e0",
      "version": 1,
      "lineItems": [
        {
          "id": "aab03288-fd14-4bb1-9a23-e506774ac040",
          "productId": "56fc033e-25de-484b-bf31-fc0b97143aa9",
          "name": {
            "en": "Some Product"
          },
          "variant": {
            "id": 1,
            "sku": "SKU-fake",
            "prices": [
              {
                "id": "a09471bc-d9d6-4147-a390-7ac558f93c3c",
                "value": {
                  "type": "centPrecision",
                  "currencyCode": "EUR",
                  "centAmount": 4000,
                  "fractionDigits": 2
                }
              }
            ]
          },
          "quantity": 1,
          "totalPrice": {
            "type": "centPrecision",
            "currencyCode": "EUR",
            "centAmount": 4000,
            "fractionDigits": 2
          }
        }
      ],
      "cartState": "Active",
      "totalPrice": {
        "type": "centPrecision",
        "currencyCode": "EUR",
        "centAmount": 4000,
        "fractionDigits": 2
      },
      "taxMode": "Platform",
      "origin": "Customer",
      "totalLineItemQuantity": 1
    }
  }
}
Expected responsejson
{
  "actions": [
    {
      "action": "setDirectDiscounts",
      "discounts": [
        {
          "value": {
            "type": "relative",
            "permyriad": 1000
          },
          "target": {
            "type": "lineItems",
            "predicate": "1=1"
          }
        }
      ]
    }
  ]
}
Save the sample body to a file, such as cart.json, and send it to the application's endpoint:
Call the service applicationshell
curl -X POST http://localhost:8080/service \
  -H 'Content-Type: application/json' \
  -d @cart.json
The action field determines whether the payload represents a created or updated resource. Set it to Create or Update to match the scenario you want to test. Check the returned update actions to confirm they match your application's logic. A service application must reply with a 200 status code.

Test with a live API Extension

Instead of sending sample data, you can create a real API Extension that calls your local application. This tests the full round trip from commercetools to your application.

Requirements

Expose your application

An API Extension calls your application over the internet, so your local server needs a public URL. The following example uses ngrok to expose port 8080:
Expose port 8080shell
ngrok http 8080
# Example public URL: https://public-sample.ngrok-free.app

Create the API Extension

Using the public URL, create an API Extension that triggers when a Cart is created or updated:

Create an API Extensionshell
curl --location 'https://api.{region}.commercetools.com/{projectKey}/extensions' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <token>' \
  --data '{
    "destination": {
      "type": "HTTP",
      "url": "https://public-sample.ngrok-free.app"
    },
    "triggers": [
      {
        "resourceTypeId": "cart",
        "actions": ["Create", "Update"]
      }
    ],
    "key": "my-extension"
  }'

Trigger the API Extension

Create or update a Cart to trigger the API Extension:

Create a Cartshell
curl --location 'https://api.{region}.commercetools.com/{projectKey}/carts' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <token>' \
  --data '{
    "currency": "EUR",
    "lineItems": [
      {
        "productId": "{productId}",
        "variantId": 1
      }
    ]
  }'

Review your application's logs and the affected resource to confirm the application ran as expected.

Test an event application

Event applications process messages delivered by a Subscription. The application receives a message envelope, acts on the message, and returns a success status code.
The message arrives in the following envelope. The message.data field is base64-encoded, and its decoded value is a commercetools message.
Message envelopejson
{
  "message": {
    "attributes": {
      "key": "value"
    },
    "data": "<base64-encoded message>",
    "messageId": "2070443601311540",
    "publishTime": "2021-02-26T19:13:55.749Z"
  },
  "subscription": "projects/myproject/subscriptions/mysubscription"
}
The decoded message uses either the PlatformFormat or the CloudEventsFormat, depending on the format set when you create the Subscription. The following examples show a decoded message for a newly created Order in each format.
PlatformFormat messagejson
{
  "notificationType": "Message",
  "projectKey": "{projectKey}",
  "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
  "version": 1,
  "sequenceNumber": 1,
  "resource": {
    "typeId": "order",
    "id": "12345678-90ab-cdef-1234-567890abcdef"
  },
  "resourceVersion": 1,
  "type": "OrderCreated",
  "order": {
    "id": "12345678-90ab-cdef-1234-567890abcdef",
    "version": 1,
    "orderNumber": "ORD-10001",
    "orderState": "Open",
    "totalPrice": {
      "currencyCode": "USD",
      "centAmount": 15000
    }
  },
  "createdAt": "2025-07-09T10:15:30.000Z",
  "lastModifiedAt": "2025-07-09T10:15:30.000Z"
}
CloudEventsFormat messagejson
{
  "specversion": "1.0",
  "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
  "type": "com.commercetools.order.message.OrderCreated",
  "source": "/{projectKey}/orders/12345678-90ab-cdef-1234-567890abcdef",
  "subject": "OrderCreated",
  "time": "2025-07-09T10:15:30.000Z",
  "data": {
    "notificationType": "Message",
    "projectKey": "{projectKey}",
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
    "version": 1,
    "sequenceNumber": 1,
    "resource": {
      "typeId": "order",
      "id": "12345678-90ab-cdef-1234-567890abcdef"
    },
    "resourceVersion": 1,
    "type": "OrderCreated"
  }
}
To call the application, base64-encode a message and send it in the message.data field of the envelope:
Call the event applicationshell
curl -X POST http://localhost:8080/event \
  -H 'Content-Type: application/json' \
  -d '{ "message": { "data": "<base64-encoded message>" } }'
To generate the encoded value, base64-encode the message JSON. For example, run base64 < message.json and copy the result into message.data.
Check your application's logs and any third-party integrations to confirm the message was processed. An event application must reply with a success status code, such as 200 or 204.

Test a job application

Job applications run on a schedule in production. A scheduler sends a POST request to the application's endpoint based on the cron expression set in properties.schedule. You can override this schedule for a specific deployment using the schedule field of DeploymentConfigurationApplication.
Locally, no scheduler is available, so you trigger the application manually by sending a POST request to its endpoint. The scheduling behavior cannot be reproduced or controlled locally.
Call the job applicationshell
curl -X POST http://localhost:8080/job
A job application does not require a request body. The scheduler triggers the task, so the application runs independently of the request content. Check your application's logs and any affected resources to confirm the task ran as expected. A job application must reply with a 200 status code.