Run Cron Jobs with CloudBase Cloud Function Timer Triggers
In one sentence: Add a
type: timerconfiguration with a 7-field cron expression tofunctions[].triggersincloudbaserc.json, then runtcb fn deployto schedule a Cloud Function to trigger at a fixed time — typical use cases are daily reports, data cleanup, and cache warming.Estimated time: 25 minutes | Difficulty: Basic
Applicable Scenarios
- Applicable: Batch processing that runs once per day / hour / week at a fixed time (daily reports, reconciliation, cleaning expired data, warming cache)
- Applicable: Aggregating external async events into daily-level reports
- Not applicable: Second-level / millisecond-level real-time scheduling (message queues or persistent connections are more appropriate)
- Not applicable: Critical transactions requiring strict "run exactly once globally" semantics (Timer Triggers may fire twice in edge cases — see "Step 4: Idempotency")
Prerequisites
| Dependency | Version |
|---|---|
| Node.js (Cloud Function runtime) | ≥ 16.13 |
@cloudbase/cli | latest |
@cloudbase/node-sdk | 3.18.1 (if the scheduled task reads or writes the database) |
Also required:
- An active CloudBase environment ID
- A
cloudbaserc.jsonat the project root; if not present, runtcb initto generate one
Step 1: Write a Minimal Scheduled Function
Create cloudfunctions/dailyReport/index.js:
const cloudbase = require('@cloudbase/node-sdk');
const app = cloudbase.init({
env: process.env.TCB_ENV || cloudbase.SYMBOL_CURRENT_ENV,
});
const db = app.database();
exports.main = async (event, context) => {
// When triggered by a timer, event contains Type / Time fields (check trigger logs for exact fields)
console.log('[cron] triggered at', new Date().toISOString(), 'event:', event);
const since = new Date(Date.now() - 24 * 3600 * 1000);
const orders = await db
.collection('orders')
.where({
createdAt: db.command.gte(since),
})
.count();
await db.collection('daily_reports').add({
date: new Date().toISOString().slice(0, 10),
orderCount: orders.total,
generatedAt: db.serverDate(),
});
return { ok: true, orderCount: orders.total };
};
cloudfunctions/dailyReport/package.json:
{
"name": "dailyReport",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@cloudbase/node-sdk": "^3.18.1"
}
}
Step 2: Configure the Timer Trigger
Add a section to cloudbaserc.json in the project root:
{
"envId": "your-env-id",
"functionRoot": "./cloudfunctions",
"functions": [
{
"name": "dailyReport",
"timeout": 60,
"memorySize": 256,
"runtime": "Nodejs16.13",
"handler": "index.main",
"triggers": [
{
"name": "every-day-9am",
"type": "timer",
"config": "0 0 9 * * * *"
}
]
}
]
}
config is a 7-field cron expression with the order "second minute hour day month weekday year". This differs from Linux's 5-field cron (minute hour day month weekday) — a common mistake.
| Meaning | Cron |
|---|---|
| Every day at 9:00 AM | 0 0 9 * * * * |
| Every hour on the hour | 0 0 * * * * * |
| Every 5 minutes | 0 */5 * * * * * |
| Weekdays 9 AM –6 PM, every hour | 0 0 9-18 * * 1-5 * |
| 1st of every month at 2:00 AM | 0 0 2 1 * * * |
| Every Monday at 10:00 AM | 0 0 10 * * 1 * |
Timezone is as shown in the Console and documentation. CloudBase Timer Trigger timezone is labeled on the Console's "Cloud Functions → Triggers" page. Confirm it before deployment to avoid accidentally scheduling to "5:00 AM" by configuring in UTC.
Step 3: Deploy
tcb login --apiKeyId your-key-id --apiKey your-key
tcb fn deploy dailyReport --httpFn -e your-env-id
After deployment, go to Console → Cloud Functions → dailyReport → Triggers to verify that the every-day-9am trigger configured in cloudbaserc.json appears.
If the trigger was not auto-created, sync it separately:
tcb fn trigger create -e your-env-id
(Command subject to the current version of the CloudBase CLI documentation.)
To temporarily disable, click "Disable" in the Console — no redeployment needed.
Step 4: Idempotency
Timer Triggers fire once at the specified time in most cases, but may fire twice in quick succession during edge cases such as releases or node switches. If the business cannot tolerate duplicate executions, add an idempotency gate:
exports.main = async (event, context) => {
const today = new Date().toISOString().slice(0, 10);
// 1. Use date + task name as idempotency key; try to insert a lock record
try {
await db.collection('cron_locks').add({
_id: `dailyReport-${today}`, // throws if _id already exists
acquiredAt: db.serverDate(),
});
} catch (e) {
if (e.code === 'DOC_ALREADY_EXISTS' || e.code === -502002) {
console.log('[cron] already ran today, skip');
return { ok: true, skipped: true };
}
throw e;
}
// 2. Business logic
// ...
return { ok: true };
};
Key points:
- Using "task name + time window" as
_idensures uniqueness and allows the lock to roll over naturally by day - The
cron_lockscollection can have a short retention period (7 days); configure auto-expiry in Console under "Database → Collection → Auto Expiry" to prevent accumulation of old data - The
e.codefield name above depends on the actual error object structure; logconsole.log(e)first when encountering an error to inspect the structure
Step 5: Failure Alerting
Scheduled tasks that fail will likely go unnoticed unless failures are surfaced proactively. The simplest integration is a WeCom group bot webhook:
const https = require('https');
async function notifyWecom(message) {
const webhook = process.env.WECOM_WEBHOOK;
if (!webhook) return;
const body = JSON.stringify({
msgtype: 'text',
text: { content: message },
});
return new Promise((resolve) => {
const req = https.request(
webhook,
{ method: 'POST', headers: { 'Content-Type': 'application/json' } },
(res) => res.on('data', () => {}).on('end', resolve),
);
req.write(body);
req.end();
});
}
exports.main = async (event) => {
try {
// Business logic
} catch (err) {
await db.collection('cron_failures').add({
task: 'dailyReport',
error: err.message,
stack: err.stack,
failedAt: db.serverDate(),
});
await notifyWecom(`[cron failed] dailyReport: ${err.message}`);
throw err; // rethrow so the platform logs it as well
}
};
For the complete WeCom webhook integration, see connect-wecom-webhook-cloud-function.
Verification
- After deployment, Console "Cloud Functions → dailyReport → Triggers" should show the trigger configuration
- To avoid waiting until 9 AM, temporarily change the cron to
0 */1 * * * * *(every minute), redeploy, verify it fires a couple of times, then change it back - Console "Cloud Functions → dailyReport → Invocation Logs" — filter for "Timer Trigger" source; execution records at the corresponding time should appear
- Console "Database → daily_reports" should have a new record with
dateequal to today - Intentionally
throw new Error('test')in the function and redeploy; confirm the WeCom group receives the alert, then roll back
Common Errors
| Error symptom | Cause | Fix |
|---|---|---|
| Trigger not created after deployment | triggers field in cloudbaserc.json is in the wrong location (placed at the top level instead of inside functions[]) | triggers must be nested under an element of the functions array |
| Cron expression fails to deploy | Using 5-field Linux cron format by mistake | Change to 7-field: second minute hour day month weekday year |
| Time is off by 8 hours | Timezone not confirmed | Check the timezone label on the Console's Triggers page; if UTC, recalculate the expression in UTC |
| Executed twice at the same time | Edge case during deployment or scaling | Add an idempotency lock (Step 4) |
| Cross-day statistics fluctuate around midnight | new Date() and database serverDate() Timezone mismatch | Write key time fields consistently with db.serverDate(); use only date strings (YYYY-MM-DD) as keys in business logic |
| Function cut off after 60 seconds | Default timeout value is too small | Increase timeout in cloudbaserc.json; maximum is 900 seconds |
Related Documentation
- Timer Trigger Configuration —
cloudbaserc.jsonfields and cron field semantics - CloudBase CLI Trigger Commands —
tcb fn triggersubcommands - Cloud Function Logs and Alerts — invocation log queries
- connect-wecom-webhook-cloud-function — failure alerts via WeCom
Next Steps
- Complete the alerting channel: connect-wecom-webhook-cloud-function
- Send Subscribe Messages to users on a schedule: add-subscribe-message-cloud-function
- Batch processing with multi-tenant isolation: secure-database-multi-tenant-rules