aws-appsync-community icon indicating copy to clipboard operation
aws-appsync-community copied to clipboard

Feature Request: Per request resolver/pipline resolver to set the default stash value

Open buggy opened this issue 6 years ago • 8 comments

I'd like to propose adding a resolver/pipeline resolver that is triggered once per request. The return value from this resolver would become the initial value of the stash for each GraphQL resolver instead of it being empty.

Use case: When the user sends a request I want to lookup tenant/permission information once and have it available in all resolvers.

Current options:

  1. Use resolver pipelines/functions and repeat the same lookup in every resolver. This wastes time and dynamodb capacity repeating the same query.

  2. Use the Pre Token Generation trigger on the Cognito User Pool to customize the identity token. This has some negative side effects and isn't support by Amplify which send the access token (see https://github.com/aws-amplify/amplify-js/issues/1772).

By providing a way to set the default value for the stash from a resolver/pipeline resolver that is executed once I can efficiently lookup information required by all (or at least most) resolvers.

buggy avatar Mar 26 '19 13:03 buggy

This sounds very similar to custom authorizers which would allow you to write custom logic to fulfill the identity for your request. You would be able to use the same feature to return information that is relevant to that user for that request. We have this on on the backlog and I will make a note of this request if it also solves your use case.

mikeparisstuff avatar Mar 27 '19 00:03 mikeparisstuff

It's different. While my main use case is fine-grained authorization this is about being able to efficiently load data once that can be used in every resolver so you don't need to keep loading it. It's also independent of the authentication mechanism. It would work with Cognito User Pools, API Key, IAM Authentication, OpenID Connect and a future custom authorization implementation.

buggy avatar Mar 27 '19 03:03 buggy

Would the ability to run a some code after the query AST is built but before the GraphQL execution starts solve your use case?

appwiz avatar Mar 30 '19 17:03 appwiz

@appwiz I think so. Providing we can interact with different data sources and store the result so it's accessible by all resolvers during the request then we can efficiently load user permissions once per request and reuse them for each resolver. For example: We might want to run a query against dynamodb to see which organisations the user is a member of. This would tell us where they admin access and member access. We would then run a second query to determine which projects they have access to as a regular member.

buggy avatar Apr 14 '19 07:04 buggy

Thanks to https://stackoverflow.com/a/58093410/1480391 I found a workaround to pass information through sub-resolvers by manipulating request headers in response mapping template.

$util.qr($context.request.headers.put("x-key", "value"))

This (ugly) workaround works both with or without pipeline.

I'm not sure being able to modify request headers is an intended AWS AppSync feature, but thanks to this "bug" I'm able to pass informations to all my sub-resolvers.

Please AWS don't fix this "bug" before a proper solution to propagate information to sub-resolvers has been released.

Edit: Warning with

  • https://github.com/aws/aws-appsync-community/issues/382

yvele avatar Oct 07 '19 15:10 yvele

If you could use $ctx.stash.put in the response mapping template (currently only supported in the request mapping template) then you could create a function for use in pipeline resolver that goes "first", finds whatever info is needed, then puts those results in stash. Then, in every pipeline resolver, those values could be referenced in the request mapping template of any following functions.

As a work-around, I now use 2 functions to perform this: 1) GetUserInfo function that brings back various data I'm going to need later, then 2) StashUserInfo function that in the request mapping template, accesses $ctx.prev.result and puts it into $ctx.stash. Now, every pipeline resolver I need to create that needs that info simply starts with those 2 functions and guarantees that the data I need is available in stash.

If you DIDN'T do this, then every pipeline resolver's first real function would have to interrogate $ctx.prev.result to validate that results from GetUserInfo were there, and put them in stash to be available for subsequent functions in the pipeline.

So bottom line, adding ability to put results in stash in response mapping template would be a big win for pipeline function development.

lspellman avatar Feb 04 '20 19:02 lspellman

@lspellman thanks Can I see your files and your json cloud formation stack file, please? (The related parts). I would like to do the same.

What about the BEFORE and AFTER mappings? How and when do you do that exactly? Thanks a lot.

Ricardo1980 avatar Feb 04 '20 23:02 Ricardo1980

I am running into the same issue requiring value to be passed between Query resolver and child resolver.

On first resolver have logic to determine a tenant identifier in a multi-tenant SAAS solution. Putting this in stash is perfect to access the identifier again in the response resolver or in pipeline functions. I have tried the suggested workaround to stash being cleared of #set($ctx.request.header.identifier = "xyz") but don't see this value in the logs to verify and in my child request resolver get a null back when #set($identifier = $ctx.request.header.identifier )

Any assistance would be appreciated.

@AWS can you please explain why stash is cleared on child resolvers as this seems like the cleanest place to continue the propagation chain and seems to me to be aligned to the responsibility of the stash, being to communicate values between resolvers

mikel67 avatar Apr 14 '21 05:04 mikel67