Skip to main content
Wire Type: Scheduled Task

Cron jobs,
same functions.

wireScheduler turns your Pikku functions into scheduled tasks with standard cron expressions and middleware support.

The Basics

Schedule in two lines

Write a void function, add a cron expression. Pikku calls it on schedule.

dailySummary.func.tsfunc.ts
const dailySummary = pikkuVoidFunc({
title: 'Daily Summary',
func: async ({ db, emailService, logger }) => {
logger.info('Generating daily summary')
const stats = await db.getDailyStats()
await emailService.sendSummary(stats)
}
})
scheduled.wiring.tswiring.ts
wireScheduler({
name: 'dailySummary',
schedule: '0 9 * * *', // Every day at 9:00 AM
func: dailySummary,
})

Standard cron format

Five-field Unix cron expressions — nothing proprietary to learn

No input, no output

Scheduled functions use pikkuVoidFunc — they run on a timer, not on demand

Service injection

Access databases, email, logging, and any other service via dependency injection

Cron Format

Standard five-field cron

Nothing proprietary. If you know cron, you know Pikku scheduling.

┌─── minute (0-59) │ ┌─── hour (0-23) │ │ ┌─── day of month (1-31) │ │ │ ┌─── month (1-12) │ │ │ │ ┌─── day of week (0-6) │ │ │ │ │
* * * * *
*/5 * * * *Every 5 minutes
0 9 * * *Daily at 9:00 AM
0 9 * * 1Every Monday at 9:00 AM
0 0 1 * *First of month at midnight
0 */6 * * *Every 6 hours
30 2 * * 0Sundays at 2:30 AM

Wire Object

Runtime context per execution

Every scheduled function gets a wire.scheduledTask object with metadata and control methods.

name & schedule

Access the task name and cron expression at runtime — useful for logging and metrics.

executionTime

The timestamp of the current execution. Use it for time-windowed queries and audit trails.

skip(reason?)

Skip the current execution with an optional reason. The task stays scheduled — only this run is skipped.

weeklyCleanup.func.ts
const weeklyCleanup = pikkuVoidFunc({
title: 'Weekly Cleanup',
func: async ({ db, logger }, _input, wire) => {
logger.info(`Running: ${wire.scheduledTask.name}`)
logger.info(`Schedule: ${wire.scheduledTask.schedule}`)
logger.info(`Execution time: ${wire.scheduledTask.executionTime}`)

const staleCount = await db.countStaleTodos()
if (staleCount === 0) {
// Skip this execution — nothing to clean
wire.scheduledTask.skip('No stale todos found')
return
}

await db.deleteCompletedTodos({ olderThan: '30d' })
logger.info(`Cleaned ${staleCount} stale todos`)
}
})

Middleware

Observe every execution

Wrap scheduled tasks with middleware for logging, metrics, error alerting, or anything else. Per-task or global.

scheduler-middleware.ts
const schedulerMetrics = pikkuMiddleware(
async ({ logger }, { scheduledTask }, next) => {
const start = Date.now()
logger.info(`Task started: ${scheduledTask.name}`)

try {
await next()
logger.info(`Task completed: ${scheduledTask.name}`, {
duration: Date.now() - start
})
} catch (error) {
logger.error(`Task failed: ${scheduledTask.name}`, {
error: error.message,
duration: Date.now() - start
})
throw error
}
}
)

wireScheduler({
name: 'dailySummary',
schedule: '0 9 * * *',
func: dailySummary,
middleware: [schedulerMetrics],
})

Per-task middleware

Attach middleware to individual scheduled tasks via the middleware array

Access wire context

Middleware receives scheduledTask with name, schedule, and executionTime

Same model as HTTP

Onion-order execution — the same middleware patterns you already know

Deploy

Runs anywhere

Same wireScheduler code works in-process or serverless. Your functions don't change — only the runtime does.

In-process

InMemorySchedulerService — cron jobs run inside your server process.

Serverless

AWS Lambda, Azure Timer, Cloudflare — same function, triggered by the platform.

Start wiring cron jobs in 5 minutes

One command to scaffold a project with scheduled tasks already configured.

$ npm create pikku@latest

MIT Licensed  ·  Works with Lambda, Azure Timer, Cloudflare & in-process