Skip to content

Add docs pass #1341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/scripts/update-contributors.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const OPENAPI_TS_CONTRIBUTORS = [
"psychedelicious",
"tkrotoff",
"pimveldhuisen",
"asvishnyakov",
]),
];

Expand Down
79 changes: 75 additions & 4 deletions docs/src/content/docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ However, APIs are language-agnostic, and may contain a different syntax style fr

Instead, treat “consistency” in a more holistic sense, recognizing that preserving the API schema as-written is better than adhering to language-specific style conventions.

### Enable `noUncheckedIndexedAccess` in your tsconfig.json
### Enable `noUncheckedIndexedAccess`

openapi-typescript generates a `Record` for `additionalProperties` and tries to avoid adding a `undefined` union to the index signature. However, this may result in unsafe property access in TypeScript, **unless** the compiler flag `noUncheckedIndexedAccess` is set ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson)). If set, TypeScript will error when you try to access a property that might not be set.
[Additional Properties](https://swagger.io/docs/specification/data-models/dictionaries/) (a.k.a. dictionaries) generate a type of `Record<string, T>` in TypeScript. TypeScript’s default behavior is a bit dangerous because it will confidently assert a key is there even if you haven’t checked for it. For that reason it’s **highly recommended** to enable `compilerOptions.noUncheckedIndexedAccess` ([docs](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)) so any `additionalProperties` key will be typed as `T | undefined`.

### Be specific in your schema

Expand Down Expand Up @@ -268,7 +268,7 @@ When it comes to **tuple types**, you’ll also get better results by representi
<table>
<thead>
<tr>
<td style="width:10%"></td>
<td style="width:10%">&nbsp;</td>
<th scope="col" style="width:40%">Schema</th>
<th scope="col" style="width:40%">Generated Type</th>
</tr>
Expand Down Expand Up @@ -352,7 +352,7 @@ prefixItems:
</tbody>
</table>

### Use `$defs` only in objects
### Use `$defs` only in object types

<a href="https://json-schema.org/understanding-json-schema/structuring.html#defs" target="_blank" rel="noopener noreferrer">JSONSchema $defs</a> can be used to provide sub-schema definitions anywhere. However, these won’t always convert cleanly to TypeScript. For example, this works:

Expand Down Expand Up @@ -418,3 +418,74 @@ export interface components {
```

So be wary about where you define `$defs` as they may go missing in your final generated types. When in doubt, you can always define `$defs` at the root schema level.

### Use `oneOf` by itself

OpenAPI’s composition tools (`oneOf`/`anyOf`/`allOf`) are powerful tools for reducing the amount of code in your schema while maximizing flexibility. TypeScript unions, however, don’t provide [XOR behavior](https://en.wikipedia.org/wiki/Exclusive_or), which means they don’t map directly to `oneOf`. For that reason, it’s recommended to use `oneOf` by itself, and not combined with other composition methods or other properties. e.g.:

#### ❌ Bad

```yaml
Pet:
type: object
properties:
type:
type: string
enum:
- cat
- dog
- rabbit
- snake
- turtle
name:
type: string
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Dog"
- $ref: "#/components/schemas/Rabbit"
- $ref: "#/components/schemas/Snake"
- $ref: "#/components/schemas/Turtle"
```

This generates the following type which mixes both TypeScript unions and intersections. While this is valid TypeScript, it’s complex, and inference may not work as you intended. But the biggest offense is TypeScript can’t discriminate via the `type` property:

```ts
Pet: ({
/** @enum {string} */
type?: "cat" | "dog" | "rabbit" | "snake" | "turtle";
name?: string;
}) & (components["schemas"]["Cat"] | components["schemas"]["Dog"] | components["schemas"]["Rabbit"] | components["schemas"]["Snake"] | components["schemas"]["Turtle"]);
```

#### ✅ Better

```yaml
Pet:
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Dog"
- $ref: "#/components/schemas/Rabbit"
- $ref: "#/components/schemas/Snake"
- $ref: "#/components/schemas/Turtle"
PetCommonProperties:
type: object
properties:
name:
type: string
Cat:
allOf:
- "$ref": "#/components/schemas/PetCommonProperties"
type:
type: string
enum:
- cat
```

The resulting generated types are not only simpler; TypeScript can now discriminate using `type` (notice `Cat` has `type` with a single enum value of `"cat"`).

```ts
Pet: components["schemas"]["Cat"] | components["schemas"]["Dog"] | components["schemas"]["Rabbit"] | components["schemas"]["Snake"] | components["schemas"]["Turtle"];
Cat: { type?: "cat"; } & components["schemas"]["PetCommonProperties"];
```

While the schema permits you to use composition in any way you like, it’s good to always take a look at the generated types and see if there’s a simpler way to express your unions & intersections. Limiting the use of `oneOf` is not the only way to do that, but often yields the greatest benefits.
2 changes: 1 addition & 1 deletion docs/src/content/docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: CLI
description: openapi-typescript CLI usage
---

The CLI is the most common way to use openapi-typescript. The CLI can parse JSON and YAML (via <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">js-yaml</a>). It can also handle both local and remote schemas.
The CLI is the most common way to use openapi-typescript. The CLI can parse JSON and YAML (via <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">js-yaml</a>). It can parse local and remote schemas (and even supports basic auth).

## Reading schemas

Expand Down
10 changes: 4 additions & 6 deletions docs/src/content/docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ description: Quickstart

<img src="/assets/openapi-ts.svg" alt="openapi-typescript" width="200" height="40" />

openapi-typescript generates TypeScript types from static <a href="https://spec.openapis.org/oas/latest.html" target="_blank" rel="noopener noreferrer">OpenAPI</a> schemas quickly using only Node.js. It is fast, lightweight, (almost) dependency-free, and no Java/node-gyp/running OpenAPI servers necessary.
openapi-typescript turns <a href="https://spec.openapis.org/oas/latest.html" target="_blank" rel="noopener noreferrer">OpenAPI 3.0 & 3.1</a> schemas into TypeScript quickly using Node.js. No Java/node-gyp/running OpenAPI servers necessary.

The code is <a href="https://github.com/drwpow/openapi-typescript/blob/main/packages/openapi-typescript/LICENSE" target="_blank" rel="noopener noreferrer">MIT-licensed</a > and free for use.

## Features

- ✅ Supports OpenAPI 3.0 and 3.1 (including advanced features like <a href="https://spec.openapis.org/oas/v3.1.0#discriminator-object" target="_blank" rel="noopener noreferrer">discriminators</a>)
- ✅ Generate **runtime-free types** that outperform old-school codegen
- ✅ Generate **runtime-free types** that outperform old school codegen
- ✅ Load schemas from YAML or JSON, locally or remotely
- ✅ Native Node.js code is fast and generates types within milliseconds
- ✅ Generate types for even huge schemas within milliseconds

_Note: OpenAPI 2.x is supported with versions `5.x` and previous_

Expand All @@ -30,9 +30,7 @@ This library requires the latest version of <a href="https://nodejs.org/en" targ
npm i -D openapi-typescript
```

> ✨ **Tip**
>
> Enabling [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in `tsconfig.json` can go along way to improve type safety ([read more](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))
> **Highly recommended**: enable [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in your `tsconfig.json` ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))

## Basic usage

Expand Down
18 changes: 6 additions & 12 deletions docs/src/content/docs/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,15 @@ title: Node.js API
description: Node.js API
---

The Node API may be useful if dealing with dynamically-created schemas, or you’re using within context of a larger application. Pass in either a JSON-friendly object to load a schema from memory, or a string to load a schema from a local file or remote URL (it will load the file quickly using built-in Node methods).
The Node API may be useful if dealing with dynamically-created schemas, or you’re using within context of a larger application. Pass in either a JSON-friendly object to load a schema from memory, or a string to load a schema from a local file or remote URL.

## Setup

```bash
npm i --save-dev openapi-typescript
```

Note that the Node.js API requires [ESM support](https://nodejs.org/api/esm.html) in Node. This can be enabled by adding

```json
"type": "module"
```

To your `package.json`. Or it can be consumed in a `.mjs` file extension (rather than `.js` or `.cjs`)
> **Recommended**: For the best experience, use Node ESM by adding `"type": "module"` to `package.json` ([docs](https://nodejs.org/api/esm.html#enabling))

## Usage

Expand All @@ -37,7 +31,7 @@ const output = await openapiTS(localPath);
const output = await openapiTS("https://myurl.com/v1/openapi.yaml");
```

> ⚠️ Note that unlike the CLI, YAML isn’t supported in the Node.js API. You’ll need to convert it to JSON yourself using <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">js-yaml</a> first.
> **Note**: a YAML string isn’t supported in the Node.js API (you’ll need to <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">convert it to JSON</a>). But loading YAML via URL is still supported in Node.js

## Options

Expand All @@ -53,10 +47,10 @@ The Node API supports all the [CLI flags](/cli#options) in `camelCase` format, p

### transform / postTransform

Use the `transform()` and `postTransform()` options to override the default Schema Object transformer with your own. This is useful for providing non-standard modifications for specific parts of your schema.
Use the `transform()` and `postTransform()` options to override the default Schema Object transformer with your own. This is useful for providing nonstandard modifications for specific parts of your schema.

- `transform()` runs **BEFORE** the conversion to TypeScript (you’re working with the original OpenAPI nodes)
- `postTransform()` runs **AFTER** the conversion to TypeScript (you’re working with TypeScript types)
- `transform()` runs **before** the conversion to TypeScript (you’re working with the original OpenAPI nodes)
- `postTransform()` runs **after** the conversion to TypeScript (you’re working with TypeScript types)

#### Example: `Date` types

Expand Down
37 changes: 22 additions & 15 deletions docs/src/content/docs/openapi-fetch/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,27 @@ import { paths } from "./v1"; // generated from openapi-typescript

const { GET, PUT } = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });

// Type-checked request
const {
data, // only present if 2XX response
error, // only present if 4XX or 5XX response
} = await GET("/blogposts/{post_id}", {
params: {
path: { post_id: "123" },
},
});

await PUT("/blogposts", {
body: {
title: "My New Post",
// ❌ Property 'publish_date' is missing in type …
},
});

// Type-checked response
const { data, error } = await GET("/blogposts/{post_id}", { params: { path: { post_id: "123" } } });
console.log(data.title); // ❌ 'data' is possibly 'undefined'
console.log(error.message); // ❌ 'error' is possibly 'undefined'
console.log(data?.foo); // ❌ Property 'foo' does not exist on type …
```

Notice **there are no generics, and no manual typing.** Your endpoint’s exact request & response was inferred automatically off the URL. This makes a **big difference** in the type safety of your endpoints! This eliminates all of the following:
`data` and `error` are typechecked and expose their shapes to Intellisence in VS Code (and any other IDE with TypeScript support). Likewise, the request `body` will also typecheck its fields, erring if any required params are missing, or if there’s a type mismatch.

`GET()`, `PUT()`, `POST()`, etc. are thin wrappers around the native [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) (which you can [swap for any call](/openapi-fetch/api#create-client)).

Notice there are no generics, and no manual typing. Your endpoint’s request and response were inferred automatically. This is a huge improvement in the type safety of your endpoints because **every manual assertion could lead to a bug**! This eliminates all of the following:

- ✅ No typos in URLs or params
- ✅ All parameters, request bodies, and responses are type-checked and 100% match your schema
Expand All @@ -56,6 +61,8 @@ npm i openapi-fetch
npm i -D openapi-typescript
```

> **Highly recommended**: enable [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in your `tsconfig.json` ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))

Next, generate TypeScript types from your OpenAPI schema using openapi-typescript:

```bash
Expand All @@ -76,18 +83,14 @@ Lastly, be sure to **run typechecking** in your project. This can be done by add

And run `npm run test:ts` in your CI to catch type errors.

> ✨ **Tip**
>
> Always use `tsc --noEmit` to check for type errors! Your build tools (Vite, esbuild, webpack, etc.) won’t typecheck as accurately as the TypeScript compiler itself.
> **Tip**: use `tsc --noEmit` to check for type errors rather than relying on your linter or your build command. Nothing will typecheck as accurately as the TypeScript compiler itself.

## Usage

Using **openapi-fetch** is as easy as reading your schema:
The best part about using openapi-fetch over oldschool codegen is no documentation needed. openapi-fetch encourages using your existing OpenAPI documentation rather than trying to find what function to import, or what parameters that function wants:

![OpenAPI schema example](/assets/openapi-schema.png)

Here’s how you’d fetch GET `/blogposts/{post_id}` and PUT `/blogposts`:

```ts
import createClient from "openapi-fetch";
import { paths } from "./v1";
Expand All @@ -110,6 +113,10 @@ const { data, error } = await PUT("/blogposts", {
});
```

1. The HTTP method is pulled directly from `createClient()`
2. You pass in your desired `path` to `GET()`, `PUT()`, etc.
3. TypeScript takes over the rest and returns helpful errors for anything missing or invalid

### Pathname

The pathname of `GET()`, `PUT()`, `POST()`, etc. **must match your schema literally.** Note in the example, the URL is `/blogposts/{post_id}`. This library will replace all `path` params for you (so they can be typechecked)
Expand Down
Loading