Skip to content

Commit e5422c1

Browse files
authored
fix(nextjs): Don't wrap res.json and res.send (#6674)
1 parent 41dad55 commit e5422c1

File tree

4 files changed

+30
-24
lines changed

4 files changed

+30
-24
lines changed

packages/nextjs/src/config/wrappers/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ import type { NextApiRequest, NextApiResponse } from 'next';
2525
export type NextApiHandler = {
2626
(req: NextApiRequest, res: NextApiResponse): void | Promise<void> | unknown | Promise<unknown>;
2727
__sentry_route__?: string;
28+
29+
/**
30+
* A property we set in our integration tests to simulate running an API route on platforms that don't support streaming.
31+
*/
32+
__sentry_test_doesnt_support_streaming__?: true;
2833
};
2934

3035
export type WrappedNextApiHandler = {

packages/nextjs/src/config/wrappers/withSentryAPI.ts

+4-24
Original file line numberDiff line numberDiff line change
@@ -130,31 +130,11 @@ export function withSentry(origHandler: NextApiHandler, parameterizedRoute?: str
130130
);
131131
currentScope.setSpan(transaction);
132132

133-
if (platformSupportsStreaming()) {
133+
if (platformSupportsStreaming() && !origHandler.__sentry_test_doesnt_support_streaming__) {
134134
autoEndTransactionOnResponseEnd(transaction, res);
135135
} else {
136-
// If we're not on a platform that supports streaming, we're blocking all response-ending methods until the
137-
// queue is flushed.
138-
139-
const origResSend = res.send;
140-
res.send = async function (this: unknown, ...args: unknown[]) {
141-
if (transaction) {
142-
await finishTransaction(transaction, res);
143-
await flushQueue();
144-
}
145-
146-
origResSend.apply(this, args);
147-
};
148-
149-
const origResJson = res.json;
150-
res.json = async function (this: unknown, ...args: unknown[]) {
151-
if (transaction) {
152-
await finishTransaction(transaction, res);
153-
await flushQueue();
154-
}
155-
156-
origResJson.apply(this, args);
157-
};
136+
// If we're not on a platform that supports streaming, we're blocking res.end() until the queue is flushed.
137+
// res.json() and res.send() will implicitly call res.end(), so it is enough to wrap res.end().
158138

159139
// eslint-disable-next-line @typescript-eslint/unbound-method
160140
const origResEnd = res.end;
@@ -223,7 +203,7 @@ export function withSentry(origHandler: NextApiHandler, parameterizedRoute?: str
223203
// moment they detect an error, so it's important to get this done before rethrowing the error. Apps not
224204
// deployed serverlessly will run into this cleanup code again in `res.end(), but the transaction will already
225205
// be finished and the queue will already be empty, so effectively it'll just no-op.)
226-
if (platformSupportsStreaming()) {
206+
if (platformSupportsStreaming() && !origHandler.__sentry_test_doesnt_support_streaming__) {
227207
void finishTransaction(transaction, res);
228208
} else {
229209
await finishTransaction(transaction, res);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
4+
// This handler calls .end twice. We test this to verify that this still doesn't throw because we're wrapping `.end`.
5+
res.status(200).json({ success: true });
6+
res.end();
7+
};
8+
9+
handler.__sentry_test_doesnt_support_streaming__ = true;
10+
11+
export default handler;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const assert = require('assert');
2+
const { getAsync } = require('../utils/server');
3+
4+
// This test asserts that our wrapping of `res.end` doesn't break API routes on Vercel if people call `res.json` or
5+
// `res.send` multiple times in one request handler.
6+
// https://github.com/getsentry/sentry-javascript/issues/6670
7+
module.exports = async ({ url: urlBase }) => {
8+
const response = await getAsync(`${urlBase}/api/doubleEndMethodOnVercel`);
9+
assert.equal(response, '{"success":true}');
10+
};

0 commit comments

Comments
 (0)