Giter Club home page Giter Club logo

graphqlworkshop's Introduction

Plan

  1. Every functionality has own branch
  2. Every step has readme.md
  3. If you get lost you can checkout next step git checkout step1 or take a look into readme

Optional

  1. Install nodemon npm install nodemon -g for live reloading
  2. Install schema generator npm install apollo-codegen -g for schema generating

Step 0

  1. git clone https://github.com/polacekpavel/graphqlworkshop
  2. cd graphqlworkshop
  3. npm install
  4. Start a client npm start -> http://localhost:3000
  5. Start a server nodemon src/server or node src/server -> http://localhost:8000

#Step 1

Basic schema definition

    # comment    
    type NameOfType {
        # Nullable string
        title: String
        # Non-Nullable string
        title: String!
        # Nullable integer
        count: Int
        # Non-Nullable integer
        count: Int!
        # Boolean
        completed: Boolean
        # Array of strings
        comments: [String]
        # Custom type
        avatar: Image
        # Array
        avatar: [Image]
    }

Basic resolvers

Query: {
        users(root, args, context) {
            return [{
                id: 1,
                firstName: 'myFirstName',
                lastName: 'myLastName',
                githubUsername: 'fake'
            }]
        }
    }
.
.
.

Completed type schema for our application

    # Entry point for our application
    type Query {
        # List of all users
        users: [User]
        # Get user detail
        user(githubUsername: String!): User
    }
    
    # Entry point for modifications
    type Mutation {
        # Create new user
        createUser(firstName: String, lastName: String, githubUsername: String!): User
    }
    
    type User {
        id: Int!,
        firstName: String
        lastName: String
        github: Github
    }
    
    type Github {
        username: String
        location: String
        avatarSrc: String
        events: [Event]
    }
    
    type Event {
        eventType: String,
        weather: Weather
    }
    
    type Weather {
        condition: String
    }

Resolvers for our application

{
    Query: {
        users(root, args, context) {
            return [{
                firstName: 'First name 1',
                lastName: 'Last name'
            },{
                firstName: 'First name 2',
                lastName: 'Last name'
            }]
        },
        user(root, args, context) {
            return {
                firstName: 'First name 1',
                lastName: args.githubUsername
            }
        }
    },
    Mutation: {
        createUser(root, args, context) {
            return {
                firstName: args.firstName,
                lastName: args.lastName
            }
        }
    },
    User: {
        github(root, args, context) {
            return {
                username: root.firstName,
                location: 'Prague',
                avatarSrc: 'https://avatars0.githubusercontent.com/u/273551?v=3&s=140'
            }
        }
    },
    Github: {
        events(root, args, context) {
            return [{
                eventType: 'Fork',
            },{
                eventType: 'Watch',
            }]

        }
    },
    Event: {
        weather(root, args, context) {
            return {
                condition: 'fog'
            }
        }
    }
}

Wire it up with Express

const makeExecutableSchema = require('graphql-tools').makeExecutableSchema;
const apolloExpress = require('apollo-server').apolloExpress;
const graphiqlExpress = require('apollo-server').graphiqlExpress;

const schema = require('./schema').schema;
const resolvers = require('./resolvers').resolvers;
app.use('*', cors())
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

const executableSchema = makeExecutableSchema({
    typeDefs: schema,
    resolvers: resolvers
});
app.use('/graphql', apolloExpress((req) => {
    return {
        schema:executableSchema
    }
}));

app.use('/graphiql', graphiqlExpress({
    endpointURL: '/graphql'
}));

Test your queries in graphiql http://localhost:8000/graphiql #Step 2

Postgres connection postgres://oakbmqixdijogm:WaIgtJyBSg9KBHa7sasNzNwBc1@ec2-54-228-192-254.eu-west-1.compute.amazonaws.com:5432/db7uuofu104gv6

const Sequalize = require('sequelize');
const db = new Sequelize('postgres://oakbmqixdijogm:WaIgtJyBSg9KBHa7sasNzNwBc1@ec2-54-228-192-254.eu-west-1.compute.amazonaws.com:5432/db7uuofu104gv6', {    
    dialectOptions: {
        "ssl": true
    },
    dialect: 'postgres',
    pool: {
        max: 1,
        min: 0,
        idle: 10000
    }
});


const UserModel = db.define('User', {
    githubUsername: { type: Sequelize.STRING, fieldName: 'github_username' },
    firstName: { type: Sequelize.STRING, fieldName: 'first_name' },
    lastName: { type: Sequelize.STRING, fieldName: 'last_name' },
}, {
    timestamps: false
});

const User = db.models.User

db.sync();

exports.user = User;

Resolvers.js

  Query: {
        users(root, args, context) {
            return user.findAll();
        },
        user(root, args, context) {
            return user.findOne({ where: { githubUsername: args.githubUsername }});
        }
    },
    Mutation: {
        createUser(root, args, context) {
            return user.create({
                firstName: args.firstName,
                lastName: args.lastName,
                githubUsername: args.githubUsername
            })
        }
    },

Test your queries and mutation in graphiql http://localhost:8000/graphiql

#Step 3

0.(optional) Update your schema apollo-codegen download-schema http://localhost:8000/graphql --output ./graphql.schema.json
1.Bootstrap Apollo client

import { ApolloProvider } from "react-apollo";
import ApolloClient, { createNetworkInterface } from "apollo-client";


const networkInterface = createNetworkInterface('http://localhost:8000/graphql');

const client = new ApolloClient({
    networkInterface
});

ReactDOM.render(
    <ApolloProvider client={client}>
        <App />
    </ApolloProvider>,
    document.getElementById('root')
);

2.Connect your react component with ApolloData (Users.js)

import { graphql } from "react-apollo";
import gql from "graphql-tag";

class Users extends Component {
 ...   
}


const UsersQuery = gql`
    query getAllUsers {
        users {
            id
            firstName,
            lastName,
            github {
                username
            }
        }
    }
`;
export default graphql(UsersQuery)(Users);

3.Connect your react component (UserDetail) with ApolloData and using variables (UserDetail.js)

import { graphql } from "react-apollo";
import gql from "graphql-tag";
class UserDetail extends Component {
 ...   
}
const UserDetailQuery = gql`
    query getUsersDetail($githubUsername: String!) {
        user(githubUsername: $githubUsername) {
            id,
            firstName,
            github {
                avatarSrc,
                events {
                    createdAt,
                    eventType,
                    weather {
                        condition
                    }
                },
                username,
                location
            },
            lastName
        }
    }
`
export default graphql(UserDetailQuery, {
    options: (props) => {
        return {
            variables: {
                githubUsername: props.user.github.username
            }
        }
    }
})(UserDetail);

Step 4

CreateUser.js

import { graphql } from "react-apollo";
import gql from "graphql-tag";
 <button className="btn btn-success"
                        onClick={() => {
                            this.props.mutate({
                                variables: {
                                    firstName: this.state.firstName,
                                    lastName: this.state.lastName,
                                    githubUsername: this.state.githubUsername
                                }
                            }).then(() => this.props.onCreate());

                        }}>
                    Save
                </button>
const CreateUserQuery = gql`
    mutation createUser ($firstName: String!, $lastName: String!, $githubUsername: String!) {
        createUser(firstName: $firstName, lastName: $lastName, githubUsername: $githubUsername) {
            firstName,
            id,
            github {
                username
            }
            lastName
        }
    }
`
export default graphql(CreateUserQuery)(CreateUser);

Refetch data

this.users.data.refetch();

Step 5

//Client index .js
networkInterface.use([{
    applyMiddleware(req, next) {
        if (!req.options.headers) {
            req.options.headers = {};
        }

        req.options.headers.authorization = 'xxx' //github api personal access token; 
        next();
    }
}]);
//Server App.js
const addSchemaLevelResolveFunction = require('graphql-tools').addSchemaLevelResolveFunction;
addSchemaLevelResolveFunction(executableSchema, (root, args, context, info) => {
    if (!context || !context.authorization) {
        throw new Error('non-auth');
    }
});
app.use('/graphql', apolloExpress((req) => {
    return {
        schema: executableSchema,
        context: {
            authorization: req.headers.authorization
        },
    }
}));

#Step 6

//client batching
const client = new ApolloClient({
    networkInterface,
    shouldBatch: true
});

API endpoints Google places api https://maps.googleapis.com/maps/api/geocode/json?address=${location}

Weather api - time machine https://api.darksky.net/forecast/${apiKey}/${lat},${long},${time}?exclude=currently,flags

Github events api https://api.github.com/users/${username}/events

Github user detail api https://api.github.com/users/${githubUsername}

//Server batching and caching
const locationLoader = new DataLoader((ids) => {
    return fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${ids}`)
        .then((res) => res.json())
        .then((res) => {
            const lat = res.results[0].geometry.location.lat;
            const long = res.results[0].geometry.location.lng;

            return [{
                lat,
                long
            }];
        })
}, {
    batch: false
});

Competed resolvers with funcionality

const user = require('./db').user;
const DataLoader = require('dataloader');
const fetch = require('node-fetch');
const locationLoader = new DataLoader((ids) => {
    return fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${ids}`)
        .then((res) => res.json())
        .then((res) => {
            const lat = res.results[0].geometry.location.lat;
            const long = res.results[0].geometry.location.lng;

            return [{
                lat,
                long
            }];
        })
}, {
    batch: false
});

exports.resolvers = {
    Query: {
        users(root, args, context) {
            return user.findAll();
        },
        user(root, args, context) {
            return user.findOne({ where: { githubUsername: args.githubUsername } });
        }
    },
    Mutation: {
        createUser(root, args, context) {
            return user.create({
                firstName: args.firstName,
                lastName: args.lastName,
                githubUsername: args.githubUsername
            })
        }
    },
    User: {
        github(root, args, context) {
            return fetch(`https://api.github.com/users/${root.githubUsername}`)
                .then((res) => res.json())
                .then((res) => {
                    return {
                        username: root.githubUsername,
                        avatarSrc: res.avatar_url,
                        location: res.location
                    }
                });
        }
    },
    Github: {
        events(root, args, context) {
            return fetch(`https://api.github.com/users/${root.username}/events`)
                .then((res) => res.json())
                .then((res) => {
                    return res.map((event) => {
                        return {
                            eventType: event.type,
                            createdAt: event.created_at,
                            location: root.location
                        }
                    });
                })
        }
    },
    Event: {
        weather(root, args, context) {
            const apiKey = '458461701954a3df9a801e38d6033d17';
            return locationLoader.load(root.location).then((res) => {
                const time = Math.round(new Date(root.createdAt).getTime() / 1000);
                return fetch(`https://api.darksky.net/forecast/${apiKey}/${res.lat},${res.long},${time}?exclude=currently,flags`)
                    .then((res) => res.json())
                    .then((res) => {
                        return {
                            condition: res.daily.data[0].icon
                        }
                    });
            });
        }
    }
}

#Step 7

import update from 'react-addons-update';
...

this.props.mutate({
                                variables: {
                                    ...
                                },
                                optimisticResponse: {
                                    createUser: {
                                        id: 100,
                                        firstName: this.state.firstName,
                                        lastName: this.state.lastName,
                                      github: {
                                          username: this.state.githubUsername
                                      }
                                    }
                                },
                                updateQueries: {
                                    getAllUsers: (prev, { mutationResult }) => {
                                        const newUser = mutationResult.data.createUser;
                                        return update(prev, {
                                            users: {
                                                $unshift: [newUser]
                                            }
                                        });
                                   

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.