GraphQL
Introduce
Communication
Between
Front & Back
Restful API
GET
POST
PUT
PATCH
DELETE
/members/:memberId
Problems?
{
"id": 1,
"name": "Stanney Yen",
"grade": "senior"
}
GET /members/1
{
"id": 1,
"name": "Stanney Yen",
"grade": "senior",
"projects": [{
"id": 1,
"name": "YiGuang"
}, {
"id": 2,
"name": "BaiYi"
}]
}
GET /members/1/withProjects
{
"id": 1,
"name": "Stanney Yen",
"grade": "senior",
"projects": [{
"id": 1,
"name": "YiGuang",
"repo": {
"url": ".../Rytass/YiGuang",
"branch": "master"
}
}, {
"id": 2,
"name": "BaiYi",
"repo": {
"url": ".../Rytass/BaiYi",
"branch": "develop"
}
}]
}
GET /members/1/withProjectURL
GraphQL
JSON like request
Strong type
Client cache
What about
CREATE / Edit
Mutation
Structure change
Strong type checker
Client model update
Update new data with mutation request
How-to
Official Library
JS framework
Relay
Apollo Client
Relay js
Single (less) data model
Pagenation
Automatic cache
Apollo Client
Less restrict
Multi-platform (iOS / Android)
Semi-automatic cache
Redux friendly
apollo Client
Define Schema
import fs from 'fs';
import {
GraphQLSchema,
} from 'graphql';
import {
introspectionQuery,
printSchema,
} from 'graphql/utilities';
/* Define queryType and mutationType */
const graphqlSchema = new GraphQLSchema({
query: queryType,
mutation: mutationType,
});
// Build schema to graphql structure
graphql(graphqlSchema, introspectionQuery)
.then((result) => {
fs.writeFileSync('schema.json', JSON.stringify(result));
});
// Build schema to .graphql file
const graphqlString = printSchema(graphqlSchema);
fs.writeFileSync('schema.graphql', graphqlString);
query Type
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLList,
} from 'graphql';
/* Define memberType and advertiserType */
const queryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
viewer: {
type: new GraphQLObjectType({
name: 'Viewer',
fields: () => ({
me: {
type: memberType,
resolve: () => Member.findMe(),
},
advertisers: {
type: new GraphQLList(advertiserType),
resolve: () => Advertiser.findAll({}),
},
}),
}),
},
}),
});
member Type
import {
GraphQLObjectType,
GraphQLString,
GraphQLInt,
} from 'graphql';
import moment from 'moment';
export default new GraphQLObjectType({
name: 'Member',
fields: () => ({
id: {
type: GraphQLInt,
},
name: {
type: GraphQLString,
resolve: member => member.name,
},
account: {
type: GraphQLString,
resolve: member => member.account,
},
createdAt: {
type: GraphQLString,
resolve: medium => moment(medium.createdAt).toISOString(),
},
createdDate: {
type: GraphQLString,
resolve: medium => moment(medium.createdAt).format('YYYY-MM-DD'),
},
}),
});
mutation Type
import {
GraphQLObjectType,
GraphQLNonNull,
GraphQLString,
} from 'graphql';
/* Define advertiserType */
const mutationType = new GraphQLObjectType({
name: 'Mutations',
fields: () => ({
createAdvertiser: {
type: advertiserType,
args: {
name: {
type: new GraphQLNonNull(GraphQLString),
},
},
resolve: (value, { name }) => (
Advertiser.createWithName(name)
),
},
}),
});
compiled schema
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
},
"mutationType": {
"name": "Mutations"
},
"subscriptionType": null,
"types": [
// ...some types
]
}
}
}
graphql literal
schema {
query: Query
mutation: Mutations
}
type Query {
viewer: Viewer
}
type Advertiser {
id: Int!
name: String!
agencies: [Agency]
createdAt: String
createdDate: String
}
type Viewer {
me: Member
advertisers: [Advertiser]
}
type Mutations {
createAdvertiser(name: String!): Advertiser
}
Express Route
import express from 'express';
import debug from 'debug';
import schema from './graphql/schema.js';
import graphqlHTTP from 'express-graphql';
const debugServer = debug('GraphQLTester');
const app = express();
app.use('/graphql', graphqlHTTP({
schema,
graphiql: process.env.NODE_ENV !== 'production',
}));
app.listen(3000, (err) => {
if (err) {
debugServer(err);
} else {
debugServer('GraphQL Server Up!');
}
});
React Binding
config
import React from 'react';
import { ApolloProvider } from 'react-apollo';
/* ...initial redux and react-router */
const networkInterface = createNetworkInterface({
uri: '/api/graphql', // API Entrypoint
});
// Bind authorization bearer to request header
networkInterface.use([{
applyMiddleware(req, next) {
if (!req.options.headers) {
req.options.headers = {};
}
const token = localStorage.getItem('accessToken');
req.options.headers.authorization = token ? `Bearer ${token}` : null;
next();
},
}]);
const client = new ApolloClient({
networkInterface,
});
export default (
<ApolloProvider client={client} store={store}>
<Router history={history}>
// ...routes
</Router>
</ApolloProvider>
);
Query
import React from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
function AdvertiserList({ data }) {
const {
loading,
viewer,
} = data;
if (loading) return null;
return (
<div>
// ...nodes
</div>
);
}
const queryHook = graphql(gql`
query AdvertiserList {
viewer {
advertisers {
id
name
}
}
}
`);
export default queryHook(AdvertiserList);
Fragment
import React from 'react';
import gql from 'graphql-tag';
function AdvertiserListItem({
id,
name,
}) {
return (
<tr>
// ...nodes
</tr>
);
}
AdvertiserListItem.fragments = {
advertiserItem: gql`
fragment AdvertiserItem on Advertiser {
id
name
}
`,
};
export default AdvertiserListItem;
compose fragment
import React from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import AdvertiserListItem from './AdvertiserListItem.jsx';
function AdvertiserList({ data }) {
const {
loading,
viewer,
} = data;
if (loading) return null;
return (
<div>
<table>
<thead>
//...titles
</thead>
<tbody>
{viewer.advertisers.map(a => (
<AdvertiserListItem name={a.name} id={a.id} key={a.id} />
)}
</tbody>
</table>
</div>
);
}
const queryHook = graphql(gql`
query AdvertiserList {
viewer {
advertisers {
...AdvertiserItem
}
}
${AdvertiserListItem.fragments.advertiserItem}
}
`);
export default queryHook(AdvertiserList);
mutation
import React, {
Component,
} from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import AdvertiserListItem from './AdvertiserListItem.jsx';
class AdvertiserCreateForm extend Component {
submit() {
this.props.createAdvertiser(this.refs.form.name.value)
}
render() {
return (
<form ref="form" onSubmit={() => this.submit()}>
//...forms
</form>
);
}
}
const mutationsHook = graphql(gql`
mutation createAdvertiser($name: String!) {
createAdvertiser(name: $name) {
...AdvertiserItem
}
}
${AdvertiserListItem.fragments.advertiserItem}
`, {
props: ({ mutate }) => ({
createAdvertiser: name => mutate({
variables: {
name,
},
}),
}),
});
export default mutationsHook(AdvertiserCreateForm);
GraphQL Introduce
By Chia Yu Pai
GraphQL Introduce
- 430