Introduce
GET
POST
PUT
PATCH
DELETE
/members/:memberId
{
"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
JSON like request
Strong type
Client cache
Structure change
Strong type checker
Client model update
Update new data with mutation request
Relay
Apollo Client
Single (less) data model
Pagenation
Automatic cache
Less restrict
Multi-platform (iOS / Android)
Semi-automatic cache
Redux friendly
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);
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({}),
},
}),
}),
},
}),
});
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'),
},
}),
});
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)
),
},
}),
});
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
},
"mutationType": {
"name": "Mutations"
},
"subscriptionType": null,
"types": [
// ...some types
]
}
}
}
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
}
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!');
}
});
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>
);
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);
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;
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);
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);