GraphQL Cheat Sheet =================== ### Overview * An alternative approach to RESTful APIs * Clients issue queries/mutations to read and update data * Clients can fetch only the entity fields that are required * GraphQL query syntax can express complex entity relations => nested objects * Mitigates the explosion of RESTful endpoints in scenarios where many different representations of an entity are needed * Graphiql is a query execution UI, also provides good documentation ### Express Server Integration ```javascript const express = require('express'); const expressGraphQL = require('express-graphql'); const schema = require('./schema/schema'); // create express app const app = express(); app.use('/graphql', expressGraphQL({ schema, graphiql: true })); // register app to listen on a port app.listen(4000, () => { console.log('Server started!'); }); ``` ### Schema Types * GraphQL Schema types define the data contract between the client and the server ```javascript const GraphQL = require('graphql'); const { GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLList, GraphQLSchema, GraphQLNonNull } = GraphQL; // define the company schema type const CompanyType = new GraphQLObjectType({ name: 'Company', // use a closure to work around cyclical references fields: () => ({ id: { type: GraphQLString }, name: { type: GraphQLString }, description: { type: GraphQLString }, users: { type: new GraphQLList(UserType), resolve(parentValue, args){ return axios.get(`http://localhost:3000/companies/${parentValue.id}/users`) .then(resp => resp.data); } } }) }); // define the user schema type const UserType = new GraphQLObjectType({ name: 'User', // use a closure to work around cyclical references fields: () => ({ id: { type: GraphQLString }, firstName: { type: GraphQLString }, age: { type: GraphQLInt }, company: { type: CompanyType, resolve(parentValue, args) { return axios.get(`http://localhost:3000/companies/${parentValue.companyId}`) .then(resp => resp.data); } } }) }); ``` ### Root Query Type ```javascript const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', fields: { user: { type: UserType, args: { id: { type: GraphQLString }}, resolve(parentValue, args) { return []; } }, company: { type: CompanyType, args: { id: { type: GraphQLString }}, resolve(parentValue, args) { // async promise return axios.get(`http://localhost:3000/companies/${args.id}`) .then(resp => resp.data); } } } }); ``` ### Root Mutation Type ```javascript const Mutation = new GraphQLObjectType({ name: 'Mutation', fields: { addUser: { type: UserType, args: { firstName: { type: new GraphQLNonNull(GraphQLString) }, age: { type: new GraphQLNonNull(GraphQLInt) }, companyId: { type: GraphQLString } }, resolve(parentValue, { firstName, age }) { return axios.post(`http://localhost:3000/users`, { firstName, age }).then(res => res.data); } }, editUser: { type: UserType, args: { id: { type: new GraphQLNonNull(GraphQLString) }, firstName: { type: GraphQLString }, age: { type: GraphQLInt }, companyId: { type: GraphQLString } }, resolve(parentValue, { id, firstName, age, companyId }) { var updatedUser = { firstName, age, companyId }; return axios.patch(`http://localhost:3000/users/${id}`, updatedUser) .then(res => res.data); } } } }); ``` ### GraphQL Query Syntax Simple Query: ``` query { user(id: "40"){ id firstName age } } ``` Query With Relations: ``` query { user(id: "40"){ id firstName age company { id, name, description } } } ``` Query Fragments: ``` query findCompanyUsers { apple: company(id:"1"){ ...companyDetails } google: company(id:"2"){ ...companyDetails } } fragment companyDetails on Company { id name description users { id firstName age } } ``` Parameterized Queries: ``` query UserQuery($id: ID!){ song(id: $id){ id firstName email } } ``` Parameter Values: ```javascript { "id": 12345 } ``` Mutation: ``` mutation { addUser(firstName: "Jeff", age: 34){ id firstName age } } ``` ### GraphQL Clients * Lokka - Simple implementation: basic queries, mutations, and simple caching * Apollo Client - Good balance between features and complexity * Relay - Amazing performance on mobile, but the most complex. ### Apollo Client Initialization Configuring a basic GraphQL client: ```javascript // ... import ApolloClient from 'apollo-client'; import { ApolloProvider } from 'react-apollo'; // tracks objects by the ID field const client = new ApolloClient({ dataIdFromObject: object => object.id }); const Root = () => { return ( ); }; ReactDOM.render( , document.querySelector('#root') ); ``` Configuring the GraphQL client network interface: ```javascript const networkInterface = createNetworkInterface({ uri: '/graphql', opts: { credentials: 'same-origin' // same-origin request, send cookies w/ request } }); const client = new ApolloClient({ networkInterface, dataIdFromObject: o => o.id }); ``` ### GraphQL Client Query Defining a simple GraphQL query: ```javascript import gql from 'graphql-tag'; export default gql` query { currentUser { id email } } `; ``` Defining a parameterized GraphQL query: ```javascript import gql from 'graphql-tag'; export default gql` query UserByIdQuery($id: ID!) { user(id: $id){ id email } } `; ``` ### GraphQL Client Mutation Defining a parameterized GraphQL mutation: ```javascript import gql from 'graphql-tag'; export default gql` mutation LoginMutation($email: String, $password: String) { login(email: $email, password: $password) { id email } } `; ``` ### React Components with GraphQL Queries ```javascript import React, { Component } from 'react'; import gql from 'graphql-tag'; class HelloGraphQL extends Component { render(){ return (

Hello, {this.props.data.name}

); } } const currentUser = gql` query { currentUser { id firstName email } } `; // funky syntax, but this binds the query to the component props export default graphql(userQuery)(HelloGraphQL); ``` An alternative syntax: ```javascript // ... // this reads a little better const withData = graphql(userQuery); export default withData(HelloGraphQL); ``` ### React Components with GraphQL Mutations An example of a basic mutation: ```javascript import React, { Component } from 'react'; import gql from 'graphql-tag'; import ListUsers from '../queries/ListUsers'; class MutationExample extends Component { onDeleteUser(){ const id = 123; this.props.mutate({ variables: { id } }) .then(() => this.props.data.refetch()); }; } render(){ return (
); } } const deleteUserMutation = gql` mutation DeleteUser($id: ID){ deleteUser(id: $id){ id } } `; const withMutation = graphql(deleteUserMutation); export withMutation(MutationExample); ``` Explicitly refetching GraphQL queries after a mutation: ```javascript // explicity reference the ListUsersQuery query for refetch this.props.mutate({ variables: { id }, refetchQueries: [ { ListUsersQuery } ] }); ``` ### React Components with Mutations and Queries ```javascript const withData = graphql(listUsersQuery); const withMutation = graphql(deleteUserMutation); export withData(withMutation(UsersList)); ``` ### React Components with Multiple Mutations ```javascript import { compose } from 'react-apollo'; const ComponentWithMutations = compose( graphql(submitNewUser, { name: 'newUserMutation' }), graphql(submitRepository, { name: 'newRepositoryMutation' }) )(Component); ```