BlackSigma
BlackSigma

Reputation: 3955

How to query all the GraphQL type fields without writing a long query?

Assume you have a GraphQL type and it includes many fields. How to query all the fields without writing down a long query that includes the names of all the fields?

For example, If I have these fields :

 public function fields()
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user'
            ],
            'username' => [
                'type' => Type::string(),
                'description' => 'The email of user'
            ], 
             'count' => [
                'type' => Type::int(),
                'description' => 'login count for the user'
            ]

        ];
    }

To query all the fields usually the query is something like this:

FetchUsers{users(id:"2"){id,username,count}}

But I want a way to have the same results without writing all the fields, something like this:

FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}

Is there a way to do this in GraphQL ??

I'm using Folkloreatelier/laravel-graphql library.

Upvotes: 385

Views: 378955

Answers (8)

Aleti
Aleti

Reputation: 75

{
  __schema {
    types {
      name
      fields {
        name
        args {
          name
          description
          type {
            name
            kind
            ofType {
              name
              kind
            }
          }
        }
      }
    }
  }
}

Upvotes: 0

Seph Reed
Seph Reed

Reputation: 11028

Less than ideal, by a long shot, but you can make a tool for handling it. Mine looks something like this:

const ExampleUser = {
  id: "u_01",
  firstName: "Seph",
}

const ExamplePermission = {
  id: "p_01",
  userId: "u_01",
  type: "admin"
}

type User = typeof ExampleUser;
type Perm = typeof ExamplePermission;

const UserFields = Array.from(Object.keys(ExampleUser))
const PermFields = Array.from(Object.keys(ExamplePermission))

// an overview of all gql possibilities, could be generated
const gqlMapOverview = {
  Users: {
    fields: UserFields,
    Perms: {
      fields: PermFields,
    }
  },
  Perms: {
    fields: PermFields,
  }
}

function createGqlString(map, queryObj){ /*real work goes here*/ }

const queryString = createGqlString(gqlMapOverview, {
  Users: {
    _filter: { where: { firstName: "Seph" }, limit: 2 },
    Perms: {}
  }
})

queryString == `Users(firstName: "Seph", _limit: 2) {
  id,
  firstname,
  Perms { id, userId, type }
}`

If you get very crafty with your Type Definitions, you can even make it so the return from the gql query is correctly typed.

It's a bummer that GQL doesn't respect the standard use case of querying an item from a server and wanting all fields, but I honestly think abstracting away from it like this sets one up to easier replace it in the future.

Upvotes: 0

Mark Chackerian
Mark Chackerian

Reputation: 23582

Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)

{
   __type(name:"UserType") {
      fields {
         name
         description
      }  
   }
}

and you'll get a response like (actual field names will depend on your actual schema/type definition)

{
  "data": {
    "__type": {
      "fields": [
        {
          "name": "id",
          "description": ""
        },
        {
          "name": "username",
          "description": "Required. 150 characters or fewer. Letters, digits, and @/./+/-/_ only."
        },
        {
          "name": "firstName",
          "description": ""
        },
        {
          "name": "lastName",
          "description": ""
        },
        {
         "name": "email",
          "description": ""
        },
        ( etc. etc. ...)
      ]
    }
  }
}

You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.

This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like

{
  __schema {
    types {
      name
      fields {
        name
        description
      }
    }
  }
}

NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.

2022 Update

Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. References: Why you should disable GraphQL introspection in production and OWASP | Testing GraphQL. I recommend that you consider if these risks pertain to you.

For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.

Upvotes: 250

PokerFace
PokerFace

Reputation: 801

Package graphql-type-json supports custom-scalars type JSON. Use it can show all the field of your json objects. Here is the link of the example in ApolloGraphql Server. https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

Upvotes: 1

JP Ventura
JP Ventura

Reputation: 5742

GraphQL query format was designed in order to allow:

  1. Both query and result shape be exactly the same.
  2. The server knows exactly the requested fields, thus the client downloads only essential data.

However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:

# Only most used selection properties

fragment UserDetails on User {
    id,
    username
} 

Then you could query all user details by:

FetchUsers {
    users() {
        ...UserDetails
    }
}

You can also add additional fields alongside your fragment:

FetchUserById($id: ID!) {
    users(id: $id) {
        ...UserDetails
        count
    }
}

Upvotes: 9

Tyrone Wilson
Tyrone Wilson

Reputation: 4628

I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.

I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.

I defined a custom scalar type called JSON which just returns a literal JSON object.

The ruby implementation was like so (using graphql-ruby)

module Graph
  module Types
    JsonType = GraphQL::ScalarType.define do
      name "JSON"
      coerce_input -> (x) { x }
      coerce_result -> (x) { x }
    end
  end
end

Then I used it for our objects like so

field :location, Types::JsonType

I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.

Upvotes: 21

Peter Horne
Peter Horne

Reputation: 6846

Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.

Upvotes: 367

tommy
tommy

Reputation: 3625

I guess the only way to do this is by utilizing reusable fragments:

fragment UserFragment on Users {
    id
    username
    count
} 

FetchUsers {
    users(id: "2") {
        ...UserFragment
    }
}

Upvotes: 70

Related Questions