How do you query using GraphQL in a manor similar to SQL's like operator?
Example: What users have a first name starting with jason?
select * from users where first_name like "jason%"
The short answer is: you don't.
The longer answer is that you have to write that code yourself. GraphQL isn't a database query language like SQL, it's an application query language. What that means is that GraphQL won't let you write arbitrary queries out of the box. It will only support the types of queries defined in your GraphQL schema.
If you want to be able to write a query that contains like, you have to
a) declare that in the schema, and
b) write a resolve function that fetches the data
For example, you could have this schema:
type Query {
users(firstName: String!): [User]
}
type User {
firstName: String
lastName: String
}
You would have to define the following resolve function for the users field:
{
Query: {
users(root, args){
return sql.raw('SELECT * FROM `users` WHERE `firstName` LIKE ?', args.firstName);
}
}
}
And finally write this query to get a list of firstName and lastName of all the users that match your search criteria:
{
users(firstName: 'jason%'){
firstName
lastName
}
}
Here's a post I wrote a while ago that clarifies some of the concepts around GraphQL: https://medium.com/apollo-stack/how-do-i-graphql-2fcabfc94a01
And here's a post that explains the interplay between GraphQL schemas and resolve functions: https://medium.com/apollo-stack/graphql-explained-5844742f195e
Not sure if this is relevant to you because you want it to start with "jason" (ie would return "jason bourne" but not "bourne jason") but I recently came across a way to query GraphQL in a "%Like%" manner. For your use case it would look something like this:
export const allUsersQuery = `
query allUsers($UserName: String!){
allUsers(
filter: {first_name_contains: $UserName}
) {
id
first_name
...INSERT_OTHER_FIELDS_HERE...
}
}
`;
FWIW: I did this using a GraphCool BAAS. I don't think you were using GraphCool because GraphCool doesn't allow "_" in variable names.
Hope this helps someone down the line :)
Related
I am trying to query an array of ids with graphQl. The query works with a single id as a variable. However it doesn't work when I enter an array of ids.
Here is my gql query with variables:
query GetAuthorContent($id: [ID]!, $idType: AuthorIdType) {
expert(id: $id, idType: $idType) {
excerpt
featuredImage {
node {
description
author {
node {
description
}
}
}
}
slug
}
}
{"id": ["author-1", "author-2", "author-3"], "idType": "SLUG" }
You can look at the definition of the graphql endpoint using a client and see if the Arrays are supported with query.
If it's supported, check the mutation signature and pass accordingly. In this case I think the services does not support querying using an Array.
Hi everyone and thank you for your help.
You guys were right, my DB doesn't allow an array of authors if it is per author singular. However it works with authors plural. This is the way my db works.
Hope it can help someone in the same situation.
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)
I'm trying to run a GraphQL query in the AWS AppSync console:
query MyQuery {
getUserInfoById(id: "1234566789") {
account {
id // need this value for getAvailableCourses
}
}
getAvailableCourses(accountId: "", pageNumber: 0) {
data {
id
name
type
}
}
}
Basically I need the value account.id in getUserInfoById for getAvailableCourses. I'm obviously new to GraphQL. How would I go about this?
To the best of my knowledge, there can be two ways you can do this.
You can handle this in your frontend by getting user's id
from the session info and pass it to the other query.
You can also merge these two queries and make it one. You will also have to change the respective fields. Then attach a resolver with AvailableCourses and use $ctx.source.id in the resolver to get further details. Schema would look something like this
type Account {
id : ID!
availableCourses: AvailableCourses
..
}
type AvailableCourses {
name: String!
type: String!
..
}
type Query {
getUserInfoById(id: ID!): Account
}
Using the returned fields as inputs for a second query into your datasource is precisely what field resolvers are for. I can't say for sure since I don't know your schema or access patterns but it looks like you need to make available courses a sub field of the user.
How would you use graphQL to query by a "relational" entity value?
For instance, lets say we have a bunch of person-objects. Each "person" then has a relation to an interest/hobby which then has a property called "name".
Now lets say that we want to query for the name of each person with a specific interest, how would such a query be "conducted" using GraphQL?
Using OData it would be something like Persons?$select=name&$expand(Interests($filter=name eq 'Surfing')).. what would be the equivalent for GraphQL?
There is no one equivalent. With the exception of introspection, the GraphQL specification does not dictate what types a schema should have, what fields it should expose or what arguments those fields should take. In other words, there is no one way to query relationships or do things like filtering, sorting or pagination. If you use Relay, it has its own spec with a bit more guidance around things like pagination and connections between different nodes, but even Relay is agnostic to filtering. It's up to the individual service to decide how to implement these features.
As an example, if we set up a Graphcool or Prisma server, our query might look something like this:
query {
persons(where: {
interest: {
name: "Surfing"
}
}) {
name
}
}
A query to a Hasura server might look like this:
query {
persons(where: {
interest: {
name: {
_eq: "Surfing"
}
}
}) {
name
}
}
But there's nothing stopping you from creating a schema that would support a query like:
query {
persons(interest: "Surfing") {
name
}
}
I learn to use GraphQL these days. In my opinion, To build a query, I need to build three parts:
Schema
type User{
id String
name String
cars [Car!]!
}
type Car{
id String
}
type Query{
user(id: String): User
}
DB Query function
{
user: async function ({id}) {
const user = await DB.user.findOne({id});
const userCars = await DB.car.find({userId: id});
user.cars = userCars;
return cars;
}
}
Client query
{
user (id: "1") {
name
cars {
id
}
}
}
That query returns a user's name and his cars. The DB query function always query for cars.
But sometimes I just need user's info:
{
user (id: "1") {
name
}
}
I don't want to query for cars, so I hope to make my DB query function can auto choose to query for cars or not.
How can I do this?
GraphQL.js will support either object properties or methods for resolver functions; this is discussed in its page on Object Types.
One way to deal with this is just to insert an anonymous function directly into the returned object:
{
user: async function ({id}) {
const user = await DB.user.findOne({id});
user.cars = () => DB.car.find({userId: id});
return cars;
}
}
Another is to create a wrapper object with a class that provides the id property and (asynchronous, lazy) cars method; some examples of this are in the GraphQL.js documentation. This approach tends to work in most GraphQL implementations in most languages.
I think you looking into auto-creating/mapping from GraphQL query into db query.
Every queries are db/project specific, so you should create this mapping. You can easily do that with graphql-fields package.
There is copy pasted WHY section from the package:
An underlying REST api may only return fields based on query params.
{
user {
profile {
firstName
},
id
}
}
should request /api/user?fields=profile,id
while
{
user {
email
}
}
should request /api/user?fields=email
Implement your resolve method like so:
resolve(root, args, context, info) {
const topLevelFields = Object.keys(graphqlFields(info));
return fetch(`/api/user?fields=${topLevelFields.join(',')}`);
}
It's best to avoid squeezing it all into one resolver function. Instead, create a separate ObjectType for Cars which has its own fields and its own resolver function. This way, the car query is only called if that field is requested.
In case you are using a RDS, join monster and data louder can help optimize performance of your queries.
Join Monster which relies on generating one big join query and also solve the problem of only requesting exactly the fields you need from the DB
Cached and Batched SQL Data Source which uses facebook's dataloader under the hood - it wont solve the problem of which fields to query (although the example uses knex, which will make that a lot easier), but instead it can cache and batch your queries