Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feature/fastify-ada…
Browse files Browse the repository at this point in the history
…pter-for-next (#254)
  • Loading branch information
NeilTheFisher authored May 24, 2023
1 parent 3624516 commit 4e363e4
Show file tree
Hide file tree
Showing 26 changed files with 25,928 additions and 13,300 deletions.
60 changes: 30 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@

```bash
# npm
npm install trpc-openapi@alpha
npm install trpc-openapi
# yarn
yarn add trpc-openapi@alpha
yarn add trpc-openapi
```

**2. Add `OpenApiMeta` to your tRPC instance.**
Expand Down Expand Up @@ -66,9 +66,9 @@ export const openApiDocument = generateOpenApiDocument(appRouter, {

**5. Add an `trpc-openapi` handler to your app.**

We currently support adapters for [`Express`](http://expressjs.com/), [`Next.js`](https://nextjs.org/) & [`node:http`](https://nodejs.org/api/http.html).
We currently support adapters for [`Express`](http://expressjs.com/), [`Next.js`](https://nextjs.org/), [`Serverless`](https://www.serverless.com/) & [`Node:HTTP`](https://nodejs.org/api/http.html).

[`Fastify`](https://www.fastify.io/) & [`Serverless`](https://www.serverless.com/) soon™, PRs are welcomed 🙌.
[`Fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), [`Fastify`](https://www.fastify.io/), [`Nuxt`](https://nuxtjs.org/), [`Workers`](https://workers.cloudflare.com/) & more soon™, PRs are welcomed 🙌.

```typescript
import http from 'http';
Expand All @@ -93,36 +93,35 @@ const body = await res.json(); /* { greeting: 'Hello James!' } */

Peer dependencies:

- [`tRPC`](https://github.com/trpc/trpc) Server v10 (`@trpc/server@next`) must be installed.
- [`Zod`](https://github.com/colinhacks/zod) v3 (`zod@^3.14.4`) must be installed.
- [`tRPC`](https://github.com/trpc/trpc) Server v10 (`@trpc/server`) must be installed.
- [`Zod`](https://github.com/colinhacks/zod) v3 (`zod@^3.14.4`) must be installed (recommended `^3.20.0`).

For a procedure to support OpenAPI the following _must_ be true:

- Both `input` and `output` parsers are present AND use `Zod` validation.
- Query `input` parsers extend `ZodObject<{ [string]: ZodString }>` or `ZodVoid`.
- Mutation `input` parsers extend `ZodObject<{ [string]: ZodAnyType }>` or `ZodVoid`.
- Query `input` parsers extend `Object<{ [string]: String | Number | BigInt | Date }>` or `Void`.
- Mutation `input` parsers extend `Object<{ [string]: AnyType }>` or `Void`.
- `meta.openapi.method` is `GET`, `POST`, `PATCH`, `PUT` or `DELETE`.
- `meta.openapi.path` is a string starting with `/`.
- `meta.openapi.path` parameters exist in `input` parser as `ZodString`
- `meta.openapi.path` parameters exist in `input` parser as `String | Number | BigInt | Date`

Please note:

- Data [`transformers`](https://trpc.io/docs/data-transformers) are ignored.
- tRPC v9 `.interop()` routers are not supported.
- Data [`transformers`](https://trpc.io/docs/data-transformers) (such as `superjson`) are ignored.
- Trailing slashes are ignored.
- Routing is case-insensitive.

## HTTP Requests

Procedures 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` content type.
Procedures 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.

### Path parameters

A 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.

#### Query parameters
### Query parameters

Query & path parameter inputs are always accepted as a `string`, if you wish to support other primitives such as `number`, `boolean`, `Date` etc. please use [`z.preprocess()`](https://github.com/colinhacks/zod#preprocess).
Query & 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).

```typescript
// Router
Expand All @@ -143,7 +142,7 @@ const res = await fetch('http://localhost:3000/say-hello/James?greeting=Hello' /
const body = await res.json(); /* { greeting: 'Hello James!' } */
```

#### Request body
### Request body

```typescript
// Router
Expand Down Expand Up @@ -187,7 +186,7 @@ Explore a [complete example here](examples/with-nextjs/src/server/router.ts).
#### Server

```typescript
import * as trpc from '@trpc/server';
import { TRPCError, initTRPC } from '@trpc/server';
import { OpenApiMeta } from 'trpc-openapi';

type User = { id: string; name: string };
Expand All @@ -201,8 +200,6 @@ const users: User[] = [

export type Context = { user: User | null };

const t = initTRPC.context<Context>().meta<OpenApiMeta>().create();

export const createContext = async ({ req, res }): Promise<Context> => {
let user: User | null = null;
if (req.headers.authorization) {
Expand All @@ -212,14 +209,16 @@ export const createContext = async ({ req, res }): Promise<Context> => {
return { user };
};

const t = initTRPC.context<Context>().meta<OpenApiMeta>().create();

export const appRouter = t.router({
sayHello: t.procedure
.meta({ openapi: { method: 'GET', path: '/say-hello', protect: true /* 👈 */ } })
.input(z.void()) // no input expected
.output(z.object({ greeting: z.string() }))
.query(({ input, ctx }) => {
if (!ctx.user) {
throw new trpc.TRPCError({ message: 'User not found', code: 'UNAUTHORIZED' });
throw new TRPCError({ message: 'User not found', code: 'UNAUTHORIZED' });
}
return { greeting: `Hello ${ctx.user.name}!` };
}),
Expand Down Expand Up @@ -326,16 +325,17 @@ Please see [full typings here](src/generator/index.ts).

Please see [full typings here](src/types.ts).

| Property | Type | Description | Required | Default |
| ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | -------- | ----------- |
| `enabled` | `boolean` | Exposes this procedure to `trpc-openapi` adapters and on the OpenAPI document. | `false` | `true` |
| `method` | `HttpMethod` | HTTP method this endpoint is exposed on. Value can be `GET`, `POST`, `PATCH`, `PUT` or `DELETE`. | `true` | `undefined` |
| `path` | `string` | Pathname this endpoint is exposed on. Value must start with `/`, specify path parameters using `{}`. | `true` | `undefined` |
| `protect` | `boolean` | Requires this endpoint to use an `Authorization` header credential with `Bearer` scheme on OpenAPI document. | `false` | `false` |
| `summary` | `string` | A short summary of the endpoint included in the OpenAPI document. | `false` | `undefined` |
| `description` | `string` | A verbose description of the endpoint included in the OpenAPI document. | `false` | `undefined` |
| `tags` | `string[]` | A list of tags used for logical grouping of endpoints in the OpenAPI document. | `false` | `undefined` |
| `headers` | `ParameterObject[]` | An array of custom headers to add for this endpoint in the OpenAPI document. | `false` | `undefined` |
| Property | Type | Description | Required | Default |
| -------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | -------- | ---------------------- |
| `enabled` | `boolean` | Exposes this procedure to `trpc-openapi` adapters and on the OpenAPI document. | `false` | `true` |
| `method` | `HttpMethod` | HTTP method this endpoint is exposed on. Value can be `GET`, `POST`, `PATCH`, `PUT` or `DELETE`. | `true` | `undefined` |
| `path` | `string` | Pathname this endpoint is exposed on. Value must start with `/`, specify path parameters using `{}`. | `true` | `undefined` |
| `protect` | `boolean` | Requires this endpoint to use an `Authorization` header credential with `Bearer` scheme on OpenAPI document. | `false` | `false` |
| `summary` | `string` | A short summary of the endpoint included in the OpenAPI document. | `false` | `undefined` |
| `description` | `string` | A verbose description of the endpoint included in the OpenAPI document. | `false` | `undefined` |
| `tags` | `string[]` | A list of tags used for logical grouping of endpoints in the OpenAPI document. | `false` | `undefined` |
| `headers` | `ParameterObject[]` | An array of custom headers to add for this endpoint in the OpenAPI document. | `false` | `undefined` |
| `contentTypes` | `ContentType[]` | A set of content types specified as accepted in the OpenAPI document. | `false` | `['application/json']` |

#### CreateOpenApiNodeHttpHandlerOptions

Expand All @@ -351,7 +351,7 @@ Please see [full typings here](src/adapters/node-http/core.ts).

---

_Still using tRPC v9? See our [`master`](https://github.com/jlalmes/trpc-openapi/tree/master) branch._
_Still using tRPC v9? See our [`.interop()`](examples/with-interop) example._

## License

Expand Down
20 changes: 10 additions & 10 deletions examples/with-express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
"dev": "ts-node-dev --respawn --transpile-only --exit-child ./src/index.ts"
},
"dependencies": {
"@trpc/server": "^10.0.0-proxy-beta.18",
"@trpc/server": "^10.9.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"jsonwebtoken": "^8.5.1",
"swagger-ui-express": "^4.5.0",
"jsonwebtoken": "^9.0.0",
"swagger-ui-express": "^4.6.0",
"uuid": "^9.0.0",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@types/cors": "^2.8.12",
"@types/express": "^4.17.14",
"@types/jsonwebtoken": "^8.5.9",
"@types/node": "^18.8.5",
"@types/cors": "^2.8.13",
"@types/express": "^4.17.16",
"@types/jsonwebtoken": "^9.0.1",
"@types/node": "^18.11.18",
"@types/swagger-ui-express": "^4.1.3",
"@types/uuid": "^8.3.4",
"@types/uuid": "^9.0.0",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
"typescript": "^4.8.4"
"typescript": "^4.9.4"
}
}
8 changes: 4 additions & 4 deletions examples/with-express/src/database.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type User = {
id: string;
email: string;
passcode: string;
passcode: number;
name: string;
};

Expand All @@ -16,19 +16,19 @@ export const database: { users: User[]; posts: Post[] } = {
{
id: '3dcb4a1f-0c91-42c5-834f-26d227c532e2',
email: '[email protected]',
passcode: '1234',
passcode: 1234,
name: 'James',
},
{
id: 'ea120573-2eb4-495e-be48-1b2debac2640',
email: '[email protected]',
passcode: '9876',
passcode: 9876,
name: 'Alex',
},
{
id: '2ee1c07c-7537-48f5-b5d8-8740e165cd62',
email: '[email protected]',
passcode: '1234',
passcode: 5678,
name: 'Sachin',
},
],
Expand Down
10 changes: 8 additions & 2 deletions examples/with-express/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ const authRouter = t.router({
.input(
z.object({
email: z.string().email(),
passcode: z.string().regex(/^[0-9]{4}$/),
passcode: z.preprocess(
(arg) => (typeof arg === 'string' ? parseInt(arg) : arg),
z.number().min(1000).max(9999),
),
name: z.string().min(3),
}),
)
Expand Down Expand Up @@ -121,7 +124,10 @@ const authRouter = t.router({
.input(
z.object({
email: z.string().email(),
passcode: z.string().regex(/^[0-9]{4}$/),
passcode: z.preprocess(
(arg) => (typeof arg === 'string' ? parseInt(arg) : arg),
z.number().min(1000).max(9999),
),
}),
)
.output(
Expand Down
8 changes: 4 additions & 4 deletions examples/with-interop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"version": "1.0.0",
"private": true,
"dependencies": {
"@trpc/server": "^10.0.0-proxy-beta.18",
"zod": "^3.19.1"
"@trpc/server": "^10.9.0",
"zod": "^3.20.2"
},
"devDependencies": {
"@types/node": "18.8.5",
"typescript": "4.8.4"
"@types/node": "^18.11.18",
"typescript": "^4.9.4"
}
}
32 changes: 16 additions & 16 deletions examples/with-nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@
"lint": "next lint"
},
"dependencies": {
"@trpc/server": "^10.0.0-proxy-beta.18",
"jsonwebtoken": "^8.5.1",
"next": "12.3.1",
"nextjs-cors": "^2.1.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"swagger-ui-react": "^4.14.3",
"@trpc/server": "^10.9.0",
"jsonwebtoken": "^9.0.0",
"next": "^13.1.5",
"nextjs-cors": "^2.1.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"swagger-ui-react": "^4.15.5",
"uuid": "^9.0.0",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.9",
"@types/node": "18.8.5",
"@types/react": "18.0.21",
"@types/react-dom": "18.0.6",
"@types/jsonwebtoken": "^9.0.1",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/swagger-ui-react": "^4.11.0",
"@types/uuid": "^8.3.4",
"eslint": "8.25.0",
"eslint-config-next": "12.3.1",
"typescript": "4.8.4"
"@types/uuid": "^9.0.0",
"eslint": "^8.32.0",
"eslint-config-next": "^13.1.5",
"typescript": "^4.9.4"
}
}
4 changes: 2 additions & 2 deletions examples/with-nextjs/src/pages/api/openapi.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { openApiDocument } from '../../server/openapi';

// Respond with our OpenAPI schema
const hander = (req: NextApiRequest, res: NextApiResponse) => {
const handler = (req: NextApiRequest, res: NextApiResponse) => {
res.status(200).send(openApiDocument);
};

export default hander;
export default handler;
8 changes: 4 additions & 4 deletions examples/with-nextjs/src/server/database.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type User = {
id: string;
email: string;
passcode: string;
passcode: number;
name: string;
};

Expand All @@ -16,19 +16,19 @@ export const database: { users: User[]; posts: Post[] } = {
{
id: '3dcb4a1f-0c91-42c5-834f-26d227c532e2',
email: '[email protected]',
passcode: '1234',
passcode: 1234,
name: 'James',
},
{
id: 'ea120573-2eb4-495e-be48-1b2debac2640',
email: '[email protected]',
passcode: '9876',
passcode: 9876,
name: 'Alex',
},
{
id: '2ee1c07c-7537-48f5-b5d8-8740e165cd62',
email: '[email protected]',
passcode: '1234',
passcode: 5678,
name: 'Sachin',
},
],
Expand Down
10 changes: 8 additions & 2 deletions examples/with-nextjs/src/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ const authRouter = t.router({
.input(
z.object({
email: z.string().email(),
passcode: z.string().regex(/^[0-9]{4}$/),
passcode: z.preprocess(
(arg) => (typeof arg === 'string' ? parseInt(arg) : arg),
z.number().min(1000).max(9999),
),
name: z.string().min(3),
}),
)
Expand Down Expand Up @@ -118,7 +121,10 @@ const authRouter = t.router({
.input(
z.object({
email: z.string().email(),
passcode: z.string().regex(/^[0-9]{4}$/),
passcode: z.preprocess(
(arg) => (typeof arg === 'string' ? parseInt(arg) : arg),
z.number().min(1000).max(9999),
),
}),
)
.output(
Expand Down
12 changes: 6 additions & 6 deletions examples/with-serverless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
"dev": "rimraf .build && serverless offline"
},
"dependencies": {
"@trpc/server": "^10.0.0-proxy-beta.18",
"zod": "^3.19.1"
"@trpc/server": "^10.9.0",
"zod": "^3.20.2"
},
"devDependencies": {
"@types/node": "^18.8.5",
"serverless": "^3.23.0",
"serverless-offline": "^11.1.2",
"@types/node": "^18.11.18",
"serverless": "^3.26.0",
"serverless-offline": "^12.0.4",
"serverless-plugin-typescript": "^2.1.4",
"typescript": "^4.8.4"
"typescript": "^4.9.4"
}
}
Loading

0 comments on commit 4e363e4

Please sign in to comment.