cloud_queue
Better MediaDeveloper docs

Options

Complete reference for all createBetterMedia configuration options.

All options are passed to createBetterMedia(config). Only storage, database, and plugins are required.

import { createBetterMedia } from "@better-media/framework";

const media = createBetterMedia({
  storage,    // required
  database,   // required
  plugins,    // required
  jobs,       // optional
  fileHandling, // optional
  trustedPolicy, // optional
  events,     // optional
  settings,   // optional
});

storage — required

A StorageAdapter instance. Controls where file bytes are stored.

storage: StorageAdapter

Available adapters: S3 · Filesystem · Memory


database — required

A DatabaseAdapter instance (or a raw pg.Pool for the built-in Postgres path).

database: DatabaseAdapter | pg.Pool

Available adapters: Postgres · MySQL · SQLite · MongoDB


plugins — required

An ordered array of PipelinePlugin instances. Plugins run in the order provided within each phase.

plugins: PipelinePlugin[]

Available plugins: Validation · Media Processing · Virus Scan


jobs — optional

A JobAdapter for background execution. When omitted and background plugins are registered, the framework defaults to an in-process memory adapter (jobs run with setImmediate).

jobs?: JobAdapter

Provide a BullMQ adapter for production:

import { bullmqJobAdapter } from "@better-media/adapter-jobs-bullmq";

jobs: bullmqJobAdapter({
  connection: { host: "localhost", port: 6379 },
  queueName: "better-media:background",     // default
  defaultJobOptions: {
    attempts: 3,
    backoff: { type: "exponential", delay: 1000 },
  },
})

See Background Jobs for full setup.


fileHandling — optional

Controls how the framework loads file bytes before passing them to plugins.

fileHandling?: {
  maxBufferBytes?: number;
}
OptionDefaultDescription
maxBufferBytesundefined (no limit)Files larger than this are streamed to a temp file instead of loaded into memory. Requires the storage adapter to implement getSize and getStream.
fileHandling: {
  maxBufferBytes: 50 * 1024 * 1024, // 50 MB — stream files larger than this
}

trustedPolicy — optional

Controls which plugins are allowed to write to trusted metadata fields. Required only when registering a custom plugin that declares trustLevel: "trusted". See Plugin Architecture for details.


events — optional

Lifecycle event callbacks. See Events & Webhooks for the full reference.

events?: {
  onUploadComplete?: (result: MediaResult) => void | Promise<void>;
  onProcessingComplete?: (event: ProcessingCompleteEvent) => void | Promise<void>;
  onError?: (error: Error, context: ErrorEvent) => void | Promise<void>;
}
events: {
  onUploadComplete: createWebhookEmitter("https://example.com/hooks", process.env.WEBHOOK_SECRET),
  onError: async (err, ctx) => { await errorTracker.capture(err, ctx); },
}

settings — optional

Framework-level settings.

settings?: {
  logLevel?: "debug" | "info" | "warn" | "error";
}
OptionDefaultDescription
logLevel"info"Controls framework log verbosity.

Full Example

import { createBetterMedia, createWebhookEmitter } from "@better-media/framework";
import { S3StorageAdapter } from "@better-media/adapter-storage-s3";
import { KyselyDbAdapter } from "@better-media/adapter-db-kysely";
import { bullmqJobAdapter } from "@better-media/adapter-jobs-bullmq";
import { validationPlugin } from "@better-media/plugin-validation";
import { mediaProcessingPlugin } from "@better-media/plugin-media-processing";
import { virusScanPlugin, ClamScanner } from "@better-media/plugin-virus-scan";
import { schema } from "@better-media/core";
import { Kysely, PostgresDialect } from "kysely";
import { Pool } from "pg";

export const media = createBetterMedia({
  storage: new S3StorageAdapter({
    region: process.env.AWS_REGION!,
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    bucket: process.env.S3_BUCKET!,
  }),

  database: new KyselyDbAdapter(
    new Kysely({ dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }) }) }),
    { config: { provider: "pg" }, schema }
  ),

  plugins: [
    validationPlugin({
      executionMode: "sync",
      allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],
      maxBytes: 25 * 1024 * 1024,
      preventDuplicates: true,
    }),
    virusScanPlugin({
      scanner: new ClamScanner(),
      executionMode: "sync",
      onFailure: "abort",
    }),
    mediaProcessingPlugin({
      executionMode: "background",
      thumbnailPresets: [
        { name: "sm", width: 320, format: "webp" },
        { name: "lg", width: 1200, format: "webp" },
      ],
    }),
  ],

  jobs: bullmqJobAdapter({
    connection: { host: process.env.REDIS_HOST!, port: Number(process.env.REDIS_PORT!) },
    defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 1000 } },
  }),

  fileHandling: {
    maxBufferBytes: 50 * 1024 * 1024,
  },

  events: {
    onUploadComplete: createWebhookEmitter(process.env.WEBHOOK_URL!, process.env.WEBHOOK_SECRET),
    onError: async (err, ctx) => console.error("[media]", err.message, ctx),
  },

  settings: {
    logLevel: "info",
  },
});

On this page