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

Distributed Web Applications with GraphQL (N26)

Distributed Web Applications with GraphQL (N26)

By their nature, user facing applications are generally designed in a monolithic style. But what if we could build distributed web apps that are more like microservices? With GraphQL, Apollo and React we have the necessary tools to create a modular and scalable architecture. However, there are some challenges to overcome: merging different API graphs, handling authorization and loading JavaScript modules, to name a few. In this talk we’re going to explore practical solutions to all of them.

Maximilian Fellner

December 06, 2018
Tweet

More Decks by Maximilian Fellner

Other Decks in Programming

Transcript

  1. BUILDING
    DISTRIBUTED
    WEB APPLICATIONS
    WITH GRAPHQL
    Maximilian Fellner

    View Slide

  2. Hi,
    I'm Max

    View Slide

  3. @mxfellner
    github.com/mfellner

    View Slide

  4. more than 500 employees
    over 2 million customers
    22 markets and over
    €1.5 billion in monthly
    transaction volume

    View Slide

  5. * not a real bank

    View Slide

  6. BY READING THIS TEXT YOU AGREE WITH
    THE TERMS AND CONDITIONS OF THE
    PRESENTATION.

    View Slide

  7. FULL STACK

    View Slide

  8. View Slide

  9. LET'S BUILD
    A WEB APP

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. ARCHITECTURE

    View Slide

  19. View Slide

  20. YOU REALIZE YOUR FRONTEND IS STILL A
    BIG BALL OF MUD

    View Slide

  21. MICRO SERVICES ✨

    View Slide

  22. FRONTEND MICRO SERVICES

    View Slide

  23. WHAT'S A MICRO SERVICE
    ANYWAY?
    independently deployable
    explicit interface
    organized around business capability
    communicate with simple protocols

    View Slide

  24. ALTERNATIVES FOR
    FRONTEND
    MICRO SERVICES
    OR "MICRO FRONTENDS"

    View Slide

  25. Portal website
    easy
    individual websites
    work independently
    not actually frontend
    micro services

    View Slide


  26. ".htm" and ".html" redirect here. For other uses, see HTM.
    For the use of HTML on Wikipedia, see Help:HTML in wikitext.
    Hypertext Markup Language (HTML) is the standard
    markup language for creating web pages and web
    applications. With Cascading Style Sheets (CSS) and
    JavaScript, it forms a triad of cornerstone technologies for
    the World Wide Web.[4]
    HTML
    HTML
    (Hypertext Markup Language)
    simple, standard HTML tag
    secure
    Window.postMessage()
    slow
    difficult to style
    not well integrated

    View Slide

  27. HTML templates










    Mosaic by Zalando
    fast
    consistent appearance
    fragments can include
    their own JavaScript
    no integration on the
    application level
    duplicate JS resources
    requires template server

    View Slide

  28. JavaScript components
    consistent appearance
    consistent behavior
    integration on the
    application level
    all modules must use the
    same technology
    bad code can break the
    entire application

    View Slide

  29. HOW TO LOAD
    JAVASCRIPT MODULES
    FOR COMPONENT-BASED
    WEB APPLICATIONS

    View Slide

  30. ECMAScript modules
    // my-component.mjs
    import React from "react";
    export const MyComponent = () =>
    React.createElement("h1", null, "Hello, World!");
    // application.js
    async function main() {
    const {
    MyComponent
    } = await import("https://cdn.com/my-component.mjs");
    }

    View Slide

  31. React.lazy
    // application.jsx
    import React, { lazy, Suspense } from "react";
    const MyComponent = lazy(() =>
    import("https://cdn.com/my-component.mjs")
    );
    const Application = () => {
    return (
    Loading...}>


    );
    };
    New API since React 16.6.

    View Slide

  32. Support for
    ECMAScript modules
    dynamic import
    .mjs
    import maps
    https://developers.google.com/web/fundamentals/primers/modules

    View Slide

  33. 1. // https://github.com/umdjs/umd/blob/master/templates/returnExports.js
    2. (function(root, factory) {
    3. if (typeof define === "function" && define.amd) {
    4. // AMD. Register as a named module.
    5. define('MyComponent', ['react'], factory);
    6. } else if (typeof module === "object" && module.exports) {
    7. // Export for Node.js.
    8. module.exports.MyComponent = factory(require('react'));
    9. } else {
    10. // Browser global (root is window).
    11. root.MyComponent = factory(root.React);
    12. }
    13. })(typeof window !== "undefined" ? window : this, function(React) {
    14. // Export a React component.
    15. return () =>
    16. React.createElement("h1", null, "Hello, World!");
    17. });
    UMD modules:
    universal module de nition

    View Slide

  34. Advantages of
    UMD modules
    Supported in the browser and under Node.js
    Webpack can create them automatically
    Dependency (imports) management works!
    AMD needs a loader (require.js)

    View Slide

  35. LOADING
    UMD MODULES WITH
    GRAPHQL

    View Slide

  36. The GraphQL query
    query myUmdQuery($url: String!) {
    myUmdModule @umd(url: $url) {
    MyComponent
    }
    }
    const variables = {
    url: "https://cdn.com/my-component.umd.js"
    };
    We use a custom directive to implement the loading magic ✨

    View Slide

  37. 1. const ModuleLoader = ({ url }) => (
    2. 3. query={gql`
    4. query myUmdQuery($url: String!) {
    5. myUmdModule @umd(url: $url) {
    6. MyComponent
    7. }
    8. }
    9. `}
    10. variables={{ url }}
    11. >
    12 {({ error loading data }) => {
    The module loader
    component

    View Slide

  38. Loading UMD modules
    with Apollo
    Supports server-side rendering
    Fine control over queries
    Queries can return additional information
    Requires custom directive implementation
    (with Apollo link)

    View Slide

  39. universal-umd-fetch
    // Load an AMD-compatible module with require.js
    function umdfetch_browser(url: string): Promise {
    return new Promise(resolve => {
    window.require([url], result => resolve(result));
    });
    }
    // Download a CommonJS module and evaluate it in Node.js
    async function umdfetch_server(url: string): Promise {
    const response = await fetch(url);
    const code = await response.text();
    return vm.runInContext(code, sandbox);
    }
    http://requirejs.org
    https://nodejs.org/api/vm.html

    View Slide

  40. apollo-link-umd
    import umdfetch from "@n26/universal-umd-fetch";
    import { ApolloLink, /* … */ } from "apollo-link";
    class ApolloLinkUmd extends ApolloLink {
    public request(
    operation: Operation,
    forward?: NextLink
    ): Observable | null {
    // If directive `@query` matches, use umdfetch:
    return umdfetch(url);
    }
    }
    https://www.apollographql.com/docs/link/overview.html

    View Slide

  41. Summary
    React components can be easily loaded over
    the network
    UMD (and AMD) modules are still useful
    Apollo can load (almost) anything with GraphQL

    View Slide

  42. MICRO FRONTEND
    ARCHITECTURE

    View Slide

  43. Dealing with legacy
    REST APIs
    apollo-link-rest
    GraphQL gateway

    View Slide

  44. Apollo link
    A link is a function that takes an operation and
    returns an observable.
    Links can be chained together between the
    client and the server.
    https://www.apollographql.com/docs/link/overview.html

    View Slide

  45. apollo-link-rest
    import { ApolloClient } from "apollo-client";
    import { InMemoryCache } from "apollo-cache-inmemory";
    import { RestLink } from "apollo-link-rest";
    const restLink = new RestLink({ uri: "https://backend.com/api" });
    const client = new ApolloClient({
    link: restLink,
    cache: new InMemoryCache()
    });
    query tagsQuery {
    tags @rest(type: "[Tag]", path: "/tags", endpoint: "v2") {
    tag_name
    posts @type(name: "Post") {
    post_name
    }
    }
    }
    https://www.apollographql.com/docs/link/links/rest.html

    View Slide

  46. GraphQL gateway
    A monolithic web
    service
    Connects to multiple
    REST APIs

    View Slide

  47. Apollo server & data sources
    const { RESTDataSource } = require("apollo-datasource-rest");
    class MoviesAPI extends RESTDataSource {
    constructor() {
    super();
    this.baseURL = "https://movies-api.example.com";
    }
    async getMovie(id) {
    return this.get(`/movies/${id}`);
    }
    async getMostViewedMovies(limit = 10) {
    const data = await this.get("movies", {
    per_page: limit,
    order_by: "most_viewed"
    });
    return data.results;
    }
    }

    View Slide

  48. Schema stitching and
    namespaces
    Independent GraphQL
    schemas are merged
    into one
    Schema type names
    may collide

    View Slide

  49. No namespaces in GraphQL
    https://github.com/facebook/graphql/issues/163

    View Slide

  50. graphql-tools + schema-
    transforms
    declare function transformSchema(
    targetSchema: GraphQLSchema,
    transforms: Array
    ): GraphQLSchema;
    interface Transform {
    transformSchema?: (schema: GraphQLSchema) => GraphQLSchema;
    transformRequest?: (request: Request) => Request;
    transformResult?: (result: Result) => Result;
    }
    modify types
    modify root fields (Query, Mutation)
    https://www.apollographql.com/docs/graphql-tools/schema-transforms

    View Slide

  51. Authorization
    https://graphql.org/learn/authorization
    Delegate authorization logic to the
    business logic layer
    - Facebook

    View Slide

  52. Centralized authorization
    How can we centralize authorization logic but
    decentralize authorization information?

    View Slide

  53. GraphQL directives
    directive @auth(
    permission: String
    ) on OBJECT | FIELD_DEFINITION
    type Mutation {
    writeData(input: MyInput!): MyOutput!
    @auth(permission: "data:write")
    }
    We can handle authorization in the directive implementation
    Permissions are declared in the GraphQL schema
    https://graphql.org/learn/queries/#directives
    https://www.apollographql.com/docs/graphql-tools/schema-
    directives.html#Enforcing-access-permissions

    View Slide

  54. Not so fast…
    Directives cannot be read through introspection
    https://github.com/facebook/graphql/issues/300

    View Slide

  55. …but we can just use plain
    descriptions instead ♂
    type Mutation {
    "@auth permission: 'data:write'"
    writeData(input: MyInput!): MyOutput!
    }

    View Slide

  56. Summary
    Micro frontends and micro backends can exist
    together
    We can deal with REST APIs
    Namespacing and authorization can be
    handled

    View Slide

  57. Thanks!
    speakerdeck.com/mfellner/distributed-web-
    applications-with-graphql
    maximilianfellner.eu

    View Slide