Remotion LabRemotion Lab
參數化與輸出API 串接:抓即時資料自動做成每日報告影片
apidata-fetchingdelayrenderautomationintermediate

API 串接:抓即時資料自動做成每日報告影片

學 data fetching、delayRender、dynamic metadata——做一支自動抓 Google Analytics(或任何 API)產生的每日報告影片。可排程、可部署、零人工介入。

成品預覽

這就是跟著教學最後能做出來的成品——一支 15 秒的「每日營收報告」動畫:標題卡、NT$ 1,248,000 營收計數、訂單數 / 客單價 / 熱門商品三大指標、▲ +12% 成長率與 sparkline 收尾。這支影片之後會被 cron 每天自動抓 API 生出來一支新的。


這篇會做出什麼

一支每天自動生成的「昨日營收報告」影片,15 秒長:

  1. 執行時從假的 https://api.example.com/daily-report 抓資料
  2. 根據資料動態計算 composition 長度
  3. 顯示昨日營收、訂單數、最熱門商品、比前一天成長 %
  4. 如果資料載入失敗顯示 fallback 畫面
  5. 可以部署成 cron job 每天早上 8 點自動產生昨日報告

做完後你會理解:Remotion 不只做動畫,是資料驅動的影片工廠


前置知識


Step 1:先做固定資料的版本

新增 Composition "DailyReport":
- 1920x1080 fps 30 durationInFrames 450
- 深色背景

畫面:
1. 標題 "2026/04/05 營收報告"
2. 大字 "NT$ 1,248,000" 黃色
3. 訂單數 "42 筆"
4. 最熱門商品 "Air Max 2026"
5. 成長率 "+12% vs 前日" 綠色

先 hardcode 這些數據,下一步再改成 API 抓。

Step 2:定義 Schema

用 zod 定義 ReportSchema:

export const ReportSchema = z.object({
  date: z.string(),
  revenue: z.number(),
  orderCount: z.number(),
  topProduct: z.string(),
  growthPercent: z.number(),
});

Composition 加 schema 和 defaultProps(用假資料)

Step 3:在 calculateMetadata 裡 fetch 資料

這是本篇的核心技巧。calculateMetadata 是 async——可以打 API:

在 Composition 加 calculateMetadata:

calculateMetadata={async () => {
  const response = await fetch("https://api.example.com/daily-report");
  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }
  const data = await response.json();

  return {
    props: {
      date: data.date,
      revenue: data.revenue,
      orderCount: data.orderCount,
      topProduct: data.topProduct,
      growthPercent: data.growthPercent,
    },
    // 根據資料長度動態決定時長
    durationInFrames: 450,
  };
}}

這個技巧的魔力:渲染開始之前資料就抓好了,直接當 props 傳給元件。元件內完全不用寫 useEffect、不用擔心非同步。

💡 Data fetching 與 calculateMetadata 的完整範式在 /docs/data-fetching/docs/dynamic-metadata


Step 4:在元件內 fetch(不推薦但有時需要)

如果 API 要認證、或資料結構複雜,你可能想在元件內 fetch。這時候要用 delayRender

如果一定要在元件內 fetch:

import { delayRender, continueRender } from "remotion";

const [data, setData] = useState(null);
const [handle] = useState(() => delayRender("Fetching data"));

useEffect(() => {
  fetch("https://api.example.com/daily-report")
    .then((res) => res.json())
    .then((json) => {
      setData(json);
      continueRender(handle);
    })
    .catch((err) => {
      cancelRender(err);
    });
}, [handle]);

if (!data) return null;

delayRender 的作用:告訴 Remotion「這一幀還沒準備好,等我」。Remotion 會暫停渲染直到 continueRender(handle) 被呼叫。預設超時是 30 秒。

💡 delayRendertimeout 的完整說明在 /docs/timeout


Step 5:處理 API 失敗

calculateMetadata throw error 會導致整個渲染失敗。加個 fallback:

包一層 try/catch:

calculateMetadata={async () => {
  try {
    const response = await fetch("https://api.example.com/daily-report");
    if (!response.ok) throw new Error("API error");
    const data = await response.json();
    return { props: { ...data } };
  } catch (err) {
    console.error("API 失敗,用 fallback 資料", err);
    return {
      props: {
        date: new Date().toISOString().split("T")[0],
        revenue: 0,
        orderCount: 0,
        topProduct: "資料載入失敗",
        growthPercent: 0,
      },
    };
  }
}}

在元件裡可以用 props.orderCount === 0 判斷是否是 fallback 狀態,顯示「⚠️ 資料暫時無法取得」的 UI。


Step 6:環境變數存 API 金鑰

不能把 API 金鑰 hardcode。用 env:

新建 .env.local:

REPORT_API_KEY=sk_xxx
REPORT_API_URL=https://api.yourcompany.com/daily-report

在 calculateMetadata 用:
const response = await fetch(process.env.REPORT_API_URL, {
  headers: {
    Authorization: `Bearer ${process.env.REPORT_API_KEY}`,
  },
});

記得 .env.local 加進 .gitignore

💡 環境變數在 Remotion Studio / renderer / Player 的傳遞方式在 /docs/env-variables


Step 7:自動化——cron job 每天渲染

把渲染包成腳本,用系統 cron 排程:

新建 scripts/render-daily-report.mjs:

import { bundle } from "@remotion/bundler";
import { renderMedia, selectComposition } from "@remotion/renderer";

const bundled = await bundle({ entryPoint: "./src/index.ts" });
const composition = await selectComposition({
  serveUrl: bundled,
  id: "DailyReport",
});

const date = new Date().toISOString().split("T")[0];
await renderMedia({
  composition,
  serveUrl: bundled,
  codec: "h264",
  outputLocation: `out/report-${date}.mp4`,
});

console.log(`✅ Rendered report for ${date}`);

加到 crontab:

# 每天早上 8 點
0 8 * * * cd /path/to/project && node scripts/render-daily-report.mjs

每天早上 8 點會自動抓昨日資料、算數據、渲染影片。影片可以自動上傳到 Slack、YouTube Unlisted、內部儀表板。


Step 8:渲染完自動上傳

在 render 腳本最後加上傳邏輯:

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({ region: "ap-northeast-1" });
const file = await fs.readFile(outputLocation);
await s3.send(new PutObjectCommand({
  Bucket: "daily-reports",
  Key: `report-${date}.mp4`,
  Body: file,
  ContentType: "video/mp4",
}));

console.log(`📤 Uploaded to s3://daily-reports/report-${date}.mp4`);

到這一步整條管線就完整了:cron → fetch → render → upload,零人工。


完成!回顧學到的概念

概念用在哪
calculateMetadata 抓 APIStep 3
delayRender / continueRenderStep 4
API 失敗 fallbackStep 5
環境變數與 API 金鑰Step 6
Cron job 自動化Step 7
S3 上傳Step 8

本篇涵蓋的官方文件


常見問題

Q:calculateMetadata 會在 Studio 每次重新整理都跑嗎? A:會。如果你的 API 有 rate limit,Studio 預覽期間可能會打爆配額。解法:在 Studio 時用 cached mock 資料(process.env.REMOTION_STUDIO === "1" 判斷)。

Q:delayRender 超過 30 秒會怎樣? A:Remotion 會 throw 一個 timeout error,整個渲染失敗。可以調整:delayRender("label", { timeoutInMilliseconds: 60000 })

Q:可以串 GraphQL 嗎? A:可以。fetch() POST 一個 GraphQL query 就是 GraphQL——Remotion 不限 REST。或者用 graphql-request 套件。

Q:cron 執行時沒有顯示器,Remotion 會不會崩? A:不會。Remotion 的 renderer 用的是 headless Chromium,完全不需要顯示器。在伺服器上跑最適合。看 T20 影片 SaaS API


下一步

有問題歡迎到 FB 社群 討論!