Skip to content

Commit

Permalink
add framework support for Hono (#539)
Browse files Browse the repository at this point in the history
## Summary

Adds support for [Hono](https://hono.dev) framework.

Currently for `hono.test.ts`,
- 16/21 tests failing (76%)
- 3/21 tests passing (14%)

In comparison, other frameworks (e.g., Koa, Fastify) have:
- 6/21 tests failing (29%)
- 13/21 tests passing (62%)

## Checklist
<!-- Tick these items off as you progress. -->
<!-- If an item isn't applicable, ideally please strikeout the item by
wrapping it in "~~"" and suffix it with "N/A My reason for skipping
this." -->
<!-- e.g. "- [ ] ~~Added tests~~ N/A Only touches docs" -->

- [ ] Added a [docs PR](https://github.com/inngest/website) that
references this PR
- [x] Added unit/integration tests
- [x] Added changesets if applicable

## Related
<!-- A space for any related links, issues, or PRs. -->
<!-- Linear issues are autolinked. -->
<!-- e.g. - INN-123 -->
<!-- GitHub issues/PRs can be linked using shorthand. -->
<!-- e.g. "- inngest/inngest#123" -->
<!-- Feel free to remove this section if there are no applicable related
links.-->
- INN-

---------

Co-authored-by: Jack Williams <[email protected]>
Co-authored-by: Jack Williams <[email protected]>
  • Loading branch information
3 people committed Apr 24, 2024
1 parent 310faea commit 24f1e7d
Show file tree
Hide file tree
Showing 18 changed files with 287 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-ducks-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"inngest": minor
---

Added framework support for Hono
10 changes: 10 additions & 0 deletions examples/framework-hono/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
dist
.wrangler
.dev.vars

# Change them to your taste:
package-lock.json
yarn.lock
pnpm-lock.yaml
bun.lockb
36 changes: 36 additions & 0 deletions examples/framework-hono/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Inngest Hono Template

This is a [Hono](https://hono.dev/) v4 project targetting Cloudflare Workers. It is a reference on how to send and receive events with Inngest and Hono.

## Getting Started

Use [`create-next-app`](https://www.npmjs.com/package/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:

```bash
npx create-next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-hono inngest-hono
```

```bash
yarn create next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-hono inngest-hono
```

```bash
pnpm create next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-hono inngest-hono
```

### Run the app locally

```sh
npm install
npm run dev
```

Open http://localhost:3000/api/inngest with your browser to see the result.

- [Inngest functions](https://www.inngest.com/docs/functions) are available at `src/inngest/`.
<!-- - The [Inngest handler](https://www.inngest.com/docs/sdk/serve#framework-hono) is available at `index.ts`. -->

## Learn More

- [Inngest Documentation](https://www.inngest.com/docs) - learn about the Inngest SDK, functions, and events
- [Hono Documentation](https://hono.dev/top) - learn about Hono features and API
13 changes: 13 additions & 0 deletions examples/framework-hono/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"scripts": {
"dev": "wrangler dev --port 3000 src/index.ts",
"deploy": "wrangler deploy --minify src/index.ts"
},
"dependencies": {
"hono": "^4.2.7"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240403.0",
"wrangler": "^3.47.0"
}
}
16 changes: 16 additions & 0 deletions examples/framework-hono/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Hono } from "hono";
import { serve } from "inngest/hono";
import { functions, inngest } from "./inngest";

const app = new Hono();

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

export default app;
4 changes: 4 additions & 0 deletions examples/framework-hono/src/inngest/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Inngest } from "inngest";
import { schemas } from "./types";

export const inngest = new Inngest({ id: "my-bun-app", schemas });
11 changes: 11 additions & 0 deletions examples/framework-hono/src/inngest/helloWorld.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { inngest } from "./client";

export const helloWorld = inngest.createFunction(
{ id: "hello-world" },
{ event: "demo/event.sent" },
async ({ event, step }) => {
return {
message: `Hello ${event.name}!`,
};
}
);
5 changes: 5 additions & 0 deletions examples/framework-hono/src/inngest/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { helloWorld } from "./helloWorld";

export const functions = [helloWorld];

export { inngest } from "./client";
10 changes: 10 additions & 0 deletions examples/framework-hono/src/inngest/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { EventSchemas } from "inngest";

type DemoEventSent = {
name: "demo/event.sent";
data: {
message: string;
};
};

export const schemas = new EventSchemas().fromUnion<DemoEventSent>();
16 changes: 16 additions & 0 deletions examples/framework-hono/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"lib": [
"ESNext"
],
"types": [
"@cloudflare/workers-types"
],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
},
}
21 changes: 21 additions & 0 deletions examples/framework-hono/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name = "framework-hono"
compatibility_date = "2023-12-01"

# [vars]
# MY_VAR = "my-variable"

# [[kv_namespaces]]
# binding = "MY_KV_NAMESPACE"
# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# [[r2_buckets]]
# binding = "MY_BUCKET"
# bucket_name = "my-bucket"

# [[d1_databases]]
# binding = "DB"
# database_name = "my-database"
# database_id = ""

# [ai]
# binding = "AI"
1 change: 1 addition & 0 deletions packages/inngest/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module.exports = {
"src/fastify.ts",
"src/h3.ts",
"src/koa.ts",
"src/hono.ts",
"src/lambda.ts",
"src/next.ts",
"src/nuxt.ts",
Expand Down
6 changes: 6 additions & 0 deletions packages/inngest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@
"import": "./deno/fresh.js",
"types": "./deno/fresh.d.ts"
},
"./hono": {
"require": "./hono.js",
"import": "./hono.js",
"types": "./hono.d.ts"
},
"./api/*": "./api/*.js",
"./components/*": "./components/*.js",
"./deno/*": "./deno/*.js",
Expand Down Expand Up @@ -210,6 +215,7 @@
"fastify": "^4.21.0",
"genversion": "^3.1.1",
"glob": "^10.3.10",
"hono": "^4.2.3",
"inquirer": "^9.2.10",
"jest": "^29.3.1",
"jest-fetch-mock": "^3.0.3",
Expand Down
1 change: 1 addition & 0 deletions packages/inngest/scripts/checkDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ checkDependencies("tsconfig.build.json", [
"src/fastify.ts",
"src/h3.ts",
"src/koa.ts",
"src/hono.ts",
"src/lambda.ts",
"src/next.ts",
"src/nuxt.ts",
Expand Down
73 changes: 73 additions & 0 deletions packages/inngest/src/hono.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as HonoHandler from "@local/hono";
import fetch, { Headers, Response } from "cross-fetch";
import { testFramework } from "./test/helpers";

const originalFetch = globalThis.fetch;
const originalResponse = globalThis.Response;
const originalHeaders = globalThis.Headers;

testFramework("Hono", HonoHandler, {
/**
* Make sure this stuff is available for all polyfilled Node environments.
*/
lifecycleChanges: () => {
beforeEach(() => {
jest.resetModules();
Object.defineProperties(globalThis, {
/**
* Fake a global `fetch` value, which is available as as a Web Standard
* API.
*/
fetch: { value: fetch, configurable: true },
/**
* Fake a global `Response` class, which is used to create new responses
* for the handler.
*/
Response: { value: Response, configurable: true },
/**
* Fake a global `Headers` class, which is used to create new Headers
* objects during response building.
*/
Headers: { value: Headers, configurable: true },
});
});
afterEach(() => {
/**
* Reset all changes made to the global scope
*/
Object.defineProperties(globalThis, {
fetch: { value: originalFetch, configurable: true },
Response: { value: originalResponse, configurable: true },
Headers: { value: originalHeaders, configurable: true },
});
});
},
transformReq: (req) => {
const c = {
req: {
// in practice, this is an absolute URL
url: new URL(`https://${req.headers["host"]}${req.url}`).href,
query: (key: string) =>
new URLSearchParams(req.url.split("?")[1] || "").get(key),
header: (key: string) => req.headers[key] as string,
method: req.method,
json: () => Promise.resolve(req.body),
},
body: (data: BodyInit, init: ResponseInit) => new Response(data, init),
};
return [c];
},
transformRes: async (_args, ret: Response) => {
const headers: Record<string, string> = {};

ret.headers.forEach((v, k) => {
headers[k] = v;
});

return {
status: ret.status,
body: await ret.text(),
headers,
};
},
});
48 changes: 48 additions & 0 deletions packages/inngest/src/hono.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { type Context } from "hono";
import {
InngestCommHandler,
type ServeHandlerOptions,
} from "./components/InngestCommHandler";
import { type SupportedFrameworkName } from "./types";

export const frameworkName: SupportedFrameworkName = "hono";

/**
* Using Hono, serve and register any declared functions with Inngest,
* making them available to be triggered by events.
*
* @example
* ```ts
* const handler = serve({
* client: inngest,
* functions
* });
*
* app.use('/api/inngest', async (c) => {
* return handler(c);
* });
* ```
*
* @public
*/
export const serve = (options: ServeHandlerOptions) => {
const handler = new InngestCommHandler({
fetch: fetch.bind(globalThis),
frameworkName,
...options,
handler: (c: Context) => {
return {
transformResponse: ({ headers, status, body }) => {
return c.body(body, { headers, status });
},
url: () => new URL(c.req.url, c.req.header("host")),
queryString: (key) => c.req.query(key),
headers: (key) => c.req.header(key),
method: () => c.req.method,
body: () => c.req.json(),
};
},
});

return handler.createHandler();
};
3 changes: 2 additions & 1 deletion packages/inngest/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,8 @@ export type SupportedFrameworkName =
| "deno/fresh"
| "sveltekit"
| "fastify"
| "koa";
| "koa"
| "hono";

/**
* A set of options that can be passed to any step to configure it.
Expand Down
10 changes: 9 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 24f1e7d

Please sign in to comment.