discordjs / discord.js

A powerful JavaScript library for interacting with the Discord API
https://discord.js.org
Apache License 2.0
25.04k stars 3.94k forks source link

Discord API REST response body access #10240

Open DEVTomatoCake opened 2 months ago

DEVTomatoCake commented 2 months ago

Which application or package is this feature request for?

rest

Feature

For debugging and logging purposes I want to be able to access the body the Discord REST API responded with after a request.

While I'm able to see the request body, e.g. using undicis diagnostic channel, or the response event of the REST client, I - as far as I know - cannot get the response body using it.

Ideal solution or implementation

Ideally, a REST event (e.g. responseBody) would be emitted once the parseResponse has finished parsing the response. https://github.com/discordjs/discord.js/blob/997887069a00b732e62ba7bdceed714e3ede1079/packages/rest/src/lib/utils/utils.ts#L54-L60 The event should include the body itself and basic information about the response, like the headers, similar to the response event. This is useful to only log the application/json content type, and ignoring binary ones.

Alternative solutions or implementations

A REST client argument which can be used to replace the parseResponse function, allowing users to implement logging directly in the parsing function.

Other context

The response REST event already includes the cloned response as second parameter, however, calling <Response>.json() results in discord.js being unable to process the response:

/node_modules/discord.js/node_modules/@discordjs/rest/node_modules/undici/lib/api/readable.js:199
    throw new TypeError('unusable')
          ^
TypeError: unusable
    at consume (/node_modules/discord.js/node_modules/@discordjs/rest/node_modules/undici/lib/api/readable.js:199:11)
    at BodyReadable.json (/node_modules/discord.js/node_modules/@discordjs/rest/node_modules/undici/lib/api/readable.js:115:12)
    at Object.json (/node_modules/discord.js/node_modules/@discordjs/rest/dist/index.js:78:23)
    at parseResponse (/node_modules/discord.js/node_modules/@discordjs/rest/dist/index.js:269:16)
    at _REST.request (/node_modules/discord.js/node_modules/@discordjs/rest/dist/index.js:1267:12)

Originally asked in Logging API response body on the discord.js Discord.

ckohen commented 2 months ago

The clone of the response object passed to the event is not guaranteed to have a useable body. If you're using the fetch strategy it will because it is calling res.clone() as you observed. However, undici.request does not return a Response object. djs will create a ResponseLike object which is why you can call .json at all. when using the undiciRequest strategy, the response passed is a shallow clone of this object.

Unfortunately, there's not a way to consume the body from the response event (when used with undiciRequest) without making it unuseable for the other consumer. We could pipe the stream to two passthroughs if you're listening to the event, but there's no guarantee that the one passed to the response event will get consumed then.

Passing only json responses seems not great for some use cases (though we'd probably name the event jsonBodyResponse or something), but honestly my main concern would be passing a shallow clone to that event, since that could end up with users mutating data unintentionally before it even gets to the main library parsing code. We could do a deep clone, though that would probably make the event unuseable for production debugging.