VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
← Back to Blog

Programmatic Video for SaaS: From Postgres Rows to Personalized MP4s

May 10, 2025 · By VideoFlowLearn how to build an automated video generation pipeline using VideoFlow and Node.js to create personalized onboarding videos from Postgres data at scale.Programmatic Video for SaaS: From Postgres Rows to Personalized MP4s

Programmatic Video for SaaS: From Postgres Rows to Personalized MP4s

Personalized onboarding is the holy grail of SaaS user activation. We know that showing a user exactly how their data looks in your platform is more effective than any generic walkthrough. But until now, generating personalized video at scale meant either expensive third-party APIs or wrestling with fragile FFmpeg shell scripts.

With the rise of programmatic video for SaaS, engineering teams are moving this logic into the application layer. In this tutorial, we’ll build a Node.js pipeline that pulls user data from a Postgres database and uses VideoFlow to render a custom MP4 onboarding video.

The Architecture: Database to Render

To build an automated video generation api, you need three components:

  1. The Data Source: A database (Postgres) containing the personalization tokens (name, company logo, recent activity).
  2. The Composition Engine: A toolkit to define the video timeline as code. We'll use @videoflow/core.
  3. The Renderer: A headless environment to turn that code into a video file. We'll use @videoflow/renderer-server.

From database rows to video layers

Step 1: Composing the Personalized Template

VideoFlow uses a fluent TypeScript API to build a "VideoJSON" document. Unlike React-based alternatives, this JSON is portable and can be generated by any backend service.

Here is how we compose a simple onboarding scene. Notice how we use normalized [x, y] coordinates (0 to 1) so the template works at any resolution.

import VideoFlow from '@videoflow/core';

async function createOnboardingVideo(userData: { name: string; avatar: string }) {
  const $ = new VideoFlow({
    width: 1280,
    height: 720,
    fps: 30,
    backgroundColor: '#0b0b1f'
  });

  // 1. Add a background image (Logo or Workspace screenshot)
  const bg = $.addImage(
    { fit: 'cover', opacity: 0.4 },
    { source: 'https://assets.my-saas.com/onboarding-bg.jpg' }
  );

  // 2. Add the personalized greeting
  const welcome = $.addText({
    text: `Welcome, ${userData.name}!`,
    fontSize: 6, // 6% of project width
    color: '#FFFFFF',
    fontWeight: 700,
    position: [0.5, 0.4]
  }, {
    transitionIn: { transition: 'blurResolve', duration: '800ms' }
  });

  // 3. Add the user's avatar
  const avatar = $.addImage(
    { borderRadius: '50%', position: [0.5, 0.6] },
    { source: userData.avatar }
  );
  avatar.fadeIn('500ms');

  $.wait('3s');
  
  welcome.fadeOut('500ms');
  avatar.fadeOut('500ms');

  return $;
}

Step 2: Wiring up Postgres

In a production environment, you might trigger this render from a background job (like BullMQ) whenever a new user signs up. Here’s a script that pulls a row and triggers the render.

import { Client } from 'pg';
import VideoFlow from '@videoflow/core';
import '@videoflow/renderer-server'; // Registers the Node renderer

async function run() {
  const client = new Client({ connectionString: process.env.DATABASE_URL });
  await client.connect();

  // Fetch user data
  const res = await client.query('SELECT name, avatar_url FROM users WHERE id = $1', [123]);
  const user = res.rows[0];

  // Compose the video
  const $ = await createOnboardingVideo({
    name: user.name,
    avatar: user.avatar_url
  });

  // Render to disk using headless Chromium (Playwright)
  await $.renderVideo({
    outputType: 'file',
    output: `./renders/onboarding-${user.name}.mp4`,
    verbose: true
  });

  await client.end();
}

JSON as a video timeline

Why VideoFlow for SaaS?

If you've looked at Remotion alternatives, you'll notice VideoFlow takes a different path. While others rely on proprietary licenses or a heavy React runtime, VideoFlow is built on Apache-2.0 and treats videos as pure data.

  • No FFmpeg Required: By default, @videoflow/renderer-server uses WebCodecs inside a headless browser to encode frames. This avoids the complexity of managing FFmpeg binaries in your Docker containers.
  • Identical Renders: The same code you write on the server can be previewed in your dashboard using the DOM renderer or exported in the user's browser tab with the Browser renderer.
  • Cinematic Primitives: You get 27 transition presets (like blurResolve and fade) and 42 GLSL effects (like bloom and vignette) out of the box. No need to write custom interpolation logic for every animation.

Scaling Your Video Pipeline

Generating personalized video at scale is no longer a niche capability reserved for companies with massive engineering budgets. By treating your video as a stack of code-defined layers, you can build dynamic, data-driven content as easily as you build a web page.

Ready to start building?

For more on server-side rendering, check out our guide on how to render an MP4 in Node without FFmpeg.

VideoFlow

Open-source toolkit for composing videos from code.

Product

CoreRenderersReact Video EditorPlayground

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContactTermsPrivacy

From the blog

All posts →Programmatic Video Animation: A Deep Dive into VideoFlow KeyframesAgentic Video: How to Give Your AI Agent a VideoFlow ToolShotstack Alternative: Why Developers are Switching to Open Source VideoFlowVideoFlow vs. Remotion: The Developer's Guide to Code-to-Video in 2025How to Render MP4 in Node.js Without FFmpegProgrammatic Video for SaaS: From Postgres Rows to Personalized MP4s
© 2026 VideoFlow. Apache-2.0 core.