To add the Clocktick SDK to your TypeScript application, you will want to install @clocktick/sdk
from npm (for example, if you are using the npm package manager, this would be done with npm i @clocktick/sdk
). From here, we will want to make a folder for all of your jobs and make a index.ts
file. This will be the entry point for your jobs:
import { createRouter } from "@clocktick/sdk";
const routes = {
// TODO: Add your routes here
} as const;
export default createRouter(
process.env.CLOCKTICK_API_KEY,
process.env.CLOCKTICK_ENCRYPTION_KEY,
process.env.CLOCKTICK_ENDPOINT_PUBLIC_KEY,
process.env.CLOCKTICK_ENDPOINT_ID,
routes,
);
You will notice that we used a number of environment variables in the above code. These are:
CLOCKTICK_API_KEY
: The API key for your organisation. You can find this in the Tokens section of your organisation.CLOCKTICK_ENCRYPTION_KEY
: The encryption key for your organisation. This is the key you will use to encrypt and decrypt your job data. You should share this across all your endpoints that use Clocktick to ensure you can decrypt the job data if you call from another service in your organisation. Can be any length.CLOCKTICK_ENDPOINT_PUBLIC_KEY
: The public key of the endpoint to send the job to. This should be a public key created within this organisation within the Endpoints section. This key is used to validate that the callback is from Clocktick.CLOCKTICK_ENDPOINT_ID
: The ID of the endpoint to send the job to. This should be a ID created within this organisation within the Endpoints section. This endpoint should point to the Next.js server you are running.
We should now setup the HTTP route. The SDK uses the JS Request
and Response
objects to handle the HTTP requests. Setting up this route varies between frameworks, but in Next.js, you just want to put this file in app/api/clocktick/route.ts
:
import { httpRoute } from "@clocktick/sdk"; import jobs from "@/jobs"; export const POST = httpRoute(jobs);
From here, the endpoint URL would be https://<domain>/api/clocktick
.
Note that you may want to do this on multiple routes and adjust the Next runtime as you see fit for your application.
Adding job routes to your application
The next step is to add routes into your job router. The routes object contains either functions or objects with functions. Functions take in any number of arguments and return a promise. Note that functions should not take in custom classes because they will be serialised and deserialised.
For example, if we wanted to make a route to destroy a user, we could make a TS file at jobs/users/destroy.ts
with the function in:
export default async function destroyUser(userId: string) {
// TODO: Implement this function
}
Then in our router index file, we would add the route:
import destroyUser from "./users/destroy";
const routes = {
users: {
destroy: destroyUser,
},
} as const;
It is also important to note that arguments either shouldn't change or the old revision should be checked for. This is because the arguments are serialised and deserialised, so if you change the arguments, the old revision may not match the types of your function.
Scheduling a job
Now we have made the router, we can go ahead and schedule a job. To do this, we can call the function within the router. The type is rewritten so that it becomes a promise with a object that has the job creation result. For example, if we want to destroy a user in a month, we could call the function:
import jobs from "@/jobs";
import { fromNow } from "@clocktick/sdk";
// ...later in the code
const { id } = await jobs.users.destroy(
fromNow((d) => d.months(1)),
// The user ID to destroy. This is typed based on the function.
"user-id",
);
Note that we may actually instead want to create a custom ID. This means that we do not need to store the ID in the database and can use the user ID as part of the ID. To do this, we can just edit the first argument:
fromNow((d) => d.months(1)).customId(`destroy-user-${user-id}`),
This will create a job with the ID destroy-user-<user-id>
. If we want to make it recurring, we can use the recurring
function on the delta builder:
await jobs.every5Mins(
fromNow((d) => d.minutes(5).recurring()),
);
If we want to run a event starting from in the future, we can use the fromDatetime
function instead of fromNow
. This function takes a string in the format YYYY-MM-DDTHH:MM:SSZ
or a Date object.
import { fromDatetime } from "@clocktick/sdk"; // With a string: await jobs.users.sayHello( // Runs midnight on the 1st of January 2025. fromDatetime("2025-01-01T00:00:00Z"), // The user ID to say hello to. "user-id", ); // With a Date object: await jobs.users.sayHello( fromDatetime(new Date("2025-01-01T00:00:00Z")), "user-id", // The user ID to say hello to. );
We can also pass in a delta builder if we want to run it every N after the start time:
await jobs.users.sendHappyBirthday(
fromDatetime("2025-01-01T00:00:00Z", (d) =>
d.years(1)).customId(`happy-birthday-${user-id}`),
// The user ID to say happy birthday to.
"user-id",
);
Deleting a job
We can simply use the deleteJob
SDK helper to do this with the ID of the job:
import { deleteJob } from "@clocktick/sdk"; // ...later in the code await deleteJob( process.env.CLOCKTICK_API_KEY, id, );