Library / SDK
inngest/inngest-js avatar
inngest/inngest-js

inngest-js: durable background jobs that live inside your serverless code

The developer platform for easily building reliable workflows with zero infrastructure for TypeScript & JavaScript

1,016 stars167 forksTypeScriptGPL-3.0

At a glance

What is it?
The inngest JavaScript SDK turns events into functions that survive restarts, with steps, retries, cron schedules and a stored event history, and it runs on any serverless platform through a per framework adapter. The trade is that your workflow now depends on a hosted service, and the SDK is published under a licence that will surprise you.
Who is it for?
inngest fits a team building on a serverless platform that needs background work to survive a cold start, wants to trigger work from webhooks and application events rather than only from a request handler, and prefers to write the workflow as ordinary code in its own repository.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Durable execution, hosted for you

Inngest is described in the README as the reliability layer for modern applications, combining durable execution, events and queues into a zero-infrastructure platform with built-in observability. The repository description calls it the developer platform for easily building reliable workflows with zero infrastructure for TypeScript and JavaScript.

The word to watch is durable. A background job on a serverless platform is fragile by default: the platform can freeze the container between the request returning and the work finishing, and the work is lost. A durable execution engine solves that by persisting the state of a running function and replaying it when needed, and that persistence lives in Inngest's service rather than in your application.

The list of things you can build with it is the usual list for this category, and it is worth reading as a description of the intended use. You write background jobs, you create scheduled and cron jobs, you build serverless queues, you write complex step functions, you build serverless event-driven systems, and you reliably respond to webhooks with retries and payloads stored for history.

The zero infrastructure claim is about your side of the deal. The README is explicit that it works on any serverless platform, naming Next.js, Deno Deploy, RedwoodJS, AWS Lambda and anything else, with no extra infrastructure. The cost is that the durability is somebody else's, and the SDK is the client for that service.

The library is published on npm as inngest, with the current version 4.24.0 released on 2026-10-14, and the repository was last pushed to on 2026-10-19.

Events in, functions with steps out

The programming model is two objects. An Inngest client, created with an id for your application, and functions registered against it.

ts
import { Inngest } from "inngest";

const inngest = new Inngest({ id: "my-app" });

A function declares what triggers it and what it does, and the body is a set of steps.

ts
export default inngest.createFunction(
  {
    id: "user-onboarding-communication",
    triggers: [{ event: "app/user.signup" }],
  },
  async ({ event, step }) => {
    await step.run("Send welcome email", async () => {
      await sendEmail({
        email: event.data.email,
        template: "welcome",
      });
    });
  }
);

The step abstraction is the heart of it. Each step.run is a named unit of work with its own position in the function, and the engine records which steps have completed. If the function is interrupted after the second of five steps, the retry resumes at the third rather than re-sending the email that already went out. That is the property that turns a sequence of awaited promises into a workflow.

The trigger is an event name, and the README notes that functions are triggered by events which can be sent via this SDK, webhooks, integrations, or with a simple HTTP request. So the same function can be driven by your own code, by an incoming webhook, or by a third party integration, and the event name is the contract.

When a matching event arrives, Inngest invokes the function automatically, with built-in retries.

Serving the functions is an adapter problem

Inngest invokes functions over HTTP, so the application has to serve them, and the SDK ships an adapter per framework.

ts
// /pages/api/inngest.ts
import { Inngest } from "inngest";
// See the "inngest/next" adapter imported here:
import { serve } from "inngest/next";
import myFunction from "../userOnboardingCommunication"; // see above function

// You can create this in a single file and import where it's needed
const inngest = new Inngest({ id: "my-app" });

// Securely serve your Inngest functions for remote invocation:
export default serve(inngest, [myFunction]);

The shape is the same everywhere. You create the client, you collect the functions, and you hand both to the framework's serve helper, which exports a request handler.

The examples directory in the repository shows how wide that surface is, and it is a better guide than the feature list. There are examples for the Next.js app router and the pages router, for SvelteKit, for Nuxt, for Remix, for Astro, for Express, for Fastify, for Koa, for Hono, for Elysia, for NestJS, for Nitro, for the Google Functions Framework, for Bun, and for the connect and CI pipeline examples, plus durable endpoints examples for a deepresearch workflow and a trip booker.

The other direction matters too. The framework list in the features section names Next.js, Redwood, Express, Cloudflare Pages, Nuxt, Fresh on Deno and Remix, and the deployment list names Vercel, Netlify, Cloudflare, Deno, Digital Ocean and others, with the point being that you keep deploying to your existing platform and only the handler changes.

Sending an event is one call, and the history is kept for you

Publishing the event that starts the workflow is deliberately a one-liner.

ts
// Send events
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });

// This will run the function above automatically, in the background
inngest.send("app/user.signup", {
  data: { email: "[email protected]", user_id: "12345" },
});

Two behaviours are described around that call. Events can trigger one or more functions automatically, which enables you to fan out work, so a single event can start several independent workflows without one knowing about the others. And Inngest stores a history of all events for observability, testing and replay.

That history is a bigger deal than it sounds. It is the record of what happened, which is the first thing you want when a workflow failed at three in the morning, and the README also lists a full UI for managing and inspecting your functions. The same history is what makes replay possible, so a bug in a downstream step can be fixed and the function run again from the stored event rather than from a synthetic reproduction.

The features list also calls out fully typed event schemas, versioning and governance out of the box, which is the answer to the question every event-driven system eventually asks, namely what happens when you want to change the shape of an event that other functions depend on.

A pnpm monorepo with a release process and thirty examples

The repository tells you how the project is run, and it is a well organised monorepo.

The root package.json is the workspace root rather than a published package, and it pins the toolchain: packageManager [email protected], with a preinstall script that runs npx only-allow pnpm to enforce it. The build script builds the inngest package first and then the rest recursively, and the release flow is built on changesets, with version, publish and tag steps that run across the workspace.

The dependency overrides are worth a glance because they are the kind of detail a maintainer pins for a reason: whatwg-url is held at 14, rolldown is pinned for a specific tsdown range, the OpenTelemetry SDK and Prometheus exporter are held at 0.217.0 or newer, and fflate is pinned to 0.8.2. These are the transitive dependencies that break a build when they move, and pinning them in the workspace is the cheapest way to keep a release reproducible.

The tooling is modern and small. Biome for linting and formatting, Vitest 3.2.4 for tests, changesets for versioning and changelog generation, and a flake.nix and flake.lock for a reproducible development environment, along with a.devcontainer, a.vscode directory, and both AGENTS.md and CLAUDE.md at the root. The packages/ directory holds the published workspaces, and scripts/ holds the release tooling, including the tag script that release:tag runs.

One oddity: the root package version reads 4.0.0-alpha.5 while the published package is at 4.24.0, so the root manifest is not a reliable indicator of the released version.

Runtime support, and the licence you should check

The version support section is short and has a clause in it that is worth reading twice.

The library works across browsers, Bun, Deno, Node and Cloudflare Workers. The project supports the LTS versions of those runtimes and the last three minor versions of TypeScript. Once a runtime version drops out of LTS, any major, minor or patch update to the inngest library may drop support for it, and that will be mentioned in the patch notes.

That is an unusual commitment to publish in a README. Most libraries simply stop working on an old runtime, and you find out from an error. Here the rule is stated in advance, which means a patch release of the SDK can remove a runtime, and a team on, say, an old Node that is not in LTS will find that an automatic dependency bump removes their platform. The lesson is to read the patch notes before upgrading the SDK in a long-lived deployment, and to pin it if you cannot move runtime versions on your own schedule.

The licence is the other thing to check before this becomes a dependency. The metadata published for this repository shows the licence as GPL-3.0, and there is no licence file in the top level of the repository listing, which is an unusual combination for an npm package whose competitors are mostly permissive. If you intend to depend on it in a proprietary product, that is a question for the project and for your own counsel, and the answer should be established before the dependency lands in your package.json, not after.

There is also a separate package, @inngest/ci, released at 0.3.0 on the same day as the 4.24.0 release of the main package. The README points to the documentation for the details of that, and to a Discord server for questions and feature requests.

Editorial conclusion

inngest fits a team building on a serverless platform that needs background work to survive a cold start, wants to trigger work from webhooks and application events rather than only from a request handler, and prefers to write the workflow as ordinary code in its own repository. It does not fit a team that cannot take a hosted dependency into its architecture, since the reliability comes from the service rather than from your code, and it does not fit anyone who needs the licence to be permissive. Verify three things before you commit: the licence, since the metadata published for this repository shows GPL-3.0 and there is no licence file at the top level, the runtime support policy, since a runtime that leaves LTS loses support in a patch release, and the framework adapter for the exact runtime and version you deploy on, because the examples directory shows the shape of the integration for each of them.

Frequently asked questions

How do I install the inngest JavaScript SDK?

Run npm install inngest. The package is published as inngest and the current version is 4.24.0, released on 2026-10-14. The library works across browsers, Bun, Deno, Node and Cloudflare Workers, and the project supports the LTS versions of those runtimes and the last three minor versions of TypeScript.

How do I serve inngest functions in a Next.js application?

Import the serve helper from inngest/next, create the client with new Inngest({ id: "my-app" }), import your functions, and export serve(inngest, [myFunction]) as the handler, for example from pages/api/inngest.ts. The same pattern applies to the other frameworks, and the repository's examples directory shows one per framework.

How do I trigger an inngest function?

Send an event with inngest.send, passing the event name and data, for example inngest.send("app/user.signup", { data: { email: "[email protected]", user_id: "12345" } }). The matching function is invoked automatically with built-in retries, and events can also arrive through webhooks, integrations or a plain HTTP request.

What does the steps API do in inngest?

Each step.run is a named unit of work whose completion the engine records, so an interrupted function resumes where it stopped instead of repeating the steps that already succeeded. That is what makes a sequence of awaited calls into a durable workflow rather than a fragile sequence of promises.

What licence is the inngest JavaScript SDK under?

The metadata published for this repository shows the licence as GPL-3.0, and there is no licence file at the top level of the repository listing, which is unusual for an npm package. Establish the terms before taking the dependency in a proprietary product rather than after. The project also publishes a separate @inngest/ci package for the CI integration.

Official sources

  1. inngest/inngest-js on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/inngest-inngest-js.svg)](https://hysenlabs.com/projects/inngest-inngest-js)

Was this useful?