bbelda.me

How to schedule Cron Jobs for your serverless Next.js application.

October 10, 2020

I will teach you how to create and manage Cron Jobs for a serveless website or application. I take the side of Next.js not because it is really related, but because that's how I used it, and because this article wouldn't be found if it's not really specific. Now let's get to the point.

But why should I do that ?

When building Next.js application, you can use hosting services like Netlify and Vercel that will make your life easier. These serverless services will handle your front-end, being service side rendered as well as static generated. They handle the API part too, guaranteeing you that your Next.js API routes will be called effectively, without having a server running on the clock.

Because there is no virtual machine, no server running every day, you can't create a Cron Job from your Next.js backend. So you need to find a way to schedule tasks differently.

Use Easycron to create managed Cron Jobs

When I had this problem, I did not find directly what I wanted to use. I knew that I had to use an external service to make it work, and I thought first about checking what's available on AWS or GCP. While there is a Cloud Scheduler on GCP, I did not find a good way to make it work programmatically, which cuts a lot of usecases (If you want to make simple CRONs which you don't need to change programmatically, it can be a good solution).

Easycron is a totally managed Cron service you can use to create your Cron Jobs programmatically or manually.

This article is not sponsored to the service or anything, this is the only service that I found that really correspond to my needs, and that is really cheap (totally free for my hobby projects).

Call Easycron API from your Next.js routes.

To configure a Cron Job, you will need to call the Easycron API. That a look at this example :

import qs from "querystring";

const EASYCRON_API_KEY = process.env.EASYCRON_API_KEY;
const FRONT_URL = process.env.VERCEL_URL || process.env.FRONT_URL;

// Localhost doesn't work with easyCron, so I rename it, use ngrok instead
const frontUrl = FRONT_URL.replace("localhost", "locolhost");

const url = `https://www.easycron.com/rest/add?${qs.stringify({
  token: EASYCRON_API_KEY,
  url: `${frontUrl}/api/saysHappyFourthofEveryMonth`,
  cron_expression: "0 12 4 * *",
  cron_job_name: Math.floor(Math.random() * 100 * 213948),
})}`;

fetch(url);

This simple piece of code creates a Cron Job that will run on the fourth of every month, and 12h00.

Easycron works by calling your API when you schedule it, the url key correspond to the call EasyCron will make every time the Job gets triggered. For the free version (which I use), you can't create POST calls.

The line where I replace localhost by locolhost may seems strange, but this is because Easy Cron doesn't allow you to write localhost in the url, because it can only call online services, I suggest you to use ngrok if you want to test your Cron Job with your local environment.

You will receive a cron_job_id from the response of this call, which I suggest you save in a database before the next step.

Edit and delete Cron Jobs from Easycron

Now that you have your freshly created Cron Job, you may need to delete it or update it, I suggested you to save the cron_job_id in a database. Now we will use this id as cronId for this code sample :

import qs from "querystring";

const EASYCRON_API_KEY = process.env.EASYCRON_API_KEY;
const FRONT_URL = process.env.VERCEL_URL || process.env.FRONT_URL;

const url = `https://www.easycron.com/rest/edit?${qs.stringify({
  token: EASYCRON_API_KEY,
  url: `${frontUrl}/api/saysHappyThirdofEveryMonth`,
  cron_expression: "0 12 3 * *",
  cron_job_name: Math.floor(Math.random() * 100 * 213948),
  id: cronId,
})}`;

fetch(url);

As you can see, this is really simple, you only need to change add to edit in the route, and pass a new key-value of the querystring, which will be cronId as the value of id.

To delete a cron now :

import qs from "querystring";

const EASYCRON_API_KEY = process.env.EASYCRON_API_KEY;

const url = `https://www.easycron.com/rest/delete?${qs.stringify({
  token: EASYCRON_API_KEY,
  id: cronId,
})}`;

fetch(url);

No comments needed 😂

I will now give you some of my usecases, so you can understand the goal.

Scheduling mails from your Next.js application

With what we say earlier, we now know that we can schedule Easycron to call any API, but only GET APIs when you are on the free version. For the paid version, you can check the Easycron documentation so you have see how to make POST calls, but I'm gonna focus on GETs.

If you want to use a mailing service, you may have plugged in Sendgrid as I did, and you saw that the scheduling is very, very limited. I solved this problem using Easycron by sending some information in the querystring of my own API. Let me explain with an example from a piece of code I use to schedule a mail which will be sent every month, in week days, approximatically the tenth day :

const url = `https://www.easycron.com/rest/${
  cronId ? "edit" : "add"
}?${qs.stringify({
  token: EASYCRON_API_KEY,
  url: `${frontUrl}/api/sendMail?${qs.stringify({
    userID,
  })}`,
  id: cronId,
  cron_expression: "0 12 10W * *",
  timezone_from: 2,
  timezone: "Europe/Paris",
  cron_job_name: `${userId} - ${Math.floor(Math.random() * 100 * 213948)}`,
})}`;

The url field is where all the logic goes, I tell Easycron to call my sendMail API with a userId in the query.

I will then use these informations from the sendMail routes to make the mailing part :

import connectToMongo from "../../utils/connectToMongo";
import sgMail from "@sendgrid/mail";

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

export default async function sendMailAPI(req, res) {
  const userID = req.query.userID;

  const mongo = await connectToMongo();

  const user = await mongo.models.User.findOne({ userID }).lean();

  await sgMail.send({
    to: user.mail,
    from: "myapplication@gmail.com",
    subject: `Hello ${user.name}`,
    text: "...",
  });

  res.statusCode = 200;
  res.json({});
}

As you can see, you recover your userID from the req.query, and then you send your mail.

Scheduling simple function calls from your Next.js application

If you only wants to call a particular function an a regular pace, without having to update it programmatically, you can use the Easycron interface to do so. From the interface, say that you want to call /api/callFunctionUpdateUser, and create a really simple route that will only call the function you want. It will work fine if you don't forget at this end to add :

res.statusCode = 200;
res.json({});

I use this mechanic to call a function that will update my database, getting rid of old and not used data inside.

Conclusion

I created this article because I had trouble the first time I wanted to do that, I searched first if there was any mailing APIs that will take Cron Job in account, and after 30 minutes of searching, not finding any solution, I decided that I need a Cron Job manager which will call the mailing API for me. I then found Easycron to do this.

I hope that this is constructive to you, and that you may find these pieces of code useful.

Tell me on Twitter if you have any trouble.