# How to generate an OpenAPI document with Hono
Source: https://speakeasy.com/openapi/frameworks/hono

import { Callout } from "@/mdx/components";



This guide walks you through generating an OpenAPI document for a [Hono](https://hono.dev/) API and using Speakeasy to create an SDK based on the generated document.

Here's what we'll do:

1. Add Zod OpenAPI and Scalar UI to a Node.js Hono project.
2. Generate an OpenAPI document using the Zod OpenAPI Hono middleware.
3. Improve the generated OpenAPI document to prepare it for code generation.
4. Use the Speakeasy CLI to generate an SDK based on the OpenAPI document.
5. Add Speakeasy OpenAPI extensions to improve the generated SDK.

We'll also take a look at how you can use the generated SDK.

Your Hono project might not be as simple as our example app, but the steps below should translate well to any Hono project.

## The OpenAPI creation pipeline

[Zod OpenAPI](https://hono.dev/examples/zod-openapi#zod-openapi) is Hono middleware that allows you to validate values and types using [Zod](https://zod.dev/) and generate an OpenAPI document. We'll begin by defining data schemas with Zod, then set up the Hono app to generate an OpenAPI document.

The quality of your OpenAPI document determines the quality of generated SDKs and documentation, so we'll look into ways you can improve the generated document based on the Speakeasy [OpenAPI best practices](/docs/prep-openapi/best-practices).

We'll then use the improved OpenAPI document to generate an SDK using Speakeasy.

We'll explain how to add SDK creation to a CI/CD pipeline so that Speakeasy automatically generates fresh SDKs whenever your Hono API changes in the future.

Finally, we'll use a simplified example to demonstrate how to use the generated SDK.

## Requirements

This guide assumes that you have an existing Hono app and basic familiarity with Hono.

<Callout title="Example repository" type="info">
  The source code for the completed example is available in the [Speakeasy Hono
  example
  repository](https://github.com/speakeasy-api/examples/tree/main/framework-hono-ts).
  If you want to follow along step-by-step, you can clone the [Speakeasy Hono
  example
  repository](https://github.com/speakeasy-api/examples/tree/main/frameworks-hono-ts/initial-app),
  which has the initial state of the app that we'll use to start this tutorial
  within the `initial-app` directory.
</Callout>

Ensure the following are installed on your machine:

- [Node.js version 18 or above](https://nodejs.org/en/download) (we used Node v22.15.1), which the Hono Node.js Adapter requires.
- The [Speakeasy CLI](/docs/speakeasy-cli/getting-started), which we'll use to generate an SDK from the OpenAPI document.

If you're using the [example initial application](https://github.com/speakeasy-api/examples/tree/main/frameworks-hono-ts/initial-app), add a `.env` file containing the following environment variables to the root of the initial app project:

```env
NODE_ENV=development
PORT=3000
```

## Adding the Zod OpenAPI middleware to a Hono project

We'll use the [Zod OpenAPI Hono](https://hono.dev/examples/zod-openapi) middleware to generate an OpenAPI document. We'll create Zod schemas to validate values and types and to generate part of the OpenAPI document.

### Creating Zod schemas

First, install the middleware and Zod:

```bash filename="Terminal"
npm i zod @hono/zod-openapi
```

Next, create a `schemas.ts` file in the `src` folder and create Zod schemas for your data:

```typescript filename="schema.ts"
import { z } from "@hono/zod-openapi";

export const UserSelectSchema = z.object({
  id: z.string(),
  name: z.string(),
  age: z.number(),
});

export const UserInsertSchema = z.object({
  name: z.string(),
  age: z.number(),
});

export const patchUserSchema = UserInsertSchema.partial();
```

The `z` object should be imported from `@hono/zod-openapi`. Note that Hono also has a [Standard Schema validator](https://github.com/honojs/middleware/tree/main/packages/standard-validator) that lets you write schemas and validate the incoming values using the same interface for multiple validation libraries that support Standard Schema. Supported validation libraries include Zod, Valibot, and ArkType.

Create schemas for your request-query parameters, messages, and error responses:

```typescript filename="schema.ts"
export const idParamsSchema = z.object({
  id: z.string().min(3),
});

export function createMessageObjectSchema(
  exampleMessage: string = "Hello World",
) {
  return z.object({
    message: z.string(),
  });
}

export function createErrorSchema<T extends ZodSchema>(schema: T) {
  const { error } = schema.safeParse(
    schema._def.typeName === z.ZodFirstPartyTypeKind.ZodArray ? [] : {},
  );
  return z.object({
    success: z.boolean(),
    error: z.object({
      issues: z.array(
        z.object({
          code: z.string(),
          path: z.array(z.union([z.string(), z.number()])),
          message: z.string().optional(),
        }),
      ),
      name: z.string(),
    }),
  });
}
```

Create a `types.ts` file in the `src/lib` folder and add the `ZodSchema` type to it:

```typescript filename="types.ts"
import type { z } from "@hono/zod-openapi";

// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
export type ZodSchema =
  | z.ZodUnion
  | z.AnyZodObject
  | z.ZodArray<z.AnyZodObject>;
```

Import this type in the `src/schemas.ts` file.

```typescript filename="schemas.ts"
import type { ZodSchema } from "./lib/types";
```

### Replacing the Hono instances with OpenAPIHono

Set up your app to use the `OpenAPIHono` instance of the Zod OpenAPI middleware instead of the `Hono` instance. Import the `OpenAPIHono` class in the `src/lib/createApp.ts` file:

```typescript filename="createApp.ts"
import { OpenAPIHono } from "@hono/zod-openapi";
```

Remove the `Hono` import and replace the `Hono` instances with `OpenAPIHono`:

```diff filename="createApp.ts"
- return new Hono({ strict: false });
+ return new OpenAPIHono({ strict: false });
```

```diff filename="createApp.ts"
- const app = new Hono({ strict: false });
+ const app = new OpenAPIHono({ strict: false });
```

The `OpenAPIHono` class is an extension of the `Hono` class that gives `OpenAPIHono` its OpenAPI document-generation functionality.

### Defining routes

Let's split the routes and handlers into separate files for better code organization.

Create a `users.routes.ts` file in the `src/routes/users` folder and use the Zod OpenAPI `createRoute` method to define your routes:

```typescript filename="users.routes.ts"
import {
  createErrorSchema,
  createMessageObjectSchema,
  idParamsSchema,
  patchUserSchema,
  UserInsertSchema,
  UserSelectSchema,
} from "@/schemas";
import { createRoute, z } from "@hono/zod-openapi";

export const list = createRoute({
  path: "/users",
  method: "get",
  responses: {
    200: {
      content: {
        "application/json": {
          schema: z.array(UserSelectSchema),
        },
      },
      description: "The list of users",
    },
  },
});

export const create = createRoute({
  path: "/users",
  method: "post",
  request: {
    body: {
      content: {
        "application/json": {
          schema: UserInsertSchema,
        },
      },
      description: "The user to create",
      required: true,
    },
  },
  responses: {
    200: {
      content: {
        "application/json": {
          schema: UserSelectSchema,
        },
      },
      description: "The created user",
    },
    404: {
      content: {
        "application/json": {
          schema: createMessageObjectSchema("Not Found"),
        },
      },
      description: "User not found",
    },
    422: {
      content: {
        "application/json": {
          schema: createErrorSchema(patchUserSchema),
        },
      },
      description: "The validation error(s)",
    },
  },
});

export const getOne = createRoute({
  path: "/users/{id}",
  method: "get",
  request: {
    params: idParamsSchema,
  },
  responses: {
    200: {
      content: {
        "application/json": {
          schema: UserSelectSchema,
        },
      },
      description: "The requested user",
    },
    404: {
      content: {
        "application/json": {
          schema: createMessageObjectSchema("Not Found"),
        },
      },
      description: "User not found",
    },
    422: {
      content: {
        "application/json": {
          schema: createErrorSchema(patchUserSchema),
        },
      },
      description: "Invalid id error",
    },
  },
});

export type ListRoute = typeof list;
export type CreateRoute = typeof create;
export type GetOneRoute = typeof getOne;
```

The `createRoute` function takes in an object that describes the route's request and possible responses. The Zod schema defines the request and response content. The route types are then exported for use in the route handlers.

### Defining route handlers

Create a `users.handlers.ts` file in the `src/routes/users` folder and add the following route handlers to it:

```typescript filename="users.handlers.ts"
import type { AppRouteHandler } from "@/lib/types";
import type {
  CreateRoute,
  GetOneRoute,
  ListRoute,
} from "@/routes/users/users.routes";

export const list: AppRouteHandler<ListRoute> = async (c) => {
  // TODO: db query to get all users
  return c.json(
    [
      {
        age: 42,
        id: "123",
        name: "John Doe",
      },
      {
        age: 32,
        id: "124",
        name: "Sarah Jones",
      },
    ],
    200,
  );
};

export const create: AppRouteHandler<CreateRoute> = async (c) => {
  const user = c.req.valid("json");
  console.log({ user });
  // TODO: db query to create a user
  return c.json(
    {
      id: "2342",
      age: user.age,
      name: user.name,
    },
    200,
  );
};

export const getOne: AppRouteHandler<GetOneRoute> = async (c) => {
  const { id } = c.req.valid("param");
  // TODO: db query to get a user by id
  const foundUser = {
    age: 50,
    id,
    name: "Lisa Smith",
  };

  if (!foundUser) {
    return c.json(
      {
        message: "Not found",
      },
      404,
    );
  }
  return c.json(foundUser, 200);
};
```

Add the following `AppRouteHandler` type to the `src/lib/types.ts` file:

```typescript filename="types.ts"
import type { RouteConfig, RouteHandler } from "@hono/zod-openapi";

export type AppRouteHandler<R extends RouteConfig> = RouteHandler<R>;
```

The handlers are made type safe by the route types. The request and response data in the Hono [context object](https://hono.dev/docs/api/context) is type checked using the schema defined in the routes. If you use an incorrect type, for example, by setting `age:` to `42`, you'll get a type error.

### Configuring the middleware for each endpoint

Replace the code in the `src/routes/users/users.index.ts` file with the following lines of code:

```typescript filename="users.index.ts"
import { createRouter } from "@/lib/createApp";
import * as handlers from "./users.handlers";
import * as routes from "./users.routes";

const router = createRouter()
  .openapi(routes.list, handlers.list)
  .openapi(routes.create, handlers.create)
  .openapi(routes.getOne, handlers.getOne);

export default router;
```

The `openapi` method takes the route and the handler as its arguments and configures the Zod OpenAPI middleware for each endpoint on the `OpenAPIHono` instance.

## Configuring and generating the OpenAPI document

Create a file called `configureOpenAPI.ts` in the `src/lib` folder and add the following lines of code to it:

```typescript filename="configureOpenAPI.ts"
import type { OpenAPIHono } from "@hono/zod-openapi";
import packageJson from "../../package.json";

export const openAPIObjectConfig = {
  openapi: "3.1.0",
  externalDocs: {
    description: "Find out more about Users API",
    url: "www.example.com",
  },
  info: {
    version: packageJson.version,
    title: "Users API",
  },
};

export default function configureOpenAPI(app: OpenAPIHono) {
  app.doc31("/doc", openAPIObjectConfig);
}
```

The `configureOpenAPI` function takes in an `OpenAPIHono` instance and uses the `doc31` method to generate an OpenAPI document based on the OpenAPI Specification version 3.1. We can view this document at the `'/doc'` endpoint. We then pass in the OpenAPI configuration object to the function to add fields to the root object of the OpenAPI document.

Now, pass in the `OpenAPIHono` app instance to the `configureOpenAPI` function in the `src/app.ts` file:

```typescript filename="app.ts"
import configureOpenAPI from "./lib/configureOpenAPI";

configureOpenAPI(app);
```

## Supported OpenAPI Specification versions in Hono and Speakeasy

Speakeasy currently supports the OpenAPI Specification versions 3.0.x and 3.1.x. We recommend using OpenAPI Specification version 3.1 if possible, as it's fully compatible with [JSON Schema](https://json-schema.org/), which gives you access to a large ecosystem of tools and libraries.

Zod OpenAPI Hono can generate an OpenAPI document using version 3.0 or version 3.1 of the OpenAPI Specification. This guide uses version 3.1.

Run the development server `npm run dev` and open [`http://localhost:3000/doc`](http://localhost:3000/doc) to see the OpenAPI document in JSON format:

```json
{
  "openapi": "3.1.0",
  "externalDocs": {
    "description": "Find out more about Users API",
    "url": "www.example.com"
  },
  "info": {
    "version": "1.0.0",
    "title": "Users API"
  },
  "components": {
    "schemas": {

    },
    "parameters": {

    }
  },
  "paths": {
    "/users": {
      "get": {
        "responses": {
          "200": {
            "description": "The list of users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    ...

```

## Adding Scalar UI middleware

Let's use the [Scalar UI middleware](https://www.npmjs.com/package/@scalar/hono-api-reference) to add an interactive documentation UI for the API.

Install the middleware:

```bash filename="Terminal"
npm i @scalar/hono-api-reference
```

Import the `Scalar` middleware in the `src/lib/configureOpenAPI.ts` file:

```typescript filename="configureOpenAPI.ts"
import { Scalar } from "@scalar/hono-api-reference";
```

Add `Scalar` as a handler for GET requests to the `/ui` route:
  
```typescript filename="configureOpenAPI.ts"
// !mark(3)
export default function configureOpenAPI(app: OpenAPIHono) {
  (app.doc31("/doc", openAPIObjectConfig),
    app.get(
      "/ui",
      Scalar({
        url: "/doc",
        pageTitle: "Users Management API",
      }),
    ));
}
```

Open your browser and navigate to [`http://localhost:3000/ui`](http://localhost:3000/ui). You should see the Scalar UI with three API endpoints in the sidebar:

![Scalar UI endpoints](/assets/openapi/hono/scalar-ui.png)

You can see the parameters required for API endpoints and try out the different API endpoints. In the `http://localhost:3000/doc` route, you can also view the OpenAPI document in JSON format.

## Registering the Zod schemas as reusable OpenAPI component schemas

The request and response content schemas of the OpenAPI document are inline:

```json
"components": {
    "schemas": {},
    "parameters": {}
  },
"paths": {
  "/users": {
    "get": {
      "responses": {
        "200": {
          "description": "The list of users",
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
```

Let's make these schemas reusable by adding them to the OpenAPI [Components Object](https://swagger.io/specification/#components-object).

Use the [`.openapi()`](https://github.com/asteasolutions/zod-to-openapi#the-openapi-method) method on the Zod object to register your Zod schemas as referenced components in the `src/schemas.ts` file:

```typescript filename="schemas.ts"
// !mark(7)
export const UserSelectSchema = z
  .object({
    id: z.string(),
    name: z.string(),
    age: z.number(),
  })
  .openapi("UserSelect");
```

This adds your schemas to the OpenAPI components object:

```json
  "components": {
    "schemas": {
      "UserSelect": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "age": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "name",
          "age"
        ]
      },
```

The schemas are referenced using a [Reference Object](https://swagger.io/specification/#reference-object) (`$ref`), which is a reference identifier that specifies the location (as a URI) of the value being referenced.

```json
  "responses": {
    "200": {
      "description": "The created user",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/UserSelect"
          }
        }
      }
    }
  }
```

## Adding OpenAPI metadata to the Zod schemas

Let's add additional OpenAPI metadata to our schemas.

In the `src/schemas.ts` file, add example values by passing in an object with an `example` property to the `openapi` method:

```typescript filename="schemas.ts"
// !mark(4:6)
export const UserSelectSchema = z
  .object({
    id: z.string().openapi({
      example: "123",
    }),
    // !mark(2:4)
    name: z.string().openapi({
      example: "John Doe",
    }),
    // !mark(2:4)
    age: z.number().openapi({
      example: 42,
    }),
  })
  .openapi("UserSelect");

// !mark(4:6)
export const UserInsertSchema = z
  .object({
    name: z.string().openapi({
      example: "John Doe",
    }),
    age: z
      .number()
      // !mark(1:3)
      .openapi({
        example: 42,
      }),
  })
  .openapi("UserInsert");
```

Define the route parameters for parameter schema:

```typescript filename="schemas.ts"
// !mark(6:9)
export const idParamsSchema = z.object({
  id: z
    .string()
    .min(3)
    .openapi({
      param: {
        name: "id",
        in: "path",
      },
      example: "423",
    })
    .openapi("idParams"),
});
```

After adding the OpenAPI metadata to your schemas, you'll see that your OpenAPI document and Scalar UI will show example values for the schemas used in requests and responses:

![Scalar UI POST request example values](/assets/openapi/hono/scalar-ui-example-post.png)

You can also view the details of the example data schemas:

![Scalar UI example data schema](/assets/openapi/hono/scalar-ui-data-schema.png)

## Adding the OpenAPI operationId using Hono Zod OpenAPI

In the OpenAPI document, each HTTP request has an `operationId` that identifies the operation. The `operationId` is also used to generate method names and documentation in SDKs.

To add an `operationId`, use the `operationId` property of the `createRoute` method in the `src/routes/users/users.routes.ts` file:

```typescript filename="users.routes.ts"
export const list = createRoute({
  operationId: 'getUsers',
```

## Adding OpenAPI tags to Zod OpenAPI Hono routes

Whether you're building a big application or only have a handful of operations, we recommend adding tags to all your Hono routes so you can group them by tag in generated SDK code and documentation.

### Adding OpenAPI tags to routes in Hono

To add an OpenAPI tag to a Zod OpenAPI Hono route, use the `tags` property to pass in an array of tags as the argument object of the `createRoute` method call:

```typescript filename="users.routes.ts"
  tags: ['Users'],
```

### Adding metadata to tags

We can add metadata to the tag by passing in a [Tag Object](https://swagger.io/specification/#tag-object), instead of a string, to a tag array item.

Add a tag to the root OpenAPI object `openAPIObjectConfig` in the `src/lib/configureOpenAPI.ts` file:

```typescript filename="configureOpenAPI.ts"
// !mark(7:14)
export const openAPIObjectConfig = {
  openapi: '3.1.0',
  externalDocs: {
    description: 'Find out more about Users API',
    url: 'https://www.example.com',
  },
  tags: [{
    name: 'Users',
    description: 'Users operations',
    externalDocs: {
      description: 'Find more info here',
      url: 'https://example.com',
    },
  }],
```

## Adding a list of servers to the Hono OpenAPI document

When validating an OpenAPI document, Speakeasy expects a list of servers at the root of the document.

Add a server by adding a `servers` property to the `openAPIObjectConfig` object:

```typescript filename="configureOpenAPI.ts"
// !mark(7:12)
export const openAPIObjectConfig = {
  openapi: '3.1.0',
  externalDocs: {
    description: 'Find out more about Users API',
    url: 'https://www.example.com',
  },
  servers: [
    {
      url: 'http://localhost:3000/',
      description: 'Development server',
    },
  ],
```

## Adding retries to your SDK with x-speakeasy-retries

[OpenAPI document extensions](/openapi/extensions) allow us to add vendor-specific functionality to the document.

- Extension fields must be prefixed with `x-`.
- Speakeasy uses extensions that start with `x-speakeasy-`.

Let's add a [Speakeasy extension](/docs/speakeasy-reference/extensions) that adds retries to requests from Speakeasy SDKs by adding a top-level `x-speakeasy-retries` schema to the OpenAPI document. We can also override the retry strategy per operation.

### Adding global retries

Apply the Speakeasy retries extension globally by adding the following `'x-speakeasy-retries'` property to the `openAPIObjectConfig` object:

```typescript filename="configureOpenAPI.ts"
// !mark(13:23)
export const openAPIObjectConfig = {
  openapi: '3.1.0',
  externalDocs: {
    description: 'Find out more about Users API',
    url: 'https://www.example.com',
  },
  servers: [
    {
      url: 'http://localhost:3000/',
      description: 'Development server',
    },
  ],
  'x-speakeasy-retries': {
    strategy: 'backoff',
    backoff: {
      initialInterval: 500,
      maxInterval: 60000,
      maxElapsedTime: 3600000,
      exponent: 1.5,
    },
    statusCodes: ['5XX'],
    retryConnectionErrors: true,
  },
```

### Adding retries per method

To create a unique retry strategy for a single route, add an `'x-speakeasy-retries'` property to the `createRoute` method call's argument object in the `src/routes/users/users.routes.ts` file:

```typescript filename="users.routes.ts"
// !mark(6:16)
export const list = createRoute({
  'operationId': 'getUsers',
  'path': '/users',
  'method': 'get',
  'tags': ['Users'],
  'x-speakeasy-retries': {
    strategy: 'backoff',
    backoff: {
      initialInterval: 300,
      maxInterval: 40000,
      maxElapsedTime: 3000000,
      exponent: 1.2,
    },
    statusCodes: ['5XX'],
    retryConnectionErrors: true,
  },
```

## Generating an SDK based on your OpenAPI document

Before generating an SDK, we need to save the Hono Zod OpenAPI-generated OpenAPI document to a file. OpenAPI files are written as JSON or YAML; we'll save it as a YAML file, as it's easier to read.

### Saving the OpenAPI document to a YAML file using a Node.js script

First install the [JS-YAML](https://www.npmjs.com/package/js-yaml) package:

```bash filename="Terminal"
npm i js-yaml
```

Then install the types for the package:

```bash filename="Terminal"
npm i --save-dev  @types/js-yaml
```

Now let's create a script to convert the OpenAPI object to a YAML file. We'll use the JS-YAML library to convert the OpenAPI object to a YAML string.

Create a script called `generateOpenAPIYamlFile.ts` in the `src` folder and add the following lines of code to it:

```typescript filename="generateOpenAPIYamlFile.ts"
import { writeFileSync } from "node:fs";
import users from "@/routes/users/users.index";
import * as yaml from "js-yaml";
import configureOpenAPI, { openAPIObjectConfig } from "./lib/configureOpenAPI";
import createApp from "./lib/createApp";

const app = createApp();

const routes = [users] as const;

configureOpenAPI(app);
routes.forEach((route) => {
  app.route("/", route);
});

// Convert the OpenAPIObject to a YAML string
const yamlString = yaml.dump(app.getOpenAPI31Document(openAPIObjectConfig));

// Save the YAML string to a file
writeFileSync("openapi.yaml", yamlString);
```

This initializes the app and routes, uses the `getOpenAPI31Document` method to generate an OpenAPI Specification version 3.1 schema object, converts the schema object to a YAML string, and saves it as a file.

Add the following script to your `package.json` file:

```json filename="package.json"
"create:openapi": "npx tsx ./src/generateOpenAPIYamlFile.ts"
```

Run the script using the following command:

```bash filename="Terminal"
npm run create:openapi
```

An `openapi.yaml` file will be created in your root folder.

### Linting the OpenAPI document with Speakeasy

The Speakeasy CLI has an OpenAPI [linting](/docs/prep-openapi/linting#configuration) command that checks the OpenAPI document for errors and style issues.

Run the linting command:

```bash filename="Terminal"
speakeasy lint openapi --schema ./openapi.yaml
```

A lint report will be displayed in the terminal, showing errors, warnings, and hints:

![Speakeasy lint report](/assets/openapi/hono/speakeasy-lint-report.png)

The Speakeasy Linter has a set of [rules](/docs/prep-openapi/linting#available-rules) that you can configure.

### Improving and customizing the OpenAPI document using Speakeasy overlays and transformations

Speakeasy [transformations](/docs/prep-openapi/transformations) are predefined functions that modify the OpenAPI document to improve it for SDK generation. You can use them to remove unused components, filter operations, and format the OpenAPI document.

[Overlays](/docs/prep-openapi/overlays/create-overlays) let you create overlay documents that can be merged with an OpenAPI document, allowing you to update and use an OpenAPI document without modifying the original OpenAPI document. Overlays are useful when the same OpenAPI document is used in multiple places. Common use cases include adding Speakeasy extensions, adding examples, and hiding internal APIs from a public SDK.

### Creating an SDK from the Speakeasy CLI

We'll use the [`quickstart`](/docs/speakeasy-cli/quickstart) command for a guided SDK setup.

Run the command using the Speakeasy CLI:

```bash filename="Terminal"
speakeasy quickstart
```

Following the prompts, provide the OpenAPI document location, name the SDK `SDK`, and select `TypeScript` as the SDK language.

In the terminal, you'll see the steps taken by Speakeasy to create the SDK.

```
│ Workflow - success
│ └─Target: sdk - success
│   └─Source: Users API - success
│     └─Validating Document - success
│     └─Diagnosing OpenAPI - success
│     └─Tracking OpenAPI Changes - success
│       └─Snapshotting OpenAPI Revision - success
│       └─Storing OpenAPI Revision - success
│   └─Validating gen.yaml - success
│   └─Generating Typescript SDK - success
│     └─Setup Environment - success
│     └─Load and Validate Document - success
│     └─Generate SDK - success
│     └─Compile SDK - success
│     └─Setup Environment - success
│     └─Load and Validate Document - success
│     └─Generate SDK - success
│   └─Generating Code Samples - success
│     └─Snapshotting Code Samples - success
│       └─Snapshotting Code Samples - success
│       └─Uploading Code Samples - success
```

Speakeasy [validates](/docs/sdks/core-concepts#validation) the OpenAPI document to check that it's ready for code generation. Validation issues will be printed in the terminal. The generated SDK will be saved as a folder in your project.

If you get ESLint styling errors, run the `speakeasy quickstart` command from outside your project.

Speakeasy also suggests improvements for your SDK using [Speakeasy Suggest](/docs/prep-openapi/maintenance), which is an AI-powered tool in Speakeasy Studio. You can see suggestions by opening the link to your Speakeasy Studio workspace in the terminal:

![Speakeasy Studio showing SDK improvement suggestions](/assets/openapi/hono/speakeasy-studio-suggestions.png)

## Adding SDK generation to your GitHub Actions

The Speakeasy [`sdk-generation-action`](https://github.com/speakeasy-api/sdk-generation-action) repository provides workflows for integrating the Speakeasy CLI into CI/CD pipelines to automatically regenerate SDKs when the OpenAPI document changes.

You can set up Speakeasy to automatically push a new branch to your SDK repositories so that your engineers can review and merge the SDK changes.

For an overview of how to set up automation for your SDKs, see the Speakeasy [SDK Workflow Matrix](/docs/workflow-reference/generation-reference).

## Using your SDK

Once you've generated your SDK, you can [publish](/docs/publish-sdk) it for use. For TypeScript, you can publish it as an npm package.

A quick, non-production-ready way to see your SDK in action is to copy your SDK folder to a frontend TypeScript project and use it there.

For example, you can create a Vite project that uses TypeScript:

```bash filename="Terminal"
npm create vite@latest
```

Copy the SDK folder from your Hono app to the `src` directory of your TypeScript Vite project and delete the SDK folder in your Hono project.

In the SDK `README.md` file, you'll find documentation about your Speakeasy SDK. TypeScript SDKs generated with Speakeasy include an installable [Model Context Protocol (MCP) server](https://www.speakeasy.com/docs/standalone-mcp/build-server) where the various SDK methods are exposed as tools that AI applications can invoke.
Your SDK documentation includes instructions for installing the MCP server.

Note that the SDK is not ready for production use. To get it production-ready, follow the steps outlined in your Speakeasy workspace.

The SDK includes Zod as a bundled dependency, as can be seen in the `sdk-typescript/package.json` file.

Replace the code in the `src/main.ts` file with the following example code taken from the `sdk-typescript/docs/sdks/users/README.md` file:

```typescript filename="main.ts"
import { SDK } from "./sdk-typescript/src/"; // Adjust the path as necessary eg if your generated SDK has a different name

const sdk = new SDK();

async function run() {
  const result = await sdk.users.getUsers();

  // Handle the result
  console.log({ result });
}

run();
```

Run the Vite dev server:

```bash filename="Terminal"
npm run dev
```

Enable CORS in your Hono dev server by importing the built-in CORS middleware in the `src/app.ts` file:

```typescript filename="app.ts"
import { cors } from "hono/cors";
```

Add the middleware and set the `origin` to the Vite dev server URL:

```typescript filename="app.ts"
app.use(
  "/users",
  cors({
    origin: "http://localhost:5173",
  }),
);
```

Run the Hono dev server as well:

```bash filename="Terminal"
npm run dev
```

You'll see the following logged in your browser dev tools console:

```
{
    "result": [
        {
            "id": "123",
            "name": "John Doe",
            "age": 42
        },
        {
            "id": "124",
            "name": "Sarah Jones",
            "age": 32
        }
    ]
}
```

The SDK functions are type safe and include TypeScript autocompletion for arguments and outputs.

If you try to access a property that doesn't exist:

```typescript filename="main.ts"
const userOne = result[0].surname;
```

You'll get a TypeScript error:

```
Property 'surname' does not exist on type 'UserSelect'
```

## Further reading

This guide covered the basics of generating an OpenAPI document using Hono. Here are some resources to help you learn more about OpenAPI, the Hono Zod OpenAPI middleware, and Speakeasy:

- [The Hono Zod OpenAPI middleware documentation](https://github.com/honojs/middleware/tree/main/packages/zod-openapi): Learn more about generating an OpenAPI document and validating values and types using Zod. The topics covered include setup, handling validation errors, configuration, RPC mode, and authorization setup.

- [The Speakeasy documentation](/docs): Speakeasy has extensive documentation covering how to generate SDKs from OpenAPI documents, how to customize SDKs, and more.
- [The Speakeasy OpenAPI reference](/openapi): View a detailed reference for the OpenAPI Specification.

- [The Speakeasy Blog](/blog): Read articles about OpenAPI, SDK generation, and more, including:
  - [Native JSONL support in your SDKs](/blog/release-jsonl-support)
  - [Introducing comprehensive SDK testing](/blog/release-sdk-testing)
  - [Model Context Protocol: TypeScript SDKs for the agentic AI ecosystem](/blog/release-model-context-protocol)