# Without this, the "cloud" worker may attempt to redirect Inngest to the wrong URL.
INNGEST_SERVE_HOST = "http://localhost:8787"
```

See an example of this in the [Hono framework example on GitHub](https://github.com/inngest/inngest-js/tree/main/examples/framework-hono).

### Framework: DigitalOcean Functions

The DigitalOcean serve function allows you to deploy Inngest to DigitalOcean serverless functions.
Because DigitalOcean does not provide the request URL in its function arguments, you **must** include
the function URL and path when configuring your handler:

<CodeGroup>
```ts {{ title: "v3" }}
// Your own function

serve({
  client: inngest,
  functions: [fnA],
  // Your digitalocean hostname.  This is required otherwise your functions won't work.
  serveHost: "https://faas-sfo3-your-url.doserverless.co",
  // And your DO path, also required.
  servePath: "/api/v1/web/fn-your-uuid/inngest",
});

// IMPORTANT: Makes the function available as a module in the project.
// This is required for any functions that require external dependencies.
module.exports.main = main;
```
```ts {{ title: "v2" }}
// Your own function

serve(inngest, [fnA], {
  // Your digitalocean hostname.  This is required otherwise your functions won't work.
  serveHost: "https://faas-sfo3-your-url.doserverless.co",
  // And your DO path, also required.
  servePath: "/api/v1/web/fn-your-uuid/inngest",
});

// IMPORTANT: Makes the function available as a module in the project.
// This is required for any functions that require external dependencies.
module.exports.main = main;
```
</CodeGroup>

Inngest functions can also be deployed to [DigitalOcean's App Platform or Droplets](/docs/deploy/digital-ocean).

### Framework: ElysiaJS

For [deployment options](https://elysiajs.com/patterns/deploy.html), Elysia can compile to a binary or to JavaScript, or you can deploy with Docker or Railway.

```ts {{ title: "src/index.ts" }}
serve({
  client: inngest,
  functions,
});

new Elysia().all("/api/inngest", ({ request }) =>
  handler(request)
);

// register the handler with Elysia
new Elysia()
.use(inngestHandler)

```

Elysia's `use` function expects a single argument. We make use of the `all` method for the inngest api route to handle the expected
methods and then get the request off of the context object passed to elysia handlers.

See the [ElysiaJS
example](https://github.com/inngest/inngest-js/tree/main/examples/framework-elysiajs)
for more information.

### Framework: Express

You can serve Inngest functions within your existing Express app, deployed to any hosting provider
like Render, Fly, AWS, K8S, and others:

<CodeGroup>
```ts {{ title: "v3" }}
// Your own function

// Important:  ensure you add JSON middleware to process incoming JSON POST payloads.
app.use(express.json());
app.use(
  // Expose the middleware on our recommended path at `/api/inngest`.
  "/api/inngest",
  serve({ client: inngest, functions: [fnA] })
);
```
```ts {{ title: "v2" }}
// Your own function

// Important:  ensure you add JSON middleware to process incoming JSON POST payloads.
app.use(express.json());
app.use(
  // Expose the middleware on our recommended path at `/api/inngest`.
  "/api/inngest",
  serve(inngest, [fnA])
);
```
</CodeGroup>

You must ensure you're using the `express.json()` middleware otherwise your functions won't be
executed. **Note** - You may need to set [`express.json()`'s `limit` option](https://expressjs.com/en/5x/api.html#express.json) to something higher than the default `100kb` to support larger event payloads and function state.

See the [Express
example](https://github.com/inngest/inngest-js/tree/main/examples/framework-express)
for more information.

#### Streaming <VersionBadge version="v3.39.2+" />

Express can also stream responses back to Inngest, potentially allowing much
longer timeouts.

To enable this, set add the `streaming: "force"` option to your serve handler:

<CodeGroup>
```ts {{ title: "v3" }}
serve({
  client: inngest,
  functions: [...fns],
  streaming: "force",
});
```
</CodeGroup>

For more information, check out the [Streaming](/docs/streaming) page.

### Framework: Fastify <VersionBadge version="v2.6.0+" />

You can serve Inngest functions within your existing Fastify app.

We recommend using the exported `inngestFastify` plugin, though we also expose a generic `serve()` function if you'd like to manually create a route.

<CodeGroup>
```ts {{ title: "Plugin" }}
Fastify();

fastify.register(fastifyPlugin, {
  client: inngest,
  functions: [fnA],
  options: {},
});

fastify.listen({ port: 3000 }, function (err, address) {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
});
```
```ts {{ title: "Custom route (v3)" }}
Fastify();

fastify.route({
  method: ["GET", "POST", "PUT"],
  handler: serve({ client: inngest, functions: [fnA] }),
  url: "/api/inngest",
});

fastify.listen({ port: 3000 }, function (err, address) {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
});
```
```ts {{ title: "Custom route (v2)" }}
Fastify();

fastify.route({
  method: ["GET", "POST", "PUT"],
  handler: serve(inngest, [fnA]),
  url: "/api/inngest",
});

fastify.listen({ port: 3000 }, function (err, address) {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
});
```
</CodeGroup>

See the [Fastify example](https://github.com/inngest/inngest-js/tree/main/examples/framework-fastify) for more information.

### Framework: Fresh (Deno)

Inngest works with Deno's <a href="https://fresh.deno.dev/" target="_blank" rel="nofollow">Fresh
framework</a> via the `esm.sh` CDN.  Add the serve handler to `./api/inngest.ts` as follows:

<CodeGroup>
```ts {{ title: "v3" }}
// Your own function

handler = serve({
  client: inngest,
  functions: [fnA],
});
```
```ts {{ title: "v2" }}
// Your own function

handler = serve(inngest, [fnA]);
```
</CodeGroup>

### Framework: Google Cloud Run Functions

Google's [Functions Framework](https://github.com/GoogleCloudPlatform/functions-framework-nodejs) has an Express-compatible API which enables you to use the Express serve handler to deploy your Inngest functions to Google Cloud Run. This is an example of a function:

<CodeGroup>
```ts {{ title: "v3" }}
// Your own function

ff.http(
  "inngest",
  serve({
    client: inngest,
    functions: [fnA],
    servePath: "/",
  })
);
```
```ts {{ title: "v2" }}
// Your own function

ff.http(
  'inngest',
  serve(
    inngest,
    [fnA],
    { servePath: "/" },
  )
);
```
</CodeGroup>

You can run this locally with `npx @google-cloud/functions-framework --target=inngest` which will serve your Inngest functions on port `8080`.

See the [Google Cloud Functions example](https://github.com/inngest/inngest-js/tree/main/examples/framework-google-functions-framework) for more information.

<Callout>
1st generation Cloud Run Functions are not officially supported. Using one may result in a signature verification error.
</Callout>

### Framework: Firebase Cloud Functions

Based on the Google Cloud Function architecture, the Firebase Cloud Functions provide a different API to serve functions using `onRequest`:

```typescript
inngest = onRequest(
  serve({
    client: inngestClient,
    functions: [/* ...functions... */],
  })
);
```

<Callout>

Firebase Cloud Functions require configuring `INNGEST_SERVE_PATH` with the custom function path.

For example, for a project named `inngest-firebase-functions` deployed on the `us-central1` region, the `INNGEST_SERVE_PATH` value will be as follows:

```
/inngest-firebase-functions/us-central1/inngest/
```

</Callout>

To serve your Firebase Cloud Function locally, use the following command:

```bash
firebase emulators:start
```

Please note that you'll need to start your Inngest Local Dev Server with the `-u` flag to match our Firebase Cloud Function's custom path  as follows:

```bash
npx --ignore-scripts=false inngest-cli@latest dev -u http://127.0.0.1:5001/inngest-firebase-functions/us-central1/inngest
```

_The above command example features a project named `inngest-firebase-functions` deployed on the `us-central1` region_.

### Framework: H3 <VersionBadge version="v2.7.0+" />

Inngest supports [H3](https://github.com/unjs/h3) and frameworks built upon it. Here's a simple H3 server that hosts serves an Inngest function.

<CodeGroup filename="index.js">
```ts {{ title: "v3" }}
createApp();
app.use(
  "/api/inngest",
  eventHandler(
    serve({
      client: inngest,
      functions: [fnA],
    })
  )
);

createServer(toNodeListener(app)).listen(process.env.PORT || 3000);
```
```ts {{ title: "v2" }}
createApp();
app.use("/api/inngest", eventHandler(serve(inngest, [fnA])));

createServer(toNodeListener(app)).listen(process.env.PORT || 3000);
```
</CodeGroup>

See the [github.com/unjs/h3](https://github.com/unjs/h3) repository for more information about how to host an H3 endpoint.

### Framework: Hono

Inngest supports the [Hono](https://hono.dev/) framework which is popularly deployed to Cloudflare Workers. Add the following to `./src/index.ts`:

<CodeGroup>
```ts
new Hono();

app.on(
  ["GET", "PUT", "POST"],
  "/api/inngest",
  serve({
    client: inngest,
    functions,
  })
);

export default app;
```
</CodeGroup>

<Tip>
  To automatically pass environment variables defined with Wrangler to Inngest function handlers, use the [Hono bindings middleware](/docs/examples/middleware/cloudflare-workers-environment-variables).
</Tip>

<Info>
  If you're using Hono with Cloudflare's Wrangler CLI in "_cloud_" mode, follow [the documentation above](#local-development-with-wrangler) for Cloudflare Workers.
</Info>

See the [Hono example](https://github.com/inngest/inngest-js/blob/main/examples/framework-hono) for more information.

### Framework: Koa <VersionBadge version="v3.6.0+" />

Add the following to your routing file:

<CodeGroup>
```ts {{ title: "v3" }}
new Koa();
app.use(bodyParser()); // make sure we're parsing incoming JSON

serve({
  client: inngest,
  functions,
});

app.use((ctx) => {
  if (ctx.request.path === "/api/inngest") {
    return handler(ctx);
  }
});
```
</CodeGroup>

See the [Koa example](https://github.com/inngest/inngest-js/tree/main/examples/framework-koa) for more information.

### Framework: NestJS

Add the following to `./src/main.ts`:

<CodeGroup>
```ts
async function bootstrap() {
  await NestFactory.create<NestExpressApplication>(AppModule, {
    bodyParser: true,
  });

  // Setup inngest
  app.useBodyParser('json', { limit: '10mb' });

  // Inject Dependencies into inngest functions

  app.get(Logger);
  app.get(AppService);

  // Pass dependencies into this function
  getInngestFunctions({
    appService,
    logger,
  });

  // Register inngest endpoint
  app.use(
    '/api/inngest',
    serve({
      client: inngest,
      functions: inngestFunctions,
    }),
  );

  // Start listening for http requests
  await app.listen(3000);
}

bootstrap();
```
</CodeGroup>

See the [NestJS example](https://github.com/inngest/inngest-js/tree/main/examples/framework-nestjs) for more information.

### Framework: Next.js

Inngest has first class support for Next.js API routes, allowing you to easily create the Inngest API. Both the App Router and the Pages Router are supported. For the App Router, Inngest requires `GET`, `POST`, and `PUT` methods.

<CodeGroup>
```typescript {{ title: "App Router" }}
// src/app/api/inngest/route.ts
// Your own functions

{ GET, POST, PUT } = serve({
  client: inngest,
  functions: [fnA],
});
```
```typescript {{ title: "Pages Router" }}
// pages/api/inngest.ts
// Your own function

export default serve({
  client: inngest,
  functions: [fnA],
});
```
</CodeGroup>

#### Streaming <VersionBadge version="v1.8.0+" />

Next.js Functions hosted on [Vercel](/docs/deploy/vercel) with Fluid compute can stream responses back to Inngest which can help you reach the maximum duration of 800s (13m20s) provided you are on a paid Vercel plan.

To enable this, add the `streaming: "force"` option to your serve handler:

**Next.js 13+ on Fluid compute**
<CodeGroup>
```ts
{ GET, POST, PUT } = serve({
  client: inngest,
  functions: [...fns],
  streaming: "force",
});
```
</CodeGroup>

<details>
  <summary>**Edge runtime**</summary>
  <div>
  If you are not using Vercel Fluid compute, you can also stream responses to Inngest by running on their [edge runtime](https://vercel.com/docs/functions/runtimes/edge).

  To enable this, set your runtime to `"edge"` and add the `streaming: "allow"` option to your serve handler:

  **Next.js 13+**
  <CodeGroup>
  ```ts
  runtime = "edge";

  { GET, POST, PUT } = serve({
    client: inngest,
    functions: [...fns],
    streaming: "allow",
  });
  ```
  </CodeGroup>
  </div>
</details>

<details>
  <summary>**Older versions (Next.js 12)**</summary>
  <CodeGroup>
    ```ts {{ title: "v3" }}
    config = {
      runtime: "edge",
    };

    serve({
      client: inngest,
      functions: [...fns],
      streaming: "allow",
    });
    ```
    ```ts {{ title: "v2" }}
    config = {
      runtime: "edge",
    };

    serve(inngest, [...fns], {
      streaming: "allow",
    });
    ```
  </CodeGroup>
</details>

For more information, check out the [Streaming](/docs/streaming) page.

### Framework: Nitro <VersionBadge version="v3.24.0" />

Add the following to `./server/routes/api/inngest.ts`:

<CodeGroup>
```ts
// Your own function

export default eventHandler(
  serve({
    client: inngest,
    functions: [fnA],
  })
);
```
</CodeGroup>

See the [Nitro example](https://github.com/inngest/inngest-js/tree/main/examples/framework-nitro) for more information.

### Framework: Nuxt <VersionBadge version="v0.9.2+" />

Inngest has first class support for [Nuxt server routes](https://nuxt.com/docs/guide/directory-structure/server#server-routes), allowing you to easily create the Inngest API.

Add the following within `./server/api/inngest.ts`:

<CodeGroup>
```ts {{ title: "v3" }}
// Your own function

export default defineEventHandler(
  serve({
    client: inngest,
    functions: [fnA],
  })
);
```
```ts {{ title: "v2" }}
// Your own function

export default defineEventHandler(
  serve(inngest, [fnA])
);
```
</CodeGroup>

See the [Nuxt example](https://github.com/inngest/inngest-js/tree/main/examples/framework-nuxt) for more information.

### Framework: Redwood

Add the following to `api/src/functions/inngest.ts`:

<CodeGroup>
```ts {{ title: "v3" }}
// Your own function

handler = serve({
  client: inngest,
  functions: [fnA],
  servePath: "/api/inngest",
});
```
```ts {{ title: "v2" }}
// Your own function

handler = serve(
  inngest,
  [fnA],
  { servePath: "/api/inngest" }
);
```
</CodeGroup>

You should also update your `redwood.toml` to add `apiUrl = "/api"`, ensuring your API is served
at the `/api` root.

### Framework: Remix

Add the following to `./app/routes/api.inngest.ts`:

<CodeGroup>
```ts {{ title: "v3" }}
// app/routes/api.inngest.ts
serve({
  client: inngest,
  functions: [fnA],
});

export { handler as action, handler as loader };
```
```ts {{ title: "v2" }}
// app/routes/api.inngest.ts
serve(inngest, [fnA]);

export { handler as loader, handler as action };
```
</CodeGroup>

See the [Remix example](https://github.com/inngest/inngest-js/tree/main/examples/framework-remix) for more information.

#### Streaming <VersionBadge version="v2.3.0+" />

Remix Edge Functions hosted on [Vercel](/docs/deploy/vercel) can also stream responses back to Inngest, giving you a much higher request timeout of 15 minutes (up from 10 seconds on the Vercel Hobby plan!).

To enable this, set your runtime to `"edge"` (see [Quickstart for Using Edge Functions | Vercel Docs](https://vercel.com/docs/concepts/functions/edge-functions/quickstart)) and add the `streaming: "allow"` option to your serve handler:

<CodeGroup>
```ts {{ title: "v3" }}
config = {
  runtime: "edge",
};

serve({
  client: inngest,
  functions: [...fns],
  streaming: "allow",
});
```
```ts {{ title: "v2" }}
config = {
  runtime: "edge",
};

serve(inngest, [...fns], {
  streaming: "allow",
});
```
</CodeGroup>

For more information, check out the [Streaming](/docs/streaming) page.

### Framework: Supabase Edge Functions

Supabase Edge Functions can use our `inngest/edge` package.

<CodeGroup>
```ts
// Your own function

Deno.serve(serve({
  client: inngest,
  functions: [fnA],
  servePath: "/functions/v1/your-function-name",
}));
```
</CodeGroup>

Ensure that `servePath` matches your Supabase Edge Function name. Alternatively, you can set this with the `INNGEST_SERVE_PATH` environment variable. This is necessary because Supabase Edge Functions rewrite the request path.

### Framework: Firebase Cloud Functions

Based on the Google Cloud Function architecture, the Firebase Cloud Functions provide a different API to serve functions using `onRequest`:

```typescript
inngest = onRequest(
  serve({
    client: inngestClient,
    functions: [/* ...functions... */],
  })
);
```

### Framework: SvelteKit <VersionBadge version="v3.5.0+" />

Add the following to `./src/routes/api/inngest/+server.ts`:

<CodeGroup>
```ts {{ title: "v3" }}
serve({ client: inngest, functions });
GET = inngestServe.GET;
POST = inngestServe.POST;
PUT = inngestServe.PUT;
```
</CodeGroup>

See the [SvelteKit example](https://github.com/inngest/inngest-js/tree/main/examples/framework-sveltekit) for more information.

### Framework: Tanstack Start 

Add the following to `./src/routes/api/inngest.ts`:

<CodeGroup>
```ts 
serve({ client: inngest, functions });

Route = createFileRoute("/api/inngest")({
  server: {
    handlers: {
      GET: async ({ request }) => handler(request),
      POST: async ({ request }) => handler(request),
      PUT: async ({ request }) => handler(request),
    },
  },
});

```
</CodeGroup>

See the [Tanstack Start example](https://github.com/inngest/inngest-js/tree/main/examples/framework-tanstack-start) for more information.

### Custom frameworks

If the framework that your application uses is not included in the above list of first-party supported frameworks, you can create a custom `serve` handler.

To create your own handler, check out the [example handler](https://github.com/inngest/inngest-js/blob/main/packages/inngest/src/test/functions/handler.ts) in our SDK's open source repository to understand how it works. Here's an example of a custom handler being created and used:

```ts
(options: ServeHandlerOptions) => {
  new InngestCommHandler({
    frameworkName: "edge",
    fetch: fetch.bind(globalThis),
    ...options,
    handler: (req: Request) => {
      return {
        body: () => req.json(),
        headers: (key) => req.headers.get(key),
        method: () => req.method,
        url: () => new URL(req.url, `https://${req.headers.get("host") || ""}`),
        transformResponse: ({ body, status, headers }) => {
          return new Response(body, { status, headers });
        },
      };
    },
  });

  return handler.createHandler();
};

new Inngest({ id: "example-edge-app" });

inngest.createFunction(
  { id: "hello-world" },
  { event: "test/hello.world" },
  () => "Hello, World!"
);

export default serve({ client: inngest, functions: [fn] });
```

</GuideSection>

<GuideSection show="go">

Inngest enables you to create a HTTP handler for your functions. This handler will be used to serve your functions over HTTP (compatible with `net/http`).

```go {{ title: "Go (HTTP)" }}
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/inngest/inngestgo"
	"github.com/inngest/inngestgo/step"
)

func main() {
	client, err := inngestgo.NewClient(inngestgo.ClientOpts{
		AppID: "core",
	})
	if err != nil {
		panic(err)
	}
	_, err = inngestgo.CreateFunction(
		client,
		inngestgo.FunctionOpts{
			ID:   "account-created",
			Name: "Account creation flow",
		},
		// Run on every api/account.created event.
		inngestgo.EventTrigger("api/account.created", nil),
		AccountCreated,
	)
	if err != nil {
		panic(err)
	}
	http.ListenAndServe(":8080", client.Serve())
}
```

</GuideSection>

<GuideSection show="python">

You expose your functions to Inngest through this HTTP endpoint.
Inngest provides integrations with Flask and FastAPI.

<CodeGroup>

```python {{ title: "Python (Flask)" }}
import logging
import inngest
from src.flask import app
import inngest.flask

logger = logging.getLogger(f"{app.logger.name}.inngest")
logger.setLevel(logging.DEBUG)

inngest_client = inngest.Inngest(app_id="flask_example", logger=logger)

@inngest_client.create_function(
    fn_id="hello-world",
    trigger=inngest.TriggerEvent(event="say-hello"),
)
def hello(ctx: inngest.ContextSync) -> str:

inngest.flask.serve(
    app,
    inngest_client,
    [hello],
)

app.run(port=8000)
```

```python {{ title: "Python (FastAPI)" }}
import logging
import inngest
import fastapi
import inngest.fast_api

logger = logging.getLogger("uvicorn.inngest")
logger.setLevel(logging.DEBUG)

inngest_client = inngest.Inngest(app_id="fast_api_example", logger=logger)

@inngest_client.create_function(
    fn_id="hello-world",
    trigger=inngest.TriggerEvent(event="say-hello"),
)
async def hello(ctx: inngest.Context) -> str:
    return "Hello world!"

app = fastapi.FastAPI()

inngest.fast_api.serve(
    app,
    inngest_client,
    [hello],
)
```

</CodeGroup>

</GuideSection>

</GuideSelector>

### Signing key

You'll need to assign your [signing key](/docs/platform/signing-keys) to an [`INNGEST_SIGNING_KEY`](/docs/sdk/environment-variables#inngest-signing-key) environment variable in your hosting
provider or `.env` file locally, which lets the SDK securely communicate with Inngest. If you can't
provide this as a signing key, you can pass it in to `serve` when setting up your framework. [Read
the reference for more information](/docs/sdk/reference/serve#reference).

### Other configuration

When using `serve`, allow requests up to 4 MB in size. This is the maximum request size that Inngest will send to your app. Configurating maximum request size is framework-specific, so check the documentation for your framework for more information.

## Reference

For more information about the `serve` handler, read the [the reference guide](/docs/reference/serve), which includes:

* [`serve()` configuration options](/docs/reference/serve#serve-client-functions-options)
* [How the serve handler works](/docs/reference/serve#how-the-serve-api-handler-works)