Skip to content
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

More precise types #5072

Closed
wants to merge 2 commits into from
Closed

More precise types #5072

wants to merge 2 commits into from

Conversation

jer-sen
Copy link

@jer-sen jer-sen commented Mar 29, 2021

With this PR createHandler will return a function returning precisely void or a promise depending on the type of callback argument (provided or undefined).

@apollo-cla
Copy link

@jer-sen: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Apollo Contributor License Agreement here: https://contribute.apollographql.com/

): (
event: APIGatewayProxyEvent,
context: LambdaContext,
callback: APIGatewayProxyCallback | undefined,
) => void | Promise<APIGatewayProxyResult> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand what your PR accomplishes. Isn't the type of the return value given when we declare the function's own type here?

Can you explain what doesn't work about the current code and how your change improves it? I don't doubt that you've got a good idea here — I just can't figure out what it is.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today, the handler returned by apollo-server-lambda returns void | Promise<APIGatewayProxyResult> but we can do better. We can use multiple signatures to return either void (if a callback is provided) or a promise if no callback is provided. It's in the case the callback is not used directly by AWS but included in some other code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I get that that is a useful goal, but how does your PR achieve that goal given that maybeCallbackify has an explicit return type declared?

Can you share some code that works with this change but not without this change (perhaps even as an addition to the test suite, but as a comment if that's too hard)?

Also CI says this doesn't build. Perhaps this is related to my confusion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. There was an error, I had partially deleted the return type. Is it ok now for you and the build ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that makes more sense. But I worry that later refactorings could break this change since it's dependent on type inference of this function's return value. Can you write some TS code in packages/apollo-server-lambda/src/__tests__/ that depends on this typing improvement? It doesn't really even have to test behavior, just the typing improvements you're going for here. (Though... I seem to recall that our test suites may be in bad shape where they are not fully type-checked, which might make this a bit of a challenge to prove. But maybe you can at least share what the code that only typechecks with this change would look like?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how to implement a test for that but here is some code (which looks like my app) that will raise a Typescript error:

const server = new ApolloServer({
	schema: someSchema,
});

const apolloGraphqlHandler = server.createHandler();


export const handler: Handler<APIGatewayProxyEvent, APIGatewayProxyResult> = async (event, context, callback) => {
	// Do something such as loading config async

	const res = await apolloGraphqlHandler(event, context, undefined); // res can be undefined but will not since callback argument is undefined

	// Do something such as waiting for some promises

	return res; // Can not return undefined => Typescript error
};

@glasser
Copy link
Member

glasser commented Apr 30, 2021

AS3 is imminent and I'm probably going to just solve this in AS3 by dropping the callback-based version (tracking in #5147).

glasser added a commit that referenced this pull request May 7, 2021
Lambda has two ways of defining a handler: as an async Promise-returning
function, and as a callback-invoking function.
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

Until v2.21.2 our handler was only a callback-invoking function.
Starting to that version, it could be called in either way. This meant
that the TypeScript types for our handler were somewhat vague (see
#5072).

All current Node Lambda runtimes support calling async functions; the
only reason that we didn't just change directly from callback-invoking
to async in v2.21.2 was to support the use case of user code that wraps
our handler in their own function. But now we're doing AS3 so we can
make backwards-incompatible changes, so if you're doing that, you should
just make your handler async too and `await` the result of our handler.

Fixes #5018.
@glasser
Copy link
Member

glasser commented May 7, 2021

I appreciate this PR but am solving the problem in the imminent Apollo Server 3 by just dropping the callback style; see #5188.

@glasser glasser closed this May 7, 2021
glasser added a commit that referenced this pull request May 7, 2021
Lambda has two ways of defining a handler: as an async Promise-returning
function, and as a callback-invoking function.
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

Until v2.21.2 our handler was only a callback-invoking function.
Starting to that version, it could be called in either way. This meant
that the TypeScript types for our handler were somewhat vague (see
#5072).

All current Node Lambda runtimes support calling async functions; the
only reason that we didn't just change directly from callback-invoking
to async in v2.21.2 was to support the use case of user code that wraps
our handler in their own function. But now we're doing AS3 so we can
make backwards-incompatible changes, so if you're doing that, you should
just make your handler async too and `await` the result of our handler.

Fixes #5018.
glasser added a commit that referenced this pull request May 7, 2021
…#5188)

Lambda has two ways of defining a handler: as an async Promise-returning
function, and as a callback-invoking function.
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

Until v2.21.2 our handler was only a callback-invoking function.
Starting to that version, it could be called in either way. This meant
that the TypeScript types for our handler were somewhat vague (see
#5072).

All current Node Lambda runtimes support calling async functions; the
only reason that we didn't just change directly from callback-invoking
to async in v2.21.2 was to support the use case of user code that wraps
our handler in their own function. But now we're doing AS3 so we can
make backwards-incompatible changes, so if you're doing that, you should
just make your handler async too and `await` the result of our handler.

Fixes #5018.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants