T
TanStack•2mo ago
conscious-sapphire

Catch error in a global middleware

hii i would like to send any unhandled error to sentry. to do that, i wrote the following code
const sentryMiddleware = createMiddleware().server(async ({ request, next }) => {
try {
return await next();
}
catch (error) {
Sentry.captureException(error, {
user: getUser(request)
});
throw error;
}
});

export const startInstance = createStart(() => {
return {
requestMiddleware: [sentryMiddleware]
};
});
const sentryMiddleware = createMiddleware().server(async ({ request, next }) => {
try {
return await next();
}
catch (error) {
Sentry.captureException(error, {
user: getUser(request)
});
throw error;
}
});

export const startInstance = createStart(() => {
return {
requestMiddleware: [sentryMiddleware]
};
});
unfortunately, in the case of server functions, the error seems to be handled by this code before my middleware https://github.com/TanStack/router/blob/80a9f3dcdf70bb055821c7ab14117b8d77027e3f/packages/start-server-core/src/server-functions-handler.ts#L284 also, for some reason, the error message is sent to the frontend even when running in prod can i somehow setup my error handler to work before/instead of server-functions-handler? 👀
5 Replies
like-gold
like-gold•2mo ago
also, for some reason, the error message is sent to the frontend even when running in prod
throwing an error in a server function is valid and will be forwarded to the client. the client needs to do a try...catch() but i get the need to catch it in a middleware we'll look into it
fair-rose
fair-rose•2mo ago
i'm pretty sure i have a similar 'global' function or logic to log unhandled or error thrown by app and it works fine.. hmm.. maybe because you are using a request middleware? rather than { type: function}
export const errorHandlerMWFn = createMiddleware({ type: "function" })
.server(
async ({ next, functionId }) => {
try {
const result = await next()
return result
} catch (error) {
//console.log("err", error)
//console.log("is zod", error instanceof ZodError);
logger('error', `Error occurred processing RPC call ${functionId}: ${(error as Error)?.message || ""}`, error);
throw error;
/* let message = 'Internal server error';
let logMessage = "";

if (error instanceof Error || error instanceof AppError) {
console.log("aaaa", message)
message = error.message;
logMessage = error.message;
} else if (error instanceof ZodError) {
const msg = error.issues.reduce((acc, curr) => acc + curr.message || "" + "\n", "")
message = `Validation error: \n ${msg}`;
logMessage = error.message;
console.log("seree", message)
} else if (error instanceof DrizzleQueryError) {
message = 'We encountered an issue with that request';
logMessage = `Drizzle Error: ${error.message}, Query: ${error.query}`;
}

console.log("fff", message)

logger('error', `Error occurred processing RPC call ${functionId}: ${logMessage}`, error);
throw new AppError(message) */
}
}
)
export const errorHandlerMWFn = createMiddleware({ type: "function" })
.server(
async ({ next, functionId }) => {
try {
const result = await next()
return result
} catch (error) {
//console.log("err", error)
//console.log("is zod", error instanceof ZodError);
logger('error', `Error occurred processing RPC call ${functionId}: ${(error as Error)?.message || ""}`, error);
throw error;
/* let message = 'Internal server error';
let logMessage = "";

if (error instanceof Error || error instanceof AppError) {
console.log("aaaa", message)
message = error.message;
logMessage = error.message;
} else if (error instanceof ZodError) {
const msg = error.issues.reduce((acc, curr) => acc + curr.message || "" + "\n", "")
message = `Validation error: \n ${msg}`;
logMessage = error.message;
console.log("seree", message)
} else if (error instanceof DrizzleQueryError) {
message = 'We encountered an issue with that request';
logMessage = `Drizzle Error: ${error.message}, Query: ${error.query}`;
}

console.log("fff", message)

logger('error', `Error occurred processing RPC call ${functionId}: ${logMessage}`, error);
throw new AppError(message) */
}
}
)
usage:
const convertRedirectErrorToExceptionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next }) => {
const result = await next()
if ('error' in result && isRedirect(result.error)) {
throw result.error
}
return result
})

export const startInstance = createStart(() => {
return {
defaultSsr: true,
serializationAdapters: [customZodErrorAdapter],
requestMiddleware: [requestLogger],
functionMiddleware: [convertRedirectErrorToExceptionMiddleware, requestLoggerMWFn, errorHandlerMWFn],
}
});
const convertRedirectErrorToExceptionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next }) => {
const result = await next()
if ('error' in result && isRedirect(result.error)) {
throw result.error
}
return result
})

export const startInstance = createStart(() => {
return {
defaultSsr: true,
serializationAdapters: [customZodErrorAdapter],
requestMiddleware: [requestLogger],
functionMiddleware: [convertRedirectErrorToExceptionMiddleware, requestLoggerMWFn, errorHandlerMWFn],
}
});
conscious-sapphire
conscious-sapphireOP•2mo ago
yeah, i may have got mixed up these concepts... but i'm not sure how to add request info with the function middleware. i guess the idea is that it's abstracted, so it doesn't allow me to populate sentry with client IP when it's called from the frontend
like-gold
like-gold•2mo ago
you can just access the request using getRequest
conscious-sapphire
conscious-sapphireOP•2mo ago
oh, i see! a bit unexpected, but it works wonderfully, both when running SSR and on an endpoint call thanks everyone! i guess now i can do proper sentry error logging

Did you find this page helpful?