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 type | Trigger in production | Local test |
|---|---|---|
| Service | An API Extension or webhook | POST an API Extension payload |
| Event | A Subscription message sent through a broker | POST a message payload |
| Job | A scheduler that runs on a cron expression | POST 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:yarninstalls dependencies.yarn buildcompiles the application, andyarn startruns the compiled output. Alternatively,yarn start:devruns the application in watch mode and rebuilds automatically when you change the source.yarn testruns 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.
{
"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
}
}
}
{
"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: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
- Your application runs locally and exposes an HTTP server on port
8080. - A tool that exposes your local server to the internet, such as ngrok.
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: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:
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:
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": {
"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.
{
"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"
}
{
"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: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.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.