{"_id":"@calasanmarko/trpc-openapi","name":"@calasanmarko/trpc-openapi","dist-tags":{"latest":"1.2.0"},"versions":{"1.2.0":{"name":"@calasanmarko/trpc-openapi","version":"1.2.0","description":"tRPC OpenAPI","author":{"name":"James Berry","email":"jb@jamesbe.com"},"private":false,"license":"MIT","keywords":["trpc","openapi","swagger"],"homepage":"https://github.com/calasanmarko/trpc-openapi","repository":{"type":"git","url":"git+https://github.com/calasanmarko/trpc-openapi.git"},"bugs":{"url":"https://github.com/calasanmarko/trpc-openapi/issues"},"main":"dist/index.js","typings":"dist/index.d.ts","workspaces":[".","examples/with-nextjs","examples/with-express","examples/with-interop","examples/with-serverless","examples/with-fastify","examples/with-nuxtjs"],"scripts":{"test":"tsc --noEmit && jest --verbose","build":"rimraf dist && tsc -p tsconfig.build.json"},"peerDependencies":{"@trpc/server":"^10.0.0","zod":"^3.14.4"},"dependencies":{"co-body":"^6.1.0","h3":"^1.6.4","lodash.clonedeep":"^4.5.0","node-mocks-http":"^1.12.2","openapi-types":"^12.1.1","zod-to-json-schema":"^3.21.1"},"devDependencies":{"@trivago/prettier-plugin-sort-imports":"^4.1.1","@trpc/client":"^10.27.1","@types/aws-lambda":"^8.10.115","@types/co-body":"^6.1.0","@types/express":"^4.17.17","@types/jest":"^29.5.1","@types/lodash.clonedeep":"^4.5.7","@types/node":"^20.2.3","@types/node-fetch":"^2.6.4","@typescript-eslint/eslint-plugin":"^5.59.7","@typescript-eslint/parser":"^5.59.7","aws-lambda":"^1.0.7","eslint":"^8.41.0","eslint-config-prettier":"^8.8.0","eslint-plugin-import":"^2.27.5","eslint-plugin-prettier":"^4.2.1","eslint-plugin-promise":"^6.1.1","express":"^4.18.2","fastify":"^4.17.0","jest":"^29.5.0","next":"^13.4.3","node-fetch":"^2.6.11","openapi-schema-validator":"^12.1.1","prettier":"^2.8.8","rimraf":"^5.0.1","superjson":"^1.12.3","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.0.4"},"_id":"@calasanmarko/trpc-openapi@1.2.0","gitHead":"13d30e830f647c8c093f64a90064b021332fa28f","_nodeVersion":"18.18.0","_npmVersion":"10.2.0","dist":{"integrity":"sha512-tFfbRsPGjZpBXpF4qjIG+FzTqb6pJBYCee0tRmzKiVb2tsaYP1ugqCGS9paAq48ZzX9vgUY3zM7220aVMGSkfA==","shasum":"29b658d150d1033ccd43c94339e1900b9f90a797","tarball":"https://registry.npmjs.org/@calasanmarko/trpc-openapi/-/trpc-openapi-1.2.0.tgz","fileCount":87,"unpackedSize":130825,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGOCsL9oyF1niNlrmSIRjEB+pmzEMdeEyhkgGRDu3LFKAiEAgIxL6Quh+ly1a7+ENAldmZ784dfHj2Qv2ZMTI34KS3Y="}]},"_npmUser":{"name":"calasanmarko","email":"calasanmarko@hotmail.com"},"directories":{},"maintainers":[{"name":"calasanmarko","email":"calasanmarko@hotmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/trpc-openapi_1.2.0_1698323516168_0.43366490378056355"},"_hasShrinkwrap":false}},"time":{"created":"2023-10-26T12:31:56.092Z","1.2.0":"2023-10-26T12:31:56.560Z","modified":"2023-10-26T12:31:56.836Z"},"maintainers":[{"name":"calasanmarko","email":"calasanmarko@hotmail.com"}],"description":"tRPC OpenAPI","homepage":"https://github.com/calasanmarko/trpc-openapi","keywords":["trpc","openapi","swagger"],"repository":{"type":"git","url":"git+https://github.com/calasanmarko/trpc-openapi.git"},"author":{"name":"James Berry","email":"jb@jamesbe.com"},"bugs":{"url":"https://github.com/calasanmarko/trpc-openapi/issues"},"license":"MIT","readme":"![trpc-openapi](assets/trpc-openapi-readme.png)\n\n<div align=\"center\">\n  <h1>trpc-openapi</h1>\n  <a href=\"https://www.npmjs.com/package/trpc-openapi\"><img src=\"https://img.shields.io/npm/v/trpc-openapi.svg?style=flat&color=brightgreen\" target=\"_blank\" /></a>\n  <a href=\"./LICENSE\"><img src=\"https://img.shields.io/badge/license-MIT-black\" /></a>\n  <a href=\"https://trpc.io/discord\" target=\"_blank\"><img src=\"https://img.shields.io/badge/chat-discord-blue.svg\" /></a>\n  <br />\n  <hr />\n</div>\n\n#### `trpc-openapi` is maintained by ProsePilot - simple, fast and free online [writing tools](https://www.prosepilot.com/tools).\n\n---\n\n## **[OpenAPI](https://swagger.io/specification/) support for [tRPC](https://trpc.io/)** 🧩\n\n- Easy REST endpoints for your tRPC procedures.\n- Perfect for incremental adoption.\n- OpenAPI version 3.0.3.\n\n## Usage\n\n**1. Install `trpc-openapi`.**\n\n```bash\n# npm\nnpm install trpc-openapi\n# yarn\nyarn add trpc-openapi\n```\n\n**2. Add `OpenApiMeta` to your tRPC instance.**\n\n```typescript\nimport { initTRPC } from '@trpc/server';\nimport { OpenApiMeta } from 'trpc-openapi';\n\nconst t = initTRPC.meta<OpenApiMeta>().create(); /* 👈 */\n```\n\n**3. Enable `openapi` support for a procedure.**\n\n```typescript\nexport const appRouter = t.router({\n  sayHello: t.procedure\n    .meta({ /* 👉 */ openapi: { method: 'GET', path: '/say-hello' } })\n    .input(z.object({ name: z.string() }))\n    .output(z.object({ greeting: z.string() }))\n    .query(({ input }) => {\n      return { greeting: `Hello ${input.name}!` };\n    });\n});\n```\n\n**4. Generate an OpenAPI document.**\n\n```typescript\nimport { generateOpenApiDocument } from 'trpc-openapi';\n\nimport { appRouter } from '../appRouter';\n\n/* 👇 */\nexport const openApiDocument = generateOpenApiDocument(appRouter, {\n  title: 'tRPC OpenAPI',\n  version: '1.0.0',\n  baseUrl: 'http://localhost:3000',\n});\n```\n\n**5. Add an `trpc-openapi` handler to your app.**\n\nWe currently support adapters for [`Express`](http://expressjs.com/), [`Next.js`](https://nextjs.org/), [`Serverless`](https://www.serverless.com/), [`Fastify`](https://www.fastify.io/), [`Nuxt`](https://nuxtjs.org/) & [`Node:HTTP`](https://nodejs.org/api/http.html).\n\n[`Fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), [`Cloudflare Workers`](https://workers.cloudflare.com/) & more soon™, PRs are welcomed 🙌.\n\n```typescript\nimport http from 'http';\nimport { createOpenApiHttpHandler } from 'trpc-openapi';\n\nimport { appRouter } from '../appRouter';\n\nconst server = http.createServer(createOpenApiHttpHandler({ router: appRouter })); /* 👈 */\n\nserver.listen(3000);\n```\n\n**6. Profit 🤑**\n\n```typescript\n// client.ts\nconst res = await fetch('http://localhost:3000/say-hello?name=James', { method: 'GET' });\nconst body = await res.json(); /* { greeting: 'Hello James!' } */\n```\n\n## Requirements\n\nPeer dependencies:\n\n- [`tRPC`](https://github.com/trpc/trpc) Server v10 (`@trpc/server`) must be installed.\n- [`Zod`](https://github.com/colinhacks/zod) v3 (`zod@^3.14.4`) must be installed (recommended `^3.20.0`).\n\nFor a procedure to support OpenAPI the following _must_ be true:\n\n- Both `input` and `output` parsers are present AND use `Zod` validation.\n- Query `input` parsers extend `Object<{ [string]: String | Number | BigInt | Date }>` or `Void`.\n- Mutation `input` parsers extend `Object<{ [string]: AnyType }>` or `Void`.\n- `meta.openapi.method` is `GET`, `POST`, `PATCH`, `PUT` or `DELETE`.\n- `meta.openapi.path` is a string starting with `/`.\n- `meta.openapi.path` parameters exist in `input` parser as `String | Number | BigInt | Date`\n\nPlease note:\n\n- Data [`transformers`](https://trpc.io/docs/data-transformers) (such as `superjson`) are ignored.\n- Trailing slashes are ignored.\n- Routing is case-insensitive.\n\n## HTTP Requests\n\nProcedures with a `GET`/`DELETE` method will accept inputs via URL `query parameters`. Procedures with a `POST`/`PATCH`/`PUT` method will accept inputs via the `request body` with a `application/json` or `application/x-www-form-urlencoded` content type.\n\n### Path parameters\n\nA procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the `meta.openapi.path` field.\n\n### Query parameters\n\nQuery & path parameter inputs are always accepted as a `string`. This library will attempt to [coerce](https://github.com/colinhacks/zod#coercion-for-primitives) your input values to the following primitive types out of the box: `number`, `boolean`, `bigint` and `date`. If you wish to support others such as `object`, `array` etc. please use [`z.preprocess()`](https://github.com/colinhacks/zod#preprocess).\n\n```typescript\n// Router\nexport const appRouter = t.router({\n  sayHello: t.procedure\n    .meta({ openapi: { method: 'GET', path: '/say-hello/{name}' /* 👈 */ } })\n    .input(z.object({ name: z.string() /* 👈 */, greeting: z.string() }))\n    .output(z.object({ greeting: z.string() }))\n    .query(({ input }) => {\n      return { greeting: `${input.greeting} ${input.name}!` };\n    });\n});\n\n// Client\nconst res = await fetch('http://localhost:3000/say-hello/James?greeting=Hello' /* 👈 */, {\n  method: 'GET',\n});\nconst body = await res.json(); /* { greeting: 'Hello James!' } */\n```\n\n### Request body\n\n```typescript\n// Router\nexport const appRouter = t.router({\n  sayHello: t.procedure\n    .meta({ openapi: { method: 'POST', path: '/say-hello/{name}' /* 👈 */ } })\n    .input(z.object({ name: z.string() /* 👈 */, greeting: z.string() }))\n    .output(z.object({ greeting: z.string() }))\n    .mutation(({ input }) => {\n      return { greeting: `${input.greeting} ${input.name}!` };\n    });\n});\n\n// Client\nconst res = await fetch('http://localhost:3000/say-hello/James' /* 👈 */, {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ greeting: 'Hello' }),\n});\nconst body = await res.json(); /* { greeting: 'Hello James!' } */\n```\n\n### Custom headers\n\nAny custom headers can be specified in the `meta.openapi.headers` array, these headers will not be validated on request. Please consider using [Authorization](#authorization) for first-class OpenAPI auth/security support.\n\n## HTTP Responses\n\nStatus codes will be `200` by default for any successful requests. In the case of an error, the status code will be derived from the thrown `TRPCError` or fallback to `500`.\n\nYou can modify the status code or headers for any response using the `responseMeta` function.\n\nPlease see [error status codes here](src/adapters/node-http/errors.ts).\n\n## Authorization\n\nTo create protected endpoints, add `protect: true` to the `meta.openapi` object of each tRPC procedure. By default, you can then authenticate each request with the `createContext` function using the `Authorization` header with the `Bearer` scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying `securitySchemes` object.\n\nExplore a [complete example here](examples/with-nextjs/src/server/router.ts).\n\n#### Server\n\n```typescript\nimport { TRPCError, initTRPC } from '@trpc/server';\nimport { OpenApiMeta } from 'trpc-openapi';\n\ntype User = { id: string; name: string };\n\nconst users: User[] = [\n  {\n    id: 'usr_123',\n    name: 'James',\n  },\n];\n\nexport type Context = { user: User | null };\n\nexport const createContext = async ({ req, res }): Promise<Context> => {\n  let user: User | null = null;\n  if (req.headers.authorization) {\n    const userId = req.headers.authorization.split(' ')[1];\n    user = users.find((_user) => _user.id === userId);\n  }\n  return { user };\n};\n\nconst t = initTRPC.context<Context>().meta<OpenApiMeta>().create();\n\nexport const appRouter = t.router({\n  sayHello: t.procedure\n    .meta({ openapi: { method: 'GET', path: '/say-hello', protect: true /* 👈 */ } })\n    .input(z.void()) // no input expected\n    .output(z.object({ greeting: z.string() }))\n    .query(({ input, ctx }) => {\n      if (!ctx.user) {\n        throw new TRPCError({ message: 'User not found', code: 'UNAUTHORIZED' });\n      }\n      return { greeting: `Hello ${ctx.user.name}!` };\n    }),\n});\n```\n\n#### Client\n\n```typescript\nconst res = await fetch('http://localhost:3000/say-hello', {\n  method: 'GET',\n  headers: { Authorization: 'Bearer usr_123' } /* 👈 */,\n});\nconst body = await res.json(); /* { greeting: 'Hello James!' } */\n```\n\n## Examples\n\n_For advanced use-cases, please find examples in our [complete test suite](test)._\n\n#### With Express\n\nPlease see [full example here](examples/with-express).\n\n```typescript\nimport { createExpressMiddleware } from '@trpc/server/adapters/express';\nimport express from 'express';\nimport { createOpenApiExpressMiddleware } from 'trpc-openapi';\n\nimport { appRouter } from '../appRouter';\n\nconst app = express();\n\napp.use('/api/trpc', createExpressMiddleware({ router: appRouter }));\napp.use('/api', createOpenApiExpressMiddleware({ router: appRouter })); /* 👈 */\n\napp.listen(3000);\n```\n\n#### With Next.js\n\nPlease see [full example here](examples/with-nextjs).\n\n```typescript\n// pages/api/[...trpc].ts\nimport { createOpenApiNextHandler } from 'trpc-openapi';\n\nimport { appRouter } from '../../server/appRouter';\n\nexport default createOpenApiNextHandler({ router: appRouter });\n```\n\n#### With AWS Lambda\n\nPlease see [full example here](examples/with-serverless).\n\n```typescript\nimport { createOpenApiAwsLambdaHandler } from 'trpc-openapi';\n\nimport { appRouter } from './appRouter';\n\nexport const openApi = createOpenApiAwsLambdaHandler({ router: appRouter });\n```\n\n#### With Fastify\n\nPlease see [full example here](examples/with-fastify).\n\n```typescript\nimport { fastifyTRPCPlugin } from '@trpc/server/adapters/fastify';\nimport Fastify from 'fastify';\nimport { fastifyTRPCOpenApiPlugin } from 'trpc-openapi';\n\nimport { appRouter } from './router';\n\nconst fastify = Fastify();\n\nasync function main() {\n  await fastify.register(fastifyTRPCPlugin, { router: appRouter });\n  await fastify.register(fastifyTRPCOpenApiPlugin, { router: appRouter }); /* 👈 */\n\n  await fastify.listen({ port: 3000 });\n}\n\nmain();\n```\n\n## Types\n\n#### GenerateOpenApiDocumentOptions\n\nPlease see [full typings here](src/generator/index.ts).\n\n| Property          | Type                                   | Description                                             | Required |\n| ----------------- | -------------------------------------- | ------------------------------------------------------- | -------- |\n| `title`           | `string`                               | The title of the API.                                   | `true`   |\n| `description`     | `string`                               | A short description of the API.                         | `false`  |\n| `version`         | `string`                               | The version of the OpenAPI document.                    | `true`   |\n| `baseUrl`         | `string`                               | The base URL of the target server.                      | `true`   |\n| `docsUrl`         | `string`                               | A URL to any external documentation.                    | `false`  |\n| `tags`            | `string[]`                             | A list for ordering endpoint groups.                    | `false`  |\n| `securitySchemes` | `Record<string, SecuritySchemeObject>` | Defaults to `Authorization` header with `Bearer` scheme | `false`  |\n\n#### OpenApiMeta\n\nPlease see [full typings here](src/types.ts).\n\n| Property       | Type                | Description                                                                                          | Required | Default                |\n| -------------- | ------------------- | ---------------------------------------------------------------------------------------------------- | -------- | ---------------------- |\n| `enabled`      | `boolean`           | Exposes this procedure to `trpc-openapi` adapters and on the OpenAPI document.                       | `false`  | `true`                 |\n| `method`       | `HttpMethod`        | HTTP method this endpoint is exposed on. Value can be `GET`, `POST`, `PATCH`, `PUT` or `DELETE`.     | `true`   | `undefined`            |\n| `path`         | `string`            | Pathname this endpoint is exposed on. Value must start with `/`, specify path parameters using `{}`. | `true`   | `undefined`            |\n| `protect`      | `boolean`           | Requires this endpoint to use a security scheme.                                                     | `false`  | `false`                |\n| `summary`      | `string`            | A short summary of the endpoint included in the OpenAPI document.                                    | `false`  | `undefined`            |\n| `description`  | `string`            | A verbose description of the endpoint included in the OpenAPI document.                              | `false`  | `undefined`            |\n| `tags`         | `string[]`          | A list of tags used for logical grouping of endpoints in the OpenAPI document.                       | `false`  | `undefined`            |\n| `headers`      | `ParameterObject[]` | An array of custom headers to add for this endpoint in the OpenAPI document.                         | `false`  | `undefined`            |\n| `contentTypes` | `ContentType[]`     | A set of content types specified as accepted in the OpenAPI document.                                | `false`  | `['application/json']` |\n| `deprecated`   | `boolean`           | Whether or not to mark an endpoint as deprecated                                                     | `false`  | `false`                |\n\n#### CreateOpenApiNodeHttpHandlerOptions\n\nPlease see [full typings here](src/adapters/node-http/core.ts).\n\n| Property        | Type       | Description                                            | Required |\n| --------------- | ---------- | ------------------------------------------------------ | -------- |\n| `router`        | `Router`   | Your application tRPC router.                          | `true`   |\n| `createContext` | `Function` | Passes contextual (`ctx`) data to procedure resolvers. | `false`  |\n| `responseMeta`  | `Function` | Returns any modifications to statusCode & headers.     | `false`  |\n| `onError`       | `Function` | Called if error occurs inside handler.                 | `false`  |\n| `maxBodySize`   | `number`   | Maximum request body size in bytes (default: 100kb).   | `false`  |\n\n---\n\n_Still using tRPC v9? See our [`.interop()`](examples/with-interop) example._\n\n## License\n\nDistributed under the MIT License. See LICENSE for more information.\n\n## Contact\n\nJames Berry - Follow me on Twitter [@jlalmes](https://twitter.com/jlalmes) 💚\n","readmeFilename":"README.md"}