How to create/Fetch data dynamically by filters and order in GraphQL? - graphql

I am creating a query in graphql + apollo client whose filters and orders can change depending on what the customer selects in the frontend. For example:
query (
$orderPart: String!
$wherePart: String!
) {
getProductInfo(
order {$orderPart}
where {$wherePart}
) {
productID
description
size
model
cathegoryNumber
}
Where $orderPart will be equal to "description: DESC" or "productID: ASC" (depending what the customer selected in a momento or another). And $wherePart will be equal to "cathegoryNumber: {eq: 12}" or "productID: {eq: 111111}".
I need to pass the order/filter clause completely as a parameter.
But it doesn't work. Syntax is error "Syntax Error: Expected name, found $".
So my question is...
Is there any way to implement these dynamic filters/orders? How could this functionality be implemented? Is there any other way to implement this dynamic filters and orders?
Thanks.
Note:
In the official documentation, I found that only values can be passed as a parameters:
query (
$orderValue: sortEnumType! = DESC
$whereValue: String! = "description1"
) {
getProductInfo(
order {productID: $orderValue}
where {description: {eq: $whereValue} }
) {
productID
description
size
model
cathegoryNumber
}
But that is not what I need because always filters/orders couldn't be changed. And they could be completely different each time (prodyuct:ASC) first time, (cathegoryNumber:DESC) second time, etc...

Thanks for your help, I found a solution. Using apollo client and variables when the template is created:
let orderValue = 'order {productID: ASC}'; // built dynamically
let whereValue = 'description: {eq: "description1"}'; // built dynamically
document = gql`
query {
getProductInfo(
${orderValue}
${whereValue}
) {
productID
description
size
model
categoryNumber
}
`;

Related

Contentful GraphQL filter with relational queries and reference fields

I'm currently trying to filter entries with a field references, many.
In other words, the field takes multiple locations with each locations consisting of city among other fields.
For my query I want to get every entry that has a location with a specific city name set.
The following query filters an entry that takes only on reference for location, how would it look like when having many?
export const NEWS_ARTICLES = gql`
query GetNewsArticles($location: String) {
entryCollection(
where: {
location: { city: $location }
}
) {
items {
location
}
}
}
`;
Pulling GraphQL's schema all I get is locationCollection_exists - is this even possible?
I found the following article, which seems to do what I desire, but for the Filter API and not GraphQL: https://www.contentful.com/developers/docs/concepts/relational-queries/
Contentful DevRel here. 👋
Filtering by a linked collection entry is not supported right now. What you can do though it flip the query around.
export const NEWS_ARTICLES = gql`
query GetNewsArticles($location: String) {
cityCollection(
# your filter
) {
items {
linkedFrom {
# all the entries linking to this entry
}
}
}
}
`;
You can find more information about this approach in the docs or this blogpost.

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).

Loading __typename fields in seperate queries Apollo-Client not Updating UI

I'm attempting to load information about a type in 2 steps (since the info I'm asking for in secondQuery will take some time to load):
Component:
const MyComponent = ({startDate}) => {
const firstQuery = useQuery(
GET_INFO_PART_ONE,
{
variables: {startDate}
}
);
const secondQuery = useQuery(
GET_INFO_PART_TWO,
{
variables: {startDate}
}
);
}
Queries:
export const GET_INFO_PART_ONE = gql`
query getInfoPartOne(
$startDate: DateTime!
) {
infoPageResults(
startDate: $startDate
) {
edges {
info {
infoID
infoFieldOne
infoFieldTwo
infoFieldThree
}
}
}
}
`;
export const GET_INFO_PART_TWO = gql`
query getInfoPartTwo(
$startDate: DateTime!
) {
infoPageResults(
startDate: $startDate
) {
edges {
info {
infoID
infoFieldFour{
netRate
}
}
}
}
}
`;
When I do this and both queries resolve, the cache's ROOT_QUERY includes the data as I would expect it, with infoPageResults containing an array of edges where each edge's info __typename includes the fields specified in the GET_INFO_PART_ONE and GET_INFO_PART_TWO queries. I would then expect firstQuery.data.infoPageResults.edges in the above component to include the fields loaded from the second query.
The Problem
After both firstQuery and secondQuery are finished loading, firstQuery.data.infoPageResults.edges does not contain the fields loaded by secondQuery, even though the cached values look as I would expect them.
Is there something obvious I'm misunderstanding about how the query hooks work?
Is there a better strategy for loading additional fields onto a _typename in 2 steps?
Just caches what was queried ... no node entries cached (additionally, separately - as cache is normalizing cache), because no id field (required by default as unique entry id) ... it can be customized to use infoID for info type (see docs).
Properly cached node (info) entries can be used to display node details (from 1st query) on 2nd query result rendering list ... using additional 'cache-only' policy queries (info node by id) in subcomponents (react).
Start with more standard example/tutorial to explore possibilities.
update
No, data fetched separately are accessible separately ... it's not cache problem and not querying problem...
1st query won't return fields/properties fetched in 2nd query ... by design;
list rendered using 1st result won't be refreshed when 2nd query result will arrive ... if doesn't contain ref/not consume 2nd data;
You can use/pass both data in parallel or combine them (in immutable way) before passing to some view component ('Presentational and Container Component Pattern') ... list can be rendered using one prop and updated when 2nd prop changes.

How to sort on nested field in graphql ruby?

How do I sort on a nested field (or a virtual attribute) in graphql-ruby?
ExampleType = GraphQL::ObjectType.define do
name 'Example'
description '...'
field :nested_field, NestedType, 'some nested field' do
// some result that is virtually calculated and returns
OpenStruct.new(a: 123//some random number, b: 'some string')
end
end
QueryType = GraphQL::ObjectType.define do
name 'query'
field: example, ExampleType do
resolve -> (_obj, args,_ctx) {
Example.find(args['id']) //Example is an active record
}
end
field: examples, types[ExampleType] do
resolve -> (_obj, args,_ctx) {
// NOTE: How to order by nested field here?
Example.where(args['id'])
}
end
end
And if I am trying to get a list of examples ordered by nested_field.a:
query getExamples {
examples(ids: ["1","2"], order: 'nested_field.a desc') {
nested_field {
a
}
}
}
You can not order Active record by virtual attribute, because Active record can not match this virtual attribute to SQL/NoSQL query. You can avoid limitation, by creating view at DB layer. In GraphQL, sorting/pagination should be implemented at DB layer. Without that sorting/pagination implementation queries all data from DB to application memory.
Also, I want to recommend you switching from order argument with string type to sort argument with [SearchSort!] type based on enums. GraphQL schema will looks like that:
input SearchSort {
sorting: Sorting!
order: Order = DESC
}
enum Sorting {
FieldName1
FieldName2
}
enum Order {
DESC
ASC
}
It helps you implement mapping from GraphQL subquery to DataBase query.

Apollo GraphQL: Can One Query Support Lookup Different Lookup Fields?

I've got a working query that looks like this:
const GETONEASSOCIATE_QUERY = gql`
query getOneAssociate($_id: String!) {
getOneAssociate(_id: $_id) {
_id
first_name
last_name
city
state
userIDinRelatedTable
}
} `;
Now I'd like to be able to look up an associate by userIDinRelatedTable. Do I have to write a whole new graphQL query, or, is there a way to set up a query so that I can specify what fields to be used for the lookup -- something like:
enter code herequery getOneAssociate($_args: args) {
Thanks in advance to all for any thoughts/advice/info!
I'm guessing in your scheme you have a userIDinRelatedTable field on Associate which resolves to RelatedTable.
So to get the fields returned in your query you can
const GETONEASSOCIATE_QUERY = gql`
query getOneAssociate($_id: String!) {
getOneAssociate(_id: $_id) {
_id
first_name
last_name
city
state
userIDinRelatedTable {
id
field1
field2
}
}
} `;

Resources