Skip to content

Commit 1054d3d

Browse files
authored
feat(nextjs): Add body data to transaction request context (getsentry#3672)
1 parent 482ca02 commit 1054d3d

File tree

2 files changed

+22
-15
lines changed

2 files changed

+22
-15
lines changed

packages/nextjs/src/utils/instrumentServer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface NextRequest extends http.IncomingMessage {
3131
url: string;
3232
query: { [key: string]: string };
3333
headers: { [key: string]: string };
34+
body: string | { [key: string]: unknown };
3435
}
3536
type NextResponse = http.ServerResponse;
3637

@@ -309,9 +310,12 @@ function shouldTraceRequest(url: string, publicDirFiles: Set<string>): boolean {
309310
* @returns The modified event
310311
*/
311312
export function addRequestDataToEvent(event: SentryEvent, req: NextRequest): SentryEvent {
313+
// TODO (breaking change): Replace all calls to this function with `parseRequest(event, req, { transaction: false })`
314+
// (this is breaking because doing so will change `event.request.url` from a path into an absolute URL, which might
315+
// mess up various automations in the Sentry web app - alert rules, auto assignment, saved searches, etc)
312316
event.request = {
313317
...event.request,
314-
// TODO body/data
318+
data: req.body,
315319
url: req.url.split('?')[0],
316320
cookies: req.cookies,
317321
headers: req.headers,

packages/node/src/handlers.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,30 +200,30 @@ export function extractRequestData(
200200
const requestData: { [key: string]: any } = {};
201201

202202
// headers:
203-
// node, express: req.headers
203+
// node, express, nextjs: req.headers
204204
// koa: req.header
205205
const headers = (req.headers || req.header || {}) as {
206206
host?: string;
207207
cookie?: string;
208208
};
209209
// method:
210-
// node, express, koa: req.method
210+
// node, express, koa, nextjs: req.method
211211
const method = req.method;
212212
// host:
213213
// express: req.hostname in > 4 and req.host in < 4
214214
// koa: req.host
215-
// node: req.headers.host
215+
// node, nextjs: req.headers.host
216216
const host = req.hostname || req.host || headers.host || '<no host>';
217217
// protocol:
218-
// node: <n/a>
218+
// node, nextjs: <n/a>
219219
// express, koa: req.protocol
220220
const protocol =
221221
req.protocol === 'https' || req.secure || ((req.socket || {}) as { encrypted?: boolean }).encrypted
222222
? 'https'
223223
: 'http';
224224
// url (including path and query string):
225225
// node, express: req.originalUrl
226-
// koa: req.url
226+
// koa, nextjs: req.url
227227
const originalUrl = (req.originalUrl || req.url || '') as string;
228228
// absolute url
229229
const absoluteUrl = `${protocol}://${host}${originalUrl}`;
@@ -242,26 +242,27 @@ export function extractRequestData(
242242
case 'cookies':
243243
// cookies:
244244
// node, express, koa: req.headers.cookie
245-
// vercel, sails.js, express (w/ cookie middleware): req.cookies
245+
// vercel, sails.js, express (w/ cookie middleware), nextjs: req.cookies
246246
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
247247
requestData.cookies = req.cookies || cookie.parse(headers.cookie || '');
248248
break;
249249
case 'query_string':
250250
// query string:
251251
// node: req.url (raw)
252-
// express, koa: req.query
252+
// express, koa, nextjs: req.query
253253
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
254-
requestData.query_string = url.parse(originalUrl || '', false).query;
254+
requestData.query_string = req.query || url.parse(originalUrl || '', false).query;
255255
break;
256256
case 'data':
257257
if (method === 'GET' || method === 'HEAD') {
258258
break;
259259
}
260-
// body data: express, koa: req.body
261-
262-
// when using node by itself, you have to read the incoming stream(see
263-
// https://nodejs.dev/learn/get-http-request-body-data-using-nodejs); if a user is doing that, we can't know
264-
// where they're going to store the final result, so they'll have to capture this data themselves
260+
// body data:
261+
// express, koa, nextjs: req.body
262+
//
263+
// when using node by itself, you have to read the incoming stream(see
264+
// https://nodejs.dev/learn/get-http-request-body-data-using-nodejs); if a user is doing that, we can't know
265+
// where they're going to store the final result, so they'll have to capture this data themselves
265266
if (req.body !== undefined) {
266267
requestData.data = isString(req.body) ? req.body : JSON.stringify(normalize(req.body));
267268
}
@@ -345,7 +346,7 @@ export function parseRequest(event: Event, req: ExpressRequest, options?: ParseR
345346
}
346347

347348
// client ip:
348-
// node: req.connection.remoteAddress
349+
// node, nextjs: req.connection.remoteAddress
349350
// express, koa: req.ip
350351
if (options.ip) {
351352
const ip = req.ip || (req.connection && req.connection.remoteAddress);
@@ -358,6 +359,8 @@ export function parseRequest(event: Event, req: ExpressRequest, options?: ParseR
358359
}
359360

360361
if (options.transaction && !event.transaction) {
362+
// TODO do we even need this anymore?
363+
// TODO make this work for nextjs
361364
event.transaction = extractTransaction(req, options.transaction);
362365
}
363366

0 commit comments

Comments
 (0)