Oscar Funes

Published on

Starting with GraphQL Part 2

Last time we talked about the caveats about migrating or creating a new API with GraphQL. This time we’re going more into GraphQL and discuss the Schema.

Schema Definition Language was recently added to the GraphQL Spec.

The schema is how we know what types and what is valid to query (or mutate) in the server. The Schema Definition Language (SDL), as it is known, defines types and relationships between types. The basic ‘unit’ of work is the object type (represents an object) which has fields.

type User {
  id: ID!
  name: String
  posts: [Post]
}

In the previous example, the User is the object type and has three fields which are id , name and posts . As you can see, the id field is of type ID which is one of the default scalar types available in GraphQL, the other being: Int , Float , String and Boolean . The exclamation point ! means that the value is not null, which says that the server promises always to return a value for the given field. Fields are nullable by default in GraphQL. Is worth noting that every field is open to accept arguments.

type Post {
  id: ID!
  user: User
  title: String
  content: String
  date(format: DateFormat = SHORTFORMAT): String
}

Fragments

Fragments are a neat little feature that let you define a reusable set of fields over an object type. For example, let’s say you want to display two queries over the same type.

{
  leftUser: userById(id: 1) {
    ...fieldsToCompare
  }
  rightUser: userById(id: 2) {
    ...fieldsToCompare
  }
}

fragment fieldsToCompare on User {
  name
  posts {
    title
  }
}

That way, you don’t have to rewrite the same fields on both queries; instead, you reuse the fragment.

Query and Mutations

GraphQL defines two special types which are Query and Mutation which should be available as your root types, which hold either all available queries or mutations respectively.

type Query {
  allUsers: [User]
}

The previous example allows us to define a query over an array of users with the fields we specifically need. Let’s assume that the Post type has a field called title .

query {
  allUsers {
    name
    posts {
      title
    }
  }
}

Input Types

When dealing with complex arguments, you can define input types which are like regular object types but only used for arguments. As a caveat, if you want to build an input type that has another nested object type, it has to be defined as another input type, the usual type AnotherType will throw an error.

input Coordinates {
  latitude: String
  longitude: String
}

type Store {
  id: ID!
  name: String
}

type Query {
  storeByCoordinates(coordinates: Coordinates!)
}

Directives

Directives are a featured that are attached to fields, which affects the execution of the query in any way the server desires. There are only two directives defined in the core GraphQL specification, which are @include(if: Boolean) and @skip(if: Boolean) . This directives either include or skip a field if the boolean argument is either true or false. As with scalar values, you can define your directives for your server.

Conclusion

Having the ability to quickly review and understand queries and the types defined by our domain is what allows us to match what front-ends need from the server and have a product first approach to designing APIs. Also, having this is what allows client-server communication to validate queries effectively, letting clients know what field is not valid, or matching expectations of what field is either null or not. A few things are missing from this post like Mutations, interfaces and union types. Let me know what your best practices for defining your Schema are!