Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

A bit of history A bit of history 3 . 1

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Client Side Rendering Client Side Rendering 3 . 4

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Server Side Rendering Server Side Rendering 4 . 1

Slide 11

Slide 11 text

The big picture The big picture

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

4 . 11

Slide 21

Slide 21 text

Some examples Some examples 5 . 1

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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(/

Slide 25

Slide 25 text

Naive example (3/4) Naive example (3/4) Add placeholders in the index.html You need to enable JavaScript to run this app. -
+
__SSR__
+ + window.__PRELOADED_STATE__ = {}; +

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Vue.js Vue.js SSR is o cially supported is pure gold ssr.vuejs.org export default { asyncData ({ store }) { return store.dispatch('getUsers'); } } 5 . 9

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Some lessons learnt Some lessons learnt 6 . 1

Slide 32

Slide 32 text

6 . 2

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

New fun bugs New fun bugs 6 . 9

Slide 40

Slide 40 text

Example Example 6 . 10

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

So what? So what? 7 . 1

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Thank you to my awesome team! 7 . 4

Slide 52

Slide 52 text

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