Skip to content

KY is forcing empty string on 204 #732

@KutnerUri

Description

@KutnerUri

Currently, when an API responds with a 204 No Content status, ky's .json() method returns an empty string (""). This is a helpful convenience that prevents an error from being thrown when trying to parse an empty response body.

However, the choice of an empty string as the default value can lead to unexpected behavior. For example, it prevents the use of standard optional chaining (?.) to safely access properties on a potentially non-existent object.

Consider the following common pattern:

const data = await ky.get('...').json();

// EDIT: the error happened when trying to access slightly nested data:
const numberOfPosts = data?.content.posts.length;
// This throws a TypeError: Cannot read properties of undefined (reading 'posts')

Because "" is not null or undefined, the optional chaining operator does not short-circuit, and the code attempts to access a property on a string, causing a runtime error. The ideal value in this scenario would be null, which works naturally with this syntax.

I attempted to use an afterResponse hook to modify this, but it appears this behavior is handled internally by ky after the hooks have run.

Proposed Solution

I propose adding a new option to ky that allows developers to specify the value that should be returned for a 204 No Content response. This would provide flexibility while maintaining the helpful default for backward compatibility.

Here are a couple of ideas for the API design:

const client = ky.create({
  // Defaults to "" for backward compatibility
  noContentValue: null
});

const data = await client.get(...).json(); // `data` will be `null` on a 204 response
const userContent = data?.content; // Works as expected, `userContent` is `undefined`

or - an extendible object

const client = ky.create({
  defaultResponseValues: {
    // Defaults to { 204: "" }
    204: null
  }
});

This would solve the issue by allowing developers to choose a value (like null) that aligns better with modern JavaScript patterns like optional chaining and nullish coalescing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions