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

Server Side Rendering from the trenches

Server Side Rendering from the trenches

Server Side Rendering (SSR) is a technique to reuse client-side JavaScript code on the server. The main advantage is to speed up the first rendering time and improve the user experience on page load. It is considered one of the most complicated features to implement in the JS ecosystem. Yet, web apps with a high traffic often need it. In this talk, I’ll start by introducing the concept of SSR, followed by examples with React, Vue and some other frameworks (like Next or Nuxt). The last part will be dedicated to what I've learned building the new Mozilla Add-ons frontend.

Online slides: http://slides.williamdurand.fr/ssr-from-the-trenches/

William Durand

May 10, 2018
Tweet

More Decks by William Durand

Other Decks in Programming

Transcript

  1. Server Side Rendering
    Server Side Rendering
    from the trenches
    from the trenches
    William Durand, jsDay 2018
    1

    View full-size slide



  2. I am going to talk about server side rendering
    applied to JavaScript applications and that are
    usually rendered in web browsers, a.k.a.
    universal/isomorphic apps.
    2

    View full-size slide

  3. A bit of history
    A bit of history
    3 . 1

    View full-size slide

  4. Before 2010
    Before 2010
    Server side frameworks
    Template engines
    jQuery, Yahoo UI
    script.aculo.us ❤
    XMLHttpRequest
    3 . 2

    View full-size slide

  5. 2011
    2011
    Backbone.js is 3 months old
    Node.js is 2 years old
    People read Fielding's dissertation (REST)
    Let's write client side applications in JavaScript!
    Let's write client side applications in JavaScript!
    3 . 3

    View full-size slide

  6. Client Side Rendering
    Client Side Rendering
    3 . 4

    View full-size slide

  7. 2013
    2013
    React initial release
    Interesting, but what is this Flux architecture again?
    Interesting, but what is this Flux architecture again?
    3 . 5

    View full-size slide

  8. 2015
    2015
    Vue.js is 1 year old
    Redux initial release
    Problem(s) solved
    Problem(s) solved
    3 . 6

    View full-size slide

  9. Since then...
    Since then...
    “Can we render this JavaScript app on the server?”
    3 . 7

    View full-size slide

  10. Server Side Rendering
    Server Side Rendering
    4 . 1

    View full-size slide

  11. The big picture
    The big picture

    View full-size slide

  12. How it works
    How it works
    On every incoming request, the server:
    1. creates the store/initial app state
    2. matches the URL to nd the right component
    3. loads the component and gets the HTML
    4. sends the HTML back to the client
    Then, the client loads the JavaScript app.
    4 . 3

    View full-size slide

  13. Bene ts?
    Bene ts?
    Accessibility (limited)
    Better performances ( rst meaningful paint)
    Better user experience (JS disabled)
    Search Engine Optimization/Social sharing
    4 . 4

    View full-size slide

  14. Googlebot
    Googlebot
    Quite good at browsing JS apps
    Give up after ~10 seconds
    Some issues with react-router
    Source: , Nov. 2016.
    Testing a React-driven website’s SEO using “Fetch as Google”
    4 . 5

    View full-size slide

  15. Drawbacks?
    Drawbacks?
    Makes everything very complicated
    Time To First Byte (TTFB) usually slower (but
    Cloudfare says it's ne in [1])
    React renderToString() holds the event loop
    [2], probably also the case for other frameworks
    [1]:
    [2]:
    Stop worrying about Time To First Byte (TTFB)
    The Bene ts of Server Side Rendering Over Client Side Rendering
    4 . 6

    View full-size slide

  16. Why is it so complicated?
    Why is it so complicated?
    1. Two di erent environments, one codebase
    2. Cookies, redirects/errors, HTTP statuses
    3. Data fetching before rendering
    4 . 7

    View full-size slide

  17. 1. T
    wo di erent environments,
    1. T
    wo di erent environments,
    one codebase
    one codebase
    + isomorphic libraries and poly lls
    4 . 8

    View full-size slide

  18. 2. Cookies, redirects/errors,
    2. Cookies, redirects/errors,
    HTTP statuses
    HTTP statuses
    You have to nd nice tricks
    hacks
    4 . 9

    View full-size slide

  19. 3. Data fetching
    3. Data fetching before
    before rendering
    rendering
    There used to be two approaches...
    static async method to fetch data and
    Promise.all() on the server
    double render
    4 . 10

    View full-size slide

  20. Some examples
    Some examples
    5 . 1

    View full-size slide

  21. React
    React
    ReactDOMServer.renderToString()
    That's all folks!
    5 . 2

    View full-size slide

  22. Naive example (1/4)
    Naive example (1/4)
    Load the application
    // server.js (express app)
    app.use((req, res) => {
    const context = {};
    // 1. Create the store.
    const store = configureStore();
    // 2. Render the application using a `StaticRouter`.
    const markup = renderToString(





    );
    // see next slide...
    5 . 3

    View full-size slide

  23. Naive example (2/4)
    Naive example (2/4)
    Send the full HTML to the client
    if (context.url) {
    redirect(301, context.url); // A `` was rendered.
    } else {
    // 3. Replace placeholders by generated state and HTML.
    const preloadedState = store.getState();
    const html = INDEX_HTML
    .replace('__SSR__', markup)
    .replace('__PRELOADED_STATE__ = {}', [
    `__PRELOADED_STATE__ =`,
    JSON.stringify(preloadedState).replace(/].join(' '));
    }
    res.send(html); // 4. Send the HTML to the client.
    });
    });
    5 . 4

    View full-size slide

  24. Naive example (3/4)
    Naive example (3/4)
    Add placeholders in the index.html


    You need to enable JavaScript to run this app.

    -
    + __SSR__
    + <br/>+ window.__PRELOADED_STATE__ = {};<br/>+

    View full-size slide

  25. Naive example (4/4)
    Naive example (4/4)
    Use the state generated on the server, if any
    // src/index.js
    -const store = configureStore();
    +const preloadedState = window.__PRELOADED_STATE__ || {};
    +// Allow the passed state to be garbage-collected
    +delete window.__PRELOADED_STATE__;
    +
    +const store = configureStore(preloadedState);
    5 . 6

    View full-size slide

  26. But...
    But...
    No data fetching
    No error handling
    5 . 7

    View full-size slide

  27. Next.js
    Next.js
    Powerful React-based , SSR-ready.
    framework
    class UsersList extends React.Component {
    static async getInitialProps({ store, isServer, ...props }) {
    const users = await getUsers();
    return { users };
    }
    render() {
    const { users } = this.props;
    // ...
    }
    }
    5 . 8

    View full-size slide

  28. Vue.js
    Vue.js
    SSR is o cially supported
    is pure gold
    ssr.vuejs.org


    <br/>export default {<br/>asyncData ({ store }) {<br/>return store.dispatch('getUsers');<br/>}<br/>}<br/>
    5 . 9

    View full-size slide

  29. Nuxt.js
    Nuxt.js
    Vue-based framework, inspired by Next.js
    Implement what is described in ssr.vuejs.org
    5 . 10

    View full-size slide

  30. Some lessons learnt
    Some lessons learnt
    6 . 1

    View full-size slide

  31. addons.mozilla.org
    addons.mozilla.org
    Universal React/Redux app
    i18n/l10n, CSP
    Open Source: mozilla/addons-frontend
    6 . 3

    View full-size slide

  32. Double render is a fragile
    Double render is a fragile
    hack, do not use it
    hack, do not use it
    6 . 4

    View full-size slide

  33. Always be careful
    Always be careful
    Unde ned reference on the server == Error 500.
    #GameOver
    6 . 5

    View full-size slide

  34. Error handling is tough
    Error handling is tough
    Accurate HTTP status codes
    Correct error pages
    On both server and client
    6 . 6

    View full-size slide

  35. Debugging made
    Debugging made
    complex
    complex
    Isomorphic logging layer but no dev tools
    disableSSR con g option to the rescue!
    but some issues are hidden
    easy
    easy
    6 . 7

    View full-size slide

  36. Server logs
    Server logs
    [...]
    INFO: proxy: 302 ~> http://127.0.0.1:3333/service-worker.js (app=
    INFO: proxy: 200 ~> https://addons-dev.allizom.org/api/v3/account
    INFO: proxy: 200 ~> https://addons-dev.allizom.org/api/v3/account
    INFO: proxy: 200 ~> https://addons-dev.allizom.org/api/v3/account
    WARN: server: restrictSearchResultsToAppVersion config set; not s
    WARN: server: restrictSearchResultsToAppVersion config set; not s
    WARN: server: restrictSearchResultsToAppVersion config set; not s
    INFO: proxy: 200 ~> https://addons-dev.allizom.org/api/v3/addons/
    INFO: proxy: 200 ~> https://addons-dev.allizom.org/api/v3/addons/
    INFO: proxy: 200 ~> https://addons-dev.allizom.org/api/v3/addons/
    INFO: server: Second component render after sagas have finished (
    INFO: proxy: 200 ~> http://127.0.0.1:3333/en-US/firefox/ (app=amo
    WARN: server: CSP has been disabled from the config (app=amo)
    INFO: server: Prepending lang to URL: en-US (app=amo)
    INFO: server: Prepending application to URL: firefox (app=amo)
    6 . 8

    View full-size slide

  37. New fun bugs
    New fun bugs
    6 . 9

    View full-size slide

  38. Example
    Example
    6 . 10

    View full-size slide

  39. No random allowed
    No random allowed
    There is a .
    React RFC for introducing isomorphic IDs
    6 . 11

    View full-size slide

  40. You must have a fresh,
    You must have a fresh,
    isolated server context
    isolated server context
    6 . 12

    View full-size slide

  41. Example
    Example
    $ repeat 10 curl https://addons.mozilla.org/en-US/firefox/addon/a
    | grep --color -oE 'updated.+?updatedil y a 2 jours (6 nov. 2017)
    updated2 days ago (Nov 6, 2017)
    updated2 days ago (Nov 6, 2017)
    updated2 hari yang lalu (6 Nov 2017)<
    updated2 days ago (Nov 6, 2017)
    updated2 days ago (6 Nov 2017)
    updated2 dias atrás (6 de nov de 2017
    updated2 days ago (Nov 6, 2017)
    updated2 (2017 11 6 )
    updated2 days ago (Nov 6, 2017)
    6 . 13

    View full-size slide

  42. You need more servers
    You need more servers
    In order to reduce the # of 500 caused by the issue, we
    had to bump up the # of instances in the cluster from
    8 to 40. And that eventually stopped the "heap oom"
    from continuously happening.
    6 . 14

    View full-size slide

  43. Security considerations
    Security considerations
    State serialization when transferring the Redux
    state from the server to the client [1]
    Sensitive data on the server, e.g., env vars
    [1]: Redux Server Rendering
    6 . 15

    View full-size slide

  44. React has useful
    React has useful
    dev warnings
    dev warnings
    TL;DR: React on the client does not generate the
    same HTML sent by the server: there is a bug. [1]
    [1]: What’s New With Server-Side Rendering in React 16
    6 . 16

    View full-size slide

  45. We can test (pretty much)
    We can test (pretty much)
    everything
    everything
    6 . 17

    View full-size slide

  46. So what?
    So what?
    7 . 1

    View full-size slide

  47. You may not need SSR.
    You may not need SSR.
    If you need it, use a framework that is SSR-ready.
    7 . 2

    View full-size slide

  48. Other ideas
    Other ideas
    Progressive Web Apps/Service workers?
    Prerender.io
    Headless Chrome: an answer to server-side
    rendering JS sites
    7 . 3

    View full-size slide

  49. Thank you to my awesome team!
    7 . 4

    View full-size slide

  50. Thank You.
    Thank You.
    Questions?
    Questions?
    è
    ¾
    ®
    ¬
    joind.in/talk/b263d
    williamdurand.fr
    github.com/willdurand
    twitter.com/couac
    8

    View full-size slide