How to find path to a field in a graphql query - graphql

I am very new to graphql. I have a following graphql query for an example:
query pets {
breed(some arguments)
{
name
items
{
owner(some arguments)
{
items
{
ID
ownerSnumber
country
address
school
nationality
gender
activity
}
}
name
phoneNumber
sin
}
}
}
Is it possible to parse a gql query and get the path of a field in the query?
For example I would like to get the path of 'ID'. For example from the above query, is it possible to get the path where the ID is: owner.items.ID

With https://graphql.org/graphql-js/ it exposes a fourth argument called resolve info. This field contains more information about the field.
Have a look at GraphQLObjectType config parameter type definition:

With a good start from the earlier answer, relying on the ResolveInfo you could do something like a recursive check going from child to parent:
export const getFieldPath = (path: Path): string => {
if (!path.prev) return `${path.key}`
return `${getFieldPath(path.prev)}.${path.key}`
}
And later in your resolver you could use it like:
const myFieldResolver = (parent, args, ctx, info) => {
const pathOfThisResolversField = getFieldPath(info.path)
// use your pathOfThisResolversField
return yourFieldResolvedData
};
Worth noting though, the solution above will include every node all the way to the query root, rather than just the ones you mentioned owner.items.ID

Related

Return custom field based on other not requested field?

Let's say that I want to get a person's age using this query:
{
getUser(id: "09d14db4-be1a-49d4-a0bd-6b46cc1ceabb") {
full_name
age
}
}
I resolve my getUser query like this (I use node.js, type-graphql and knex):
async getUser(getUserArgs: GetUserArgs, fields: UserFields[]): Promise<User> {
// Return ONLY ASKED FIELDS
const response = await knex.select(this.getKnexFields(fields)).from(USER).whereRaw('id = ?', [getUserArgs.id]);
// returns { full_name: 'John Smith' }
return response[0];
}
The problem is that then I can't calculate age field, because I did not get born_at (datetime field stored in a db) in the first place:
#FieldResolver()
age(#Root() user: User, #Info() info: GraphQLResolveInfo): number {
console.log(user); // { full_name: 'John Smith' } no born_at field - no age, so error
// calculate age from born_at
return DateTime.fromJSDate(user.born_at).diff(DateTime.fromJSDate(new Date()), ['years']).years;
}
Is there some fancy graphql-build-in way / convention to predict that born_at will be needed instead of doing it manually through info / context?
You should always return full entity data from the query-level resolvers, so they are available for field resolvers.
The other solution is to manually maintain a list of required fields for field resolvers, so your "fields to knex" layer can always include them additionally".
Further improvements might be to can a list of additional columns based on the requested fields (thus the field resolvers that will be triggered).

How to access query path properties in a resolver? GraphQL

I have a database with the following structure.
I'm writing a GraphQL resolver for the bottom-most node (the "rows" node).
As the image shows, each "rows" node corresponds to a specific path. (Company)->(DB)->(Table)->(rows)
A Query would be of the form:
{
Company(name: "Google") {
Database(name: "accounts") {
Table(name: "users") {
rows
}
}
}
}
Question: How can I include/access Company.name, Database.name, Table.name information in the rows resolver so that I can determine which rows node to return?
In other words: I know I can access Table.name using parent.name, but is there a way to get parent.parent.name or parent.parent.parent.name?
If there isn't a way to access ancestor properties, should I use arguments or context to pass these properties manually into the rows resolver?
Note: I can't use the neo4j-graphql-js package.
Note: This is the first simple example I thought of and I understand there are structural problems with organizing data this way, but the question still stands.
You can extract the path from the GraphQLResolveInfo object passed to the resolver:
const { responsePathAsArray } = require('graphql')
function resolver (parent, args, context, info) {
responsePathAsArray(info.path)
}
This returns an array like ['google', 'accounts', 0, 'user']. However, you can also pass arbitrary data from parent resolver to child resolver.
function accountResolver (parent, args, context, info) {
// Assuming we already have some value at parent.account and want to return that
return {
...parent.account,
message: 'It\'s a secret!',
}
}
function userResolver (parent, args, context, info) {
console.log(parent.message) // prints "It's a secret!"
}
Unless message matches some field name, it won't ever actually appear in your response.

GraphQL-java add variables to query

I am new to GraphQL. I know this is a basic question but hope someone could help me to add variables to my query as I tried many times and failed :(
In my query, below schema is used:
type Query {
ContinentInfo(id: ID): Continent
}
type Continent {
id : ID
name: String
countries: [Country]
}
type Country {
id : ID
name : String
population: Float
}
Below query is executed successfully:
{
ContinentInfo(id: "continent01") {
name
countries {
name
population
}
}
}
Then I want to add more conditions in the query, for example add a variable "populationMoreThan" to filter the result. so the query may look like:
{
ContinentInfo(id: "continent01") {
name
countries(populationMoreThan: $populationMoreThan) {
name
population
}
}
}
but it always failed when I tried to add this variable in the schema and in the query.
Could anyone provide me an example of adding variable in my case?
Also, it looks I need to pass the parameter value into the query? Now I'm using graphql.GraphQL.execute(queryString) to pass the query string. How to pass the variable value here?
Finally found a way to filter the result.
Update the schema with:
type Continent {
id : ID
name: String
countries(populationMoreThan: Float = 0): [Country]
}
And query with:
{
ContinentInfo(id: "continent01") {
name
countries(populationMoreThan: 1.0) {
name
population
}
}
}

clean way to get same field by different key

Here is the problem. I can get member by ID and my query looks like below:
{
member(memberId:[1,2]) {
firstName
lastName
contacts {
}
}
}
Now I need to add few more query to get member by name and email like below
{
member(email:["abc#xy.com","adc#xy.com"]) {
firstName
lastName
contacts {
}
}
}
{
member(name:["abc","adc"]) {
firstName
lastName
contacts {
}
}
}
How do I design my graphQL query and schema? Should my query have just 1 field with multiple optional arguments? like below
Field("member", ListType(Member),
arguments = ids :: email :: name,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Or should I have multiple query with different field under a schema. like below
Field("memberById", ListType(Member),
arguments = Id :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Field("memberByEmail", ListType(Member),
arguments = email :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Field("memberByName", ListType(Member),
arguments = name :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Thank you in advance. let me know in case you need more details.
You should think about advantanges and disadvantages of both solutions.
If you will prepare separate fields, you will get a lot of boilerplate.
On the other hand you can set all possible inputs as OptionalInputType, it makes schema field only. Disadvantage of this solutions is that Sangria cannot validate a field that at least one argument should be required, so you have to cover this case with proper response or whatever.
The third option is to make generic solution at Schema level. You can create a query with two arguments filterName and filterValues, first would be EnumType for Id, Email, Name, the second would be a list of strings.
Such solution avoid disadvantages of both previous solutions, it has required fields and it doesn't need spreading fields in schema for every filter. Additionally if you want to add any additional function you have only edit FilterName enum and a resolver function to cover this.
Finally you schema will looks like this:
enum FilterName {
ID
EMAIL
NAME
}
type Query {
member(filterName: FilterName!, filterValues: [String]!): Member!
}

How can I do a WpGraphQL query with a where clause?

This works fine
query QryTopics {
topics {
nodes {
name
topicId
count
}
}
}
But I want a filtered result. I'm new to graphql but I see a param on this collection called 'where', after 'first', 'last', 'after' etc... How can I use that? Its type is 'RootTopicsTermArgs' which is likely something autogenerated from my schema. It has fields, one of which is 'childless' of Boolean. What I'm trying to do, is return only topics (a custom taxonomy in Wordpress) which have posts tagged with them. Basically it prevents me from doing this on the client.
data.data.topics.nodes.filter(n => n.count !== null)
Can anyone direct me to a good example of using where args with a collection? I have tried every permutation of syntax I could think of. Inlcuding
topics(where:childless:true)
topics(where: childless: 'true')
topics(where: new RootTopicsTermArgs())
etc...
Obviously those are all wrong.
If a custom taxonomy, such as Topics, is registered to "show_in_graphql" and is part of your Schema you can query using arguments like so:
query Topics {
topics(where: {childless: true}) {
edges {
node {
id
name
}
}
}
}
Additionally, you could use a static query combined with variables, like so:
query Topics($where:RootTopicsTermArgs!) {
topics(where:$where) {
edges {
node {
id
name
}
}
}
}
$variables = {
"where": {
"childless": true
}
};
One thing I would recommend is using a GraphiQL IDE, such as https://github.com/skevy/graphiql-app, which will help with validating your queries by providing hints as you type, and visual indicators of invalid queries.
You can see an example of using arguments to query terms here: https://playground.wpgraphql.com/#/connections-and-arguments

Resources