Problems:
Context: I use tuyau to communicate from a next.js app (server-side) with my Adonis API. I try to return errors in a consistent shape from my API, using custom exceptions with error codes where needed.
I've been consistently running into these problems
- The
TuyauHTTPError and { cause: HTTPError } from ky both contain the full response shape. I have to be careful not to log this as it includes sensitive headers, and also generally leads to noisy logs. This is partially a next problem in that overriding the global exception handler isn't possible without some hacks, but it also means any intermediate logs with the errors need sanitizing
- On the front-end I have to wrap every tuyau call-site with custom error handling code to extract my error codes
Things I've tried
-
ky hooks
Unfortunately the beforeError hook still needs to return a HTTPError otherwise tuyau will fall back to wrapping it in a TuyauNetworkError. Modifying the request shape too much to get the details also caused some errors when tuyau then came to read them
-
A wrapper around tuyau
This is my current approach: a custom request() function which takes the same args as tuyau.request and then returns a domain specific error. Not bad, but I have to remember to use the wrapper in all call-sites, and unfortunately the stack trace shows the throwing of my custom error rather than the API call (see examples below).
This also means I can only use the tuyau.request(path) pattern, without creating some complicated Proxy to enable the fluent interface
Example stack traces
⨯ Error [ApiError]: message from server
at request (src/api.ts:36:22)
at async TopicsPage (src/app/(authenticated)/enrichment/topics/page.tsx:15:20)
34 | } catch (error) {
35 | if (error instanceof TuyauHTTPError) {
> 36 | throw ApiError.fromTuyauError(error);
| ^
37 | }
38 | throw error;
39 | } {
status: 409,
code: 'E_CONCURRENT_SESSION',
method: 'GET',
path: '/api/v1/families/8b7f22a8-6436-4856-8146-29226cd36618/enrichment/overview',
requestId: 'e11fa095-d10c-4de3-b799-244e6c29bbb5',
digest: '710563856'
}
⨯ Error [TuyauHTTPError]: Request failed with status code 409: GET /api/v1/families/8b7f22a8-6436-4856-8146-29226cd36618/enrichment/overview
at async TopicsPage (src/app/(authenticated)/enrichment/topics/page.tsx:15:20)
13 | const [user, resolvedSearch] = await Promise.all([getUser(), searchParams]);
14 |
> 15 | const overview = await tuyau.request('families.enrichment.overview', {
| ^
16 | params: { familyId: user.organization.id },
17 | });
18 | {
- Wrapping each call-site. Too tedious and error-prone
What would help
Some kind of pattern for constructing custom errors would solve the above, giving me a central way to do redaction and mapping to domain specific errors. Something akin to ky's beforeError hook personally feels like a natural fit here.
If you think this is worth working on and have a direction in mind, I'm happy to raise a PR
Related:
I briefly discussed a similar issue on discord with Harminder, and I think a good DX could involve some inferring of error types based on thrown exceptions (a-la request.validateUsing), but that's obviously a lot more complicated and doesn't solve the above
Problems:
Context: I use tuyau to communicate from a next.js app (server-side) with my Adonis API. I try to return errors in a consistent shape from my API, using custom exceptions with error codes where needed.
I've been consistently running into these problems
TuyauHTTPErrorand{ cause: HTTPError }fromkyboth contain the full response shape. I have to be careful not to log this as it includes sensitive headers, and also generally leads to noisy logs. This is partially a next problem in that overriding the global exception handler isn't possible without some hacks, but it also means any intermediate logs with the errors need sanitizingThings I've tried
ky hooks
Unfortunately the
beforeErrorhook still needs to return aHTTPErrorotherwise tuyau will fall back to wrapping it in aTuyauNetworkError. Modifying the request shape too much to get the details also caused some errors when tuyau then came to read themA wrapper around tuyau
This is my current approach: a custom
request()function which takes the same args astuyau.requestand then returns a domain specific error. Not bad, but I have to remember to use the wrapper in all call-sites, and unfortunately the stack trace shows the throwing of my custom error rather than the API call (see examples below).This also means I can only use the
tuyau.request(path)pattern, without creating some complicatedProxyto enable the fluent interfaceExample stack traces
What would help
Some kind of pattern for constructing custom errors would solve the above, giving me a central way to do redaction and mapping to domain specific errors. Something akin to ky's
beforeErrorhook personally feels like a natural fit here.If you think this is worth working on and have a direction in mind, I'm happy to raise a PR
Related:
I briefly discussed a similar issue on discord with Harminder, and I think a good DX could involve some inferring of error types based on thrown exceptions (a-la
request.validateUsing), but that's obviously a lot more complicated and doesn't solve the above