GraphQL; select all records - graphql

The service provider for an ERP software recently implemented GraphQL for those customers who wish to use their data beyond the tools they provide. I was asked to investigate its usefulness in creating digests of this data into more user-friendly data formats (excel, google sheets, ect ... )
While experimenting with the API using Altair I've run across a situation I don't understand, as I'm approaching the challenge as I would a RDMS:
{
__type(name:"Jobs") {
fields {
name
description
}
}
}
The above returns all the fields for the object 'Jobs'. For this examples, it returns a list of fields: JobNo, PartNo, PartRev, CustomerID, DueDate
I wanted to return a list of existing 'JobNo' in the Jobs object, so I tried the query:
{
Jobs {
JobNo DueDate
}
}
Which resulted in:
{
"errors": [
"Expected a non-null value for argument \"JobNo\" on field \"Jobs\"."
]
}
If I use the following query:
{
Jobs(JobNo:"12345")
{
JobNo DueDate
}
}
I get results, but its not very useful as I would have to know something about the contents of the data.
Is there a way of querying for ALL JobNo? Or is the purpose of GraphQL to know at least some specifics of the data, like the JobNo, before constructing your query?
Thank you.

Related

GraphQL Tag dynamic table name in query (apollo)

In my app every customer has a own table for its data. Depending on which customer was selected I want to dynamically build the tablename for the Graphql query.
For example:
// Query for customer 1
gql`
query overviewQuery {
customer_1 {
aggregate {
count
}
}
}
`
// Query for customer 2
gql`
query overviewQuery {
customer_2 {
aggregate {
count
}
}
}
`
I have the customer id in my vuex store and want to insert a variable into the query like in the following pseudocode which is not working.
const tblUserAggregateName = `customer_${this.$store.state.customer.customerId`
gql`
query overviewQuery {
${this.tblUserAggregateName} {
aggregate {
count
}
}
`
Is there an option how to do this or how can this problem be solved?
It is not an option to hardcode all different customer queries and selected them on runtime.
Thanks!
In the answers it was mentioned, that it is against best practice to dynamically change the table name of GraphQL queries.
As changing the complete database structure for all clients (they each have a separate Database with a own PostgreSQL schema name) is not an option, I found a solution the looks a little bit hacky and definitely not best practice but I thought I might be interesting to share for others with the same problem.
The approach is pretty easy: I write the query as a simple string where I could use variables and convert them later to the gql AST Object with graphql-tag
const query = () => {
const queryString = `
{
${customerId}_table_name {
aggregate {
count
}
}
}`
return gql`${queryString}`
}
Works, but I you have a better solution, I am happy to learn!
You can supply variables, but they should not be used to dynamically infer schema object names. This violates core concepts of GraphQL. Try this instead:
gql`
query overviewQuery ($customerId: ID!) {
customerData (id: $customerId) {
tableName
aggregate {
count
}
}
}`
Apollo provides great documentation on how to supply variables into a query here: https://www.apollographql.com/docs/react/data/queries/
The backend can then use the customerId to determine what table to query. The client making requests does not need to know (nor should it) where that data is or how it's stored. If the client must know the table name, you can add a field (as shown in the example)

GraphQL - Select different fields of a returned list per item

Lets say i have the following query:
query getBooksQuery($userId: String) {
getBooks(userId: $userId) {
id
description
image
user {
email
firstName
lastName
image
}
}
}
Imagine i have to show a list with books of a user and display the author details above the list. Since I'm querying with a userId, i know that all books belong to the same user.
So, i would not like to get for each book of the list the same user details, but get them only once in the response.
Is there any way to declare that i want these info only for the first book i.e.?
Or is it the only way to have two objects in the response, like:
query getBooksQuery($userId: String) {
getBooks(userId: $userId) {
books {
id
description
image
}
user {
email
firstName
lastName
image
}
}
}
Unfortunately with the second solution, even if its clearer at this point, will require server-side work to support this custom query. So i was thinking if it is still feasible using the current api.
(Also ofc i can do two requests, one for the user and one for the books, but meh...)
I'm using Apollo without relay, but I'm experimenting at this point, so if not feasible in Apollo but feasible otherwise I'm still interested
You can use the same param for 2 'subqueries' in one request
query getBooksQuery($userId: String) {
getBooks(userId: $userId) {
books {
id
description
image
}
}
getUsers(userId: $userId) {
user {
email
firstName
lastName
image
}
}
}

How to approach a GraphQL query that returns a boolean value?

Need to check whether an email is available or taken during the user sign-up process. The goal is to quickly query, using GraphQL, the API server and have it tell us if the email is available or taken.
What is the general best practice on a simple boolean-ish type of situation using GraphQL?
Below is what I have come up with but I am unsure if this is a good practice or not and want to hear feedback on a better practice on queries like this.
Request:
query {
emailExists(email:"jane#doe.com") {
is
}
}
Response:
{
"data": {
"emailExists": {
"is": true
}
}
}
A "query" is just a field on what happens to be the Query type. A field can return any output type, including scalars -- it doesn't need to return an object. So it's sufficient to have a schema like:
type Query {
emailExists(email: String!): Boolean!
}
The only reason to prefer an object type would be if you anticipated wanting to add additional fields in the future (i.e. something other than your current is field).

GraphQL: Is it possible to search nested field?

I am specifically using the shopify graphql admin api to query orders.
I want to do a search for a nested related field.
Below is my query.
export const orderHistoryQuery = gql`
query Order($productsFirst: Int!, $productsAfter: String, $filterQuery: String) {
orders(first: $productsFirst, after: $productsAfter, reverse: true, query:$filterQuery) {
edges {
cursor
node {
id
name
customer {
id
metafields(first: 10) {
edges {
node {
id
key
value
namespace
}
cursor
}
}
}
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
subtotalPriceSet {
shopMoney {
amount
currencyCode
}
}
totalRefundedSet {
shopMoney {
amount
currencyCode
}
}
currencyCode
email
phone
processedAt
totalShippingPriceSet {
shopMoney {
amount
currencyCode
}
}
totalTaxSet {
shopMoney {
amount
currencyCode
}
}
shippingAddress {
firstName
lastName
address1
address2
city
province
zip
country
}
billingAddress {
firstName
lastName
address1
address2
city
province
zip
country
}
customAttributes {
key
value
}
}
}
}
}
`;
I want to query metafields or ANYTHING really but it doesn't seem like it's supported. I am not sure if I just have the wrong query syntax or if it's not supported. The shopify search syntax documenation doesn't really help and this is where my knowledge of graphql falls apart.
Is it possible to do this in graphql? I also tried adding metafields(id: $whateverID) which is not supported by their setup.
Unfortunately, Shopify doesn't support query filters on metafields. The best way to figure this out is by using a graphql explorer like GraphiQL. Shopify dashboard has this built in if you go to Apps > Shopify GraphiQL App.
Using GraphiQL you can see that:
Customers query doesn't have metafields supported:
Orders query doesn't have customers or metafields supported:
And metafields on customers doesn't have a query param:
I think your options are to either query by what you can and filter after you get the results or use a customer tag and query by tag.
You would really help your cause out by simplifying things. My advice to you is to try a simple query. Can you get an order? Since an order has a customer (usually but not always), can you get a metafield associated with that customer?
You have so many obstacles in your attempt to show what you are trying to do, it is almost as if you want a migraine headache in trying to debug anything. GraphQL calls to endpoints are documented fairly well from the GraphQL website perspective, and Shopify is nothing but a vanilla implementation of that, with the caveat that they charge you for calls based on complexity, so you had best be monitoring your credits.
So ya, try simple calls. Get a product and it's Metafields. Get a customer record and it's Metafields. If you can do that, you are not challenging the documentation much, nor the concept of GraphQL queries. Once a basic all works, you can work in variables, cursors, paging, etc... but until a one-off call gives you what you want, debugging should be concentrated on the simplest of calls, not everything and the kitchen sink.
Also, when you screw up a call to the endpoint, Shopify usually returns a response with details about where you screwed up, providing you with a first place to look. We see nothing of your response, so there is little to go on to help you.

Can graphql return aggregate counts?

Graphql is great and I've started using it in my app. I have a page that displays summary information and I need graphql to return aggregate counts? Can this be done?
You would define a new GraphQL type that is an object that contains a list and a number. The number would be defined by a resolver function.
On your GraphQL server you can define the resolver function and as part of that, you would have to write the code that performs whatever calculations and queries are necessary to get the aggregate counts.
This is similar to how you would write an object serializer for a REST API or a custom REST API endpoint that runs whatever database queries are needed to calculate the aggregate counts.
GraphQL's strength is that it gives the frontend more power in determining what data specifically is returned. Some of what you write in GraphQL will be the same as what you would write for a REST API.
There's no automatic aggregate function in GraphQL itself.
You can add a field called summary, and in the resolve function calculate the totals.
You should define a Type of aggregated data in Graphql and a function you want to implement it. For example, if you want to write the following query:
SELECT age, sum(score) from student group by age;
You should define the data type that you want to return:
type StudentScoreByAge{
age: Int
sumOfScore: Float
}
and a Graphql function:
getStudentScoreByAge : [StudentScoreByAge]
async function(){
const res = await client.query("SELECT age, sum(score) as sumOfScore
from Student group by age");
return res.rows;
}
... need graphql to return aggregate counts? Can this be done?
Yes, it can be done.
Does GraphQL does it automatically for you? No, because it does not know / care about where you get your data source.
How? GraphQL does not dictate how you get / mutate the data that the user has queried. It's up to your implementation to get the requested aggregated data. You could get aggregated data directly from your MongoDB and serve it back, or you get all the data you need from your data source and do the aggregation yourself.
If you are using Hasura, in the explorer, you can definitely see an "agregate" table name, thus, your query would look something similar to the following:
query queryTable {
table_name {
field1
field2
}
table_name_aggregate {
aggregate { count }
}
}
In your results, you will see the total row count for the query
"table_name_aggregate": {
"aggregate": {
"count": 9973
}
This depends on whether you build the aggregator into your schema and are able to resolve the field.
Can you share what kind of GraphQL Server you're running? As different languages have different implementations, as well as different services (like Hasura, 8base, and Prisma).
Also, when you say "counts", I'm imagining a count of objects in a relation. Such as:
query {
user(id: "1") {
name
summaries {
count
}
}
}
// returns
{
"data": {
"user": {
"name": "Steve",
"summaries": {
"count": 10
}
}
}
}
8base provides the count aggregate by default on relational queries.

Resources