kit
kit copied to clipboard
Way to access the response body size in the hook
Describe the problem
I want to set up logging in my sveltekit hook. One of the things I want to log is the number of bytes in the response body. It would be nice if the Response
exposed the body length so that we can access it. Something like this...
export async function handle({ event, resolve }) {
response = await resolve(event)
// these don't actually work, they are undefined. But something LIKE this would be nice
console.log(response.body.length)
console.log(response.headers.get("content-length"))
return response
}
I notice that the length is somewhere on the object if I just console.log(response)
when returning JSON...
Response {
[Symbol(realm)]: { settingsObject: {} },
[Symbol(state)]: {
aborted: false,
rangeRequested: false,
timingAllowPassed: false,
requestIncludesCredentials: false,
type: 'default',
status: 200,
timingInfo: null,
cacheState: '',
statusText: '',
headersList: HeadersList {
[Symbol(headers map)]: [Map],
[Symbol(headers map sorted)]: null
},
urlList: [],
body: {
stream: undefined,
source: '{"result":{"rows":[["to become more autonomous in data analysis"],["to better structure your thoughts"],["to develop a useful professional skill"]],"columns":[{"name":"reason","type":"VARCHAR"}],"moreRows":false}}',
length: 213
}
},
[Symbol(headers)]: HeadersList {
[Symbol(headers map)]: Map(2) {
'content-type' => 'application/json; charset=utf-8',
'etag' => '"1ba7x9x"'
},
[Symbol(headers map sorted)]: null
}
}
...but that piece of data is private (attached to a symbol) and I can't access it.
In my opinion, being able to observe information like this in my hook is important to building debuggable, observable web services. Response payload size is an important metric to monitor. Putting that logging logic in the hook seems to be the only way to make sure it is reliably logged.
Describe the proposed solution
The workaround I have right now is await response.clone().text()
, but that is not ideal because I have to consume the stream. This is wasteful especially if I only want the length.
Alternatives considered
I suppose I could do something with Object.getOwnPropertySymbols()
, but I think that's frowned upon?
Importance
would make my life easier
Additional Information
As I understand it (may be wrong), the content length isn't necessarily always known without reading the stream, but there are many cases where the length is known at the time of the response instantiation, like when JSON or HTML are being returned from a string in memory. Sveltekit should be able to set the length in cases where length is known.
Here is a simple case where content length is known but not exposed: https://stackblitz.com/edit/sveltejs-kit-template-default-9ydsbu?file=src%2Fhooks.js&terminal=dev
Response
is not an API we've defined: https://developer.mozilla.org/en-US/docs/Web/API/Response
Thanks for the response @benmccann (see what I did there 😂). Anyways, what if Sveltekit set the content-length
header? That wouldn't require alteration of the standard Response
API. Is that feasible to do efficiently?
This is terrible and you shouldn't use it, but it can go in the hooks handle (really what you already mentioned, I realize after re-reading):
const bytes = await response.clone().arrayBuffer()
console.log('response size:', bytes.byteLength)
You might be able to avoid the clone impact somewhat by returning your own streaming response that you pipe the current response to, counting the size as you do.
In the case where you're rendering a page, you can do this:
let length = 0;
const response = await resolve(event, {
transformPageChunk({ html }) => {
length += html.length;
return html;
}
});
(In future if we add a stream
option you'd need to wait until the markup was rendered before reading the length, but for now length
will be accurate as soon as you have the response.)
As of https://github.com/sveltejs/kit/discussions/5748, non-page responses must be generated by you, the app author — we don't have a way to inspect the Response
object to determine the length.
@Rich-Harris Thanks for looking into this. I suppose there are 3 cases here:
- pages
- page endpoints
- standalone endpoints
Getting the content size for (1) pages is taken care of by transformPageChunk
as you suggested. As for (3) standalone endpoints, since I am responsible for creating the whole Response
, I can calculate content-length and set it myself inside the endpoint handler. However, I don't see a way to do it in (2) page endpoints, since only a POJO is returned.
As a tangent, I'm grateful for the thoughtful changes in #5748. I like the new distinction between standalone endpoints (returning a Response
) and page endpoints (returning only data). I definitely had been conflating the 2 up to this point. Overall the changes are nice and I'm excited to upgrade.