Skip to content

Commit c7a930c

Browse files
authored
Minor docs updates (openapi-ts#1641)
1 parent 7e3df85 commit c7a930c

21 files changed

+456
-211
lines changed

docs/6.x/advanced.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ And the magic that produces this would live in a `test/utils.ts` file that can b
8585
<details>
8686
<summary>📄 <strong>test/utils.ts</strong></summary>
8787

88-
::: code-group [test/utils.ts]
88+
::: code-group
8989

90-
```ts
90+
```ts [test/utils.ts]
9191
import type { paths } from "./api/v1"; // generated by openapi-typescript
9292

9393
// Settings
@@ -123,7 +123,7 @@ export function mockResponses(responses: {
123123
fetchMock.mockResponse((req) => {
124124
const mockedPath = findPath(
125125
req.url.replace(BASE_URL, ""),
126-
Object.keys(responses),
126+
Object.keys(responses)
127127
)!;
128128
// note: we get lazy with the types here, because the inference is bad anyway and this has a `void` return signature. The important bit is the parameter signature.
129129
if (!mockedPath || (!responses as any)[mockedPath])
@@ -142,11 +142,11 @@ export function mockResponses(responses: {
142142
// helper function that matches a realistic URL (/users/123) to an OpenAPI path (/users/{user_id}
143143
export function findPath(
144144
actual: string,
145-
testPaths: string[],
145+
testPaths: string[]
146146
): string | undefined {
147147
const url = new URL(
148148
actual,
149-
actual.startsWith("http") ? undefined : "http://testapi.com",
149+
actual.startsWith("http") ? undefined : "http://testapi.com"
150150
);
151151
const actualParts = url.pathname.split("/");
152152
for (const p of testPaths) {

docs/advanced.md

+50-10
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ Note that debug messages will be suppressed if the output is `stdout`.
2929

3030
Example:
3131

32-
```yaml
32+
::: code-group
33+
34+
```yaml [my-openapi-3-schema.yaml]
3335
ErrorCode:
3436
type: integer
3537
format: int32
@@ -47,9 +49,13 @@ ErrorCode:
4749
- "Something went wrong"
4850
```
4951
52+
:::
53+
5054
Will result in:
5155
52-
```ts
56+
::: code-group
57+
58+
```ts [my-openapi-3-schema.d.ts]
5359
enum ErrorCode {
5460
// User is not authorized
5561
Unauthorized = 100
@@ -60,6 +66,8 @@ enum ErrorCode {
6066
}
6167
```
6268

69+
:::
70+
6371
Alternatively you can use `x-enumNames` and `x-enumDescriptions` ([NSwag/NJsonSchema](https://github.com/RicoSuter/NJsonSchema/wiki/Enums#enum-names-and-descriptions)).
6472

6573
## Styleguide
@@ -274,7 +282,9 @@ OpenAPI’s composition tools (`oneOf`/`anyOf`/`allOf`) are powerful tools for r
274282

275283
#### ❌ Bad
276284

277-
```yaml
285+
::: code-group
286+
287+
```yaml [my-openapi-3-schema.yaml]
278288
Pet:
279289
type: object
280290
properties:
@@ -296,19 +306,27 @@ Pet:
296306
- $ref: "#/components/schemas/Turtle"
297307
```
298308
309+
:::
310+
299311
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:
300312

301-
```ts
313+
::: code-group
314+
315+
```ts [my-openapi-3-schema.d.ts]
302316
Pet: ({
303317
/** @enum {string} */
304318
type?: "cat" | "dog" | "rabbit" | "snake" | "turtle";
305319
name?: string;
306320
}) & (components["schemas"]["Cat"] | components["schemas"]["Dog"] | components["schemas"]["Rabbit"] | components["schemas"]["Snake"] | components["schemas"]["Turtle"]);
307321
```
308322

323+
:::
324+
309325
#### ✅ Better
310326

311-
```yaml
327+
::: code-group
328+
329+
```yaml [my-openapi-3-schema.yaml]
312330
Pet:
313331
oneOf:
314332
- $ref: "#/components/schemas/Cat"
@@ -330,13 +348,19 @@ Cat:
330348
- cat
331349
```
332350

351+
:::
352+
333353
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"`).
334354

335-
```ts
355+
::: code-group
356+
357+
```ts [my-openapi-3-schema.d.ts]
336358
Pet: components["schemas"]["Cat"] | components["schemas"]["Dog"] | components["schemas"]["Rabbit"] | components["schemas"]["Snake"] | components["schemas"]["Turtle"];
337359
Cat: { type?: "cat"; } & components["schemas"]["PetCommonProperties"];
338360
```
339361

362+
:::
363+
340364
_Note: you optionally could provide `discriminator.propertyName: "type"` on `Pet` ([docs](https://spec.openapis.org/oas/v3.1.0#discriminator-object)) to automatically generate the `type` key, but is less explicit._
341365

342366
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.
@@ -345,7 +369,9 @@ While the schema permits you to use composition in any way you like, it’s good
345369

346370
[JSONSchema $defs](https://json-schema.org/understanding-json-schema/structuring.html#defs) can be used to provide sub-schema definitions anywhere. However, these won’t always convert cleanly to TypeScript. For example, this works:
347371

348-
```yaml
372+
::: code-group
373+
374+
```yaml [my-openapi-3-schema.yaml]
349375
components:
350376
schemas:
351377
DefType:
@@ -360,9 +386,13 @@ components:
360386
$ref: "#/components/schemas/DefType/$defs/myDefType"
361387
```
362388
389+
:::
390+
363391
This will transform into the following TypeScript:
364392
365-
```ts
393+
::: code-group
394+
395+
```ts [my-openapi-3-schema.d.ts]
366396
export interface components {
367397
schemas: {
368398
DefType: {
@@ -377,9 +407,13 @@ export interface components {
377407
}
378408
```
379409

410+
:::
411+
380412
However, this won’t:
381413

382-
```yaml
414+
::: code-group
415+
416+
```yaml [my-openapi-3-schema.yaml]
383417
components:
384418
schemas:
385419
DefType:
@@ -393,9 +427,13 @@ components:
393427
$ref: "#/components/schemas/DefType/$defs/myDefType"
394428
```
395429
430+
:::
431+
396432
Because it will transform into:
397433
398-
```ts
434+
::: code-group
435+
436+
```ts [my-openapi-3-schema.d.ts]
399437
export interface components {
400438
schemas: {
401439
DefType: string;
@@ -406,6 +444,8 @@ export interface components {
406444
}
407445
```
408446

447+
:::
448+
409449
So be wary about where you define `$defs` as they may go missing in your final generated types.
410450

411451
::: tip

docs/cli.md

+43-15
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ npx openapi-typescript https://petstore3.swagger.io/api/v3/openapi.yaml -o petst
2929

3030
To transform multiple schemas, create a `redocly.yaml` file in the root of your project with [APIs defined](https://redocly.com/docs/cli/configuration/). Under `apis`, give each schema a unique name and optionally a version (the name doesn’t matter, so long as it’s unique). Set the `root` value to your schema’s entry point—this will act as the main input. For the output, set it with `x-openapi-ts.output`:
3131

32-
```yaml
32+
::: code-group
33+
34+
```yaml [redocly.yaml]
3335
apis:
3436
core@v2:
3537
root: ./openapi/openapi.yaml
@@ -41,6 +43,8 @@ apis:
4143
output: ./openapi/external.ts
4244
```
4345
46+
:::
47+
4448
::: tip
4549
4650
This will preserve schemas 1:1 input:output. To bundle multiple schemas into one, use Redocly’s [bundle command](https://redocly.com/docs/resources/multi-file-definitions/#bundle)
@@ -73,7 +77,9 @@ You can read more about the Redoc’s configuration options [in their docs](http
7377

7478
Authentication for non-public schemas is handled in your [Redocly config](https://redocly.com/docs/cli/configuration/#resolve-non-public-or-non-remote-urls). You can add headers and basic authentication like so:
7579

76-
```yaml
80+
::: code-group
81+
82+
```yaml [redocly.yaml]
7783
resolve:
7884
http:
7985
headers:
@@ -85,6 +91,8 @@ resolve:
8591
envVariable: SECRET_AUTH
8692
```
8793

94+
:::
95+
8896
Refer to the [Redocly docs](https://redocly.com/docs/cli/configuration/#resolve-non-public-or-non-remote-urls) for additional options.
8997

9098
## Flags
@@ -113,30 +121,42 @@ The following flags are supported in the CLI:
113121

114122
By default, your URLs are preserved exactly as-written in your schema:
115123

116-
```ts
124+
::: code-group
125+
126+
```ts [my-openapi-3-schema.d.ts]
117127
export interface paths {
118128
"/user/{user_id}": components["schemas"]["User"];
119129
}
120130
```
121131

132+
:::
133+
122134
Which means your type lookups also have to match the exact URL:
123135

124-
```ts
125-
import type { paths } from "./api/v1";
136+
::: code-group
137+
138+
```ts [src/my-project.ts]
139+
import type { paths } from "./my-openapi-3-schema";
126140
127141
const url = `/user/${id}`;
128142
type UserResponses = paths["/user/{user_id}"]["responses"];
129143
```
130144

145+
:::
146+
131147
But when `--path-params-as-types` is enabled, you can take advantage of dynamic lookups like so:
132148

133-
```ts
134-
import type { paths } from "./api/v1";
149+
::: code-group
150+
151+
```ts [src/my-project.ts]
152+
import type { paths } from "./my-openapi-3-schema";
135153

136154
const url = `/user/${id}`;
137155
type UserResponses = paths[url]["responses"]; // automatically matches `paths['/user/{user_id}']`
138156
```
139157

158+
:::
159+
140160
Though this is a contrived example, you could use this feature to automatically infer typing based on the URL in a fetch client or in some other useful place in your application.
141161

142162
_Thanks, [@Powell-v2](https://github.com/Powell-v2)!_
@@ -147,7 +167,9 @@ This option is useful for generating tuples if an array type specifies `minItems
147167

148168
For example, given the following schema:
149169

150-
```yaml
170+
::: code-group
171+
172+
```yaml [my-openapi-3-schema.yaml]
151173
components:
152174
schemas:
153175
TupleType
@@ -158,17 +180,23 @@ components:
158180
maxItems: 2
159181
```
160182
183+
:::
184+
161185
Enabling `--array-length` would change the typing like so:
162186

163-
```diff
164-
export interface components {
165-
schemas: {
166-
- TupleType: string[];
167-
+ TupleType: [string] | [string, string];
168-
};
169-
}
187+
::: code-group
188+
189+
```ts [my-openapi-3-schema.d.ts]
190+
export interface components {
191+
schemas: {
192+
TupleType: string[]; // [!code --]
193+
TupleType: [string] | [string, string]; // [!code ++]
194+
};
195+
}
170196
```
171197

198+
:::
199+
172200
This results in more explicit typechecking of array lengths.
173201

174202
_Note: this has a reasonable limit, so for example `maxItems: 100` would simply flatten back down to `string[];`_

docs/examples.md

+14-10
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ A good fetch wrapper should **never use generics.** Generics require more typing
2626

2727
After [generating your types using the CLI](/introduction), pass in the proper `paths` response for each endpoint:
2828

29-
```ts
29+
::: code-group
30+
31+
```ts [src/my-project.ts]
3032
import { Hono } from "hono";
31-
import { components, paths } from "./path/to/my/types";
33+
import { components, paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
3234

3335
const app = new Hono();
3436

@@ -50,6 +52,8 @@ app.get("/users", async (ctx) => {
5052
export default app;
5153
```
5254

55+
:::
56+
5357
::: tip
5458

5559
TypeChecking in server environments can be tricky, as you’re often querying databases and talking to other endpoints that TypeScript can’t introspect. But using generics will alert you of the obvious errors that TypeScript _can_ catch (and more things in your stack may have types than you realize!).
@@ -86,9 +90,9 @@ Let’s say we want to write our mocks in the following object structure, so we
8690

8791
Using our generated types we can then infer **the correct data shape** for any given path + HTTP method + status code. An example test would look like this:
8892

89-
::: code-group [my-test.test.ts]
93+
::: code-group
9094

91-
```ts
95+
```ts [my-test.test.ts]
9296
import { mockResponses } from "../test/utils";
9397
9498
describe("My API test", () => {
@@ -133,10 +137,10 @@ And the magic that produces this would live in a `test/utils.ts` file that can b
133137
<details>
134138
<summary>📄 <strong>test/utils.ts</strong></summary>
135139

136-
::: code-group [test/utils.ts]
140+
::: code-group
137141

138-
```ts
139-
import type { paths } from "./api/v1"; // generated by openapi-typescript
142+
```ts [test/utils.ts]
143+
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
140144

141145
// Settings
142146
// ⚠️ Important: change this! This prefixes all URLs
@@ -171,7 +175,7 @@ export function mockResponses(responses: {
171175
fetchMock.mockResponse((req) => {
172176
const mockedPath = findPath(
173177
req.url.replace(BASE_URL, ""),
174-
Object.keys(responses),
178+
Object.keys(responses)
175179
)!;
176180
// note: we get lazy with the types here, because the inference is bad anyway and this has a `void` return signature. The important bit is the parameter signature.
177181
if (!mockedPath || (!responses as any)[mockedPath])
@@ -190,11 +194,11 @@ export function mockResponses(responses: {
190194
// helper function that matches a realistic URL (/users/123) to an OpenAPI path (/users/{user_id}
191195
export function findPath(
192196
actual: string,
193-
testPaths: string[],
197+
testPaths: string[]
194198
): string | undefined {
195199
const url = new URL(
196200
actual,
197-
actual.startsWith("http") ? undefined : "http://testapi.com",
201+
actual.startsWith("http") ? undefined : "http://testapi.com"
198202
);
199203
const actualParts = url.pathname.split("/");
200204
for (const p of testPaths) {

0 commit comments

Comments
 (0)