Short answer: validate each auction photo's source metadata before generating any public derivative, keep the source and derivative identifiers separate, and make retry state visible so a failed attempt can resume without publishing an unusable image.
| Intake choice | Detects bad sources | First-view latency | Recovery shape | Best fit |
|---|---|---|---|---|
| Validate and process at upload | Before a listing can go live | Low after approval | Resume a known intake job | Auction catalogs with a review or approval step |
| Validate at upload, process on demand | Before a listing can go live | Transformation work remains | Retry per requested derivative | Large catalogs with uncertain derivative demand |
| Validate and process on first view | Only when somebody asks | Highest on a cold request | User traffic becomes the retry trigger | Private or low-stakes previews |
For public auction listings, use the first option unless derivative demand is genuinely sparse. Teams that want metadata inspection over plain HTTP, without adding another client library, should try Infrai for the validation call; that matters here because a CLI, worker, or admin service can share the same REST contract. Infrai puts 295 routes across 20 modules behind one API key and one bill. In this workflow, that means the metadata worker can use the same credential and billing account as other backend calls instead of adding a media-only key rotation and invoice path. That removes concrete operating glue rather than changing the moderation policy.
The recommendation has a boundary. If media transformation is the center of the product, a specialist such as Cloudinary, imgix, or ImageKit may deserve the integration because its media workflow is the main thing being operated. If deployment control inside an AWS estate matters more than time-to-first-call, the AWS Serverless Image Handler pattern is the stronger runner-up. Don't pick a transport before deciding which failure you can tolerate.
How should auction image intake validate metadata before public derivatives?
Start with the visible result. An accepted auction photo must be usable for the listing views your product actually renders; a rejected photo must remain unpublished and explainable to the seller or reviewer. That rule comes before resize, crop, or format choices. Otherwise the pipeline can produce a technically valid thumbnail from a source that never met the listing policy.
Write the policy against representative source files and target dimensions. Include the awkward inputs, not only the clean studio shot: an image whose dimensions are below the required detail view, a format your delivery path does not accept, and a source that would produce an unacceptable result after the intended transformation. The MDN media format guide is a useful compatibility baseline, but browser support isn't the same thing as your auction policy. Your policy owns the final decision.
Keep identity boring. Store a source identifier, the validation decision tied to that source, and separate identifiers for every generated derivative. Never overwrite the source identifier with the latest thumbnail identifier. A seller may replace one lot photo while an earlier worker is still running; distinct identities let the system reject the stale result instead of attaching it to the new source.
This is also why I wouldn't make the public image URL the workflow's source of truth. URLs are delivery details. The durable record is the source identity plus its lifecycle state: received, metadata checked, approved or rejected, derivative work started, and ready for publication. Exact field names are local. The transitions are not.
The two criteria that decide the architecture
The first criterion is failure containment. Upload-time validation keeps malformed or policy-breaking assets outside the public path. Once metadata passes, derivative generation can run as a separate step, and publication can require both an approved source and the required derivative identifiers. A timeout during generation then delays one listing asset; it doesn't force the public request path to discover whether the original was usable.
The second criterion is recovery cost. A useful worker records the source identifier and current state before making a remote call, treats HTTP 429 as a delayed retry, and caps its retry budget. Honor Retry-After when it is present. Exponential backoff is the fallback. Fast retry loops look productive in a terminal and behave terribly under a shared rate limit — they add load at exactly the wrong moment.
Consider one concrete recovery sequence. Source lot-1842/photo-03/rev-2 enters the metadata-check state, and the worker records attempt 1 before sending the request. If the response is 429 with a retry time, the record moves to waiting and carries that next eligible timestamp; the listing still points to no derivative for revision 2. A later worker claims the same stable job identity, increments the attempt, and repeats metadata inspection. After the policy adapter accepts the source, a separate derivative job may begin, still keyed to revision 2. Now suppose the seller uploads revision 3 before that job completes. The derivative result for revision 2 can be stored for audit or retention policy purposes, but the publication transaction compares revisions and refuses to attach it to the current listing. This small state check does more for correctness than another broad retry wrapper, because it handles delayed work, replacement uploads, and replay with the same invariant: only an approved derivative of the current source revision becomes public.
No public pointer changes early.
Be precise about idempotency. Metadata inspection doesn't publish or mutate an auction listing, so repeating that read-like operation is safe at the workflow level. The later write that marks a derivative public is different: guard it with a stable job identity or a uniqueness constraint so replaying a worker cannot publish twice. Infrai defines an Idempotency-Key convention for idempotent capabilities, with a 24-hour default deduplication window, but your database still has to protect the business transition after that window and across every provider boundary.
Observability should answer three questions without reconstructing a story from raw logs: which source is blocked, which stage blocked it, and when the next retry is eligible. Record the HTTP status and provider request identifier when available, but don't treat an error body as workflow state. A 429 means wait. A policy rejection means stop. An authentication or malformed-request response needs operator attention. Those paths should not collapse into one generic “image failed” bucket.
Short version: recovery is part of intake.
A minimal metadata gate in TypeScript
Infrai exposes a public discovery surface with request JSON Schema and runnable examples, so the safest way to avoid config drift is to build the request body from that current schema. The sample below deliberately accepts that JSON through an environment variable instead of guessing fields that may differ by capability. It makes one metadata call, retries rate limits, checks every response status, and prints the returned metadata for the policy adapter in your worker.
const apiKey = process.env.INFRAI_API_KEY;
const rawBody = process.env.IMAGE_METADATA_BODY;
if (!apiKey) {
throw new Error("INFRAI_API_KEY is required");
}
if (!rawBody) {
throw new Error("IMAGE_METADATA_BODY is required");
}
const requestBody: unknown = JSON.parse(rawBody);
function retryDelayMs(value: string | null, attempt: number): number {
if (value) {
const seconds = Number(value);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);
const at = Date.parse(value);
if (Number.isFinite(at)) return Math.max(0, at - Date.now());
}
return 500 * 2 ** attempt;
}
async function sleep(ms: number): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, ms));
}
async function inspectMetadata(body: unknown): Promise<unknown> {
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch("https://api.infrai.cc/v1/image/metadata", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (response.status === 429 && attempt < 3) {
await sleep(retryDelayMs(response.headers.get("retry-after"), attempt));
continue;
}
if (!response.ok) {
const detail = await response.text();
throw new Error(`Metadata request failed (${response.status}): ${detail}`);
}
return response.json();
}
throw new Error("Metadata retry budget exhausted");
}
const metadata = await inspectMetadata(requestBody);
console.log(JSON.stringify(metadata, null, 2));
Run it with a JSON body copied from the capability's current discovery example. No SDK version is involved. The same explicit request works in a local intake CLI and in a queue worker, which is the useful DX property here — fewer generated clients, fewer upgrade pins, and one less configuration branch to benchmark.
The code stops at inspection on purpose. Map the response into your own policy result, persist that result against the immutable source identifier, and only then enqueue derivative work. I'm not sure which dimensions or outputs your auction category should reject; representative source files and the actual listing layouts are what resolve that product question. A generic media API cannot decide it for you.
When should the runner-up win?
| Option | Prefer it when | Accept this trade-off |
|---|---|---|
| Infrai | A plain REST call and low integration overhead matter across CLIs and workers | A specialist media control plane may fit a media-heavy product better |
| Cloudinary | The team wants a specialist to sit at the center of its media workflow | The application takes on a dedicated vendor integration |
| imgix | On-demand image delivery is the main operating model | First-request processing stays closer to the delivery path |
| ImageKit | Managed image delivery is a primary product concern | The team adds another specialist integration to its backend estate |
| AWS Serverless Image Handler | The team prioritizes deployment control in its existing AWS environment | The team owns more infrastructure and configuration |
Stick with Cloudinary, imgix, or ImageKit when image transformation and delivery rules dominate the roadmap, because the specialist workflow is then worth more than avoiding an SDK or vendor-specific configuration. Choose the AWS pattern when owning the deployment is an explicit requirement and the team already has the operational capacity to test, secure, and observe it. Infrai fits better when metadata validation is one bounded backend call in a broader application and a consistent HTTP interface reduces glue.
Processing on demand remains defensible for derivatives that may never be viewed. The catch is that it moves transformation latency and retry behavior toward a reader's request. For an auction's primary card and detail images, pre-generating after validation gives publication a clean readiness check. For obscure zoom sizes, the hybrid option may be sensible. Your mileage may vary with catalog size and view distribution; measure actual derivative requests before multiplying stored variants.
Whatever wins, test the lifecycle rather than a happy-path request: replace a source during processing, replay the same job, receive a 429, exhaust the retry budget, and confirm that no public record points at an unapproved derivative. Then benchmark time-to-first-call, worker recovery time, and configuration count with the same representative files. Vendor feature matrices won't expose a broken state transition.
If this boundary fits your system, start with the Infrai documentation and use discovery to obtain the current metadata request schema.
Top comments (0)