-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enables context creation to be async and capture errors with opt-in logging #1024
Conversation
…e found in originalError
…lo error to add code
throw new HttpQueryError( | ||
500, | ||
JSON.stringify({ | ||
errors: formatApolloErrors([e], { debug: debugDefault }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error will return when the options to runHttpQuery are wrapped in a function, which throws an error. Currently this will only get called when the old middleware creation(expressGraphQL
) is used, since the new server construction expects an object.
This line makes me nervous, since a production server that does not use a NODE_ENV of 'production' or 'test' will return a stacktrace to the client. A stacktrace is extremely informative for server creators to debug their systems. The other option would be to log the error in the console, which is non-ideal, since logging should be opt-in. The unfortunate part is that the user provided logging function is not resolved yet when the error occurs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate you raising this concern here explicitly. Is there any way we can await the resolution of the logging function prior to throwing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking pretty good! Some comments within...
@@ -270,17 +278,16 @@ export class ApolloServerBase<Request = RequestInit> { | |||
async request(request: Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The await
itself has been removed, does this need to be an async
function still?
error: GraphQLError, | ||
debug: boolean = false, | ||
) { | ||
export function enrichError(error: GraphQLError, debug: boolean = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be export
ed? Preference to not expose it if we don't need to!
@@ -270,17 +278,16 @@ export class ApolloServerBase<Request = RequestInit> { | |||
async request(request: Request) { | |||
let context: Context = this.context ? this.context : { request }; | |||
|
|||
//Differ context resolution to inside of runQuery |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: Defer
?
): Array<Error> { | ||
const { formatter, debug, logFunction } = options; | ||
return errors.map(error => enrichError(error, debug)).map(error => { | ||
if (formatter !== undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than having this larger if
block surrounding the totality of this map
, could we do something like:
if (!formatter) {
return error;
}
// Remainder of existing logic, indented by two less spaces.
try {
// ...
?
By hoisting that logic, it allows the reader of this code to focus on the remaining functionality, rather than needing to keep in mind that we're still inside a conditional block (the if
).
@@ -218,9 +250,16 @@ export async function runHttpQuery( | |||
return Promise.reject(e); | |||
} | |||
|
|||
return Promise.resolve({ errors: [formatErrorFn(e)] }); | |||
return Promise.resolve({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Promise.resolve
here can be removed since an async
function implicitly wraps return values in Promise.resolve
.
throw new HttpQueryError( | ||
500, | ||
JSON.stringify({ | ||
errors: formatApolloErrors([e], { debug: debugDefault }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate you raising this concern here explicitly. Is there any way we can await the resolution of the logging function prior to throwing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems basically fine although I don't actually know the motivation behind it. A few questions below.
@@ -4,6 +4,8 @@ All of the packages in the `apollo-server` repo are released with the same versi | |||
|
|||
### vNEXT | |||
|
|||
* Remove tests and guaranteed support for Node 4 [PR #1024](https://github.com/apollographql/apollo-server/pull/1024) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation here? (I can certainly believe it's a pain to maintain Node 4 compat but I'm curious what the straw that broke the camel's back was.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same code and setup lead to a different error code. Possibly worth investigating, though I think it's more distracting than valuable
@@ -2,5 +2,7 @@ | |||
|
|||
### vNEXT | |||
|
|||
* `apollo-server-core`: Replace console.error with logFunction for opt-in logging [PR #1024](https://github.com/apollographql/apollo-server/pull/1024) | |||
* `apollo-server-core`: errors in context creation can be async and is formatted to include error code [PR #1024](https://github.com/apollographql/apollo-server/pull/1024) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not just error can be async — context creation in general can be async?
@@ -267,20 +275,19 @@ export class ApolloServerBase<Request = RequestInit> { | |||
if (this.engine || engineInRequestPath) this.engineEnabled = true; | |||
} | |||
|
|||
async request(request: Request) { | |||
request(request: Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just want to reiterate now that 2.0 is me and you that I find the name of this method to be incomprehensible and I hope we change it before release. Doesn't have to be in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely agree! Will be another PR most likely
context = {}; | ||
} else if (typeof context === 'function') { | ||
try { | ||
context = await context(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So isn't this a backwards-incompatible change? Doesn't this break non-async context functions?
I mean that's OK because this is 2.0 I guess, but let's not bury the lede here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await unwraps a Promise if it's there. If the value on the rhs of await
is an object or value, then it is returned immediately. I think this is bug fix or at minimum a strict addition to api to allow the context to return a Promise in addition to a value. What this change does break is context functions that returns a promise and the resolvers expect the context argument to be a Promise. I think this case is pretty unlikely and not a good best practice.
https://github.com/apollographql/apollo-server/pull/1024/files/4225c08b10c90dad5c0acc0b27fc72f10482ad12#diff-5102db46bcc7f6cc0c59149e9cc375d3R26 is the type addition to the context
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
shows an example that has await 20
.
This PR allows the context option in apollo-server-core's runQuery/runHttpQuery to be an async function, which would be common in authentication fetching scenarios.
Additionally, this PR increases the capture and formatting of errors during the creation of the options and context.