6. Challenges with Traditional APIs
‣ 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
}
}
query
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
resources
query {
conferences {
name
speakers {
name
}
}
}
sub-selection
22. Arguments
‣ You can pass named
arguments to each field
and nested object.
{
conference(name: "LonghornPHP") {
speakers {
name
}
}
}
argument
23. Variables
‣ Dynamic values can be
passed into queries via
variables
query SearchConfs($name: String){
conferences(nameFilter:$name) {
name
}
}
{"name": "LonghornPHP"}
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. Lokka
a simple graphql client library
‣ Lokka 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. Apollo
complete data management solution
‣ Declarative API for queries and
mutations
‣ Normalized client-side caching
‣ Combine local and remote data
‣ Pagination, error handling,
refetching, and optimistic UI
‣ Client libraries for popular frontend
frameworks (React.js, Angular, Vue),
as well as native Android and iOS
applications
<Query client={client} query={CONFERENCES_QUERY}>
{({ loading, error, data }) => {
if (loading) return 'Loading...';
if (error) return `Error!`;
return (
<ul>
{data.conferences.map(conference => (
<li>{conference.name}</li>
))}
</ul>
);
}}
</Query>
36. 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.
38. 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.
40. Handle queries
‣ Queries are made via HTTP
requests to a single endpoint
‣ GraphQL::executeQuery
parses, validates, and
executes the query
$schema = new Schema([
'query' => Types::query()
]);
$result = GraphQL::executeQuery(
$schema,
$requestData['query'],
null,
$appContext,
(array)$requestData['variables']
);
$output = $result->toArray();
42. Root fields
‣ This root type (Query) has a
list of root fields, which are
the entry points to the API
‣ Each field has a type, and a
resolve method for getting
the data
use GraphQLTypeDefinitionObjectType;
use GraphQLTypeDefinitionType;
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'message' => [
'type' => Type::string(),
'resolve' => function () {
return 'hello world';
}
],
]
]);
43. Fields can return objects
‣ A field can also return a
custom ObjectType, which is
a type with its own collection
of fields.
‣ It can also return lists of other
types
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'conferences' => [
'type' => Types::listOf(Types::conference()),
'description' => 'Returns conferences',
'resolve' => function() {
return DataSource::getConferences();
}
],
'message' => [
'type' => Type::string(),
'resolve' => function () {
return 'hello world';
}
],
]
]);
44. Resolvers
‣ Resolve functions tell the server
how to get the data for the field
‣ Resolve function can be
implemented however you’d like to
get the data: SQL queries, cache, or
another API
‣ For scalars, the return value will be
the value of the field
‣ For object types, the return value
will be passed on to nested fields
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'conferences' => [
'type' => Types::listOf(Types::conference()),
'description' => 'Returns conferences',
'resolve' => function() {
return DataSource::getConferences();
}
],
'message' => [
'type' => Type::string(),
'resolve' => function () {
return 'hello world';
}
],
]
]);
45. ...let’s take a closer look at a resolve function
function($root, $args, $context, ResolveInfo $info) {
return DataSource::getData($root->id);
}
root / parent
result
arguments app context
query AST and
other meta info
47. 1. Define your object type
2. Add it to a new field
3. Write the field resolver function
48. Custom object type
‣ Just like the root query type,
a custom type is a collection
of fields, each with their own
types
$config = [
'name' => 'Conference',
'fields' => [
'name' => Type::nonNull(Types::string()),
'url' => Type::nonNull(Types::string()),
'location' => Types::string(),
]
];
49. Custom object type
‣ Just like the root query type,
a custom type is a collection
of fields, each with their own
types
‣ Fields on a type can also
have custom ObjectTypes
$config = [
'name' => 'Conference',
'fields' => [
'name' => Type::nonNull(Types::string()),
'url' => Type::nonNull(Types::string()),
'location' => Types::string(),
'speakers' => [
'type' => Types::listOf(Types::speaker()),
'resolve' => function($root) {
return DataSource::getSpeakersAtConf($root->id);
}
]
]
];
57. Types can be used in client-side code
‣ If you’re using a client-side language that supports types, you can
generate types for graphql queries automatically from the GraphQL
schema using tools such as Apollo Codegen
‣ Examples: TypeScript, FlowType, or Swift (iOS)
59. 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
}
}
}
60. Solution: deferred resolvers
‣ graphql-php provides Deferred
objects 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);
});
}
61. Limiting Query Complexity and Depth
‣ graphql-php provides a method to calculate and limit the complexity
of any query, based on the sum of complexity scores set per field
‣ You can also limit the nested fields requested in queries
62. 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:
63. Subscriptions
Client Server
Subscribe to an event
‣ GraphQL subscriptions push
data to the client when
events happen, usually over
a long-lived WebSocket
connection.
‣ graphql-php does not
implement support for
subscriptions
subscription SubscribeToVotes {
newVote {
time,
voter,
value
}
}
65. GraphQL is a new paradigm that offers a lot of new opportunities
for developing performant APIs, but with that comes new
challenges as well.
66. Challenges when using GraphQL
‣ It’s more complex than RESTful APIs
‣ ORMs are not ready to work performantly with GraphQL (yet)
‣ Caching is a lot more challenging
‣ Application metrics are more complicated
Robert Zhu’s “The Case Against GraphQL” (bit.ly/against-graphql)
67. GraphQL might not always be the best choice for your API:
consider your use case and the problems you’re trying to solve,
weigh the tradeoffs, and make an informed decision.
68. GraphQL makes it easier to make
more efficient queries between your
client and your server.
69. GraphQL provides new ways to think
about your APIs and the structure of
your application’s data.