Pass input variable to output - graphql

Is it possible to pass an input variable to the output? I tried something like
this:
query GetUrlTitleDetails(
$geo: Country!
$platform: Platform! = WEB
) {
offers(country: $geo, platform: $platform) {
country
standardWebURL
}
}
but I get this result:
{"message": "Cannot query field \"country\" on type \"Offer\"."}

What are you trying to accomplish here? This is not possible (at least just using a GraphQL document).
Your outputs are defined by the GraphQL server. Not the client. Of course the output is derived using the inputs, but you just can't directly set outputs from your request. To do this, you have to change your GraphQL server.
Your code sample just shows your GraphQL document, which is simply the client request. If you want to pass the input variable to the output, you have to write the GraphQL service to pass the input to the output.
If I really want to do this, I'd do something like this (using Ballerina since I am more familiar with it).
import ballerina/graphql;
service on new graphql:Listener(9090) {
resource function get offers(string country, string platform) returns Offer {
return {
country: country,
standardWebURL: "<URL>"
};
}
}
type Offer record {|
string country;
string standardWebURL;
|};
Running this service, you can get the input country as part of the output, from your provided document.

Related

GraphQL Schema Language Handle Map Type from Uncontrolled API [duplicate]

Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm

How are arguments added to GraphQL, do they need to be defined before?

Hi Everyone I am just trying to learn graphql as I am using Gatsby. I want to know does each field in graphql take an argument or does it need to be defined somehow before. So for example if you visit this link graphql search results
https://graphql.org/swapi-graphql?query=%7B%0A%09allPeople%20%7B%0A%09%20%20people%20%7B%0A%09%20%20%20%20id%0A%20%20%20%20%20%20name%0A%20%20%20%20%20%20birthYear%0A%20%20%20%20%20%20eyeColor%0A%09%20%20%7D%0A%09%7D%0A%7D%0A
If i wanted to limit people by eye color how would I do that. In the docs it seems easy as you would just do something like people(eyecolor: 'brown') but that doesn't seem possible. Am I missing something? I basically want to do a SQL style search for all people where eye color is brown.
Thanks.
Arguments need to be defined in the schema and implemented in the resolver. If you're consuming a 3rd party API (like the link you provided), you're limited to their schema. You can tell by looking at their schema (by clicking Docs on the right side of the page) which fields take arguments. For example, person takes id and personID arguments:
people doesn't take any arguments, as seen in the schema:
If you're building your own schema, you can add arguments to any field, and when you implement the resolver for that field you can use the arguments for logic in that resolver.
If you're working with a schema that you don't control, you'll have to add filtering on the frontend:
const {people} = data.allPeople;
const brownEyedPeople = people.filter(({eyeColor}) => eyeColor === 'brown');
When you start developing in Gatsby and actually pull your data into Gatsby, there will be a filter query option that automatically becomes available in the query arguments.
https://www.gatsbyjs.org/docs/graphql-reference/#filter
You can expect to be able to filter your people by eyeColor by using the below query:
{
allPeople(filter: { eyeColor: { eq: "brown" } }) {
edges {
node {
id
name
birthYear
eyeColor
}
}
}
}

Multiple field resolver resolves same rest API with different query parameters

We are planning to use graphql for orchestrations (For e.g. UI client invokes graphql service which goes to multiple rest endpoint and return the result). Problem here is from one rest endpoint we have to pass different types of query parameters based on the field requested by our client.
We use spring-boot-graphql and graphql-java-tools libraries to initialize graphql
type Query{
user(id: ID): User
}
type User{
phone: [Phone]
address: [Address]
}
type Phone{...}
type Address{...}
My code resolves user field and invoke rest endpoint to fetch phone and address information in a single call like
https:restservice.com\v1\user\123?fields=phone,address
How to resolve two fields expecting data from same rest service. I want something like when client request for phone then i needs to send fields in request parameters as phone alone without address. Can we do that? or is there any other way to define schema to solves this problem?
query {
user(userId : "xyz") {
name
age
weight
friends {
name
}
}
}
Knowing the field selection set can help make DataFetchers more efficient. For example in the above query imagine that the user field is backed by an SQL database system. The data fetcher could look ahead into the field selection set and use different queries because it knows the caller wants friend information as well as user information.
DataFetcher smartUserDF = new DataFetcher() {
#Override
public Object get(DataFetchingEnvironment env) {
String userId = env.getArgument("userId");
DataFetchingFieldSelectionSet selectionSet = env.getSelectionSet();
if (selectionSet.contains("user/*")) {
return getUserAndTheirFriends(userId);
} else {
return getUser(userId);
}
}
};
https://www.graphql-java.com/documentation/v12/fieldselection/

Apollo/GraphQL field type for object with dynamic keys

Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm

Is there a way to pass a fragment to graphiql?

It's possible to pass a query, but apparently not a fragment:
server.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
query: `# Welcome to GraphiQL
query PostsForAuthor {
author(id: 1) {
firstName
posts {
title
votes
}
}
}`}));
Update 10/12/2017
It is possible to send fragments along with a query using Apollo's client:
http://dev.apollodata.com/core/fragments.html
This is not a solution to the original question, however; I would like to pass fragments to a graphiql server instance at startup.
by startup do you mean from the server? if so I don't believe that's how fragments are used. my understanding is as follows:
on the server you provide Types (like User)
on the client you query those Types using queries and fragments
for instance, if you provide type User on the server, on the client graphQL you can use fragments to query that type:
graphQL (client)
fragment authorData on AuthorType{
firstName
posts {
title
votes
}
}
query PostsForAuthor {
author(id: 1) {
...authorData
}
}
As you noticed (and as detailed here) GraphiQL takes a query argument:
query: an optional GraphQL string to use as the initial displayed query, if undefined is provided, the stored query or defaultQuery will be used.
If putting a fragment in as the value for that argument doesn't work, then I don't believe there is any way to start with a fragment ... but really why would you even want to? A fragment by itself isn't executable, and the whole idea is to start GraphiQL with a (executable) query.
If all you want is to be able to copy/paste in some text that you use frequently in your queries, a bookmarklet might be a better idea.

Resources