Next.js
Inngest works with NextJS out of the box, and allows you to write background jobs, scheduled functions, and event driven systems using Next's API routes.
Follow along to learn how to:
- Integrate your event types within functions
- Write functions in NextJS
- Serve your functions with a new API route
- Register your functions with Inngest after deploying to your hosting service.
Integrating event types into your project
Inngest fully types every event you send, ensuring that your functions and data are always valid. To generate your type library, run:
npx inngest-cli types ts
This will prompt you to log in to your Inngest account, then generate types for every event
in your account (plus public events) inside the __generated__
folder.
Writing functions in NextJS
Firstly, create a new directory within the root of your NextJS app called inngest
. This
will contain all of your functions:
bashmkdir ./inngest
Within this directory you can write your Inngest functions, eg ./inngest/new_pr.ts
:
typescript// @filename: ../__generated__/inngest.tsexport const Action = {OPENED: "opened",CLOSED: "closed",MERGED: "merged",REVIEW_REQUESTED: "review_requested",SYNCHRONIZE: "synchronize",EDITED: "edited",} as const;export type Action = typeof Action[keyof typeof Action];export type GithubPullRequest = {name: "github/pull_request";data: {action: Action;organization: {description: string;events_url: string;login: string;public_members_url: string;repos_url: string;url: string;avatar_url: string;id: number;issues_url: string;members_url: string;node_id: string;hooks_url: string;};pull_request: {diff_url: string;labels: Array<unknown>;title: string;body: string;closed_at: unknown;deletions: number;commits_url: string;merged_at: unknown;statuses_url: string;user: {events_url: string;node_id: string;organizations_url: string;type: string;url: string;following_url: string;gists_url: string;html_url: string;repos_url: string;followers_url: string;id: number;site_admin: boolean;starred_url: string;subscriptions_url: string;avatar_url: string;gravatar_id: string;login: string;received_events_url: string;};author_association: string;base: {label: string;ref: string;repo: {branches_url: string;name: string;subscribers_url: string;svn_url: string;topics: Array<unknown>;allow_merge_commit: boolean;git_url: string;releases_url: string;assignees_url: string;events_url: string;full_name: string;private: boolean;trees_url: string;updated_at: string;watchers_count: number;allow_rebase_merge: boolean;issue_comment_url: string;issue_events_url: string;milestones_url: string;watchers: number;disabled: boolean;downloads_url: string;license: unknown;merges_url: string;teams_url: string;allow_squash_merge: boolean;collaborators_url: string;commits_url: string;contents_url: string;languages_url: string;mirror_url: unknown;visibility: string;allow_auto_merge: boolean;archive_url: string;has_downloads: boolean;size: number;ssh_url: string;statuses_url: string;allow_forking: boolean;contributors_url: string;default_branch: string;fork: boolean;forks_url: string;git_refs_url: string;keys_url: string;subscription_url: string;tags_url: string;created_at: string;forks_count: number;has_wiki: boolean;open_issues: number;open_issues_count: number;is_template: boolean;allow_update_branch: boolean;archived: boolean;forks: number;git_commits_url: string;has_issues: boolean;has_pages: boolean;html_url: string;issues_url: string;blobs_url: string;compare_url: string;git_tags_url: string;labels_url: string;language: string;delete_branch_on_merge: boolean;notifications_url: string;stargazers_count: number;clone_url: string;has_projects: boolean;id: number;pulls_url: string;owner: {node_id: string;organizations_url: string;repos_url: string;events_url: string;html_url: string;login: string;avatar_url: string;type: string;subscriptions_url: string;following_url: string;id: number;received_events_url: string;site_admin: boolean;starred_url: string;url: string;followers_url: string;gists_url: string;gravatar_id: string;};comments_url: string;description: string;homepage: unknown;pushed_at: string;stargazers_url: string;deployments_url: string;hooks_url: string;node_id: string;url: string;};sha: string;user: {events_url: string;followers_url: string;following_url: string;gravatar_id: string;starred_url: string;subscriptions_url: string;site_admin: boolean;type: string;node_id: string;organizations_url: string;repos_url: string;avatar_url: string;gists_url: string;html_url: string;id: number;login: string;received_events_url: string;url: string;};};before?: string;after?: string;milestone: unknown;node_id: string;number: number;requested_teams: Array<unknown>;comments_url: string;mergeable_state: string;merged: boolean;locked: boolean;mergeable: unknown;merged_by: unknown;patch_url: string;rebaseable: unknown;active_lock_reason: unknown;created_at: string;head: {label: string;ref: string;repo: {pulls_url: string;releases_url: string;compare_url: string;contributors_url: string;git_commits_url: string;issue_events_url: string;license: unknown;private: boolean;updated_at: string;url: string;has_projects: boolean;keys_url: string;language: string;notifications_url: string;pushed_at: string;size: number;allow_auto_merge: boolean;git_tags_url: string;html_url: string;id: number;languages_url: string;topics: Array<unknown>;collaborators_url: string;created_at: string;has_downloads: boolean;has_issues: boolean;is_template: boolean;name: string;allow_forking: boolean;commits_url: string;contents_url: string;default_branch: string;forks: number;owner: {starred_url: string;subscriptions_url: string;type: string;node_id: string;site_admin: boolean;organizations_url: string;repos_url: string;gists_url: string;id: number;events_url: string;login: string;following_url: string;gravatar_id: string;html_url: string;received_events_url: string;url: string;avatar_url: string;followers_url: string;};allow_merge_commit: boolean;archived: boolean;forks_url: string;issues_url: string;subscribers_url: string;svn_url: string;tags_url: string;visibility: string;allow_squash_merge: boolean;milestones_url: string;watchers: number;comments_url: string;delete_branch_on_merge: boolean;git_url: string;issue_comment_url: string;statuses_url: string;subscription_url: string;deployments_url: string;fork: boolean;git_refs_url: string;merges_url: string;watchers_count: number;assignees_url: string;branches_url: string;has_wiki: boolean;allow_update_branch: boolean;clone_url: string;description: string;open_issues: number;stargazers_url: string;trees_url: string;allow_rebase_merge: boolean;archive_url: string;blobs_url: string;full_name: string;has_pages: boolean;homepage: unknown;disabled: boolean;downloads_url: string;events_url: string;forks_count: number;hooks_url: string;open_issues_count: number;mirror_url: unknown;ssh_url: string;stargazers_count: number;teams_url: string;labels_url: string;node_id: string;};sha: string;user: {node_id: string;organizations_url: string;received_events_url: string;url: string;id: number;repos_url: string;login: string;subscriptions_url: string;type: string;avatar_url: string;events_url: string;gravatar_id: string;html_url: string;starred_url: string;followers_url: string;following_url: string;gists_url: string;site_admin: boolean;};};requested_reviewers: Array<unknown>;assignee: unknown;comments: number;html_url: string;review_comments_url: string;state: string;additions: number;assignees: Array<unknown>;auto_merge: unknown;merge_commit_sha: unknown;id: number;review_comment_url: string;review_comments: number;updated_at: string;url: string;draft: boolean;issue_url: string;maintainer_can_modify: boolean;};repository: {branches_url: string;html_url: string;mirror_url: unknown;size: number;topics: Array<unknown>;forks_url: string;has_issues: boolean;has_wiki: boolean;homepage: unknown;stargazers_url: string;trees_url: string;updated_at: string;compare_url: string;downloads_url: string;id: number;git_url: string;contributors_url: string;disabled: boolean;git_commits_url: string;keys_url: string;open_issues: number;open_issues_count: number;ssh_url: string;subscribers_url: string;collaborators_url: string;comments_url: string;fork: boolean;git_tags_url: string;node_id: string;contents_url: string;deployments_url: string;notifications_url: string;owner: {login: string;node_id: string;repos_url: string;site_admin: boolean;url: string;followers_url: string;gravatar_id: string;html_url: string;id: number;received_events_url: string;starred_url: string;events_url: string;type: string;avatar_url: string;following_url: string;gists_url: string;organizations_url: string;subscriptions_url: string;};releases_url: string;stargazers_count: number;blobs_url: string;issue_events_url: string;tags_url: string;default_branch: string;events_url: string;hooks_url: string;statuses_url: string;forks: number;has_downloads: boolean;language: string;subscription_url: string;archived: boolean;created_at: string;has_pages: boolean;merges_url: string;pushed_at: string;git_refs_url: string;labels_url: string;languages_url: string;license: unknown;milestones_url: string;teams_url: string;description: string;private: boolean;pulls_url: string;svn_url: string;visibility: string;forks_count: number;full_name: string;is_template: boolean;issues_url: string;archive_url: string;assignees_url: string;commits_url: string;has_projects: boolean;watchers: number;allow_forking: boolean;clone_url: string;issue_comment_url: string;name: string;url: string;watchers_count: number;};sender: {events_url: string;gists_url: string;login: string;url: string;followers_url: string;following_url: string;id: number;site_admin: boolean;subscriptions_url: string;type: string;html_url: string;node_id: string;avatar_url: string;gravatar_id: string;organizations_url: string;received_events_url: string;repos_url: string;starred_url: string;};};user: {};v?: string;ts?: number;};// @filename: index.ts// ---cut---import { createFunction } from "inngest";// Import the type for the event you want to listen to. This fully types// the arguments to your function. The types are generated by running// `npx inngest-cli types ts`.import { GithubPullRequest } from '../__generated__/inngest';export const newPR = createFunction<GithubPullRequest>("New PR", "github/pull_request", ({ event }) => {// This function is triggered when the `github/pull_request` event is received via// a GH webhook.if (event.data.action === 'opened') {// New PR opened.}});
Serving functions in NextJS
Create a new ./pages/api/inngest.ts
file to serve the Inngest function API.
Import serve
from the inngest/next
package to create your endpoint, with all of the
functions you'd like to enable:
typescript// @declaration// @filename: index.d.tsdeclare global {var process: NodeJS.Process;namespace NodeJS {interface Process {env: Record<string, string>;}}}// @filename: ../../inngest/new_pr.tsimport { createFunction, InngestFunction } from "inngest";export const newPR: InngestFunction = createFunction("New PR", "github/pull_request", ({ event }) => {// This function is triggered when the `github/pull_request` event is received via// a GH webhook.});// @filename: index.ts// hack to get process.env to workdeclare global {var process: NodeJS.Process;namespace NodeJS {interface Process {env: Record<string, string>;}}}// ---cut---import { serve } from "inngest/next";import { newPR } from "../../inngest/new_pr";// You must export the serve handler, which hosts all of the provided functions// under one API endpoint.export default serve("My app/service name", [newPR]);
This hosts an API endpoint at ${your-url}/api/inngest
, which we'll use to call your
functions.
serve
syntax
serve(appName, [functions]);
serve(appName, [functions], options);
serve
arguments
appName
: The name of your app or microservice, used to group all functions together[functions]
: An array of all functions to enableoptions
: An optional object of options, with the type:
tstype Options = {/*** A key used to sign requests to and from Inngest in order to prove that the* source is legitimate.** You must provide a signing key to communicate securely with Inngest. If* your key is not provided here, we'll try to retrieve it from the* `INNGEST_SIGNING_KEY` environment variable.** You can retrieve your signing key from the Inngest UI inside the "Secrets"* section at {@link https://app.inngest.com/secrets}. We highly recommend* that you add this to your platform's available environment variables as* `INNGEST_SIGNING_KEY`.** If no key can be found, you will not be able to register your functions or* receive events from Inngest.*/signingKey?: string;/*** Controls whether a landing page with introspection capabilities is shown* when a `GET` request is performed to this handler.** Defaults to true if NODE_ENV !== production.** This page is highly recommended when getting started in development,* testing, or staging environments.*/landingPage?: boolean;/*** The URL used to register functions with Inngest.* Defaults to https://api.inngest.com/fn/register*/inngestRegisterUrl?: string;/*** If provided, will override the used `fetch` implementation. Useful for* giving the library a particular implementation if accessing it is not done* via globals.** By default the library will try to use the native Web API fetch, falling* back to a Node implementation if no global fetch can be found.*/fetch?: typeof fetch;}
Checking function configuration
To make sure everything is set up as you expect it to be, Inngest provides a local dashboard to introspect your served handler, show any found functions, and raise any errors before you get to production.
Visit ${your-url}/api/inngest
(usually http://localhost:3000/api/inngest
) to see the dashboard. If there are any problems detected, they'll show up here.
Deploying your Next.js application
When you deploy your application, Inngest needs to know where your applicaiton is running so it can call your functions. You can "register" your app in a couple of ways:
Registering automatically
Use one of our officially supported integrations which will automatically notify Inngest whenever you've deployed updated functions:
Registering manually
If you're deploying your appliction to another platform, you can register your app via the Inngest UI or via our API with a curl request.