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 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 Slide

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

    View Slide

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

    View 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 Slide

  6. Client Side Rendering
    Client Side Rendering
    3 . 4

    View 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 Slide

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

    View Slide

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

    View Slide

  10. Server Side Rendering
    Server Side Rendering
    4 . 1

    View Slide

  11. The big picture
    The big picture

    View 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 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 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 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 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 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 Slide

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

    View 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 Slide

  20. 4 . 11

    View Slide

  21. Some examples
    Some examples
    5 . 1

    View Slide

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

    View Slide

  23. 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 Slide

  24. 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 Slide

  25. 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 Slide

  26. 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 Slide

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

    View Slide

  28. 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 Slide

  29. 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 Slide

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

    View Slide

  31. Some lessons learnt
    Some lessons learnt
    6 . 1

    View Slide

  32. 6 . 2

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. 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 Slide

  38. 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 Slide

  39. New fun bugs
    New fun bugs
    6 . 9

    View Slide

  40. Example
    Example
    6 . 10

    View Slide

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

    View Slide

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

    View Slide

  43. 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 Slide

  44. 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 Slide

  45. 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 Slide

  46. 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 Slide

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

    View Slide

  48. So what?
    So what?
    7 . 1

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide