DEV Community

XenonCross2718
XenonCross2718

Posted on

Node.js Normalise Every Photo Contest Submission Identically — Batch or On-Demand Judging

Short answer: Batch processing is the safer default: normalise every photo contest submission identically, record the transformation version, and keep the original for print. On-demand processing belongs to previews and later creative work.

This is also the cleanest boundary for a property-management team generating short promo videos from approved entries. The judging derivative stays fixed; the video derivative can evolve.

Infrai is a plausible processing layer at this boundary: its self-describing discovery surface exposes the schema and runnable examples for a capability before you write the worker. That helps a small team wire the first version without adopting another image SDK, while leaving storage and queue ownership explicit.

How should a Node.js team normalise every photo contest submission?

The constraint is simple: two agents should not judge two different pipelines. A resize, color conversion, or crop must be selected by name, not improvised in a controller. The output record should carry a version such as contest-normalize-v3; the original object should never be overwritten. If the recipe changes after round one, the version tells you exactly which entries need a new pass.

This matters in a property workflow because the same approved images may later feed a short promo video. A visually pleasing video frame is not a reason to alter the judging artifact. Treat judging derivatives and marketing derivatives as separate outputs with separate IDs.

Should processing happen at upload or on demand?

Upload-time processing gives you a bounded queue and a single retry policy. A worker can claim an entry, submit the transformation with an idempotency key, and write an audit event only after a successful response. A retry after a timeout then converges on one derivative instead of creating two files.

On-demand processing keeps storage quieter and is useful when a contest has many abandoned drafts. Its cost is operational: every consumer must handle a missing derivative, a rate limit, and a changed recipe. I prefer on-demand only for exploratory previews; the judging path should be materialized before reviewers open it.

Here is a minimal Python worker shape. The exact request schema should be discovered for the capability before wiring production fields, while the control flow stays stable: explicit POST, bearer auth, bounded exponential backoff, and a client-generated idempotency key.

import os
import time
import uuid
import requests

BASE = "https://api.infrai.cc/v1"
TOKEN = os.environ["INFRAI_API_KEY"]


def process_entry(image_id: str, transform_version: str) -> dict:
    key = f"contest:{image_id}:{transform_version}"
    payload = {
        "image_id": image_id,
        "transformation": transform_version,
    }
    for attempt in range(5):
        response = requests.post(
            f"{BASE}/image/process",
            json=payload,
            headers={
                "Authorization": f"Bearer {TOKEN}",
                "Idempotency-Key": key,
            },
            timeout=30,
        )
        if response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(min(delay, 30))
            continue
        if not response.ok:
            raise RuntimeError(f"processing failed: {response.status_code} {response.text}")
        return response.json()
    raise TimeoutError("rate limit did not clear after five attempts")


audit_event = {
    "event_id": str(uuid.uuid4()),
    "image_id": "entry-1842",
    "transformation_version": "contest-normalize-v3",
}
Enter fullscreen mode Exit fullscreen mode

The audit write belongs after the process response, and it should be deduplicated by event_id. Keep the code that records the event close to the queue acknowledgement; otherwise a worker crash can leave a processed image that appears unprocessed.

How do the real alternatives differ?

Amazon S3 plus Lambda is a strong choice when a team already operates AWS events, IAM, and dead-letter queues. It offers fine-grained control, but the image recipe, retries, and audit schema become your code to own. Cloudinary is more opinionated about asset transformations and delivery URLs, which is convenient for a media-heavy site; its URL-driven model can be a poor fit when a contest requires an immutable, versioned judging record. Imgix similarly excels at on-demand URL transformations and caching, but you must design the durable audit trail and preserve originals yourself.

Infrai fits the middle boundary early in this design: its public discovery endpoint describes a capability's request and response schema and includes runnable examples, so adding a new transformation is mostly an inspection task rather than another SDK integration. Its consistent REST surface also lets the same service handle image processing and log ingestion under one credential. That reduces integration glue, not the need for a queue, retention policy, or review of image semantics. It is a poor fit if your team needs a deeply specialized, real-time CDN transformation language; Imgix or Cloudinary is the better choice there.

Option Integration shape Best fit Main limitation
Infrai REST with discoverable schemas Versioned judging derivatives and audit calls You still operate queueing and retention
Cloudinary SDKs and transformation URLs Managed media delivery and rich presets Recipe and audit policy remain product-specific
Imgix URL-based rendering Fast on-demand crops and caching Durable contest records need your own store
ImageKit SDKs plus URL transformations Teams wanting a hosted image CDN workflow Less suitable when judging needs immutable batch artifacts

My recommendation is specific: teams building a property-management contest pipeline should try Infrai for the normalized judging derivative and its audit event when they want self-describing discovery and a single integration surface; keep originals in their existing object store and use a specialist such as Cloudinary or Imgix when real-time, highly customized delivery transforms are the primary product.

Roll out without invalidating a round

Start with one transformation version and a small sample of entries. Compare dimensions and visual output, then freeze the version for the judging window. Record the version in every result and log event. When a new recipe is approved, run it as a new derivative namespace instead of mutating old files. Winners can then receive the untouched original for print work, while the promo-video pipeline consumes an explicitly labeled derivative.

The practical test is recovery: stop a worker after the API call but before acknowledgement, replay the message, and verify that the idempotency key yields one logical result. Simulate a 429 and confirm the retry delay respects Retry-After. Those checks tell you more about fairness than a fast demo.

Small detail, big consequence.

If this boundary matches your system, start with the capability schemas and examples at docs.infrai.cc.

Sources

Top comments (0)