Description
openapi-fetch version
0.13.7
Description
It's currently impossible to differentiate between success and error if both responses contain no body.
Consider the following OpenAPI document:
{
"openapi": "3.0.1",
"info": {
"title": "API",
"version": "1.0.0"
},
"paths": {
"/resource/{id}": {
"post": {
"tags": [
"API"
],
"operationId": "ResourceById:intPost",
"responses": {
"404": {
"description": "Not Found"
},
"204": {
"description": "No Content"
}
}
}
}
},
"components": { }
}
If the POST succeeds, a 204 with no body is returned. If the resource with the specified ID is not found, a 404 with no body is returned.
The data
and error
in the response from this endpoint are always undefined so the response status would need to be checked.
This makes checking for success/error inconsistent between different endpoints.
A more complete example with a few different endpoints where a 400 Bad Request contains a error response:
const { POST, GET, DELETE } = createClient<paths>();
async function getResource(id: number) {
const { data, response } = await GET("/resource/{id}", {
params: { path: { id } },
});
if (data) {
return data;
}
if (response.status === 404) {
throw new NotFoundError();
}
throw new InternalServerError(response.headers.get("request-id"));
}
async function updateResource(id: number) {
const { error, response } = await POST("/resource/{id}", {
params: { path: { id } },
});
if (response.status === 204) {
return;
}
if (response.status === 404) {
throw new NotFoundError();
}
if (error && response.status === 400) {
throw new BadRequestError(error);
}
throw new InternalServerError(response.headers.get("request-id"));
}
async function deleteResource(id: number) {
const { response } = await DELETE("/resource/{id}", {
params: { path: { id } },
});
if (response.status === 204) {
return;
}
if (response.status === 404) {
throw new NotFoundError();
}
throw new InternalServerError(response.headers.get("request-id"));
}
Reproduction
Configure the client and check the types:
export interface paths {
"/resource/{id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post: operations["ResourceById:intPost"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
}
export type webhooks = Record<string, never>;
export interface components {
schemas: never;
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
export interface operations {
"ResourceById:intPost": {
parameters: {
query?: never;
header?: never;
path: {
id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description No Content */
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @description Not Found */
404: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
}
const { POST } = createClient<paths>();
const { data, error } = await POST("/resource/{id}", {
params: { path: { id: 123 } },
});
Expected result
Responses with no content should probably be returned as null
instead of undefined, as they are indeed defined but empty.
Checking for successful responses will be as easy as comparing with undefined:
const isSuccess = data !== undefined;
Extra
- I’m willing to open a PR (see CONTRIBUTING.md)