Building Universal JavaScript Applications

Jack Franklin speaking at Front-End London in May, 2016
1232Views
 
Welcome to Sessions!
Want the latest videos in your inbox?

About this talk

The main criticism of single page applications has always been their reliance on JavaScript but recently we've seen a focus in running these client side applications on the server. In this talk Jack discusses why and how you can run your client side JavaScript application on the server.


Transcript


[00:00:12] Hey, everyone. Thank you for the intro, Andy, and for having me for the third time here, I think, and an epic fail as well, so I don’t know why he’s still putting up with me, but he keeps letting me come along and I keep talking about JavaScript. Hopefully I’m not boring people too much. My name is Jack, that’s me on Twitter. I work for a company called “Pusher”. I’m interested, who’s heard of Pusher. That’s pretty good; that’s my job done for the evening. Those who haven’t, we’re like a hosted real-time platform. Come and chat to me about Pusher, I’m not here to talk about them. What I am here to talk about is actually my old company. One of my colleagues whom is sat in the audience. I just work for a company called, “Go Cardless”. Who’s heard of Go Cardless? It’s not going to be every slide. Okay, more people have heard of Go Cardless, so I’ve made a dreadful career move, but anyway, so this whole story started and the reason this talk came about was we had to rebuild this website. Go Cardless is a payment provider and at the time we were based purely in the UK and we only served UK businesses. That was fine. The Go Cardless, what I would call “the splash pages”, the static site, was a jackal build. It was all in English because we only served the UK. That was easy. We just generated a HTML and just threw it onto the internet. [00:01:16] Then we launched in France and we needed a French version, so we needed actual French pages. We did what every good developer does and copied the folder, made another one called “France” and got the people who speak French in the Office, which is not me, to make the French version. That kind of worked, just about. Duplication is okay once and all the rest of it. Then we needed to launch in – I can’t remember – Germany, wherever it was. Another language basically. We couldn’t quite bring ourselves to do it three times; although, it was quite tempting. We started thinking about what else we might instead do. We needed to build a framework to let us manage all of these multiple languages on the site. Additionally, this site, this is the Whole Go colour site, so there’s all about our product but there’s also things like the jobs pages about us. We needed not just engineers to be able to edit this stuff. Ideally, this should be as well across the whole company. At the time, there was this little library called “React” floating around and we used this as a bit of an excuse to see if we could build a React application that would run all of the splash pages for Go Cardless. [00:02:22] We’d have a French version and so on and so forth, but we built is as sort of a JavaScript framework, if you like. Of course, we didn’t want Go Cardless to be a massive JavaScript application that wasn’t assessable. There was no need for it to be a JavaScript application, though these days everyone is doing that. We started to look at how we could write React on the server as well. React does come out of the box with server-side support. That means you can run React in a node environment and just get it to spit out strings of HTML for you. That’s really where we started from. The website launched. I can’t remember how long ago it launched. It was quite a while ago. It’s been out a while. I’ve not been there a while but I’m led to believe it’s still doing a good job, and still plugging along. It’s a really nice experience because if you visit it without JavaScript, you get the website because it comes from the server. If you happen to have JavaScript loaded, for whatever reason, then you get the full JavaScript experience. It’s all loaded in the page so every click is incredibly snappy because you’re just swapping out bits of content, basically, using React. If you load it and the you lose connection or your JavaScript doesn’t load, you click a button, it just goes back to the server again. [00:03:25] It’s a really nice experience for all, really, and why we really bought into it and why I bought into it. I’ll link to all these at the end but I wrote a blog post for 24 ways last Christmas on Universal React and then from the blog post the talk came about. There are a few reasons why building JavaScript applications that are 100% reliant on JavaScript is a bad idea. I used to work at GDS as well, along with Lawn Sharon, and we did this study – I saw we, someone in the company, but if I say “we” if sounds like I was involved – did this study. On the GovUK website, one in 93 or 1.1% of people weren’t getting JavaScript, so when they visited GovUK, for whatever reason, we didn’t have any JavaScript running. What’s more interesting than that stat is that of those 1.1%, only 0.2% were people who had explicitly gone into their browser and configured JavaScript to be turned off. The rest hadn’t got JavaScript for any number of reasons: their connectivity was bad, whatever, they were on a train, they went in a tunnel, they were on a national rail train – whatever it might be. This was really interesting for me because I did used to have this gung ho attitude of, “Well, if people have turned off JavaScript, then they don’t deserve anything.” I still think that might be a little bit true, but most people who are visiting without JavaScript didn’t actually have it in the first place. Not out of choice because of their certain situation. [00:04:52] This blog post includes that quote. Only a small slice of people who don’t run JavaScript are the ones who have chosen not to. This comes down to the whole progressive enhancement and does it just mean if JavaScript is turned off or not. Jake Archibald in this blog post about progressive enhancement says it’s never been about people who have turned JavaScript off, these greens by the way are all links and I’ll put the slides up afterwards. A guy called “Steward Language” did this really cool float chart, which, gain, is linked to at the bottom, which goes through the steps taken from a user hitting Gocardless.com and it downloading all the files and all the points, for some reason, are requested and therefore, JavaScript won’t make it to the end user on a train. There was an instance where Sky, they decided that JQuery, I think the Google CDN was a virus and blocked JQuery. Again, it might be the best decision they’ve ever made, but suddenly a load of these websites broke completely because Sky made a mistake. We’ve had browser add-ons can mess with JavaScript. There are so many different circumstances in which your JavaScript can partially execute, not execute at all, be a bit, or whatever. It’s really important that we deal with that and bear that in mind. [00:06:00] A really recent talk from Google IO, which I’ve stupidly not put the link to, so you’ll have to copy it from the screenshot. Yes, that was a bit silly of me. This is Addy Osmani talking about progressive web apps across all frameworks. This was literally last week. I just want to put this in there and I’ll mention this again in a minute, whilst I’m going to talk within the context of a React application, because that’s what I know and have worked on, this general thinking is spreading across all frameworks as well. Even if you personally are not using React, if you’re using something else, I hope that the talk can still be relevant to you. Another thing, just to be clear, I’m definitely not here to advocate building all websites and applications as React applications that are run on this server and the client as well. You have to think about if this is the right fit for you. Go Cardless could easily be a server-side application only and that would be fine. We found that with a little bit of JavaScript we could really improve the experience. This isn’t a talk where I’m going to advocate React for absolutely everything. Also, caveats, we’re just starting to really get deep into the nitty gritty of server-side rendering and frameworks and how this plays nicely. There is a bit of rough edges. I’m going to throw some code at you. Obviously, all of the slides will be online. There’s also a GitHub repo that has an actual working example. Some of the APIs and stuff are a little bit rough. These will probably change over time and be outdated in six months or a year or a day. Just bear with me. Some of the stuff is a bit rough around the edges but we are really at the boundaries of this stuff. [00:07:27] As I said, React paved the way but we have Ember, have a thing called “Fast Boot” which is the same idea. You can run your Ember app in a node environment. Angular two has Angular universal, which is a similar idea too, that you can run these apps away from the browser. The whole idea here with all of these frameworks is that we can get this not for free but with very little effort. The goal here is that you can write your React or Ember or Angular or whatever app and just put a small amount of extra effort in to get that server-side rendering for you. Just to give you a link, again, I’ll Tweet the slides and hashtag it and put it in the Slack group and all the rest of it. There is a repo on GitHub, there is a branch called “FEL” because I’ve done this talk about four times, that has the latest version of all the demos and stuff on them. If we take a look at a standard React application, in depth knowledge of React definitely not required for the talk, but we have a component called “My App” and it has a render method and in that render method I declare what this component should look like when it’s put into the browser. Then we call React.DOM render, we give it the component to render. This is my app tag here and then we just tell it where to put it onto the page. In this case, it was going to render that My App component and put it into the div on the page with an ID of App. That’s the browser-side. If I want to swap this to a server-side rendered app, I just swap out the react.DOM bit to render to string. [00:08:47] That instead of trying to rending into a DOM, into a browser, will just give me a literal string of HTML. That’s it. Talk’s over. It’s really cool that React gives you all that out of the box and it is that simple. The key thing to note here is that the My App bit never changes and that’s the thing that I want you to keep in your heads as we go through. If you start building apps in this way, as little of your application as possible should know whether it’s on the server or the client or whatnot. Ideally, we keep it entirely agnostic and only at the very boundaries do we change where we need to render it to. Of course, there’s a little bit more involved in that. We can setup a little express node server, which just imports this app component. We pull in the render to string method and then on any request we just generate our mark up and we render. This just renders a template and just renders the HTML. The details of that aren’t too important. That’s it. That will spin up a server that does indeed work. In some examples you’ll see this method, render to string used. There’s also another method called, “render to static mark-up” that React provides for us. There’s a subtle difference between the two. You should use render to string when you’re expecting the HTML that is rendered to be picked up by React applications on the client. Render to string will include some kind of extra attributes in the HTML that will allow the client-side JavaScript to pick it up and work with it. [00:10:08] If you know that’s not going to be the case, you’re purely working on the server-side, you’re not going to run client-side JavaScript for whatever reason, you can just call render to static mark-up. That will give you lean mark-up, if you like, that doesn’t have any of the specific React attributes. If I take this server and I run it, I just get back this. I think actually this, with the latest version of React, some of these nitty gritty details have changed, but the idea is the same. This is generated purely from React. That’s a very basic server-side example but one of the benefits we found at Go Cardless was that we could then take all of this code and push it onto the client as well. Those people who did have JavaScript would get just a little bit more of a snappy experience. If we start looking at how we can take this client-side, we’re going to have a shared set of components that are environment agnostic. This is all our React components. We’ll have a server rendering step, just that little JavaScript file saw, that will run on our server. We’ll have a similar file that runs on the client, which we’ll look at in a minute and we need some form of bundler, like a Web Pack or a Browserify type thing that can take all of our JavaScript and create a browser version of it as well. The real detail here is the agnostic components. Your React components shouldn’t know whether they’re on the server or the client. You of course can check on either the typically checkers to check if type of window in undefined. If you’re on the server or the client, but you should try to do that as little as possible and try and keep all of your components plain and without knowledge. [00:11:46] We are going to use Web Pack, some people love it; some people hate it, but in the example I have we use Web Pack to bundle, but I’m not going to go into too detail on that. The example in the GitHub repo has all this stuff setup so you can have a look through it. If we look at rendering on the client, the first thing we need to do is update the actual template that we’re using from the server. My server-side template looks something like this. The mark-up here, that’s going to be the thing that’s generated by React on the server and this tag will be swapped out by the HTML we generated. Build.js will be the client-side bundle that’s pulled in through Web Pack. To create my client.js file, all you have to do is write this as if there was no server-side rending ever. You just write this as if you’re only building a JavaScript on the browser. Again, I import what I need, I import my app and I call react.DOM render. React.DOM’s render method is really, really clever. If it calls render and the stuff inside this div looks like the same React app, it knows that and it will not completely re-render your entire application. We’re not getting the thing where we render from the server, the JavaScript loads and we then get a big flash of white as the React app re-renders itself. It’s clever enough to pick up and know where it is, so it doesn’t have to do that. The important thing here, as I said, I’m going to keep harping on about it, we have the same components on the clients and the server. This My App component is the same one on both sides. Very briefly, look at how we can configure the client-side build. You’ll notice I’m using quite a lot of ES 2015 features and so on and also the JS X HTML and NEO JavaScript syntax that React provides. We install a bunch of things, Web Pack and Bable and Bable something else and Bable something and Bable something else. You keep guessing what you need to install and you write random config files that might work and they might not work. They’ll be out of date next week and who knows? [00:13:23] Eventually, then you’ll write some more, but eventually, once you get through all this stuff, you’re able to run Web Pack or whatever you’ve chosen and get this build out. It’s worth knowing as well, I’m not going to touch upon how in production I would like Minify this and all the rest of it, but this build, as you’ll notice, 690 KB, this is for a hello world React application. This is incredibly optimised. This would be way smaller if I actually bothered to do a proper demo and wasn’t such a shoddy speaker. We’ve got this far but so far the components we’ve built have just been like hello world and there’s not much too it. If you’re building an application for real, you’ve probably gone a bit over the top doing all this just to render hello world on to the screen. If we imagine a more interactive component, this component might be a little bit small but basically, it’s a button you can click a button and it increments a number by one and shows it on the page. It looks somewhat like that, so I can click and we get the count. If I then disable JavaScript, I can refresh and although I still get something, so although I can’t click obviously because we have no JavaScript, we can still get something on to the page. Now, I’m an expert at contrived examples and this is an incredibly contrived example, but what I’m trying to illustrate here is that a read only experience is way better than no experience for users. This is – I don’t think many of us are building that application in production, this is kind of analogous is you think of an app that pulls from an API and puts some data on the screen and then lets you click a button to say delete one of those entries or whatever it might be. You might not be able to replicate all of that without JavaScript enabled. You might be able to with a bit more work, but even if you can just get your list of data rendered without JavaScript, that’s way better than getting a blank screen or an error instead. [00:15:03] Whilst ideally we are striving for 100% functionality without JavaScript, in reality, sometimes we can’t quite get there. That’s absolutely fine. Even the read only view is going to be way better than absolutely nothing. Now, if we’re going to start building a proper application, like, say Go Cardless, the one thing we do need to look at is routing. If you’re building just client-side applications, you can get away with not bothering with URLs; although, you definitely shouldn’t. I think we’ve all taken a URL from a JavaScript app and sent it to a friend and they’ve clicked that URL and got a completely different page because the developer hasn’t kept the URL in sync. Obviously, on the server-side we have to setup URLs. The facto standard solution for this in React is called, “React router”. It might as well be built into React at this point. It’s very much the winner, if you like, it’s a very good library. The first thing I’m going to do is actually build my proper app. I’m going to have an app.js component and this is my top level component. I’ll have my nice heading and then this .props.children is where the nested routes will go. This is my layout file, if you like, and this is just a placeholder for whichever route we’re currently on. Its content will be put there. Again, the nitty gritty details of routing and React components aren’t too important here. Then create an index page, which just returns us to the index page. I then define my routes. Again, I have this top route called, “App component” and this path/, I have this index component. [00:16:39] Now, on the server-side, what we need to do is take the current URL that’s just been requested, see if we have a route that matches and render the correct components. Now, the API here is a little bit hairy and it looks, trust me, it does look worse on a projector than it actually is if you were to actually write it yourself. Please, stick with me. The other thing is that you’d only ever need to write this once and then you can pretend it doesn’t exist, so please bear with me. We need a couple of things from React router. We need this match function, which is a function that can take a URL and all of our routes and decide if they match up. Then router context is the React component that we need to render that will deal with rendering everything else. The overall code looks like that – easy. Okay, next. What we’ll do is we’ll break that down a little bit. The first thing I’ll do is I call match, I give it all our routes and I tell it the location that if using the express on the server is the request.url property. That will do that asynchronously. We then have a call-back function which takes an error, a redirect location and render properties. If we’ve got an error, that means something went wrong and we should probably return a 500 or something similar or return the fail whale type page. If we get a redirect location, that means we’ve actually hit a route that is a redirect because React router supports redirect. Finally, if we don’t have an error and we don’t have a redirect, then we have these render props, which means we’ve got all the data we need to render and generate our HTML. Render props is basically all the information the router needs to generate the HTML and render the components. [00:18:06] Firstly, if there’s an error, I just send the error message with a 500. You would probably want to do something a bit more polished for your app. Else, if we have a redirect, I’ll just redirect with a 302. Finally, if we’ve got this far we can actually render successfully. I’ll render my index template again and I’ll call render to string and I have to use this router context component and pass on all the properties. This isn’t really important; this is basically just saying: if we get to this stage, I’ve got all the information I need to render and generate your HTML for you, and this just does it. Finally, if we get passed and we get to this last bit, not really sure what happens, let’s just 404. Again, you’d probably fail whale or you’d log this so you’d be aware of it as the developer working on it. This actually completely works. It’s pretty dull but it does work with or without JavaScript. Let’s make it a bit more interesting, let’s add an about page. We add an about page with some really dreadful text on it and then we add it in as a route, just down here. Then I’m going to use – React router gives us a link component, which is just like a layer on top of an anchor. The great thing about this is it means in a non-JavaScript environment it will just be an anchor, effectively. In a JavaScript environment, React router will hijack the click and progress you through JavaScript. I’ll add a link to the home and about page. [00:19:25] With no client-side bundles, this is all server-side. This does work. I can click between them, I get the right content rendered, and as a user I’m none the wiser. Nice white flash at the end. I’m none the wiser that something is wrong, I don’t have JavaScript enabled or something’s gone wrong with my request or whatever it might be. We can update the client-side generator as well or client-side JavaScript. I just call React.dom.render and I would just render this router component, which comes from React router. I can talk in much more detail about React router, there are lots of tutorials online, so I won’t go into too much of what’s going on here. Then if I regenerate Web Pack and I visit it with JavaScript turned on. It’s a bit unclear to see but these clients now aren’t going to the server, they’re just all in my browser. Note as well that React router is great and deals with things like forwards and back and it all works for us, as you would expect. I don’t think I included it but this also works without JavaScript as well. We’re at this point now where the application works pretty seamlessly between JavaScript and non-JavaScript. [00:20:28] The final step is dealing with data on the server and on the client. A lot of React applications or JavaScript applications generally will make a request to some API, get some data in probably JSON and then render that in some form and let the user manipulate it. This is actually something we didn’t have to tackle at Go Cardless because ours was more of a marketing page, you weren’t hitting so many APIs. I’ve delved into this a little bit more since. This area, if you think server-side rendering is a bit more of a work in progress, this is particularly work in progress. There are a number of different approaches to this and thoughts going on. There are lots of unknowns here. I think better solutions or improved solutions will reveal themselves over time, but for now this is what I’ve come up with. The general idea here is that we want to be able to fetch data from some API on the server and on the client. If I don’t have JavaScript, that request should happen on the server and we should get the results of it, fetching that data and rendering it into my HTML. However, if I do have JavaScript enabled, the server should still make the request but then when I come back into my browser and everything is rendered, I don’t want the browser to then remake that same request within a second of the server doing it. That’s just a complete waste of time. [00:21:38] What we really want to do is avoid making the request again and that’s where this gets a little bit tricky. There are a number of libraries out there, I’ve come to really enjoy one called, “React Resolver”. This, again, is a link. The general idea here is we have a component, so just look at this top bit for now. This component renders some information about someone from GitHub, so I reference this .props, which is properties you can give a component some extra information. .github, which will be the data I’ve got from GitHub and then public repos is one of the keys that GitHub provides you. It just tells you how many repositories you’ve got that are public. What I can then do, down here, I can then use React Resolver, which is where this resolve function comes from to say the GitHub prop that I want to find on this component should be the result of fetching this URL and then returning the data from it. React Resolver lets us basically say, “This components needs this data from some API”. Then on the client-side, what we do is instead of using react.dom.render I use resolver.render. On the server-side, it’s a little bit more involved but I resolve and then we do some extra stuff. Basically, we’re resolving, we use React Router to render everything. The key thing here to now I’ve got this data, I need to spit out my HTML that I’ve just generated that this has generated for me, or sorry, this render to string call here, but I also need to pass the data from any API request down to the client. If I don’t do that, the client can’t reuse that data and is going to have to make the request all over again. [00:23:11] Then my template, I had this extra property called, “React Resolver payload” and that’s going to be any data the server had to fetch from an API. We update the server-side rendering to spit out this payload, we update the client-side rendering to use React Resolver. All data gets resolved on the server by default, it’s rendered and it’s then used to populate the data on the client-side as well. React resolver will set this property to all the data it got. Then when the client runs in the browser, it can see that this exists and it can use data from that rather than having to remake all of those requests to an API. Now, this definitely works with and without JavaScript. See, I’ve not made any extra requests here and I’ve still got over here my GitHub repo count, 201. That is actually coming from the API, you’re going to have to trust me on that. With and without JavaScript we do get the full application experience. I appreciate that that was quite a lot of stuff to throw at you in quite a short space of time. I was quite relieved to see the previous two talks didn’t have lots of code, or else you’d be way fed up of all this by now. That’s a link to GitHub, it’s just that repository, basically. If you have any questions after, I’m on Twitter. I can also be emailed as jack@pusher.com. [00:24:28] Some final closing thoughts, I think universal JavaScript is here to stay. I expect the techniques and libraries and approaches to change over time. If I did this talk in 12 months, if Andy has me back yet again, I’d probably do a very different slide deck and the code would look pretty different, but I think we’re getting there. With React and Ember and Angular and I think View Jess might be working on it. All the rest of them are going in this direction. I’ve no doubt that we’ll improve our approaches and when one framework figures something out, that will all come through to all the others as well. The goal here is as developers, we should be able to get this working with very little effort. That is the direction we’re going in. The fast boot will do it all for you, Angular two, universal should do a lot of it for you. As you’ve seen React does make it pretty straightforward too. Long term, I’d love to see frameworks to server-side by default and it be the baseline that a new JavaScript framework of which there are already ten or so out since I started, will be server-side by default and just work. Hopefully, we’ll get somewhere towards that soon. Go Cardless was a site that we used it on. There are loads of sites now using it as well. I think Packed Coffee we’ve probably all come across, they use the same approach too and they’ve had a lot of success from that. Loads of people are doing this, so they’ll be a lot of advancement in this area. [00:25:48] Final links. That’s the blog post on 24 ways. That’s a blog post by my former colleague Jamie on how we built the new Go Cardless website from whenever it was. The actual source code to the Go Cardless site is entirely freely available. At least it was. I’m pretty sure it still is. That’s a link to that. Then the example application on GitHub as well. It wouldn’t be talk or a meetup if I didn’t do a plug. If you want to do more React or you’d like to learn more about it, I’m doing day-long workshops in London. Sorry, a day long workshop in London. There are still tickets available. It’s on the 10th of June, so it’s not too long away. It is south of the river. It is in Elephant and Castle, but if you can bear that there are tickets available. It wasn’t my choice of venue. Thank you very much for listening. I hope that was useful. I’m Jack Franklin, javascriptplayground.com is my blog. I will put the slides onto Speaker Deck, so you can find me there. If you have any questions when you wake up tomorrow morning about React, life, or anything else or Raffa Bonita staying at New Castle, jack@pusher.com is where you’ll find me. Thank you very much for listening.