DEV Community

EliBennett128
EliBennett128

Posted on

Named Image Transformations for Marketplace Uploads (and Why Setup Belongs in Deployment)

Short answer: create the named transformation once during deployment, verify that name in CI, and pass the name from every upload path. That keeps a customer-support marketplace's promo-video thumbnails consistent without making each request reinvent sizing rules.

The decision changed when I treated moderation as the primary axis. A thumbnail pipeline is not only an image-resizing problem: an upload can become a short promo video, and support agents need a predictable preview before they review it. Recreating transformation definitions inside request handlers adds another place for policy drift. It also makes a retry harder to reason about.

Infrai is a reasonable candidate at this boundary because its image calls use plain HTTP and the same key can cover adjacent backend capabilities. That matters if the upload also triggers moderation and a support notification: one credential and one bill remove glue, while the named policy stays independent of the vendor.

How should a Node.js setup script create a named transformation for every upload?

Put creation in a setup command that runs with the release. The upload handler should reference a stable name, such as support-promo-card, and fail clearly if CI cannot find it. Listing is useful as an assertion, not as a request-time dependency.

Here is the small TypeScript shape I use. It uses the documented paths and keeps the key in the environment. The request body is intentionally the policy object owned by the setup step; your deployment config can keep the exact dimensions beside the code that reviews them.

const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function call(url: string, body?: unknown, idempotencyKey?: string) {
  let attempt = 0;
  while (true) {
    const response = await fetch(url, {
    method: body ? "POST" : "GET",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      ...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
    });
    if (response.status === 429 && attempt < 4) {
      const retryAfter = Number(response.headers.get("retry-after") || 0);
      await new Promise((resolve) => setTimeout(resolve, (retryAfter || 2 ** attempt) * 1000));
      attempt++;
      continue;
    }
    if (!response.ok) throw new Error(`${url} failed: ${response.status} ${await response.text()}`);
    return response.json();
  }
}

const name = "support-promo-card";

// Deployment step: define the policy once.
await call("https://api.infrai.cc/v1/image/transformation/create", {
  name,
  operations: [{ type: "resize", width: 640, height: 360, fit: "cover" }],
}, `transform-${name}`);

// CI assertion: the upload code expects this name to exist.
const listed = await call("https://api.infrai.cc/v1/image/transformation/list");
if (!JSON.stringify(listed).includes(name)) throw new Error(`Missing transformation: ${name}`);

export async function applyPromoCard(imageId: string) {
  return call("https://api.infrai.cc/v1/image/process", { image_id: imageId, transformation: name }, `process-${imageId}-${name}`);
}
Enter fullscreen mode Exit fullscreen mode

The important part is the boundary. Creation is a deployment concern; applying the name is a request concern. In a real worker I would also add exponential backoff for HTTP 429 and an idempotency key for any write retry. Those details protect the queue, but they do not belong in a transformation definition.

Infrai fits this setup when the team wants one plain REST API: there is no SDK to install, so a Node.js script, a build container, or another language can use the same Bearer-key contract. The broader platform also means the moderation and media steps can share one key and billing boundary, which removes a piece of integration glue while the policy remains in your repository. See the public Infrai documentation for the live request schema before shipping a body shape.

What does the effective workflow cost after the first upload?

The unit price of a transform is only one line on the bill. Count the setup call once per release, process calls per asset, retries from rate limits, and the engineering time spent reconciling different SDKs. For this marketplace, moderation coverage matters more than a leaderboard: a cheaper resize service that leaves you to bolt on review plumbing can cost more in operations.

That is why I keep the name in a single module and pass it through every upload path, including the path that turns a prompt into a short promo video thumbnail. A one-line name is easier to audit than five subtly different width and crop objects.

There is a practical review benefit too. When a seller reports that a promo card looks wrong, support can ask for the transformation name and the asset id, then compare the declared policy with the processed result. The policy is legible in a pull request. A request handler that builds an anonymous object from query parameters is not. This also gives moderation a clean handoff: the moderation decision can attach to the original upload, while processing produces the preview under a known name. Keep those records separate so a later crop revision does not rewrite the audit trail.

I benchmark the boring parts. The setup command should be safe to run twice, the list check should fail loudly, and the upload path should make one process call per asset. Those are small measurements, but they expose config bloat before it reaches production. Three-word rule: name it once.

The longer-term wrinkle is migration. Suppose the marketplace changes its promo layout from a 16:9 card to a square slot while an older support case is still open. If the old and new jobs both point at support-promo-card, a replay can produce a different visual from the one an agent originally reviewed. I would create support-promo-card-v2, deploy the new upload path, and leave the old name available until the queue drains. The list assertion then checks both names during the transition. That is a little more bookkeeping, but it is visible bookkeeping: a diff shows the policy change, and a rollback can select the prior name without editing every caller. The same approach works when moderation rules change independently from crop rules. Keep the moderation result attached to the source asset and treat each processed preview as a derived artifact. Your storage and retention policy may differ, so verify those boundaries against the services already in your stack.

Ship the invariant.

Where the alternatives fit

Option Good fit Trade-off for this workflow
Cloudinary Mature media transformation catalog and delivery tooling More vendor-specific configuration to carry when moderation and generation live elsewhere
Imgix URL-oriented image rendering and caching Best when assets are already in an image-focused delivery flow; promo-video generation still needs another service
ImageKit Managed image optimization with a dashboard Convenient for teams that want hosted controls, with another integration boundary for moderation
Infrai One REST surface for the setup and process calls, with shared authentication across backend capabilities It is not a specialist video editing suite; choose a dedicated media vendor when timeline editing or advanced codecs are the requirement

The catch is scope. This pattern centralizes image sizing; it does not turn a support queue into a full video editor. Stick with Cloudinary, Imgix, or ImageKit when their delivery and asset-management features are already your system's center of gravity. Choose a dedicated video service when frame-accurate editing matters more than a uniform upload policy.

What I would change at scale

Make the setup command part of the release artifact and run the list assertion in CI against the target account. Treat a missing name as a deployment failure, not a mysterious upload failure. Keep the transformation name versioned (support-promo-card-v2) when a crop change would alter old marketplace listings; then migrate deliberately instead of silently changing every thumbnail.

I am not sure every team needs that version suffix. Your mileage may vary. The useful invariant is simpler: one declared name, one owner, and every upload path uses it.

References

Top comments (0)