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

how pass query variables to client in every request ? #6068

Open
namdroid opened this issue Jul 23, 2024 · 9 comments
Open

how pass query variables to client in every request ? #6068

namdroid opened this issue Jul 23, 2024 · 9 comments
Labels
⌛ Waiting for info More information is required ❓ Type: Question

Comments

@namdroid
Copy link

namdroid commented Jul 23, 2024

Question

my request shows like that:

curl --request POST \
 -H 'Content-Type: application/json' \
 --data '{"query":"query GetBestSellers($category: ProductCategory){bestSellers(category: $category){title}}", "operationName":"GetBestSellers", "variables":{"category":"BOOKS"}}' \
 https://rover.apollo.dev/quickstart/products/graphql

How can i pass "variables":{"category":"BOOKS"}} ?

@martinbonnin
Copy link
Contributor

Hi 👋 Apollo Kotlin uses codegen to generate models for your operation. In this example, you should get a GetBestSellers(category: ProductCategory) class where you can pass your variables. You can take a look at https://www.apollographql.com/docs/kotlin/advanced/operation-variables for more details.

@martinbonnin martinbonnin added the ⌛ Waiting for info More information is required label Jul 23, 2024
@namdroid
Copy link
Author

namdroid commented Jul 23, 2024

Hi Martin, of course i can pass parameter to GetBestSellers directly, but waht i need is a addtional
variables

{"query":"query
{
  footer(lang: GERMAN) {
      links {
        text
        url
      }
    }
  }
}", "variables":{"appName":"myapp"}}'

Bildschirmfoto 2024-07-23 um 14 45 54

@namdroid
Copy link
Author

namdroid commented Jul 23, 2024

in javascript should be:

const gqlResp = await client
      .query({
        query,
        variables: {
          lang,
          appName: 'myApp',
          ...moreVariables,
        },
        fetchPolicy: 'no-cache',
      });

@martinbonnin
Copy link
Contributor

martinbonnin commented Jul 23, 2024

Looking at your initial query:

query GetBestSellers($category: ProductCategory){
  bestSellers(category: $category) {
    title
  }
}

This query defines a $category variable, which is sent as JSON and Apollo Kotlin will serialize it automatically when you construct a GetBestSellers operation:

val operation = GetBestSellers(ProductCategory(Books))

Your second query doesn't have an appName variable:

query {
  footer(lang: GERMAN) {
    links {
      text
      url
    }
  }
}

You could patch the HTTP POST body to include additional variables if that's what you're trying to do but I would recommend against that. variables is for GraphQL variables. It's surprising to pass values there that are not in your GraphQL operation.

If you're trying to send additional context to your server, I would recommend using an HTTP header instead.

@namdroid
Copy link
Author

Ho can i patch the HTTP POST body to include additional variables ?

@martinbonnin
Copy link
Contributor

The most efficient way is to implement your own HttpRequestComposer.

Another easier albeit slightly less efficient (because it needs to rewrite the post body), is to use OkHttp interceptors

@namdroid
Copy link
Author

namdroid commented Jul 24, 2024

thanks martin, so here is my HttpRequestComposer class:

    class GraphQLHttpRequestComposer(url: String, gson: Gson) : HttpRequestComposer {
        override fun <D : Operation.Data> compose(apolloRequest: ApolloRequest<D>): HttpRequest {
            val operation = apolloRequest.operation

            // Create a MapJsonWriter to capture variables
            val mapJsonWriter = MapJsonWriter()

            // Convert the captured map to a JsonObject
            val variablesJson = gson.toJsonTree(mapJsonWriter.root()).asJsonObject

            // Add appName to variables
            variablesJson.addProperty("appName", „MyApp“)

            // Create the full request body
            val bodyJson = JsonObject().apply {
                addProperty("query", operation.document())
                addProperty("operationName", operation.name())
                add("variables", variablesJson)
            }

            val bodyString = gson.toJson(bodyJson)

            val httpBody = object : HttpBody {
                override val contentLength: Long = bodyString.toByteArray().size.toLong()
                override val contentType: String = "application/json; charset=utf-8"

                override fun writeTo(bufferedSink: BufferedSink) {
                    bufferedSink.writeUtf8(bodyString)
                }
            }

            return HttpRequest.Builder(HttpMethod.Post, "url")
                .addHeader("Content-Type", "application/json")
                .body(httpBody)
                .build()
        }
    }


    fun provideApolloClient(okHttpClient: OkHttpClient, graphqlUrl: String): ApolloClient {


        val networkTransport = HttpNetworkTransport.Builder()
            .httpRequestComposer(GraphQLHttpRequestComposer(graphqlUrl, gson))
            .okHttpClient(okHttpClient)
            .build()

        return ApolloClient.Builder()
            .networkTransport(networkTransport)
            .addInterceptor(LoggingApolloInterceptor())
            .httpMethod(HttpMethod.Get)
            .autoPersistedQueries()
            .build()
    }

But it don't work !I don't know why

@namdroid
Copy link
Author

may new method added to Apollo client in the future that support variables in request body ?

@martinbonnin
Copy link
Contributor

Try something like below (wildly untested):

class MyHttpRequestComposer(val serverUrl: String): HttpRequestComposer {
  override fun <D : Operation.Data> compose(apolloRequest: ApolloRequest<D>): HttpRequest {
    check(apolloRequest.httpMethod == HttpMethod.Post) {
      "This composer only supports POST"
    }

    val operation = apolloRequest.operation
    val customScalarAdapters = apolloRequest.executionContext[CustomScalarAdapters]!!

    val body = buildJsonByteString {
      writeObject {
        name("query")
        value(operation.document())
        name("operationName")
        value(operation.name())
        name("variables")
        writeObject {
          operation.serializeVariables(this, customScalarAdapters, false)
          // Add your variables here
          name("appName")
          value("MyAppName")
        }
      }
    }
    return HttpRequest.Builder(HttpMethod.Post, serverUrl)
        .body(
            object : HttpBody {
              override val contentType = "application/json"
              override val contentLength = body.size.toLong()

              override fun writeTo(bufferedSink: BufferedSink) {
                bufferedSink.write(body)
              }
            }
        )
        .build()
  }
}

may new method added to Apollo client in the future that support variables in request body ?

That is very unlikely. As said before, sending extra fields in GraphQL variables is not standard. Is there a reason you cannot use a HTTP header or another way for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⌛ Waiting for info More information is required ❓ Type: Question
Projects
None yet
Development

No branches or pull requests

2 participants