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);
      });
    }
  }
);

Helpers

The TS SDK exports some helper types to allow you to access the type of particular Inngest internals outside of an Inngest function.

GetEvents
v2.0.0+

Get a record of all available events given an Inngest client.

It's recommended to use this instead of directly reusing your own event types, as Inngest will add extra properties and internal events such as ts and inngest/function.failed.

import { type GetEvents } from "inngest";
import { inngest } from "@/inngest";

type Events = GetEvents<typeof inngest>;

By default, the returned events do not include internal events prefixed with inngest/, such as inngest/function.finished.

To include these events in

v3.13.1+
, pass a second true generic:

type Events = GetEvents<typeof inngest, true>;

GetFunctionInput
v3.3.0+

Get the argument passed to Inngest functions given an Inngest client and, optionally, an event trigger.

Useful for building function factories or other such abstractions.

import { type GetFunctionInput } from "inngest";
import { inngest } from "@/inngest";

type InputArg = GetFunctionInput<typeof inngest>;
type InputArgWithTrigger = GetFunctionInput<typeof inngest, "app/user.created">;

GetStepTools
v3.3.0+

Get the step object passed to an Inngest function given an Inngest client and, optionally, an event trigger.

Is a small shim over the top of GetFunctionInput<...>["step"].

import { type GetStepTools } from "inngest";
import { inngest } from "@/inngest";

type StepTools = GetStepTools<typeof inngest>;
type StepToolsWithTrigger = GetStepTools<typeof inngest, "app/user.created">;

Inngest.Any / InngestFunction.Any
v3.10.0+

Some exported classes have an Any type within their namespace that represents any instance of that class without inference or generics.

This is useful for typing lists of functions or factories that create Inngest primitives.

import { type InngestFunction } from "inngest";

const functionsToServe: InngestFunction.Any[] = [];