Everything you need to know about GraphQL before an Interview
A quick reference sheet for everything GraphQL.
Here are the notes I keep coming back to year on year while learning about GraphQL. Enjoy!
Schema First Design
Schema-first design typically involves three major steps:
- Defining the schema: We identify which data our feature requires, and then we structure our schema to provide that data as intuitively as possible.
- Back-end implementation: We build out our GraphQL API using Apollo Server and fetch the required data from whichever data sources contain it. In this first course, we will be using mocked data. In a following course, we'll connect our app to a live REST data source.
- Front-end implementation: Our client consumes data from our GraphQL API to render its views.
Graph
The graph represents an app's data points and how they're connected as a collection of nodes and edges.
Type Definition
type PascalCase {
key1: String! (! means non-nullable)
key2: Int
key3: [OtherType] (array of OtherType)
}
Query Type
The Query
type's fields define which data clients can query from our schema. The Query
type contains the entry points to our schema. There are 2 other possible entry points: Mutation
and Subscription
.
Query Definition
query queryName {
key1
key2
}
Best practices when creating client queries
- Assign each query string to a constant with an ALL_CAPS name.
- Export query definitions for use in unit tests.
- Test out queries in the Apollo Studio Explorer and copy them over.
- Wrap each query in the gql template literal.
Queries must use valid GraphQL syntax and must only include schema-defined fields.
GraphQL Mutations
Mutation names should start with a verb, followed by whatever data you're modifying.
Queries and mutations are both types of GraphQL operations. Queries are read operations that always retrieve data. Mutations are write operations that always modify data. Similar to Query
fields, fields of the Mutation
type are also entry points into a GraphQL API.
GraphQL Variables
Variables are denoted by the $ symbol. They are used to provide dynamic values for arguments to avoid including hardcoded values in a query. Each one's type must match the type specified in the schema.
query getMission($isScheduled: Boolean) {
mission(scheduled: $isScheduled) {
id
codename
}
}
GraphQL Resolvers
A resolver function is responsible for populating the data for a single field in your schema.
When a query executes successfully, a data key containing a result object with the same shape as the query is returned.
Resolver Chains
One should define a separate resolver for each field, including subfields which need to fetch more data.
A resolver function populates the data for a field in your schema. The function has four parameters. The first, parent
, contains the returned data of the previous function in the resolver chain. The second, args
, is an object that contains all the arguments provided to the field. We use the third parameter, context
, to access data sources such as a database or REST API. Finally, the fourth parameter, info
, contains informational properties about the operation state.
Apollo Data Sources
They help avoid the N+1 problem of data-fetching, which is, making N calls to the exact same endpoint to fetch the exact same data.
Resolver Cache
How might a resource cache be useful for our data source?
- It helps resolve query fields that have already been fetched much faster.
- It helps manage the mix of different endpoints with different cache policies.
- It prevents unnecessary REST API calls for data that doesn't get updated frequently.
Apollo GraphQL
useQuery Hook
The useQuery hook returns an object with 3 useful properties that we use in our app: loading
indicates whether the query has completed and results have been returned. error
is an object that contains any errors that the operation has thrown. data
contains the results of the query after it has completed. To set variables in our query, we declare them in the second parameter of the useQuery
hook, inside an options object.
useMutation Hook
We use hooks to send requests to our GraphQL API from a React client. To send a mutation, we use the useMutation
hook. This returns an array, where the first element is the mutate function used to trigger the mutation. The second element is an object with more information about the mutation, such as loading
, error
and data
. This hook takes in a GraphQL operation as the first parameter. It also takes in an options
object as the second parameter, where properties like variables
are set.
Entities
Entities are GraphQL Schemas can be resolved across multiple subgraphs.
Anatomy of GraphQL Types
type Query {
getMission(isScheduled: Boolean): [Mission!]!
}
type Mutation {
createMission(updatedMission: Mission!): Mission
}
type Mission {
id: ID!
name: String
# ...etc
}
Security
Authentication is checking that a user is who they say they are. Authorization is checking that a user is allowed to do what they're trying to do.
- Authentication is determining whether a given user is logged in, and subsequently determining which user someone is.
- Authorization is determining what a given user has permission to do or see.
Note: AuthenticationError
and ForbiddenError
are both error subclasses defined by Apollo Server. For more information on these and other kinds of errors, check out the Apollo Docs on error handling.
With field-level authorization, each resolver determines whether the logged-in user has permission to access a field, query, or mutation in the schema.
GraphQL Directives
A directive decorates part of a GraphQL schema or operation with additional configuration. GraphQL servers can read a GraphQL document's directives and perform custom logic as appropriate.
Each directive can only appear in certain locations within a GraphQL schema or operation. These locations are listed in the directive's definition.
directive @deprecated( reason: String = "No longer supported" ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE
Defaults
- Deprecated - Marks the schema definition of a field or enum value as deprecated with an optional reason.
- Skip - If
true
, the decorated field or fragment in an operation is not resolved by the GraphQL server. - Include - If
false
, the decorated field or fragment in an operation is not resolved by the GraphQL server.
Federation
In a federated architecture, your individual GraphQL APIs are called subgraphs, and they're composed into a supergraph. By querying your supergraph's router, clients can fetch data from all of your subgraphs with a single request:
The router serves as the public access point for your supergraph. It receives incoming GraphQL operations and intelligently routes them across your subgraphs. To clients, this looks exactly the same as querying any other GraphQL server—no client-side configuration is required.