@AndrewRota | Longhorn PHP 2019
APIs are important
Native Mobile Apps
Web browsers
(http requests from JavaScript)
External API
API
Native Mobile Apps
Web browsers
External API
API
article
comment
comment
author
author
author
Native Mobile Apps
Web browsers
External API
API
article
comment
comment author
Native Mobile Apps
Web browsers
External API
APIgetBlog
Challenges with APIs Today
‣
‣
‣
@AndrewRota
Associate Director, Software Engineering
“GraphQL is a query
language for APIs and a
runtime for fulfilling those
queries with your existing
data.”
{
conferences {
name
dates
}
}
"conferences": [
{
"name": "LonghornPHP",
"dates": "May 2 - 4, 2019"
}
]
{
conferences {
name
speakers {
name
twitter
}
}
}
{
"conferences": [
{
"name": "LonghornPHP",
"speakers": [
{
"name": "Andrew Rota",
"twitter": "https://twitter.com/andrewrota"
}
]
}
]
}
Topics
‣
‣
‣
‣
‣
‣
GraphQL
‣
‣
‣
GraphQL Implementations
‣ technology agnostic
‣
‣
GraphQL Advantages
‣
‣
‣
‣
GraphQL in a web stack
QueryClient
(e.g., browser, mobile
app)
/graphql on
PHP Server
response
Database
GraphQL in a web stack
QueryClient
(e.g., browser, mobile
app)
/graphql on
PHP Server
response
Cache
Service
Database
GraphQL in a web stack
QueryClient
(e.g., browser, mobile
app)
/graphql
server
response
Cache
REST Service
Database
PHP
Queries + Fields
‣
fields
‣
same shape
query {
conferences {
name
dates
}
}
query
field
Fields
‣
‣
sub-selection
‣ avoid
multiple requests
query {
conferences {
name
speakers {
name
}
}
}
sub-selection
Arguments
‣
arguments each field {
conference(name: "LonghornPHP") {
speakers {
name
}
}
}
argument
Variables
‣
query SearchConfs($name: String){
conferences(nameFilter:$name) {
name
}
}
{"name": "LonghornPHP"}
Types + Schemas
‣
set of types
Types + Schemas
‣
type Conference {
name: String!
url: String!
description: String
location: String
dates: String!
# List of speakers at this conference
speakers: [Speaker]
}
Types + Schemas
‣
‣
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
Query + Mutation Types
‣
Query Mutation
‣ Root fields
entry points
type Query {
# Returns conferences
conferences: [Conference]
# Returns speakers
speakers: [Speaker]
}
root
fields
root type
Queries
‣
‣
‣
query ConferenceNamesAndDates{
conferences {
name
dates
}
}
operation nameoperation type
fields
Mutations
‣
‣ mutation
mutation {
addSpeaker(
name: "Andrew Rota",
twitter: "https://twitter.com/andrewrota")
{
id
}
}
Queries from JavaScript
‣
‣
query ConferenceNamesAndDates{
conferences {
name
dates
}
}
Lokka
a simple graphql client library
‣ const t = new HttpTransport('/graphql');
t.send(`query ConferenceNamesAndDates{
conferences {
name
dates
}
}`).then(response => {
console.log(response);
});
Apollo
complete data management solution
‣
‣
‣
‣
‣
<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>
graphql-php
‣
‣
‣
‣
implementation of the
GraphQL spec in PHP
Handle queries
‣
‣ Schema
‣ GraphQL::executeQuery
$schema = new Schema([
'query' => Types::query()
]);
$result = GraphQL::executeQuery(
$schema,
$requestData['query'],
null,
$appContext,
(array)$requestData['variables']
);
$output = $result->toArray();
Query.
Root fields
‣
fields
‣ type
resolve
use GraphQLTypeDefinitionObjectType;
use GraphQLTypeDefinitionType;
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'message' => [
'type' => Type::string(),
'resolve' => function () {
return 'hello world';
}
],
]
]);
Fields can return objects
‣
‣
$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';
}
],
]
]);
Resolvers
‣
‣
‣
‣
$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';
}
],
]
]);
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
Custom object type
‣
$config = [
'name' => 'Conference',
'fields' => [
'name' => Type::nonNull(Types::string()),
'url' => Type::nonNull(Types::string()),
'location' => Types::string(),
]
];
Custom object type
‣
‣
$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);
}
]
]
];
n+1 problem
‣
{
conferences {
name
speakers{
name
}
}
}
Solution: deferred resolvers
‣
‣
‣
'resolve' => function($root) {
SpeakerCollection::add($root->id);
return new Deferred(function() use ($root) {
return SpeakerCollection::get($root->id);
});
}
Limiting Query Complexity and Depth
‣
‣
Persisted Queries
queryClient Server
query {
conferences {
name
dates
}
}
idClient Server{ id: 001 }
‣
‣
‣
‣
With persisted queries:
Introspection
‣
‣
‣
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
},
"types": [
{
"kind": "OBJECT",
"name": "Query",
"description": null,
"fields": [
{
"name": "conferences",
"description": "Returns a list of PHP conferences",
"args": [],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Conference",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
]
}
]
}
}
}
GraphiQL - in browser IDE for exploring GraphQL
Graphql Playground - like GraphiQL, but more features
Voyager - Any GraphQL API as an interactive graph
PHPStorm JS GraphQL Plugin - IDE Integration
Types can be used in client-side code
‣
Apollo Codegen
‣
@AndrewRota
Resources
‣
‣
‣
‣

Getting Started with GraphQL && PHP