API 串接:抓即時資料自動做成每日報告影片
學 data fetching、delayRender、dynamic metadata——做一支自動抓 Google Analytics(或任何 API)產生的每日報告影片。可排程、可部署、零人工介入。
成品預覽
這就是跟著教學最後能做出來的成品——一支 15 秒的「每日營收報告」動畫:標題卡、NT$ 1,248,000 營收計數、訂單數 / 客單價 / 熱門商品三大指標、▲ +12% 成長率與 sparkline 收尾。這支影片之後會被 cron 每天自動抓 API 生出來一支新的。
這篇會做出什麼
一支每天自動生成的「昨日營收報告」影片,15 秒長:
- 執行時從假的
https://api.example.com/daily-report抓資料 - 根據資料動態計算 composition 長度
- 顯示昨日營收、訂單數、最熱門商品、比前一天成長 %
- 如果資料載入失敗顯示 fallback 畫面
- 可以部署成 cron job 每天早上 8 點自動產生昨日報告
做完後你會理解:Remotion 不只做動畫,是資料驅動的影片工廠。
前置知識
- T19 參數化影片模板 — 先懂 props 和 schema
- 會用
fetch()和 async/await
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 秒。
💡
delayRender與timeout的完整說明在 /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 抓 API | Step 3 |
delayRender / continueRender | Step 4 |
| API 失敗 fallback | Step 5 |
| 環境變數與 API 金鑰 | Step 6 |
| Cron job 自動化 | Step 7 |
| S3 上傳 | Step 8 |
本篇涵蓋的官方文件
- /docs/data-fetching — API 資料抓取
- /docs/dynamic-metadata — 動態 metadata
- /docs/timeout —
delayRendertimeout 設定 - /docs/env-variables — 環境變數
- /docs/ssr — Server-side rendering
常見問題
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。
下一步
- T16:批量渲染——CSV 一次產 100 支影片 — 一次產多日的報告
- T20:打造影片 SaaS API + Docker 部署 — 把這條管線做成 API service
有問題歡迎到 FB 社群 討論!