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

Feature request: Allow modifying input field values programmatically #3714

Closed
drakhart opened this issue Aug 23, 2022 · 2 comments
Closed

Feature request: Allow modifying input field values programmatically #3714

drakhart opened this issue Aug 23, 2022 · 2 comments

Comments

@drakhart
Copy link

Feature

We’d like to be able to modify input field values programmatically, not just defaulting them to some specific value.

Example use case

A clear use case would be in conjunction with auth: having the server read the user id from a JWT and inject it when the user performs a query (i.e. get the current user details) or a mutation (i.e. creating a new blog post authored by the user), without the user needing to send their id on purpose and even prevent them from forging a request with a different user id (the server itself would take care of this validation).

Take the following example request:

curl --location --request POST 'http://localhost:3000/graphql' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JJZCI6MSwiaWF0IjoxNjYwODM1MTE4fQ.PQNELTXz6bdCIkLoTcEI_YJHEoiCXvfR7D4t12mF6L4' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"mutation {\n    createPost(input: {\n        title: \"foo\"\n    }) {\n        id\n        authorId\n    }\n}","variables":{}}'

Query (for readability):

mutation {
    createPost(input: {
        title: "foo"
    }) {
        id
        authorId
    }
}

JWT payload (for clarity):

{
    "authorId": 1,
    "iat": 1660835118
}

That would yield the following response:

{
    "data": {
        "createPost": {
            "id": 32,
            "authorId": 1
        }
    }
}

Notice how the mutation itself doesn’t contain any user related information even if it’s actually passed on to the resolver, as the server itself would inject it after decoding the JWT.

Suggestions

Add a coerce function into input field definitions

This would be pretty much like the resolve function included in field definitions, which already allows to programmatically modify the resolved value.

This function could also be called via custom directives, as the resolve one.

Example:

fieldConfig.coerce = (source, args, context, info) => {
  const { authorId } = context.reply.request.jwt

  return authorId
}

Extend custom directives and enable them to modify the input value (not just validate it)

Custom directives can already be used to, for example, validate the length of an input value. Nothing prevents them from being used also to modify it.

This is something that could potentially be already done by extending the GraphQLScalarType class and overriding the serialize method, like in this example provided in Apollo documentation, but unfortunately there’s definitely no access to the context at that point (please read “Considerations” below).

Considerations

Please note that these suggested solutions would need that the whole context is passed when calling the value modifier function, otherwise there wouldn’t be access to the request (i.e. we wouldn’t be able to retrieve the JWT). This is an existing limitation when, for example, calling the getArgumentValues function, where it takes just a small part of the context (argument values) instead of the whole context.

Please note also that these would expose the input field in the schema. While this is harmless at first glance, maybe it’s not something to be desired.

Q&A

Why didn’t you request this on the spec repository?

The spec doesn’t forbid custom directives to adopt new functionalities. The Directives section actually specifies that “As future versions of GraphQL adopt new configurable execution capabilities, they may be exposed via directives. GraphQL services and tools may also provide any additional custom directive beyond those described here.”

AFAIK no other spec implementation includes this feature (I’ve checked the codebases for Go, Python and C#), but that doesn’t mean JS couldn’t be the first one to.

Why don’t you just create a custom schema parser that modifies the input object entirely to inject a specific input field?

This would be cumbersome to do and would yield flaky results if not done properly, even risky if the result is an unintended but still valid AST.

Help wanted?

@simoneb and myself can help implementing the final solution, once concerns have been expressed and consensus has been reached.

@Warxcell
Copy link

Another use-case would be on micro-services deployment.
Imagine micro-services deployment where 2 services exists: Questionnaires and Questionnaire Answer storage.

User sees questions from service 1 but their answer is stored in service 2 - so service 2 would need to define some fields which are populated by stitched gateway and gateway itself will get these values from service 1.

@saihaj
Copy link
Member

saihaj commented Apr 10, 2024

This is already possible with almost all the server implementations where a context is always injected to every incoming request. The context is created for each request. Don't think we need to change anything in this library.
Checkout different servers

@saihaj saihaj closed this as completed Apr 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants