← All examples
Local moduleAPISecrets

Add Google Maps as a local service

When a connector catalog stops, a small local module can extend the workflow without hiding its structure.

This example keeps orchestration in geocode-address.woml and the Google Maps implementation in google-maps.ts. The module becomes services.googleMaps, just like a built-in capability from the workflow author’s point of view.

The local module

TypeScript
type GeocodingResponse = {
  status: string;
  results: Array<{
    formatted_address: string;
    geometry: {
      location: { lat: number; lng: number };
    };
  }>;
};

export async function geocode(address: string, apiKey: string) {
  const response = await services.http.request({
    url: "https://maps.googleapis.com/maps/api/geocode/json",
    query: { address, key: apiKey },
    timeout: "15s"
  }, { name: "google-maps-geocode" });

  const data = response.data as GeocodingResponse;
  const match = data.results[0];

  if (data.status !== "OK" || !match) {
    throw new Error(`Google Maps could not geocode the address: ${data.status}`);
  }

  return {
    address: match.formatted_address,
    latitude: match.geometry.location.lat,
    longitude: match.geometry.location.lng
  };
}

The workflow

WOML
<woml>
  <imports>
    <module name="googleMaps" from="./google-maps.ts" />
  </imports>

  <workflow
    id="geocode-address"
    name="Geocode address"
    description="Resolve a submitted address through a local Google Maps service."
    version="1.0.0"
  >
    <triggers>
      <webhook
        id="submitAddress"
        path="/webhooks/geocode"
        method="POST"
        auth="bearer"
        secret="{{secrets.GEOCODE_WEBHOOK_TOKEN}}"
      >
        <schema>
          {
            "type": "object",
            "required": ["address"],
            "properties": {
              "address": { "type": "string", "minLength": 1 }
            },
            "additionalProperties": false
          }
        </schema>
      </webhook>
    </triggers>

    <steps>
      <step id="location" name="Find location">
        <script>
          return services.googleMaps.geocode(
            context.payload.address,
            secrets.GOOGLE_MAPS_API_KEY
          );
        </script>
      </step>

      <step id="coordinates" name="Return coordinates">
        <script>
          return {
            address: context.steps.location.address,
            latitude: context.steps.location.latitude,
            longitude: context.steps.location.longitude
          };
        </script>
      </step>
    </steps>
  </workflow>
</woml>

Run it

Keep both files in the same directory, store the two required secrets, and activate the workflow.

Terminal
woml secrets set GEOCODE_WEBHOOK_TOKEN
woml secrets set GOOGLE_MAPS_API_KEY
woml check geocode-address.woml
woml run geocode-address.woml

Local modules use named ESM exports and static relative JavaScript or TypeScript imports. They receive services and native Fetch; workflow data and secrets remain explicit arguments at the call site.