Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building tools for GraphQL

Building tools for GraphQL

Adopting GraphQL can be fairly demanding and it takes some time to find the right tooling. What can we do to improve DX and supercharge our GraphQL development? Let's deep-dive into GraphQL tooling that help us building GraphQL at scale.

Glenn Reyes

October 19, 2018
Tweet

More Decks by Glenn Reyes

Other Decks in Programming

Transcript

  1. Building tools for
    GraphQL
    @glnnrys
    Glenn Reyes

    View Slide

  2. Glenn Reyes
    !
    @glnnrys
    Freelance Front-End Engineer | React & GraphQL

    View Slide

  3. View Slide

  4. We need to migrate
    to GraphQL

    View Slide

  5. View Slide

  6. GraphQL all the things!

    View Slide

  7. GraphQL all the things!
    (but not all at once )

    View Slide

  8. Where to start?

    View Slide

  9. Setup the server
    1. Install GraphQL server
    2. Configure server
    3. Setup an IDE
    4. Install build & type system
    5. Configure build & type system
    6. ...

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. I'm doing repetitive
    tasks

    View Slide

  15. How to fix it?

    View Slide

  16. Tools & Features
    GraphQL Server
    IDE (Playground, GraphiQL)
    GraphQL imports
    Live reloading
    Babel (Latest ES features)

    View Slide

  17. graphql-yoga

    View Slide

  18. const { GraphQLServer } = require('graphql-yoga');
    const typeDefs = `
    type Query {
    hello(name: String): String!
    }
    `;
    const resolvers = {
    Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
    },
    };
    const server = new GraphQLServer({ typeDefs, resolvers });
    server.start(() => console.log('Server is running on localhost:4000'));

    View Slide

  19. const { GraphQLServer } = require('graphql-yoga');
    const typeDefs = `
    type Query {
    hello(name: String): String!
    }
    `;
    const resolvers = {
    Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
    },
    };
    const server = new GraphQLServer({ typeDefs, resolvers });
    server.start(() => console.log('Server is running on localhost:4000'));
    const { GraphQLServer } = require('graphql-yoga');

    View Slide

  20. const { GraphQLServer } = require('graphql-yoga');
    const typeDefs = `
    type Query {
    hello(name: String): String!
    }
    `;
    const resolvers = {
    Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
    },
    };
    const server = new GraphQLServer({ typeDefs, resolvers });
    server.start(() => console.log('Server is running on localhost:4000'));


    const typeDefs = `
    type Query {
    hello(name: String): String!
    }
    `;

    View Slide

  21. const { GraphQLServer } = require('graphql-yoga');
    const typeDefs = `
    type Query {
    hello(name: String): String!
    }
    `;
    const resolvers = {
    Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
    },
    };
    const server = new GraphQLServer({ typeDefs, resolvers });
    server.start(() => console.log('Server is running on localhost:4000'));







    const resolvers = {
    Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
    },
    };

    View Slide

  22. const { GraphQLServer } = require('graphql-yoga');
    const typeDefs = `
    type Query {
    hello(name: String): String!
    }
    `;
    const resolvers = {
    Query: {
    hello: (_, { name }) => `Hello ${name || 'World'}`,
    },
    };
    const server = new GraphQLServer({ typeDefs, resolvers });
    server.start(() => console.log('Server is running on localhost:4000'));













    const server = new GraphQLServer({ typeDefs, resolvers });
    server.start(() => console.log('Server is running on localhost:4000'));

    View Slide

  23. graphql-yoga
    ✅ GraphQL Server
    ✅ IDE (Playground)
    ❌ GraphQL imports
    ❌ Live reloading
    ❌ Babel (Latest ES features)

    View Slide

  24. apollo-server

    View Slide

  25. const { ApolloServer, gql } = require('apollo-server');
    const typeDefs = gql`
    type Query {
    "A simple type for getting started!"
    hello: String
    }
    `;
    const resolvers = {
    Query: {
    hello: () => 'world',
    },
    };
    const server = new ApolloServer({
    typeDefs,
    resolvers,
    });
    server.listen().then(({ url }) => {
    console.log(` Server ready at ${url}`);
    });

    View Slide

  26. const { ApolloServer, gql } = require('apollo-server');
    const typeDefs = gql`
    type Query {
    "A simple type for getting started!"
    hello: String
    }
    `;
    const resolvers = {
    Query: {
    hello: () => 'world',
    },
    };
    const server = new ApolloServer({
    typeDefs,
    resolvers,
    });
    server.listen().then(({ url }) => {
    console.log(` Server ready at ${url}`);
    });
    const { ApolloServer, gql } = require('apollo-server');

    View Slide

  27. const { ApolloServer, gql } = require('apollo-server');
    const typeDefs = gql`
    type Query {
    "A simple type for getting started!"
    hello: String
    }
    `;
    const resolvers = {
    Query: {
    hello: () => 'world',
    },
    };
    const server = new ApolloServer({
    typeDefs,
    resolvers,
    });
    server.listen().then(({ url }) => {
    console.log(` Server ready at ${url}`);
    });


    const typeDefs = gql`
    type Query {
    "A simple type for getting started!"
    hello: String
    }
    `;

    View Slide

  28. const { ApolloServer, gql } = require('apollo-server');
    const typeDefs = gql`
    type Query {
    "A simple type for getting started!"
    hello: String
    }
    `;
    const resolvers = {
    Query: {
    hello: () => 'world',
    },
    };
    const server = new ApolloServer({
    typeDefs,
    resolvers,
    });
    server.listen().then(({ url }) => {
    console.log(` Server ready at ${url}`);
    });








    const resolvers = {
    Query: {
    hello: () => 'world',
    },
    };

    View Slide

  29. const { ApolloServer, gql } = require('apollo-server');
    const typeDefs = gql`
    type Query {
    "A simple type for getting started!"
    hello: String
    }
    `;
    const resolvers = {
    Query: {
    hello: () => 'world',
    },
    };
    const server = new ApolloServer({
    typeDefs,
    resolvers,
    });
    server.listen().then(({ url }) => {
    console.log(` Server ready at ${url}`);
    });














    const server = new ApolloServer({
    typeDefs,
    resolvers,
    });
    server.listen().then(({ url }) => {
    console.log(` Server ready at ${url}`);
    });

    View Slide

  30. apollo-server
    ✅ GraphQL Server
    ✅ IDE (Playground)
    ❌ GraphQL imports
    ❌ Live reloading
    ❌ Babel (Latest ES features)

    View Slide

  31. apollo-server
    backpack
    ✅ GraphQL Server
    ✅ IDE (GraphiQL, Playground)
    ❌ GraphQL imports
    ✅ Live reloading
    ✅ Babel (Latest ES features)
    ✅ Live reloading
    ✅ Babel (Latest ES features)

    View Slide

  32. apollo-server

    backpack
    graphql-tag/loader
    ✅ GraphQL Server
    ✅ IDE (GraphiQL, Playground)
    ✅ GraphQL imports
    ✅ Live reloading
    ✅ Babel (Latest ES features)

    View Slide

  33. Look up what we need & install
    Write down all the boilerplate code
    Set up and configure build system
    Check if everything works as expected

    View Slide

  34. We can do better!

    View Slide

  35. What if ...

    View Slide

  36. Setup the server
    1. Install GraphQL server
    2. Configure server
    3. Setup an IDE
    4. Install build & type system
    5. Configure build & type system
    6. ...

    View Slide

  37. Setup the server
    1. Install GraphQL server
    2. Configure server
    3. Setup an IDE
    4. Install build & type system
    5. Configure build & type system
    6. ...

    View Slide

  38. What if we can focus
    building the actual
    GraphQL code?

    View Slide

  39. graphpack

    View Slide

  40. graphpack
    ⚡ Zero config
    ⚡ Run server by single command
    ⚡ Hide boilerplate

    View Slide

  41. src
    "## resolvers.js
    $## schema.graphql

    View Slide

  42. Focus on writing
    GraphQL

    View Slide

  43. CLI
    $ graphpack # start dev
    $ graphpack build # create build

    View Slide

  44. "scripts": {
    "start": "graphpack"

    "build": "graphpack build"
    },
    package.json

    View Slide

  45. View Slide

  46. What's inside?

    View Slide

  47. #!/usr/bin/env node
    const nodemon = require('nodemon');
    const webpack = require('webpack');
    const config = require('../webpack.config');
    const compiler = webpack(config);
    const serverPaths = Object.keys(compiler.options.entry).map(entry =>
    path.join(compiler.options.output.path, `${entry}.js`),
    );
    compiler.watch(
    config.watchOptions,
    once((error, stats) => {
    if (error || stats.hasErrors()) {
    throw Error(error || stats.toJson().errors);
    }
    nodemon({ script: serverPaths[0], watch: serverPaths }).on(
    'quit',
    process.exit,
    );
    }),
    );

    View Slide

  48. #!/usr/bin/env node
    const nodemon = require('nodemon');
    const webpack = require('webpack');
    const config = require('../webpack.config');
    const compiler = webpack(config);
    const serverPaths = Object.keys(compiler.options.entry).map(entry =>
    path.join(compiler.options.output.path, `${entry}.js`),
    );
    compiler.watch(
    config.watchOptions,
    once((error, stats) => {
    if (error || stats.hasErrors()) {
    throw Error(error || stats.toJson().errors);
    }
    nodemon({ script: serverPaths[0], watch: serverPaths }).on(
    'quit',
    process.exit,
    );
    }),
    );




    const compiler = webpack(config);



    compiler.watch(




    nodemon(

    );
    }),
    );
    webpack

    View Slide

  49. Entry file: server.js
    import { ApolloServer } from 'apollo-server';
    import { resolvers, typeDefs } from './srcFiles';
    const server = new ApolloServer({ typeDefs, resolvers });
    server
    .listen({ port: 4000 })
    .then(({ url }) => console.log(` Server ready at ${url}`));
    export default server;

    View Slide

  50. Entry file: server.js
    import { ApolloServer } from 'apollo-server';
    import { resolvers, typeDefs } from './srcFiles';
    const server = new ApolloServer({ typeDefs, resolvers });
    server
    .listen({ port: 4000 })
    .then(({ url }) => console.log(` Server ready at ${url}`));
    export default server;
    import { resolvers, typeDefs } from './srcFiles';

    View Slide

  51. Importing typeDefs & resolvers
    const importFirst = req =>
    req.keys().map(mod => req(mod).default || req(mod))[0];
    export const resolvers = importFirst(
    require.context(
    process.env.GRAPHPACK_SRC_DIR,
    true,
    /^\.\/(resolvers|resolvers\/index)\.(js|ts)$/,
    ),
    );

    export const typeDefs = importFirst(
    require.context(
    process.env.GRAPHPACK_SRC_DIR,
    true,
    /^\.\/(schema|schema\/index)\.(graphql|js|ts)$/,
    ),
    );

    View Slide

  52. Importing typeDefs & resolvers
    const importFirst = req =>
    req.keys().map(mod => req(mod).default || req(mod))[0];
    export const resolvers = importFirst(
    require.context(
    process.env.GRAPHPACK_SRC_DIR,
    true,
    /^\.\/(resolvers|resolvers\/index)\.(js|ts)$/,
    ),
    );

    export const typeDefs = importFirst(
    require.context(
    process.env.GRAPHPACK_SRC_DIR,
    true,
    /^\.\/(schema|schema\/index)\.(graphql|js|ts)$/,
    ),
    );




    require.context(




    )




    require.context(




    )

    View Slide

  53. Wrap up
    ⭐ Start with zero configuration
    ⭐ Run the server by a single command
    ⭐ Hide all the boilerplate code
    ⭐ Focus on writing GraphQL

    View Slide

  54. Can I use it now?

    View Slide

  55. github.com/glennreyes/graphpack

    View Slide

  56. Installation
    $ yarn add graphpack --dev
    $ npm install graphpack --save-dev

    View Slide

  57. github.com/glennreyes/graphpack

    View Slide

  58. github.com/glennreyes/graphpack

    View Slide

  59. What about tools

    on the client?

    View Slide

  60. react-apollo

    View Slide

  61. import gql from 'graphql-tag';
    import { Query } from 'react-apollo';
    const getTalks = gql`
    query getTalks {
    talks {
    title
    speaker {
    name
    }
    }
    }
    `;
    const Talks = () => (

    {({ loading, error, data }) => {
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return data.talks.map(talk => (

    {talk.title}
    By {talk.speaker.name}

    ));
    }}

    );

    View Slide

  62. import gql from 'graphql-tag';
    import { Query } from 'react-apollo';
    const getTalks = gql`
    query getTalks {
    talks {
    title
    speaker {
    name
    }
    }
    }
    `;
    const Talks = () => (

    {({ loading, error, data }) => {
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return data.talks.map(talk => (

    {talk.title}
    By {talk.speaker.name}

    ));
    }}

    );
    import gql from 'graphql-tag';
    import { Query } from 'react-apollo';

    View Slide

  63. import gql from 'graphql-tag';
    import { Query } from 'react-apollo';
    const getTalks = gql`
    query getTalks {
    talks {
    title
    speaker {
    name
    }
    }
    }
    `;
    const Talks = () => (

    {({ loading, error, data }) => {
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return data.talks.map(talk => (

    {talk.title}
    By {talk.speaker.name}

    ));
    }}

    );


    const getTalks = gql`
    query getTalks {
    talks {
    title
    speaker {
    name
    }
    }
    }
    `;

    View Slide

  64. import gql from 'graphql-tag';
    import { Query } from 'react-apollo';
    const getTalks = gql`
    query getTalks {
    talks {
    title
    speaker {
    name
    }
    }
    }
    `;
    const Talks = () => (

    {({ loading, error, data }) => {
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return data.talks.map(talk => (

    {talk.title}
    By {talk.speaker.name}

    ));
    }}

    );













    const Talks = () => (

    {({ loading, error, data }) => {
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return data.talks.map(talk => (

    {talk.title}
    By {talk.speaker.name}

    ));
    }}

    );

    View Slide

  65. const Talks = () => (

    {({ loading, error, data }) => {
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return data.talks.map(talk => (

    {talk.title}
    By {talk.speaker.name}

    ));
    }}

    );

    query={getTalks}

    View Slide

  66. import gql from 'graphql-tag';


    const TalksQuery = gql`
    query getTalks {
    talks {
    title
    speaker {
    name
    }
    }
    }
    `;

    View Slide

  67. import TalksQuery from './getTalks.graphql'

    View Slide

  68. Less code

    View Slide

  69. Less bugs

    View Slide

  70. github.com/glennreyes/graphql-components

    View Slide

  71. Takeaways
    ⚡ Automate repetitive tasks
    ⚡ Build tools to reduce boilerplate code
    ⚡ Make them open source
    ⚡ Focus on writing GraphQL code
    ⚡ Less code === less bugs

    View Slide

  72. Thank You!
    @glnnrys
    Glenn Reyes

    View Slide