DEV Community

Cover image for How to create a Vercel Cron Job using NextJS 13
Chris Nowicki
Chris Nowicki

Posted on • Edited on • Originally published at chrisnowicki.io

How to create a Vercel Cron Job using NextJS 13

As a new developer I am constantly trying new technologies and recently have really enjoyed coding in NextJS 13 using their /app directory experimental feature.

Use Case

I wanted to be able to show total ALL TIME GitHub commits across my repositories on the about me section of my portfolio. Why, because I can lol. As I started working on the code I realized that this was going to be an expensive tasks from a resource perspective.

Assumptions

  1. You are familiar with nextJS 13/React Frameworks
  2. You are familiar with Vercel and getting your project linked using their CLI or gitHub Repository.

Limitations

Vercel has the following limitations based on your plan:

Screenshot of Vercel cron job limits

Let's Create a Cron Job!

  1. Create a directory in the /app/api folder called "cron"
  2. Create a folder within cron with the name of the cron job you want to run. example: /app/api/cron/github-metrics-sync
  3. Create a file called route.ts
  4. Your code should look like this:
// /api/cron/github-metrics-sync/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  ...
}
Enter fullscreen mode Exit fullscreen mode

Where ... you replace with code you want to run when the cron job is executed.

For my use case I am storing the metrics in a PlanetScale database. I then fetch the data from the GitHub API, parse through the data, and then update the database.

Let's see the full code for my route:

import { Octokit } from '@octokit/rest'
import { NextResponse } from 'next/server'
import { updateGithubMetrics } from '../../../../lib/planetscale'

// zod env type checking
import { env } from 'env'

export async function GET() {
  const octokit = new Octokit({
    auth: env.GITHUB_TOKEN,
  })

  // retrieve all repos from my account
  const repos = await octokit.request('GET /user/repos', {
    per_page: 100,
    affiliation: 'owner',
  })

  // count all repos
  const totalRepos = repos.data.length

  // retrieve all commits and count them
  let totalCommits = 0

  for (const repo of repos.data) {
    const commits = await octokit.request(
      'GET /repos/{owner}/{repo}/contributors',
      {
        owner: repo.owner.login,
        repo: repo.name,
      }
    )

    // only count commits from my account
    if (commits.data.length > 0) {
      for (const contributor of commits.data) {
        if (contributor.login === 'chris-nowicki') {
          totalCommits += contributor.contributions
        }
      }
    }
  }

  try {
    // update the planetscale database with new metrics
    updateGithubMetrics(totalCommits, totalRepos)
    return NextResponse.json({ totalCommits, totalRepos })
  } catch (error) {
    console.error(error)
    return NextResponse.json({ error })
  }
}
Enter fullscreen mode Exit fullscreen mode

I won't go over all the lines of code in this tutorial but if you have any questions please let me know!

FINAL STEP

Add a vercel.json file in the root of your project folder:

{
    "crons": [
        {
            "path": "/api/cron/github-metrics-sync",
            "schedule": "*/10 * * * *"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  • path is the location of the folder we created in step #2
  • schedule is telling vercel how often the cron job should run. */10 * * * * is running the cronjob about every 10 minutes. You can play around with different schedules using crontab guru.

Once that is complete deploy your project to vercel and you can check the status of your cron job by going to the settings > crons menu for your project. You should see something like this:

Screen shot of Vervel Dashboard with Cron Job Menu

CONCLUSION

I hope this was helpful! I found this to be a nice solution for the server to take on the expensive task so those visiting my site don't have to wait for the fetch/parsing from GitHub API for the /about page to load.

Top comments (11)

Collapse
 
goodjoe01 profile image
Ivan Gamboa

I am following everything you put in the post, but for some reason my cronjob does not run at the scheduled time, I enter the vercel dashboard and I see the cronjob created correctly, also when I hit "run" if I see the log, but automatically does not run it.

I am using version "13.4.13", from next.

Collapse
 
rrhapsod profile image
Renan Rondon

Same problem here. I'm on Next 13.4.17

Collapse
 
chrisnowicki profile image
Chris Nowicki

Hi @rrhapsod, Are you using a Vercel Pro account? Can you provide a code snippet of your vercel.json file? or a link to your repository?

Thread Thread
 
rrhapsod profile image
Renan Rondon

It's the exact same problem from this discussion:
github.com/orgs/vercel/discussions...

Thread Thread
 
chrisnowicki profile image
Chris Nowicki

question ... in your build logs is it a server route or does it show a circle next to it indicating a static route?

Thread Thread
 
chrisnowicki profile image
Chris Nowicki

If it shows a circle or static route ... try adding this in the route right before the function:

// Nextjs route segment config
export const dynamic = 'force-dynamic' // Force dynamic (server) route instead of static page
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
chrisnowicki profile image
Chris Nowicki

This is what I mean by if the route is shown as static or a server component. Here is a screen shot of my latest build log. /api/github-metrics-sync is a Cron Job.

Image description

Collapse
 
goodjoe01 profile image
Ivan Gamboa

Hi Renan, I'm using upstash service and it works perfectly.

I think the "issue" with vercel was that it was indicating a local time but you must indicate UTC time, I think that was the "problem". I noticed that when using upstash, since in this service it specifies that.

Thread Thread
 
chrisnowicki profile image
Chris Nowicki

@goodjoe01 great! looks like you figured it out?

Thread Thread
 
goodjoe01 profile image
Ivan Gamboa

I did, thank you anyway!

Collapse
 
chrisnowicki profile image
Chris Nowicki • Edited

Hi @goodjoe01, Are you using a Vercel Pro account? Can you provide a code snippet of your vercel.json file? or a link to your repository?