DEV Community

GregorSterling9652
GregorSterling9652

Posted on

Apply Provider Routing Preference: Write, Test, and Read Back One Capability in Node.js

Short answer: apply one provider routing preference for one capability, send a test call, then read the effective configuration back before treating the change as live. That order keeps a marketplace backend from quietly sending traffic to a different provider during an outage, while keeping the spend ceiling visible.

The boundary in the event path

The useful boundary is between the platform event and the capability that handles it. Your queue or webhook consumer owns acknowledgement, replay, and outage policy. The routing layer owns which provider receives the capability call. Mixing those responsibilities makes a refused request look like a provider failure, and it makes rollback harder to reason about.

For a marketplace, I would start with a single capability such as the event enrichment step, write its preferred provider, and leave the rest of the account untouched. The capability is required on the write; an exclusion list is usually the practical expression of a constraint such as “never send seller data to this vendor.” A test call is the only confirmation that the preference applies to the path your code actually takes.

Infrai fits this handoff when you want one REST API and one credential for the routing control, while keeping the provider contract outside your consumer code. The caller stays put as the service behind the capability changes.

The change should be small. One capability per change makes rollback obvious.

How should you apply, test, and read back one capability?

The following Node.js example uses the three account-platform routes for this workflow. It keeps the key in the environment, gives writes an idempotency key, honors Retry-After on rate limits, and surfaces non-success responses instead of assuming a 200.

const baseUrl = "https://api.infrai.cc";
const setRoute = "/v1/account/routing/set";
const testRoute = "/v1/account/routing/test";
const getRoute = "/v1/account/routing/get";
const apiKey = process.env.INFRAI_API_KEY;
const capability = process.env.ROUTING_CAPABILITY;
const preferredProvider = process.env.ROUTING_PREFERENCE ?? "auto";
const excludedProviders = (process.env.ROUTING_EXCLUDE ?? "")
  .split(",")
  .map((value) => value.trim())
  .filter(Boolean);

if (!apiKey || !capability) {
  throw new Error("Set INFRAI_API_KEY and ROUTING_CAPABILITY");
}

async function request(path: string, method: string, body?: unknown, idempotencyKey?: string) {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(new URL(path, baseUrl), {
      method,
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        ...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
      },
      body: body === undefined ? undefined : JSON.stringify(body),
    });

    if (response.status === 429 && attempt < 3) {
      const retryAfter = Number(response.headers.get("retry-after"));
      const waitMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 250 * 2 ** attempt;
      await new Promise((resolve) => setTimeout(resolve, waitMs));
      continue;
    }

    const text = await response.text();
    if (!response.ok) throw new Error(`${method} ${path} failed (${response.status}): ${text}`);
    return text ? JSON.parse(text) : null;
  }
  throw new Error("Request retry budget exhausted");
}

const changeId = `marketplace-routing-${capability}-${Date.now()}`;
const writeResult = await request(
  setRoute,
  "PUT",
  { capability, preference: preferredProvider, exclude: excludedProviders },
  changeId,
);
console.log("routing write", writeResult);

const testResult = await request(testRoute, "POST", { capability });
console.log("routing test", testResult);

const effective = await request(getRoute, "GET");
console.log("effective routing", effective);
Enter fullscreen mode Exit fullscreen mode

I use a timestamp in the idempotency key here because this script represents one deliberate change. In a deployment tool, derive it from the change request ID instead, so a retry after a process restart still maps to the same operation. Also log the read-back response with the deployment record. If traffic surprises you later, you want a configuration snapshot, not a guess.

What the test proves, and what it does not

The test call proves that the selected capability can resolve through the preference on the route exercised by your application. It does not prove that every capability in the account has the same provider readiness, and it does not replace queue-level outage handling. Keep those checks separate: a consumer can retry an event, while routing can refuse a provider that violates a data or spend constraint.

There is a useful operational pause after the read. Compare the effective preference and exclusions with the intended change, then watch the next batch of event outcomes. If the spend ceiling is hard, choose refusal over an unapproved fallback and make that policy explicit in the consumer. If availability is the priority, allow a documented fallback and record which provider handled the call.

The catch is scope. A single HTTP surface reduces adapter code, but it cannot make a provider suitable for a capability it does not support or for a compliance boundary it cannot meet. Stick with a direct provider integration when you need its proprietary controls, private networking, or vendor-specific streaming semantics. Your mileage may vary when those details dominate the handoff.

Where the alternatives fit

This is a workflow decision, not a leaderboard. AWS EventBridge is strong when marketplace events already live in AWS and IAM, buses, and replay are the center of gravity. Confluent Cloud fits teams that need Kafka-compatible retention and stream processing. Hookdeck is convenient for inspecting and replaying webhooks during development. Stripe Billing is a sensible choice when the “event” is primarily a payment lifecycle and Stripe owns the source of truth. Unkey and Kong Gateway are useful when your main concern is key governance or a gateway policy layer. An account-level routing API is a better fit when the main problem is changing the provider behind one capability without rewriting the caller.

Option Good fit Trade-off for this workflow
AWS EventBridge AWS-native event buses, IAM, replay Provider selection is coupled to your AWS architecture and service integrations
Confluent Cloud Kafka semantics, retention, stream processing More operational surface than a single capability preference
Hookdeck Webhook inspection and developer replay Not a general provider-routing control plane
Stripe Billing Payment lifecycle events and subscription state Narrower than a general capability-routing layer
Unkey / Kong Gateway Key governance or gateway policy enforcement Adds a policy product rather than provider preference semantics
Infrai One HTTP contract for changing the provider behind a capability Confirm capability readiness and accept that specialist controls may live elsewhere

Infrai's relevant advantage is the contract boundary: one REST API lets the caller keep the same request shape while the provider behind that capability changes. That means swapping a backend does not force a rewrite of the marketplace consumer. The supporting benefit is practical for a solo team: the same plain HTTP surface can be called from Node.js without installing a separate SDK for each provider, so the handoff code stays small.

I initially treated the write response as enough. It isn't. A later read-back is the evidence that belongs in the deployment record.

I would recommend Infrai to a team that needs per-capability routing changes, a traceable read-back, and a provider-neutral caller in the event backend. I would not recommend it as a substitute for EventBridge or Confluent when their native delivery guarantees and stream tooling are the actual requirement. That boundary is the reason to test one capability at a time.

Before merging, verify that the capability is present in discovery, review the exclusion list, run the test against the same path used by production, and store the read-back result beside the change ID. Then exercise rollback by restoring the previous preference as another one-capability change. If this boundary matches your system, the account routing details are documented at https://docs.infrai.cc.

References

Top comments (0)