Next.js template

Scheduled actions and Cron jobs, ready to run

A working Next.js example for running application actions once in the future or repeatedly with RocketStack.

SchedulerFunction
CronFunction

Architecture

Run once: Next.js → Scheduler → Function

Run repeatedly: Cron → Function

Source path: examples/nextjs-scheduled-actions

What the template includes

  • Next.js scheduling route

    App Router handler at POST /api/schedules for one-time actions.

  • Future-time validation

    Validates account IDs and rejects past execution times before creating a schedule.

  • RocketStack Scheduler integration

    Creates one-off schedules with typed payloadTemplate values and Function delivery.

  • RocketStack Cron integration

    Creates recurring unix5 cron jobs with UTC timezone and Function delivery.

  • Typed processing Function

    One Function handles trial reminders and nightly report payloads safely.

  • Setup and testing guide

    README covers deployment, environment variables, and console inspection.

One-time versus recurring

  • Run once with Scheduler

    Create a one-off schedule from a server-side route when a user chooses a future time.

    Next.jsSchedulerFunction
  • Run repeatedly with Cron

    Create a recurring cron job that invokes the same Function on a fixed schedule.

    CronFunction

Architecture

One-time action:
Next.js server route
  |
  v
RocketStack Scheduler
  |
  v
RocketStack Function

Recurring action:
RocketStack Cron
  |
  v
RocketStack Function

Key code excerpts

Create a one-time schedule

Create a one-time schedule
export async function createOneTimeSchedule(input: {
  name: string
  runAt: string
  functionId: string
  payload: ScheduledActionPayload
}): Promise<ScheduleCreatedResponse> {
  const scheduler = getSchedulerClient()

  const response = await scheduler.createSchedule({
    name: input.name,
    runAt: input.runAt,
    deliveryTarget: {
      type: 'function',
      functionId: input.functionId
    },
    payloadTemplate: input.payload
  })

  const data = response.data?.data
  if (!data?.scheduleId || !data?.status) {
    throw new Error('Scheduler create returned an unexpected response')
  }

  return {
    scheduleId: data.scheduleId,
    status: data.status,
    runAt: input.runAt
  }
}

Create a recurring Cron job

Create a recurring Cron job
export async function createCronJob(input: {
  name: string
  expression: string
  functionId: string
  payload: RecurringActionPayload
}): Promise<CronCreatedResponse> {
  const cron = getCronClient()

  const response = await cron.createCronJob({
    name: input.name,
    schedule: {
      expression: input.expression,
      format: 'unix5',
      timezone: 'UTC'
    },
    deliveryTarget: {
      type: 'function',
      functionId: input.functionId
    },
    payloadTemplate: input.payload
  })

  const data = response.data?.data
  if (!data?.cronJobId || !data?.status) {
    throw new Error('Cron create returned an unexpected response')
  }

  return {
    cronJobId: data.cronJobId,
    status: data.status,
    schedule: data.schedule
  }
}

Process the scheduled action

Process the scheduled action
exports.handler = async function handler(event) {
  const payloadTemplate = event?.payloadTemplate
  if (!isActionPayload(payloadTemplate)) {
    throw new Error('Invalid scheduled action payload')
  }

  const completionStore = createKvCompletionStore()
  const processedAt = new Date().toISOString()

  if (event.type === 'schedule') {
    const executionDate = (event.scheduledFor || processedAt).slice(0, 10)
    const completionKey = `trial-reminder:${payloadTemplate.accountId}:${executionDate}`

    if (completionStore && await completionStore.hasCompleted(completionKey)) {
      return { success: true, actionType: payloadTemplate.type, skipped: true }
    }

    // Replace this with your application-specific reminder logic.
    await processTrialReminder(payloadTemplate, executionDate)
  }

  if (event.type === 'cron') {
    const reportDate = (event.scheduledAt || processedAt).slice(0, 10)
    const completionKey = `nightly-report:${payloadTemplate.workspaceId}:${reportDate}`

    if (completionStore && await completionStore.hasCompleted(completionKey)) {
      return { success: true, actionType: payloadTemplate.type, skipped: true }
    }

    // Replace this with your report-generation workflow.
    await processNightlyReport(payloadTemplate, reportDate)
  }

  return {
    success: true,
    actionType: payloadTemplate.type,
    resourceId: event.scheduleId || event.cronJobId,
    processedAt
  }
}

Setup steps

Open the README
  1. Step 1

    Clone the example

    Copy examples/nextjs-scheduled-actions and install dependencies.

  2. Step 2

    Configure environment variables

    Set ROCKETSTACK_API_KEY and ROCKETSTACK_FUNCTION_ID in .env.local.

  3. Step 3

    Deploy the Function

    Deploy process-scheduled-action and copy the Function ID.

  4. Step 4

    Create a schedule or Cron job

    Use the local app to create a one-time schedule or recurring Cron job.

Full install, deployment, and troubleshooting steps are in examples/nextjs-scheduled-actions/README.md.

UTC and time-zone handling

  • Browser input is local time

    The example uses accessible date and time inputs interpreted in the user’s local time zone.

  • Server validates UTC

    The API route receives an ISO timestamp, validates that it is in the future, and passes RFC 3339 UTC to Scheduler.

  • Cron uses UTC by default

    This example sets schedule.timezone to UTC. Adjust the timezone field if your product needs another IANA timezone.

  • Preserve user context

    Store the user’s original time zone in your application when future daylight-saving behavior matters.

Repeated-execution guidance

  • No exactly-once guarantee

    RocketStack may deliver a schedule or cron trigger more than once. Design handlers to be safe on retry.

  • Optional KV markers

    The demo Function can store completion markers in KV, but get-then-put is not an atomic claim.

  • Use your application database

    Production applications should persist completion state where strong business guarantees are required.

Production considerations

  • Keep RocketStack credentials server-side and out of client bundles.
  • Validate account and workspace identifiers before creating schedules or cron jobs.
  • Store Scheduler runAt values in UTC and preserve user time-zone context separately when needed.
  • Make reminder and report handlers idempotent because delivery can repeat.
  • Use Queue for long-running work instead of blocking inside a Function.
  • Monitor failed Function executions and review Scheduler or Cron invocation history in the console.
  • Disable or delete obsolete schedules and cron jobs when business rules change.

Scheduled-actions workflow · Scheduler documentation · Cron documentation · Pricing

Schedule your first application action

Create a free account, deploy the Function, and run the one-time or recurring example locally.