TypeScript
The Inngest SDK leverages the full power of TypeScript, providing you with some awesome benefits when handling events:
- 📑 Autocomplete
Tab ↹ your way to victory with inferred types for every event. - Instant feedback
Understand exactly where your code might error before you even save the file.
All of this comes together to provide some awesome type inference based on your actual production data.
Using types
Once your types are generated, there are a few ways we can use them to ensure our functions are protected.
new Inngest()
client
We can use these when creating a new Inngest client via new Inngest()
.
This comes with powerful inference; we autocomplete your event names when selecting what to react to, without you having to dig for the name and data.
inngest/client.ts
import { EventSchemas, Inngest } from "inngest";
type UserSignup = {
data: {
email: string;
name: string;
};
};
type Events = {
"user/new.signup": UserSignup;
};
export const inngest = new Inngest({
id: "my-app",
schemas: new EventSchemas().fromRecord<Events>(),
});
inngest/sendWelcomeEmail.ts
import { inngest } from "./client";
export default inngest.createFunction(
{ id: "send-welcome-email" },
{ event: "user/new.signup" },
async ({ event }) => {
// "event" is fully typed to provide typesafety within this function
return await email.send("welcome", event.data.email);
}
);
Sending events
TypeScript will also enforce your custom events being the right shape - see Event Format for more details.
We recommend putting your new Inngest()
client and types in a single file, i.e. /inngest/client.ts
so you can use it anywhere that you send an event.
Here's an example of sending an event within a Next.js API handler:
pages/api/signup.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { inngest } from "../../inngest/client";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const user = createNewUser(req.body.email, req.body.password, req.body.name);
// TypeScript will now warn you if types do not match for the event payload
// and the user object's properties:
await inngest.send({
name: "user/new.signup",
data: {
email: user.email,
name: user.name,
}
});
res.status(200).json({ success: true });
}
Using with waitForEvent
When writing step functions, you can use waitForEvent
to to, pause the current function until another event is received or the timeout expires - whichever happens first. When you declare your types using the Inngest
constructor, waitForEvent
leverages any types that you have:
inngest/client.ts
import { EventSchemas, Inngest } from "inngest";
type UserSignup = {
data: {
email: string;
user_id: string;
name: string;
};
};
type UserAccountSetupCompleted = {
data: {
user_id: string;
};
};
type Events = {
"user/new.signup": UserSignup;
"user/account.setup.completed": UserAccountSetupCompleted;
};
export const inngest = new Inngest({
id: "my-app",
schemas: new EventSchemas().fromRecord<Events>(),
});
inngest/onboardingDripCampaign.ts
import { inngest } from "./client";
export default inngest.createFunction(
{ id: "onboarding-drip-campaign" },
{ event: "user/new.signup" },
async ({ event, step }) => {
await step.run("send-welcome-email", async () => {
// "event" will be fully typed provide typesafety within this function
return await email.send("welcome", event.data.email);
});
// We wait up to 2 days for the user to set up their account
const accountSetupCompleted = await step.waitForEvent(
"wait-for-setup-complete",
{
event: "user/account.setup.completed",
timeout: "2d",
// ⬇️ This matches both events using the same property
// Since both events types are registered above, this is match is typesafe
match: "data.user_id",
}
);
if (!accountSetupCompleted) {
await step.run("send-setup-account-guide", async () => {
return await email.send("account_setup_guide", event.data.email);
});
}
}
);