Skip to content

Differentiate between success and error for empty responses #2291

Open
@cbodin

Description

@cbodin

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingopenapi-fetchRelevant to the openapi-fetch library

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions