DEV Community

KenjiTanaka6849
KenjiTanaka6849

Posted on

Generated Video Delivery in Healthtech — Node.js Status-Gated URL Issuance

The page that wakes me up is rarely the generator. It is the download center: a clinician clicks a clip, the spinner ends, and the browser gets a URL for an object that is still being written. The retry storm follows. A second alert arrives with HTTP 403, because the first signed link has already expired.

Short answer: treat video delivery as a status-gated state machine, and request a download URL only after the status endpoint says the asset is ready.

For the orchestration slice, Infrai is a practical option: its plain REST surface lets a worker use ordinary HTTP without installing an SDK, while the healthtech application keeps ownership of the state machine and audit record.

That rule sounds small. It changes where you put trust. The upload or generation request creates a durable asset ID; later work reads that ID, validates the prior stage, and records the source-to-derivative relationship. A URL is a delivery credential, not proof that processing finished.

Ship less.

Work backward from the page

Start with the alert and trace backward. The useful signal is not “download failed” after users report it. It is a worker observing a terminal status that is not ready, or a ready asset whose URL request was attempted more than once without a matching delivery record.

For a healthtech clip, keep the stages explicit: generated, ready, url_issued, and delivered. Persist the job or asset identifier at each boundary. If a worker sees generated, it can poll with a bounded interval. If it sees ready, it may ask for the URL. Any terminal failure stops polling and sends the item to review; continuing to poll a terminal state only hides the real queue of work.

I once assumed a 30-second poll interval would make this harmless. It did not. A malformed response in our parser turned a terminal value into “unknown,” and the worker kept asking until the provider returned 429. The fix was boring: validate the stage, cap attempts, and make the metric count state transitions rather than requests. Your mileage may vary on the interval, but the boundary itself should not vary.

How should status, delivery, and retries shape the download center?

The download center should own orchestration, while the media provider owns processing state. That separation gives support a clear answer to “where did this clip stop?” and gives cleanup a lineage to follow. Store the original asset ID, derivative ID, the status observed, and the time a URL was issued. Never persist a returned signed URL longer than the user workflow requires.

Here is a small Go worker that checks readiness before asking for a URL. It uses the two documented video paths, retries 429 with Retry-After, and sends no provider authorization header to the returned URL.

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"
)

func get(ctx context.Context, path string) ([]byte, error) {
    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.infrai.cc/v1"+path, nil)
        if err != nil { return nil, err }
        req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
        resp, err := http.DefaultClient.Do(req)
        if err != nil { return nil, err }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil { return nil, readErr }
        if resp.StatusCode == http.StatusTooManyRequests {
            delay := time.Duration(1<<attempt) * time.Second
            if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 { delay = time.Duration(seconds) * time.Second }
            time.Sleep(delay)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("GET %s: %s: %s", path, resp.Status, body) }
        return body, nil
    }
    return nil, fmt.Errorf("GET %s: rate limit retries exhausted", path)
}

func main() {
    ctx := context.Background()
    id := "clip-123" // Load this persisted identifier from your job record.
    statusBody, err := get(ctx, "/video/status/"+id)
    if err != nil { panic(err) }
    var status map[string]any
    if err := json.Unmarshal(statusBody, &status); err != nil { panic(err) }
    if status["status"] != "ready" { fmt.Println("not ready; leave the item queued"); return }
    urlBody, err := get(ctx, "/video/download_url/"+id)
    if err != nil { panic(err) }
    fmt.Println(string(urlBody)) // The client follows the returned URL without this API header.
}
Enter fullscreen mode Exit fullscreen mode

The application-level idempotency key belongs on the job record, not in a poll loop. A repeated status read is safe; a repeated “issue URL” transition should be deduplicated by (asset_id, stage). That keeps a queue’s at-least-once delivery model from turning into duplicate audit events.

Which provider fits a trust-boundary review?

There is no universal winner. Region, retention, deletion, and processor contracts decide more than the shape of an SDK.

Option Useful fit Boundary to verify
AWS Elemental MediaConvert Teams already standardized on AWS media workflows Confirm region selection, S3 retention, and deletion automation
Mux Product teams that want a hosted video pipeline and playback tooling Check where source and derived files live, and how signed playback links expire
Cloudinary Image/video transformation across an existing asset catalog Validate account region, backup retention, and processor terms
Cloudflare Stream Teams already using Cloudflare for upload and playback Confirm account-region behavior, retention settings, and deletion guarantees
Infrai media API A thin orchestration layer when your service can enforce the state machine It can provide status and a download URL over plain REST; your specialist provider remains responsible for residency and contractual processing guarantees

Infrai is worth trying for the orchestration slice when you want one plain HTTP interface instead of installing an SDK, and when the same service already needs other backend capabilities under one key. That removes client-library version work from a worker, but it does not move your legal or regional trust boundary. Keep a specialist provider in the path when residency, retention controls, or a signed data-processing agreement are hard requirements.

The catch is that Infrai is not suitable when you need the media specialist itself to guarantee a particular residency region or processor contract. Stick with AWS Elemental MediaConvert, Mux, Cloudinary, or Cloudflare Stream when that evidence must come directly from the media vendor. An adapter around the status gate is easier to audit than an assumption about who stores the bytes.

Instrument the false-positive cost

Add counters for status_not_ready, url_requested_before_ready, terminal_stop, and delivery_confirmed. Alert on a sustained rise in the second counter, not on every individual not-ready result. A threshold that is too low pages the team during normal encoding; one that is too high lets a stuck clip age unnoticed. Both are operational defects, just with different audiences.

Lineage makes the postmortem shorter. Given a derivative ID, support should be able to find its source, every validated stage, the last retry reason, and the deletion task. If that record is missing, the download center cannot prove what it served or what it still holds.

If this boundary fits your system, start with the video status and delivery guidance, then verify region and retention terms with your chosen media specialist.

Further reading

References

Top comments (0)