Skip to content

Commit

Permalink
docs: add initial documentation for response helper
Browse files Browse the repository at this point in the history
  • Loading branch information
christoph-fricke committed May 15, 2024
1 parent bf0a311 commit df138aa
Showing 1 changed file with 107 additions and 13 deletions.
120 changes: 107 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ type-safety and editor suggestion benefits:
current path
- **Request Body:** Automatically typed with the request-body schema of the
current path
- **Response:** Automatically forced to match the response-body schema of the
current path
- **Response:** Automatically forced to match the available status-codes,
content-types, and response-bodies schema of the current path

```typescript
import { HttpResponse } from "msw";
import { createOpenApiHttp } from "openapi-msw";
// 1. Import the paths from your OpenAPI schema definitions
import type { paths } from "./your-openapi-schema";
Expand All @@ -50,23 +49,25 @@ import type { paths } from "./your-openapi-schema";
const http = createOpenApiHttp<paths>();

// TS only suggests available GET paths
const getHandler = http.get("/resource/{id}", ({ params }) => {
const getHandler = http.get("/resource/{id}", ({ params, response }) => {
const id = params.id;
return HttpResponse.json({ id /* ... more response data */ });
return response(200).json({ id /* ... more response data */ });
});

// TS only suggests available POST paths
const postHandler = http.post("/resource", async ({ request, query }) => {
// TS infers available query parameters from the OpenAPI schema
const sortDir = query.get("sort");
const postHandler = http.post(
"/resource",
async ({ request, query, response }) => {
const sortDir = query.get("sort");

const data = await request.json();
return HttpResponse.json({ ...data /* ... more response data */ });
});
const data = await request.json();
return response(201).json({ ...data /* ... more response data */ });
},
);

// TS shows an error when "/unknown" is not defined in the OpenAPI schema paths
const otherHandler = http.get("/unknown", () => {
return new HttpResponse();
return new Response();
});
```

Expand Down Expand Up @@ -110,7 +111,8 @@ one for unknown paths instead.

For an even better type-safe experience, OpenAPI-MSW provides optional helpers
that are attached to MSW's resolver-info argument. Currently, the helper `query`
is provided for type-safe access to query parameters.
is provided for type-safe access to query parameters. Furthermore, the helper
`response` can be used for enhanced type-safety when creating HTTP responses.

#### `query` Helper

Expand Down Expand Up @@ -142,6 +144,98 @@ const handler = http.get("/query-example", ({ query }) => {
});
```

#### `response` Helper

Type-safe response constructor that limits allowed response bodies based on the
chosen status and content type. As usual, all information about available status
codes, content types, and response bodies for the current path is extracted from
the provided OpenAPI spec. For the following example, imagine an OpenAPI
specification that defines various response options for an endpoint:

| Status Code | Content-Type | Content |
| :---------- | :----------------- | :--------------------- |
| `200` | `application/json` | _Some Object Schema_ |
| `200` | `text/plain` | Literal: "Hello World" |
| `204` | | No Content |

```typescript
const http = createOpenApiHttp<paths>();

const handler = http.get("/response-example", ({ response }) => {
// Error: Status Code 204 only allows "no content"
const invalidRes = response(204).text("Hello World");

// Error: Status Code 200 does not allow "no content"
const invalidRes = response(200).empty();

// Error: Status Code 200 only allows "Hello World" as text
const invalidRes = response(200).text("Some other string");

// No Error: This combination is part of the defined OpenAPI spec
const validRes = response(204).empty();

// No Error: This combination is part of the defined OpenAPI spec
const validRes = response(200).text("Hello World");

// No Error: This combination is part of the defined OpenAPI spec
const validRes = response(200).json({
/* ... */
});
});
```

##### Wildcard Status Codes

The OpenAPI specification allows the
[definition of wildcard status codes](https://spec.openapis.org/oas/v3.1.0#patterned-fields-0),
such as `"default"`, `"3XX"`, and `"5XX"`. OpenAPI-MSW's `response` helper
supports using wildcards as status codes. When a wildcard is used, TypeScript
requires you to provide a matching status code that will be used for the
response.

**Note:** The `"default"` wildcard is categorized as "any error status code" in
OpenAPI-TS. To align with its assumption, OpenAPI-MSW only allows matching
`"4XX"` and `"5XX"` status codes for the response when the `"default"` wildcard
is used.

```typescript
const http = createOpenApiHttp<paths>();

const handler = http.get("/untyped-response-example", ({ response }) => {
// Error: A wildcards is used but no status code provided
const invalidRes = response("5XX").text("Fatal Error");

// Error: Provided status code does not match the used wildcard
const invalidRes = response("5XX").text("Fatal Error", { status: "403" });

// No Error: Provided status code matches the used wildcard
const validRes = response("5XX").text("Fatal Error", { status: 503 });

// No Error: "default" wildcard allows 5XX and 4XX status codes
const validRes = response("default").text("Fatal Error", { status: 503 });
});
```

##### Untyped Response Fallback

Sometimes an OpenAPI spec might not define status codes that get returned. This
can be quite common for server error responses (5XX). Nonetheless, being able to
still test those with MSW is useful and important. OpenAPI-MSW supports this
scenario with an `untyped` wrapper on the `response` helper, which type-casts
any response into an allowed response.

```typescript
const http = createOpenApiHttp<paths>();

const handler = http.get("/untyped-response-example", ({ response }) => {
// Any response wrapped with `untyped` can be returned.
// Regardless of the expected response body.
return response.untyped(
HttpResponse.json({ message: "Teapot" }, { status: 418 }),
);
});
```

## License

This package is published under the [MIT license](./LICENSE).

0 comments on commit df138aa

Please sign in to comment.