Start from the email template

Copy the official template, read its connect.yaml, and prepare its credentials, dependencies, and configuration for your own Connector.

Ask about this Page
Copy for AI
View as Markdown

After completing this page, you should be able to:

  • Start a Connector from an official template with the Connect CLI.

  • Explain the application, endpoint, configuration, and lifecycle scripts in a connect.yaml file.

  • Replace per-application API Client credentials with generated credentials that use least-privilege scopes.

  • Update dependencies so that software composition analysis passes, and adjust the destination validator for Google Cloud Regions.

A template is the smallest build that meets a requirement, but it's a starting point, not a finished Connector. This page takes the official email template from a fresh copy to a state you can stage, and shows how to prepare any template for your own environment on the way.

Zen Electron wants branded order confirmation emails sent through its own email service provider. In Choose where it runs and how to adopt it, the team applied the adoption ladder to the order confirmation email. No public Connector fits Zen Electron's email service provider, and no configuration option closes the gap, so the team starts at step 4 with the transactional email template. By the end of this page, you have your own repository with a tagged v0.1.0 that is ready for the next page.
In this module, you build that Connector in your own GitHub repository, publish it for your own Projects, and then install and operate it the way any Client would. The hands-on steps apply the Integrate transactional email guide. Every page applies a concept from Integration essentials, links back to it, and doesn't teach it again.

Prepare your tools

You need the following:

  • A Node.js version that Connect supports. See supported runtimes.
  • Docker. The Connect CLI uses it to build and validate your application locally.
  • A GitHub account for your own repository.
  • An API Client for the CLI, as described in the following section.
Install the Connect CLI as described in Install the Connect CLI, and then run commercetools --version to confirm that the CLI is available.

Create an API Client for the CLI

The CLI acts as an API Client in your Project. This is a different identity from the one the Connector runs as. Create a client in the Merchant Center Developer settings with the following scopes:
ScopeWhere this module uses it
manage_connectorsStage and publish the Connector (Stage, preview, and publish privately).
manage_connectors_deploymentsInstall and operate Deployments (Install and test your Connector and Operate a Deployment).
manage_api_clientsDeploy a Connector that uses inheritAs (see the warning after the next section).
manage_ordersImport the test Order that triggers the email (Install and test your Connector).
manage_subscriptions or view_subscriptionsCheck the Subscription that the Connector creates (Install and test your Connector).
Hosts and authorization describes the Connect scopes.

Then sign in:

commercetools auth login --client-credentials \
  --client-id <client-id> \
  --client-secret <client-secret> \
  --project-key <project-key> \
  --region <region>
Use your Project's Region for --region. commercetools auth login describes each flag. Keep the client secret out of your repository.

Create your repository

Scaffold the project from the email template. The CLI's --template value is email-integration, which differs from the documentation page name, transactional-emails:
commercetools connect init zen-order-confirmation --template email-integration
cd zen-order-confirmation
git init
git add .
git commit -m "Start from the email template"

Create an empty repository in your GitHub account, then push:

git branch -M main
git remote add origin git@github.com:<your-account>/zen-order-confirmation.git
git push -u origin main
The template has one application, in a folder that matches its name in connect.yaml:
zen-order-confirmation/
├── connect.yaml
└── mail-sender/
    ├── package.json
    ├── src/
    └── tests/

The template is JavaScript, not TypeScript. This module keeps its code in JavaScript.

Read connect.yaml

The connect.yaml file at the root of the repository tells Connect what to build and how to run it. The template's file looks like this (abbreviated):
connect.yaml (unmodified template, abbreviated)yaml
deployAs:
  - name: mail-sender
    applicationType: event
    endpoint: /mailSender
    scripts:
      postDeploy: npm install && npm run connector:post-deploy
      preUndeploy: npm install && npm run connector:pre-undeploy
    configuration:
      standardConfiguration:
        - key: CTP_REGION
          description: commercetools Composable Commerce API region
      securedConfiguration:
        - key: CTP_PROJECT_KEY
        - key: CTP_CLIENT_ID
        - key: CTP_CLIENT_SECRET
        - key: CTP_SCOPE
        - key: EMAIL_PROVIDER_API_KEY
        - key: SENDER_EMAIL_ADDRESS
        - key: CUSTOMER_REGISTRATION_TEMPLATE_ID
        - key: CUSTOMER_EMAIL_TOKEN_CREATION_TEMPLATE_ID
        - key: CUSTOMER_PASSWORD_TOKEN_CREATION_TEMPLATE_ID
        - key: ORDER_CONFIRMATION_TEMPLATE_ID
        - key: ORDER_STATE_CHANGE_TEMPLATE_ID
        - key: ORDER_REFUND_TEMPLATE_ID
The real file has a description for every key. Configure connect.yaml defines each field. In this template, note the following:
  • name must match the application's folder, mail-sender.
  • endpoint is /mailSender. The Express router in mail-sender must serve this path, or deliveries never reach the handler. The email integration guide describes this requirement.
  • scripts create and delete the template's Subscription.
  • securedConfiguration holds every key except CTP_REGION, so the installer can't read those values back after setting them.

Whoever installs a Connector supplies a value for each key. The template asks for 13 values, four of which are an API Client's project key, ID, secret, and scope. The next section removes them.

Generate credentials instead of collecting them

With the template's configuration, every installer must create an API Client and paste its credentials into the install form. The inheritAs.apiClient.scopes setting makes Connect create the API Client at deployment time, with only the scopes you list. Nobody handles a secret.
Change connect.yaml as follows:
connect.yamldiff
       securedConfiguration:
-        - key: CTP_PROJECT_KEY
-          description: commercetools Composable Commerce project key
-        - key: CTP_CLIENT_ID
-          description: commercetools Composable Commerce client ID
-        - key: CTP_CLIENT_SECRET
-          description: commercetools Composable Commerce client secret
-        - key: CTP_SCOPE
-          description: commercetools Composable Commerce client scope
         - key: EMAIL_PROVIDER_API_KEY
           description: The API key used to communicate with email provider
         # ...the remaining keys stay as they are...
         - key: ORDER_REFUND_TEMPLATE_ID
           description: Identifier of order refund email template stored in email service provider
+inheritAs:
+  apiClient:
+    scopes:
+      - manage_subscriptions
+      - view_orders
+      - view_customers
inheritAs is a top-level key, a sibling of deployAs. The connect.yaml file now asks installers for nine values.
Connect injects the CTP_* variables that Variables available in the Connect application lists. The template's mail-sender/src/utils/config.utils.js already reads CTP_CLIENT_ID, CTP_CLIENT_SECRET, CTP_PROJECT_KEY, and CTP_SCOPE, so the application code needs no change. The template doesn't read CTP_API_URL or CTP_AUTH_URL. Instead, the template builds both hosts from CTP_REGION, so CTP_REGION stays as a value that the installer enters.
The three scopes match what the application does: manage_subscriptions for the Subscription that postDeploy creates and preUndeploy deletes, view_orders for the Order the confirmation handler retrieves, and view_customers for the Customer. The email integration guide maps each capability to its scope, and notes that flows creating Customer tokens need manage_customers. This module doesn't exercise those flows.
Add manage_api_clients to the CLI's API Client, not to the Connector's inheritAs.apiClient.scopes list. Without it, a Deployment of this Connector fails with 403. See Automatically generate API Client credentials.

Validate and update dependencies

Run the Connect validation locally. It's the same pipeline that Connect runs when you preview or publish a Connector. The validation pipeline page describes each step.
commercetools connect validate
Validation writes a .connect/ folder with build and scan logs. Add .connect/ to .gitignore so that you don't commit it.
Templates pin dependency versions at the time they're published, and new advisories appear afterwards. Before you build on any template, run the validation and expect software composition analysis (SCA) to flag dependencies that need updating. The SCA step scans development dependencies as well as production dependencies, so moving a package to devDependencies doesn't hide its findings. Only updating or removing the package changes the result.
Also check that the manifest separates runtime dependencies from test tooling. In the template's mail-sender/package.json:
  • sinon, supertest, and globals are listed under dependencies, although only tests use them.
  • mocha is listed too, but the tests run on Jest, so nothing uses it.
Remove mocha, move the test packages to devDependencies so that the manifest shows what the application needs at runtime, then apply the available security updates:
cd mail-sender
npm uninstall mocha
npm install --save-dev sinon supertest globals
npm audit fix
You can make the same change by editing package.json and running npm install. Commit the updated package-lock.json. Connect installs from it.
Run commercetools connect validate again from the repository root. If the SCA step still fails, run npm audit in mail-sender to see which packages are affected, then update or remove them. If an advisory has no fix available, check whether your code uses the affected package before you decide how to proceed.
SCA fails only on critical findings, so a passing SCA step doesn't mean the dependencies have no high-severity findings. Run npm audit and triage any remaining advisories before production use.

Treat local SAST as an early warning

Local static application security testing (SAST) can report code smells, such as an empty constructor or high cognitive complexity, that the remote pipeline accepts. The remote pipeline runs when you preview or publish a Connector. Local analysis can be stricter than the remote gate.

You don't need to clear every local finding to continue. Stage, preview, and publish privately shows how to read the remote report, which decides whether you can preview and publish. If you want a clean local run, remove empty constructors, extract duplicated logic into shared helpers, and split long functions into smaller ones.

Adjust the destination validator for Google Cloud

The template's environment validation needs one change before it works in a Google Cloud Region. Connect sets the variable CONNECT_SUBSCRIPTION_DESTINATION to GoogleCloudPubSub, which is 17 characters. The template's validator in mail-sender/src/validators/env.validators.js accepts at most 10:
mail-sender/src/validators/env.validators.js (excerpt)Javascript
optional(standardString)(
  ['connectSubscriptionDestination'],
  {
    code: 'InvalidSubscriptionDestination',
    message: 'Subscription destination is required.',
    referencedBy: 'environmentVariables',
  },
  { min: 2, max: 10 }
),
Reading the configuration then throws, postDeploy creates no Subscription, and the running application can't process events. An AWS Region isn't affected, because its destination value, SNS, is 3 characters.

Reproduce the failure locally before you change it. The command needs no deployment:

cd mail-sender
npm ci
CTP_CLIENT_ID=aaaaaaaaaaaaaaaaaaaaaaaa \
CTP_CLIENT_SECRET=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
CTP_PROJECT_KEY=my-project CTP_REGION=europe-west1.gcp \
CONNECT_SUBSCRIPTION_DESTINATION=GoogleCloudPubSub \
CONNECT_GCP_TOPIC_NAME=event-abc-mail-sender \
CONNECT_GCP_PROJECT_ID=ct-connectors-preview \
node --input-type=module -e "import r from './src/utils/config.utils.js'; r(); console.log('ok')"
Before the change, the command throws. The error's statusCode is InvalidEnvironmentVariablesError, and its errors list contains the code InvalidSubscriptionDestination. That entry's message, Subscription destination is required., is misleading: the value is set, but it's longer than the limit. Remove the upper limit. A separate validator, validDestinations, already restricts the value to GoogleCloudPubSub or SNS:
mail-sender/src/validators/env.validators.js (updated)Javascript
  { min: 2, max: undefined }
Run the command again. It prints ok.
Templates change over time, so check the template repository before you start. If your copy already allows the longer value, you can skip this step.
You can't see this problem from the Deployment. In Install and test your Connector, the Deployment reports success while no Subscription exists. Making this change now saves a debugging session later.

Tag your first release

Commit your changes and tag the release. Connect builds a Connector from a repository URL and a tag, so every version that you stage needs its own tag:

cd ..
git add .
git commit -m "Generate API Client credentials, update dependencies, adjust GCP destination validator"
git tag v0.1.0
git push origin main --tags
Zen Electron's order confirmation Connector now exists as v0.1.0 in your repository. It still sends no real email: the template's sendMail method only logs. Implement and test the event app to the contract narrows the Connector to the Messages that Zen Electron sends email for and tests its mapping, and keeps the provider call stubbed so that you don't need a provider account.

Other starting points

The email template is one of several ways to start a Connector:

  • Other templates. commercetools connect init accepts other --template values, which commercetools connect init lists. The values don't match the documentation page names, and fulfilment-integration has no documentation page. Compare the CLI reference with the templates overview.
  • An empty application. commercetools connect application add adds an application to an existing project.
  • A fork. Fork an open-source Connector when it covers most of your requirement (step 3 of the adoption ladder).
  • An existing integration. Convert an existing integration describes how to add connect.yaml to code that you already run.

Key takeaways

  • A template is a starting point. Read, validate, and test it before you build on it, and expect to update its dependencies and configuration for your environment.
  • Each deployAs[].name must match its folder, and the endpoint must match the path your router serves.
  • inheritAs.apiClient.scopes makes Connect generate the Connector's API Client with only the scopes you list. The identity that deploys the Connector needs manage_api_clients.
  • Software composition analysis scans dependencies and development dependencies, and fails on critical findings. Moving a package to devDependencies doesn't hide it: remove what you don't use and update what's vulnerable.
  • Local analysis can be stricter than the remote pipeline. Use it as an early warning, and treat the remote report as the gate for preview and publish.
  • A Deployment that reports success can still create no Subscription. In a Google Cloud Region, the template's destination validator rejects GoogleCloudPubSub until you remove the length limit.
  • Updating the SDK packages to their current versions is a sensible follow-up. This module leaves it out to keep the tested path stable.

Test your knowledge