Writing Functions
Follow along to learn how to:
- Write a background function triggered by an event
- Write a scheduled function
- Write a durable step function anywhere
Writing functions is the same no matter what platform you deploy to — so this example can be used for any TypeScript or JavaScript project (such as Next.js, Remix, or RedwoodJS).
👉 We recommend setting up your API handler first, so you can start testing functions right away. If you haven't done so, check out Serving the Inngest API.
Install the SDK
Firstly, you need to install the SDK via npm or yarn:
npm install inngest # or yarn add inngest
Writing a background function
Copy the following code into a new file in your project, for example within ./inngest/myFunction.ts
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });
export default inngest.createFunction(
{ id: "import-product-images" },
{ event: "shop/product.imported" }, // The event that will trigger this function
// This function will be called every time an event payload is received
async ({ event, step }) => {
return copyAllImagesToS3(event.data.imageURLs); // You can write whatever you want here.
}
);
This defines a new background job which runs any code that you pass it. This function is automatically triggered in the background whenever the shop/product.imported
event is received. You can test this function using standard tooling such as Jest or Mocha by exporting the job code and running standard unit tests.
💁 Why event-driven?
Using events to trigger functions has many benefits:
- It’s simple: there’s no config to define, and no queues to configure
- You can run many functions with a single event (fan-out)
- Each event has its own type, so that your data is always correct
- Inngest stores each event you receive, making testing and debugging simple
Writing a scheduled function
The following code defines a scheduled function, which runs automatically using the specified cron schedule:
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });
export default inngest.createFunction(
{ id: "weekly-digest-email" }, // The name of your function, used for observability.
{ cron: "TZ=America/New_York 0 9 * * MON" }, // The cron syntax for the function. TZ= is optional.
// This function will be called on the schedule above
async ({ step }) => {
return await sendDigestToAllUsers(); // You can write whatever you want here.
}
);
This is very similar to defining an event-driven function, except it uses a cron
trigger instead of an event
to run your function at the specified interval. This code can be deployed to any platform and will run automatically, without any HTTP calls.
Writing a multi-step function
Any function can become a step function, where each step is individually retriable and tools are provided to coordinate beween events or wait for arbitrary periods of time (up to months)!
Check out the Multi-step functions page for more!
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });
export default inngest.createFunction(
{ id: "activation-email" },
{ event: "app/user.created" },
async ({ event, step }) => {
// Send the user a welcome email
await step.run("send-welcome-email", () =>
sendEmail({ email: event.data.email, template: "welcome" })
);
// Wait for the user to create an order, by waiting and
// matching on another event
const order = await step.waitForEvent("wait-for-order", {
event: "app/order.created",
match: "data.user.id",
timeout: "24h",
});
if (!order) {
// User didn't create an order within 24 hours; send
// them an activation email
await step.run("send-activation-email", async () => {
// Some code here
});
}
}
);
Use a multi-step function to create a logical flow of actions triggered from an initial event. It's just like writing a regular synchronous function, but Inngest will step in to help coordinate between events, wait for long periods, or retry failed steps.
In this example, we send the user a welcome email when they are created, then send an activation email after 24 hours if they haven't created an order in that time. Each call to step.run()
can be retried independently, meaning a failure in one step won't affect any others.
Coordinating between events like this often requires a lot of code and infrastructure to handle retries, timeouts, state, queues, and many edge cases. Inngest handles all of this for you, so you can focus on writing the code that matters in a clean, readable format.
You can learn more about the Inngest SDK's multi-step functions on our Multi-step functions page.
TypeScript
The Inngest SDK lets you strictly type all of your code against your real production data. This powerful feature helps to:
- Protect you from making breaking changes
- Encourage discoverability of your events via autocomplete
- Give you instant feedback as to whether your code will run as intended
inngest/client.ts
import { EventSchemas, Inngest } from "inngest";
type AppUserCreated = {
data: {
email: string;
};
user: {
id: string;
};
};
type Events = {
"app/user.created": AppUserCreated;
};
export const inngest = new Inngest({
id: "my-app",
schemas: new EventSchemas().fromRecord<Events>(),
});
You can then import this inngest
client into where you're creating functions or sending events:
import { inngest } from "./client";
export default inngest.createFunction(
{ id: "add-to-intercom" },
{ event: "app/user.created" },
async ({ event, step }) => { /* ... */ }
);
You can learn more about the Inngest SDK's TypeScript support on our Using with TypeScript page.
Serving
For Inngest to be able to call your functions, you need to serve them through an API endpoint. There is a more in-depth and pre-framework documentation in here, but after that is set up, you need add all of your functions to your serve
handler. For example, in a Next.js project:
pages/api/inngest.ts
import { serve } from "inngest/next";
import addToIntercom from "../../inngest/addToIntercom";
import { inngest } from "../../inngest/client";
import sendWeeklyDigestCron from "../../inngest/sendWeeklyDigestCron";
// You need to serve all functions, event driven or scheduled:
export default serve({
client: inngest,
functions: [addToIntercom, sendWeeklyDigestCron],
});
The serve
handler enables Inngest to remotely and securely call your functions running on any platform. Check out those docs for a deeper dive.
Show All Served Functions
Now that your functions are being served via HTTP, start and open the Inngest Dev Server and navigate to the Functions page to see all of your functions: