6. Challenges with APIs Today
‣ Over-fetching data
‣ Under-fetching data, requiring multiple round-trips
‣ Time spent iterating on endpoints and expected data shape
7. GraphQL offers an alternative
architecture for developing
efficient, easy to understand APIs.
14. GraphQL Implementations
‣ GraphQL is technology agnostic on both client and server
‣ Client implementations:
‣ Server implementations:
15. GraphQL Advantages
‣ Client requests exactly the shape of the data it needs
‣ Multiple resources can be queried in a single request
‣ API is defined with a strongly-typed schema
‣ Enables strong tooling for developers
16. GraphQL in a web stack
QueryClient
(e.g., browser, mobile
app)
/graphql on
PHP Server
response
Database
17. GraphQL in a web stack
QueryClient
(e.g., browser, mobile
app)
/graphql on
PHP Server
response
Cache
Service
Database
18. GraphQL in a web stack
QueryClient
(e.g., browser, mobile
app)
/graphql
server
response
Cache
REST Service
Database
PHP
20. Queries + Fields
‣ In GraphQL you make
queries for fields on objects
‣ The response will have the
same shape as the query
query {
conferences {
name
dates
}
}
quer
y
field
21. Fields
‣ Fields might be scalar values,
or they might be other
Objects.
‣ Fields can refer to Objects,
and you can make a sub-
selection for fields of these
Objects.
‣ This lets you avoid making
multiple requests for related
query {
conferences {
name
speakers {
name
}
}
}
sub-selection
22. Arguments
‣ You can pass named
arguments to each field
and nested object.
{
conference(name: "PHPDetroit") {
speakers {
name
}
}
}
argument
23. Variables
‣ Dynamic values can be
passed into queries via
variables
query SearchConfs($name: String){
conferences(nameFilter:$name) {
name
}
}
{"name": "Detroit"}
24. Types + Schemas
‣ Every GraphQL service
defines the a set of types
that describe what data can
be requested
25. Types + Schemas
‣ GraphQL servers can be
written in any language, so
we describe types with a
language-agnostic
“GraphQL schema
language”
type Conference {
name: String!
url: String!
description: String
location: String
dates: String!
# List of speakers at this conference
speakers: [Speaker]
}
26. Types + Schemas
‣ GraphQL servers can be
written in any language, so
we describe types with a
language-agnostic
“GraphQL schema
language”
‣ Types include: object,
scalar, list, enumeration,
union, interface, and non-
nullable.
type Conference {
name: String!
url: String!
description: String
location: String
dates: String!
# List of speakers at this conference
speakers: [Speaker]
}
non-nullable
scalar type
list of
object
types
27. Query + Mutation Types
‣ There are two special types
in every GraphQL schema:
Query and Mutation
‣ Root fields you define on
Query and Mutation are the
entry points of requests
type Query {
# Returns conferences
conferences: [Conference]
# Returns speakers
speakers: [Speaker]
}
root
fields
root type
28. Queries
‣ Queries ask for for data;
analogous to GET requests.
‣ GraphQL clients (e.g.,
browsers, mobile apps),
make queries against a
single GraphQL endpoint
‣ Operation name and type
can be optional
query ConferenceNamesAndDates{
conferences {
name
dates
}
}
operation nameoperation type
fields
29. Mutations
‣ Mutations are for modifying
data; analogous to
POST/PUT/DELETE
requests.
‣ They start with the
mutation root type, and
will often leverage
arguments, but are
otherwise the same as
queries
mutation {
addSpeaker(
name: "Andrew Rota",
twitter: "https://twitter.com/andrewrota")
{
id
}
}
31. Client-side GraphQL is about writing queries to request data
from a GraphQL server with a defined schema.
32. Queries from JavaScript
‣ Queries are made via HTTP
requests to a single
endpoint
‣ There are several libraries
available to manage
GraphQL on the client
query ConferenceNamesAndDates{
conferences {
name
dates
}
}
33. Queries from JavaScript
‣ lokka-http-transport is a
simple JavaScript library for
sending GraphQL queries in
JavaScript, just like
standard fetch or ajax
requests
const t = new HttpTransport('/graphql');
t.send(`query ConferenceNamesAndDates{
conferences {
name
dates
}
}`).then(response => {
console.log(response);
});
34. Advanced GraphQL Client Libraries
There are also more advanced GraphQL
clients with features such as...
‣ Declarative data fetching patterns
‣ Client-side caching
‣ Abstractions for managing local +
remote state
‣ Integration with react.js or other
frontend libraries
36. webonyx/graphql-php
Provides:
‣ Type primitives for your Type system
‣ Query parsing, validation, and
execution against a Type system
‣ Type introspection
‣ Tools for deferred field resolution
Feature-complete implementation of the
GraphQL spec in PHP, inspired by
Facebook’s original node-js reference
library.
37. webonyx/graphql-php
Used by:
‣ Folkloreatelier/laravel-graphql
‣ overblog/GraphQLBundle (symfony)
‣ ivome/graphql-relay-php
‣ wp-graphql/wp-graphql
‣ tim-field/graphql-wp
Feature-complete implementation of the
GraphQL spec in PHP, inspired by
Facebook’s original node-js reference
library.
38. Client-side GraphQL is about writing queries to request data
from a GraphQL server with a defined schema.
Server-side GraphQL is about implementing that schema to
return data.
39. 1. Parse the query into an AST
2. Validate the query against the schema
3. Traverse the query, breadth first, and execute a resolver
function for each field
40. The schema defines what queries can be made, what types of
data can be requested, and the relationships between those
types
The resolver functions define how to get the data for each
field.
definition
implementation
41. 1. Define your object type
2. Add it to a new field
3. Write the field resolver function
43. Handle queries
‣ Queries are made via HTTP
requests
‣ Schema instance contains
your type hierarchy, and
starts with the special root
types, Query and Mutation.
‣ GraphQL::executeQuery
executes the query and
returns the result (or failure)
$schema = new Schema([
'query' => Types::query()
]);
$result = GraphQL::executeQuery(
$schema,
$requestData['query'],
null,
$appContext,
(array)$requestData['variables']
);
$output = $result->toArray($debug);
45. Start with the Query type
‣ ObjectType is a collection
of fields
‣ Query has root fields, the
entry points for queries.
‣ Each field must have a
name and type. It can also
have a resolve function,
args, description, and
other properties.
use GraphQLTypeDefinitionObjectType;
use GraphQLTypeDefinitionType;
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'hello' => [
'type' => Type::string(),
'args' => [
'message' => Type::nonNull(Type::string()),
],
'resolve' => function ($root, $args) {
return 'hello ' . $args['message'];
}
],
]
]);
46. Add a root field
‣ Type can be an Array of
some type; use
Types::listOf()
‣ Resolve function can be
implemented however you’d
like to get the data: query
SQL or NoSQL databases,
access in-memory storage,
or query another API
endpoint.
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'conferences' => [
'type' => Types::listOf(Types::conference()),
'description' => 'Returns list of PHP
conferences',
'resolve' => function() {
return DataSource::getConferences();
}
]
]
]);
47. Implement its Type
‣ Type can be an Array of
some type; use
Types::listOf()
‣ 3 ways to define resolvers:
○ Default field resolver (built-
in)
○ Default Field Resolver per
Type (resolveField option in
type config)
○ Field resolver (resolve option
$config = [
'name' => 'Conference',
'fields' => [
'name' => Type::nonNull(Types::string()),
'url' => Type::nonNull(Types::string()),
'location' => Types::string(),
]
];
precedence
48. Let’s add another field
'fields' => [
'name' => Type::nonNull(Types::string()),
'url' => Type::nonNull(Types::string()),
'location' => Types::string(),
'speakers' => [
'type' => Types::listOf(Types::speaker()),
'description' => 'List of speakers at this
conference',
'resolve' => function($root) {
return
DataSource::getSpeakersAtConference($root->id);
}
]
],
49. ...and take a closer look at the resolve function
[
'resolveField' => function($root, $args, $context, ResolveInfo $info) {
return DataSource::getSpeakersAtConference($root->id);
}
]
root / parent
result
arguments app context
query AST and
other meta info
50. Building a GraphQL server is primarily about structuring schema
types, and then implementing their field resolvers
52. n+1 problem
‣ Data-fetching problem that
occurs when you need to
fetch related items in a one-
to-many relationship
{
conferences {
name
speakers{
name
}
}
}
53. Solution: deferred resolvers
‣ We can use deferreds to delay field
resolution until we can make a
single batch request for the data
‣ Once all non-deferred fields are
resolved, graphql-php will call the
wrapped closures
‣ If you have an environment that
supports async operations (e.g.,
HHVM, ReactPHP, PHP threads),
some fields can also resolve async.
'resolve' => function($root) {
SpeakerCollection::add($root->id);
return new Deferred(function() use ($root) {
return SpeakerCollection::get($root->id);
});
}
54. Persisted Queries
queryClient Server
query {
conferences {
name
dates
}
}
idClient Server{ id: 001 }
‣ In production, queries can
be extracted at build-time
as “persisted queries”
‣ Clients send the server the
reference to the query
‣ Reduce data sent to server
‣ Restrict queries that can be
run to a pre-built whitelist
With persisted queries: