SURMA: Hello, and welcome to
the Supercharged Livestream. I'm Surma. JAKE: And I'm Jake. SURMA: And today we're
going to Server-side-- JAKE: Service workers. See, Paul couldn't make
it into the office because of the British
train system, so I thought we could have a whole
episode entirely devoted to service workers. PAUL: Hey, I made it, I made it. What are you doing here? JAKE: Oh, because
you were late, I thought we could do this
entire thing all about service workers. PAUL: No, you can't do this,
you keep trying to take over. Can we have one
thing around here that is not about you
and service workers? JAKE: OK, show of hands. Who wants this episode to
be about service workers? Fine. PAUL: Well, that's
rid of him, then. So sorry about the tardiness
on the show in the [INAUDIBLE] front. Yes, so what we doing? SURMA: We are actually going
to do sever side rendering. And we're going to do it
with you and not with Jake. There was a lot of
confusion on Twitter. Was actually very enjoyable. PAUL: Whoops. Yeah, anyway, I did make
it, and that's fine. I'm not doing the coding
today, you're doing the coding. Yeah, it's a bit
of a switch around. I'm going to be on hand to
answer questions and talk to the folks, and I
can already see we got lots of comments coming in. SURMA: Loads of people already. PAUL: So hello to
everybody in the chat. Thank you for joining us. We love to have you
here, so do feel free to keep chatting away. SURMA: And you can
make Paul interrupt me so that I do more mistakes. PAUL: Yes! SURMA: My goal for today is try
to do less typos than you do, and I'm pretty sure
I'm going to fail. Although you do set
the bar pretty high. PAUL: But this is my revenge. I mean, I'm going to be
very supportive throughout. SURMA: Server-side rendering. PAUL: Server-side rendering. OK, what are you doing? SURMA: Server-side rendering. Yeah, I should probably
explain, because this word has been-- or those three words. Two words if you're
using a hyphen. PAUL: Just those words. SURMA: Have been
thrown around a lot. And I feel like there's not a
very consistent understanding of what it actually
means, and so I'm trying to capture it in a few words. Server-side
rendering is what you want to do if you have
a single page app. If you think back to the
first router that we did, we actually used a
little pipe [INAUDIBLE], I think you wrote that
whatever you request, you would get back
the index HTML. So if we requested slash about,
or just home, or slash contact, you would always get
the same index HTML. Which means in that
site there wouldn't be the content that you
actually wanted to read. So you would have to
wait for the JavaScript to spin up, to analyze the
path that has been requested, and swatch swap out
the content in the Dom with the actual content. PAUL: And then we switched it
up to the advanced router, which did actually have individual
pages for like about, contacts, misc, whatever it was. And then we pulled
those in over-- SURMA: But that would mean that
whenever you change something in the layout that you would
have to do the edits in all of these individual pages. PAUL: Right. So presumably what we're
trying to figure today. SURMA: Server-side
running basically still is always
deliver the index HTML, but already inject the content
before serving it to the user. So you render the Dom
on the server side, that is why it's called this way,
and send it to the users so that content's already
there without the JavaScript needing to run at all. And then hydration
happens, where the JavaScript
spins up and takes care of all the dynamic parts. PAUL: Go, go, go. SURMA: So, let's get our hands
dirty and write some code. So we're going to write
note on the back end. We're going to write
a Note web server that does the server-side rendering. So I'm going to make the
script with copyright headers so that we can run JavaScript
just with executing the script. So in the folder here
I have an app folder where basically our
advanced router code is in, the thing that we wrote
two sessions ago, I think? It's exactly the same, not
much anything has been done. And for this one I decided
that I want to use Express. Express is just a little
bit of convenience about Note's native web server,
so that we have a simple web server. And you use it by
creating an Express app. And this app object is really
going to do all the routes. PAUL: So you going
to MPN install? SURMA: Yeah, I have the
modules in here with Express, and all the other things
I plan to be using. I'll explain as I go along
what I'm actually using. So we want to start
with something simple, we're just going to write
a website that it's just a straight web server. It does nothing else. So what we can do
there is we want to say that our app
uses the middleware that is called static. So what this line does,
it just says to Express, just serve the app
folder as static content. [INAUDIBLE] set up web server. And then we're going to
use Note's HTTP server, create server that
serves our app. And we make it listen on 8080. PAUL: Have you got this
all committed in your head? SURMA: Well, that is what
Note excels at, right? Very simple code that you can-- PAUL: Well, bravo. Well done, it's very good. SURMA: So let's do this. PAUL: Who doesn't love
a bit of chmodding? SURMA: Chmodding. PAUL: Plus X, that makes
it executable, right? Because you added
that line to the top so that it's actually
a thing that can run? SURMA: Then if I do
this, hopefully-- Nope. PAUL: Can't find module Express. SURMA: Well that's good. It's in there though, right? No, it's not. That's like the only
module I forgot to install. So let's do some
good old NPM install. PAUL: Oh, that we have to do
the install dance while we wait for it to go. [HUMMING] SURMA: But Express install is
already done, because Express is not that big. PAUL: Oh, I was only part way
through the install dance. SURMA: He does have a very
elaborate install dance. PAUL: Fine. So we got a question here. Why not use import
express from Express? Why are you doing the old
require that rather than anything newer? SURMA: So the thing is
that modules, for me, are still a little bit iffy. It's just something,
especially on the NPM side, it doesn't feel as right. I'm not even sure if Note
4, which is what I'm using, already supports
the import syntax. I'm not sure if we would
need an enable in between, I don't know. And also, imports are
declarative and not imperative. And sometimes, you do want
to have, like I did here, you do want to have
the imperative way. PAUL: OK, like grab this
thing, and then do this thing. SURMA: Exactly. So for now I'm just
sticking to require until the entire
JavaScript ecosystem sorts out how to do modules. That's just for me
to keep me sane. So. PAUL: Well that's a start,
it's actually working. SURMA: There's no
error, at least. That's good. So if you go to a local
host, there it is. Oh, this is actually
a little bit crammed. PAUL: Can you do
the-- oh, there we go. I can feel the tension
as you're trying to-- don't, get it right! OK. I like how you've selected
them all as well, by accident. It feels good. SURMA: So I want to
see the protocols roll. PAUL: Ah, that ruined it. Why don't you just make
[INAUDIBLE] a bit bigger, dude? SURMA: Oh, I could. PAUL: Hate to be 1.1. OK, there you go. SURMA: So we got this. And now what I want it to do is
I want it to port this to HB2. Because, you know, this is the
future and we should do this. But for that, we need
TLF certificates. So because I forgot
to download it, I will go to Google
Chrome simple HB2 server. Because not only is it
a simple HB2 server, but it will also allow us
to generate certificates. So I'm just going to
download the Darwin version. PAUL: Is that going
to go to your-- SURMA: Which should be here. There we go. And then you can download
simple, boom, boom. And now we have a
certificate, which we can use. And I'm going to
show you how exactly. PAUL: So if anybody has
not seen this before, you built like the
Python simple server, but it's the built and go. And it's an HTTP2 server, so
it has to be running on HTTPS. So you get a
self-signed certificate that it makes for you. And then you're basically,
wherever you run it from, whichever folder you run it
from, treat that as the folder that you're serving from. SURMA: Exactly. And I just use it for the
ability generate a certificate. Because using the open
s all command line tools is so painful, which is
the original motivation to write simple HB2 server, to
not have to do that manually anymore. PAUL: So what you're using it
for, you want the certificate, so now you're going
to use them here. SURMA: Exactly. So what we can do
exactly is that we are going to use require FS,
and do FS read file sync. We're going to do synchronous,
because I'm dirty like that. And we're going to use
the key and the cert, read file sync cert.pem. And now what we
should be able to do is just to turn over to HTTPS
and add the options parameter to our create server, and now
it should actually be HTTPS so let's try that. No, error, that's good. We can close this. Let's add this. PAUL: So we get this,
which is basically, because it's a
self-signed certificate, Chrome is going to say-- SURMA: It's not actually secure. It is secure in
terms of encryption, but not authenticity. It can't be sure
that we're actually talking to the right server. But since you're
on a local host, we have the option to say, you
know, I know what I'm doing, let's proceed. And as you can see, we
have HTTPS with a red flag, because it's not
authentic, but it works. PAUL: So now we're still
serving on HTTP1, is that right? SURMA: Yes, because I
just switched to HTTPS. Now ideally, what I would
like to do is using HTTP2. However, Express
currently doesn't work with the HTTP module,
because the HTTP2 and PM module is still very
much in development. What we can use, however,
is the speedy module. Which, interestingly enough,
does actually serve HTTP2, you just don't have
the option to push. So it is a valid HTTP server. And since you're not going
to be pushing today-- oh, I have to restart. [PANTING] PAUL: It's just going
to work like this. SURMA: Actually, no it isn't. Did I save? Let's restart it
just to make sure. Hm, this is interesting. Options, worked on TOS. Let's just give HTTP2
a try, just to-- I'm not sure I have it
installed, actually. I have it installed, apparently. Let's try it. PAUL: Oh, something very
strange is going on, isn't it? SURMA: This is already strange. PAUL: Why don'y you try a
different port entirely? SURMA: Yeah, let's
try a different port. Let's try 8081. Let's restart that, go to here. PAUL: Oh, interesting. SURMA: That is interesting. PAUL: So the question is,
why do we not get HTTP2? SURMA: So we are
reading the certificate. I mean, there's not much code
that I could do something wrong here, honestly. PAUL: No, there isn't. SURMA: I'm passing
in the options, I'm passing in the app. PAUL: Hey, well,
do you know what? I feel better that it's not
just me that has the bugs. SURMA: This is what I expected,
I expected us to-- I am also in the right folder, right? PAUL: As far as I can see. SURMA: Yeah, there's
speedy, everything. And at the very least,
it should be speedy. See what I did there? Should be speedy. Anyway. PAUL: Should be speedy. SURMA: I'm just going to
skip over this for now. PAUL: OK, we'll come back to it. SURMA: We'll probably
get back to it. So let's take with HTTP1. PAUL: Somebody's suggesting
it might be the caching. SURMA: I did do a hard refresh. PAUL: Yeah, I saw
you do that, too. SURMA: Oh, that's
because I have to-- PAUL: Do you go back to 8080? SURMA: Oh, that's
because I still have the options in there. Actually, that's let's
stay with HTTPS just to-- PAUL: Make life. SURMA: Let's start
the server again. Put the HTTPS back in there. This is working. Let's stick with this. PAUL: Yeah. Somebody said
restart the browser. I like this. People are basically saying,
switch it off, then on again. SURMA: You know what, I'm
going to give this a try. PAUL: And I like the people, I
mean, we all like to do that. SURMA: I'm going to
restart the server. I'm going to kill this. I'm going to restart Chrome,
open dev tools on the Network tab, and go to HTTPS
local host 8081. PAUL: No, it really
doesn't like it. SURMA: OK, well,
let's just keep going. So right now what we're doing,
this should all be working. We can go to
different subsections, and it loads because we're
just doing a static server. What we actually
want to do, though, is we want to add special
handling whenever an HTML file is requested,
because this is where we do our server-side rendering. So what we're going
to use is app.get, saying whenever there's
a get request for. And now we're going to
use regular expressions, so don't hurt yourself. I'm going to try to make
this understandable. So what we want to do is-- PAUL: Why do you have
the I on the end there? SURMA: Because it's
case insensitive. I like case insensitive. PAUL: Sorry, I'm going
to be that person. SURMA: I've noticed. PAUL: Yeah, because
reading somebody else's regular expressions is-- SURMA: It can be very painful. PAUL: It can. SURMA: And this is probably
not going to be pretty. Also, because I'm just
talking about this, this is all production code. It's not going to end up being
a production ready web server or production ready backend. I'm not going to pay a
lot of attention to doing, like, I/O
optimizations, or to do very efficient
server-side catching, or these kind of things. This is mostly to
show how to implement server-side rendering. It's about a
technique, not about a specific high performance
implementation in this regard. PAUL: Yeah. SURMA: Although we're going
to have performance in mind, because this is why we're doing
this, and this is Supercharged. PAUL: Yes, it is what we do. SURMA: All right, so
let's think about this. When do we actually want
to handle a request? That is either when the
path is being requested ends with a slash, or-- which
is the or for expression-- if it has a slash,
something that is not a slash with
a slash at the end. PAUL: That right? SURMA: Or index.html, right? PAUL: Wow. That's already
tremendously readable. So what we're going for here
is, so you could ask for, like, slash home. SURMA: Just /, /home/-- PAUL: Or /home/index.html/ OK,
so that's what we're trying to capture. So if you come up with
any of those three, otherwise we're going to
fall through to the static. SURMA: Exactly. So all the flags that
are there are going to be served just as we know. And this is why I love mode
4, because we have function. So what I'm going
to do for now is I'm just going to check
that it's actually working, and just send back
a test string. Hi. PAUL: Could've been on
brand, but whatever. OK, fine. SURMA: OK, so I'm going
to use supercharged hi. Let's restart the
server, I hope. No syntax error, which is good. So if we just go
here, supercharged hi. If you're going to go to
About, supercharged hi. If I go to about
index.html, supercharged hi. PAUL: And then presumably
if we do like-- SURMA: Static, what do we have? We have app, we
have static, app.js. Jass PAUL: So this should
now fall through-- OK, so that's the static
hosting that we had before. SURMA: Also, I wanted to
show the staic middleware, Express is really nice
because in the response they take care of doing sensible
defaults like default cache control, default
adding an e-tag, doing the last modified stuff. PAUL: So let's talk
about this very briefly, because I know this is a bit
of a hidden gem, I suppose. When you understand
about these headers, life can be a lot
more straightforward. I have seen people, for
example, using a service worker, and then trying to,
like, cache bust, and do all sorts of things. Because they're fighting
with their headers. SURMA: Exactly. Which sometimes is OK, because
for example, in the pages, you don't have any
control over the headers that they use so you might
have to resort to these things. But usually, if you have any
control over your backend which, for example,
on S3 hosting you do, fix your headers. Because caching is part of
HTTP and the entire ecosystem, so good caching
headers over so much. So at least give
yourself proper e-tags so that caching works
for your clients and you reduce bandwidth that
they use on your website. PAUL: So somebody is basically
saying here that e-tags, they've never really
understood what the point is. So why don't we take a moment
to explain what e-tags are? SURMA: E-tags are pretty great. Basically what they
mean is that when you receive a document from
a server, it gets an e-tag. And that e-tag is a
guarantee that it will only change if the contents of
the files have changed. Which is why most
server implementations use some kind of checksum, like
sha1 or sha2, or MD5, whatever, to generate these e-tags. What the client can then do,
put these files into its cache. And when the user goes back
and requests the same file somewhere in the future,
it can put an if non match header in the request
and add the e-tag so the server knows which
version the user has available locally. And if the service sees,
hey, I have the same version here that the user already
has, the server responds with a three or four,
meaning not modified, and you save the
user re-downloading the entire contents. And this is actually kind of
neat, there's also weak e-tags. PAUL: So you file this, it
begins with a w/ That stands for weak e-tag. SURMA: Which is defined
as the e-tag only changes if the semantics of
the documents change, but they're not necessarily
byte equivalent. PAUL: What does that even mean? SURMA: Exactly. I have never encountered
them in the wild. Express uses them by default. PAUL: But it still does
byte different e-tags. SURMA: I guess. So what it means
basically is they only have an effect if you're
not doing range requests. Because they're not byte
equivalent necessarily, you can't do range requests, or
use a part of it for a cache. But as long as it's
the whole document, you can use the one you have
if the e-tags are equivalent. I wouldn't worry
about it too much, we're going to cover
our own e-tags later. PAUL: Ooh, how exciting. SURMA: Exciting indeed. So we are now in control of
delivering our own HTML calls. PAUL: Before you go any further,
if you've just joined us, we are doing some
server-side rendering today. We are taking the
advanced routing that we did a few
episodes ago, and instead of having a completely static
setup for the HTML files, we're actually building them
on the fly in the server. Surma has got some
JavaScript code up and running, which is just
at the moment sending out some HTTPS basically. And somebody remarks
that you have managed to create a
regular expression that worked the first time
around, which is impressive. And I would agree. I watched you type that out. I was like, I mean,
that's unintelligible, I'll grant you that. Look at that in a few weeks, and
you'll be absolutely terrified. But all the same, well done. So basically, we got this
situation now where if we have a requested slash, slash
section slash-- as in /home/, or /home/index.html, we will
serve via this chunk of code in the middle. And then everything else
is going to fall through to our static hosting. SURMA: Exactly. Because also in
Express, the order in which you declare your
handlers for the steps is important,
because Express goes to through them
from top to bottom. All right, so now
we have a handler that we have to implement. And the reason I did this
is because let's look at our index.html, the original
one, which you might remember or you might not. The thing is that
between this index.html, the index.html in /about,
the one in /contact, they're all pretty much the same
except in the SC view element that we wrote. The actual, for
example, this one, Home has contents
while the other ones are declared as remote
because we're not on the page right now. But everything above and below
is the same for all pages. So let's split those
apart, and this is exactly what I'm going to do. So this one is what I'm
going to call the header. Let's expand so people can
see where I'm actually going. Server-side rendering app. And this is going to be
the header.partial.html. And let's go to the rest and
call this the-- oh come on, don't always throw me out
of the folder, there we go. The footer.partial.html. So now we have split the
file in header and footer, and the actual
index.html will not contain much more than this. And I will add something here,
which I'll explain later. All right. So the actual index.html,
which just half of the content as relevant for this page, is
just one line in this case, because it's just one element. I already prepared this
for the other sub pages. So the about intex.html
also has just one line. Same for contact,
just the content for the actual subsection. And the rest has been split
apart in header and footer. And what we're
going to do now is we're basically
going to assemble the header, the actual content,
and the footer in our handler. And I'm going to introduce
something new, which is we have been using
the Note file system order to read files. But the thing is
that Note currently-- and they said they won't do this
any time soon in the future-- they don't use promises. And reading multiple files
with a callback based API makes my head explode. And that's why there is a
module called Modernize, or MZ, which has wrappers around
all these basic APIs from Note and turns them into
promise based APIs. So we can do now basically
is we can do promise.all. PAUL: OK, so you're you
doing three requests, then. One for the header? SURMA: Exactly. So I'm going to do
app/header.partial.html. I'm going to do-- PAUL: This is really depressing,
you're not making typos! Oh, fine, carry on. SURMA: And now we're going
to need something here, which we're going to talk
about in a second, which is index.html. The actual content of the
section we want to load. And we're going to read the
app-- see, I had a typo. PAUL: Yay. I think this it was just
a pity typo, wasn't it? SURMA: Maybe. So we have to sort out what
we're actually loading. And this is where
the regex comes in, because I have put parentheses
about the thing that will be the section
that we want to load. And Express, being
a nice guy, we can actually go and say
the item that you want to load is-- I think it's this? Let's check this. Let's just send back our item. So what I did here is, I think
it is parens on the request if it contains the
parenthesized parts of a regex. I'm just going to send it back
to see if it actually works. So let's restart the server. Let's split our screen
into-- where is my Chrome? There we go. This is too small. So if I go here, we
shouldn't see much. But if I go here,
yeah, we have About. If I go by Contact,
we have Contact. Exactly what we want. PAUL: One question
that's come in is, the routing that we
have here effectively, it's different to the front
end code that we had. SURMA: Totally. PAUL: And today, are
we going to cover off using the same routing, or
are we completely avoiding that for this? SURMA: No. PAUL: Cool. SURMA: The code that you
wrote in the route advanced-- PAUL: Was a disaster. SURMA: No. That's what you said, not me. It's mostly going
to remain untouched. We've got to do a little
of tricking later maybe. If we get to it, I have an
idea that I want to try out. But for now, we're just going
to do the delivery part. I mean, if we get
this done, it might be interesting to talk
about writing JavaScript works in the front end, in
the back end, maybe even the service record. Which is the
trisomorphic JavaScript, which is a great buzzword
that people came up with. But that's-- PAUL: For the future. SURMA: For now, let's start-- PAUL: [INAUDIBLE] that must
be server side, client side, and service worker
based routing. SURMA: Yes. Which eventually, maybe. PAUL: Terrific. SURMA: So we figured
out what our item is, so we can just put this in
this great template expression. So I'll just put item here. And what we're
going to do next is we now have an array of files. So in case you don't know, read
file returns buffer objects. We don't want buffers,
we want strings. So what we're going
to do is we're going to map over all our
files and say to string. And say this is UTF-8
encoded, and now we have an array of strings,
and not an array of buffers. PAUL: Oh, easy. SURMA: That is so easy. Now, what we're going to do-- PAUL: Typo. SURMA: Are you going to call
me out on any [INAUDIBLE]? PAUL: Yes. SURMA: We have an error
strings, so now we can do response, send
files, join like this. And because we're good, we're
also going to catch our errors. So if any of these fail,
because, for example, if any of these
files don't exist, the promise.all will
not resolve but reject. And now we can handle that
the catch part of our promise chain and say, a response should
have a status of 500 for now and send the error to string. So we're actually doing
error handling, which usually in one-off code doesn't happen. So that is pretty cool. PAUL: So it doesn't in mine,
as we may have noticed. SURMA: So let's restart our
web server, and let's do curl, because we haven't
done curl yet. Actually-- you know
what, let's do curl. We're going to use dash I, so
we want to see the headers. But we're also going to
use dash k, that means ignore certificate errors. Because, as
remember, we're still serving over TLS with a
self signed certificate, and curl is going to
reject unless we give it dash k as an option. PAUL: Please tell me you know
that stuff by heart, as well. SURMA: I've done so much curl
that I actually know these, too. PAUL: Well, hey, server error. Oh, it's undefined. Why's it undefined? SURMA: Well, that's not nice. PAUL: Items, the param zero. SURMA: Because if
it's undefined, we're going to use
just an empty string. PAUL: Oh, OK. SURMA: The easy fix, right? Let's restart the
server, do another curl. There we go. So we have reassembled
the entire file, because you can see the
header and the footer. However, if you
take a closer look, you will see that we
have five ST views. Because our header has all
the remote views, and then we basically reiterate the home
view with the actual content. Which it I don't even know
if your code handles this. PAUL: No. Don't do it. SURMA: I want to see it break. PAUL: It's going to break. SURMA: Let me open the console
for more fun in the error department. PAUL: Oh, thanks, pal. It's just broken. SURMA: There's
not even an error. PAUL: It's just broken. It just-- yeah. SURMA: Let's look at the
code, but it should be--yeah. So as you can see here, I
can zoom in a little bit. We have five sc-views,
which is not what we want. So let's fix that. And one typical thing,
or one easy way to fix it is to turn our partials
into a templates that we can use with
a template engine. And my template engine
of choice in this regard, because I know it pretty
well, is handlebars. PAUL: OK, yeah,
that's pretty common. SURMA: So what I'm
going to do is I'm going to require in Handlebars. Require handlebars. PAUL: So got a question here. Is it normal to end up with
routing in three places, or is that a no-no? SURMA: Currently I would
say it is pretty normal. I'm assuming that something
like for isomorphic-- at least isomorphic you
can use in the front end, and your service worker
is going to become a pretty common practice. PAUL: So I think there's
a distinction here between just having the
routing all together. SURMA: Yeah. PAUL: So when you do
progressive enhancement, you're going to build routing
on the server side like this anyway, right? You're just going to do. SURMA: You're going to usually
have APIs ice and content delivery, all these kind
of things in your app. PAUL: Right. And then you are probably
going to progressively enhance into some routing on
the client anyway. And then if you
progressively enhance to have a service worker, you're
going to have routing in there. You might do a pass
through, actually. Just kind of going, well,
unless what you're building streams on the fly in the
service worker, for example. SURMA: Don't say that to
him, that'll be really bad. PAUL: He will come
back in the room if I say streams too loudly. That's Jake, by the way. SURMA: You're safe,
I think, so far. PAUL: So far. Sound-proof wall. SURMA: OK. Good investment. PAUL: Anyway, it's
probably pretty normal. Whether it's actually
the same code, the isomorphic,
trisomorphic thing. SURMA: So personally I'm not a
big believer in trisomorphic. Because even though I'm
writing Note right now, that's not the only language
you can use on the back end. There's like, Go, Python-- PAUL: DHP, come on. You've got to say it. SURMA: There's Java,
there's server pages. And you can share JavaScript
code with a PHP back end, unless you're doing
really weird things. PAUL: Technically
there's still cgi bin. SURMA: That is--
yes, please don't. PAUL: OK. So I interrupted you. SURMA: I just imported,
required, Handlebars. And now what I'm
basically just going to do is, I'm going to
take all my files. And for each file, I'm going
to say Handlebars, compile this as a template
and execute it with a request as the context. So that means that inside our
header content and footer, we have access to everything
in the request object. But since we have
item, I'm just going to add item as a new
object to our request so that we have this
inside our context. So I'm turning all the
files into strings, compiling them with Handlebars,
executing them, and then reassembling the website. What we're going to
do now is I would like to use Handlebars
if, but the problem is that Handlebars if only
checks for truthy or falsey values. It doesn't actually
evaluate expressions. What I would like to do is
if item is equal to home, then I would like to skip this. But actually in this
case, it would be empty, because home is the default. But Handlebars can't
do this, so I'm going to write my own helper. Which means if not equal. Which doesn't exist yet,
we're going to write it. So let's if not equal. PAUL: So we can have one of
these for every single section. SURMA: Exactly. So we're going to see
if this is for about. PAUL: If you're just
joining us, Surma and I are doing some server-side
rendering today. We are taking the
advanced router from the previous episodes,
and we are basically making it, rather than having
a bunch of static files, we're now actually
putting in some JavaScript to generate these
files on the fly. And we are in the
middle of that process. You're currently basically
making our templates, and making the HTML dynamic so
that it can be built on the fly by the JavaScript that we have. SURMA: Well-observed. PAUL: Thank you very much, I am
actually here for the duration. And of course, you can join us. You can ask your questions. There are some brilliant
remarks already coming in. Unfortunately Jake is still
shouting service worker every other second. SURMA: You can't hear
it, buit it's there. PAUL: Well, I hear it. I mean, I read it and I hear
it, and it's a big problem. All the same, do fire in your
questions and your comments, and we'll carry on on this end. So you go Handlebars,
you're making these dynamic. SURMA: So we just
used if not equal, even though it doesn't exist. So let's better make it exist. So there's a register
helper call, if not exist. And this is where Handlebars
turns into black magic, because it just gives
you as many parameters as the helper takes and a magic
options element at the end. But we're going to
do here is we're going to check if A is not equal
to B. We are going to options function this. Now this is just something I
have to admit I memorized this. Because what this does
is the options object contains the function
that will generate the string that is in
between our mustache braces in the mark up code. And this is just the context
that we passed it originally, so that is going to
be the request object. Basically what we're
doing is, we're only going to render the
things in between our if not equals
blocks if A does not equal B which is what we want
to say with if not equal. So fingers crossed if this
actually works or not. I'm going to restart
my server, I'm going to expect
some text errors. PAUL: There aren't any! Oh, you. SURMA: Big showoff! Oh, yeah. So let's restart this. That's actually a little
bit big, isn't it? PAUL: That looks like
it's actually correct, because you're going straight
to the Contacts section. SURMA: We can click again, and
it's got-- look at the code. And as you can see, we
only have four sc-views. And the one that
is actually live is missing from the remote ones. PAUL: OK. Wow. SURMA: So it achieved our
goal of only rendering certain parts depending on
what page we are currently on. So that means we
basically just achieved our server-side rendering. We are injecting the
content that is actually requested before any JavaScript
on the client side runs, and yet we are more or less
just delivery on the same page over and over, our
head and our footer. PAUL: So what would you do in
terms of, say, caching this? Because if you build
these pages all the time, what would you actually
do to say, for example, not have to rebuild these
for every individual visitor? SURMA: You know
what, let's do that. Let's build good
caching headers. So before we sent the file,
we just assembled our content. So this content variable
holds the actual bytes that we're about to
send down to the client. What we could do is you could
use these contents to actually calculate a hash
value of the contents and use that as an e-tag. So let's do that. Luckily, [INTERPOSING VOICES]
Note has crypto. PAUL: Hey, you know
in the last episode I asked you what the
German was for CSS? SURMA: Yeah. PAUL: Is there an appropriate
kind of German term? SURMA: For
server-side rendering? PAUL: Yeah. SURMA: Server-side
rendering, I guess, could be called [GERMAN SPEECH]
which is exactly what we're doing right now. And in case you're German, you
know exactly what we just said. PAUL: Again, like
always, I've got no chance of either remembering
or pronouncing it correctly, but it just sounds so cool. Well, I'm still going to call
it server-side rendering. SURMA: If you want
to have more German, let us know and I'll
make sure it happens. So we have the contents,
and what we're going to know is going to calculate the hash. And the hash is going to be
crypto create hash, maybe? Actually, let's use Note and
say crypto is require crypto, and then crypto create hash. Tag completion, for the win. And we're going to use sha256,
because that is decently good. Do some indentation. We're going to append-- it's
called update, because you can incrementally add
data to a hash, and have it hash it over time
if you just have segments. That's why the function
is called update, which I find a little bit confusing. And then, in the
end, you want to have a digest as a hex value. So that basically
gives it a string that encodes the hash
value with a hex format. What we can do now, since we
have the hash, we can go here and say respond set. Set the call that sets the
headers of our response. We can just give
it a dictionary. And in this dictionary,
we can say, for one, we want to set the
e-tag, which is our hash. And because while
we're at it we can also set a proper cache control. Public no cache. And this is something
I wanted to talk about. Because cache control
is such a complex beast. I've been trying to
wrap my head around it, and there's so many facets
to this entire thing, it can do so much
that I just memorized basically two
things that I'm ever going to use cache control. It can do, like, private
caches, intermediate proxies, you have control
over everything. But most of the time, you
just want public no cache. And it sounds
wrong, but it's not. So something I used before
when I didn't know what I was doing was must revalidate. Because that sounds
right to me, right? PAUL: So you are saying is
that this should go out, it should try and get
it from the server again before anything else. SURMA: Exactly. So I want the client
to send a request to the server using the cache
checkers like, if none match, or if modified since,
to see it should always rebalance the server if the
cached version is still fresh. So up to date. However, must revalidate
doesn't do that. Because what must
revalidate actually means is, once the
resource expires, it should be revalidated. So what you would
actually have to divide is max age 0, meaning it expires
immediately after reception, it should always revalidate. But as it turns out, max age
0, and it must revalidate, there's a short version,
which is called no cache. So even though it says-- PAUL: Typo. SURMA: So even though
it says no cache, caching is actually
still happening, but with a validation
front route. PAUL: OK. So we got a question
from somebody basically asking whether
the code will be visible somewhere after the session. SURMA: Of course. As always, we're going to put
it onto our gitHub repository. we're going to link it up in
the video description I guess it's on our Google
chromo or get up and everything's going to
be in there as it always has been with all other questions. PAUL: Yeah, it goes in
our kind of umbrella. It's github/com/googlechrome/ui
element samples. We will link it, you can find
it based off previous episodes, it's always within the
description for that, and this code will go up there. Bit of a funny name,
UI element samples. This isn't really UI work today,
but I hope you'll understand. SURMA: It's sort of, but
differently, and evolved. PAUL: So your no cache
is basically there to do the max age
zero must revalidate, squish it down to no cache. It's a kind of a
shorthand version. SURMA: Exactly. It's just a shorthand, really. PAUL: Brilliant. SURMA: So let's see
if I did this right. I'm going to kill the
server, restart it. Still no error, which is eerie. I don't trust it. know Let's use Curl. And actually I'm going to use
capital I, which means only show me the headers because I
don't care about the content right now. And actually, let's
start with static fjazz. Which is a file as
we tested before that is delivered by the middleware. So we should have the
somewhat weird weak e-tag. PAUL: Which we do. SURMA: Sure enough, we do. And now let's request
something that is delivered by
our own function, and we should have a really
long e-tag, a sha256 hash, which we do. PAUL: And we also get your
public no cache there, as well. SURMA: Exactly. So that means that
when assembling the header, the
content, and the footer, we will recalculate
this hash every time and the user will only
redownload it if they don't have it cached already. PAUL: Perfect. SURMA: Which is nice. So now we have caching. Ideally, probably, we would
write a little more complex code to not recalculate
the hash value every time. But these are the kind
of things that I say, this is not production code. This is more about-- PAUL: Right, and you'd probably
consider a CDN, and other kind of intermediate
caches, and so forth to make sure that things
only hit your server when they absolutely have to. But for the purposes of
today, we're showing-- and this does come up
on the chat sometimes. We aren't always going to
jump to a final solution that, maybe, a prefabricated one. And the reason is not
because we don't like them, often they do a
very, very good job. But this is our opportunity to
kind of show the underpinnings, if you like. SURMA: The essentials. PAUL: Yeah, the essentials. The engine inside so that
it's about the principles more than the code. SURMA: Also, I think we're both
kind of of the opinion that you have to know the basics that
certain frameworks are built upon to be able to
fix them if they don't do what you want for once. Which, if you use favx, you
will have run into the problem that something is not doing the
thing you would like it to do. PAUL: Maybe you go somewhere
and they say, you know what, all we do is write Python. And you think, I don't know, I'm
used to writing in JavaScript and Note. What do I do? Well if you know the kinds of
things, the kind of headers you should be setting,
the kind of ways you might be able-- it's more
a case of plugging things together rather
than anything else. SURMA: It's what you do
versus how you do it. PAUL: There you go. Right. SURMA: Quote me on this. All right. So technically, we just
got server-side rendering, which is really, really cool. So if we go here, they should
all be working as it is before, and we're actually assembling
these pages on the fly. However, something that might
be a little-- here, let me-- can I disable cache? Let's disable
cache, because I see that we get three or fours,
which is not modified. PAUL: Which is good, right? That's what we wanted. SURMA: That's exactly
what we wanted. But in this case, I
wanted to point out that if we don't
have caching enabled, that if the site
downloads contacts it actually downloads
the entire site. Even though, again,
header and footer are pretty much the same. And if you look at--
the only [? count ?] we actually need from
Contact is this single line. So we're actually
pretty being pretty wasteful with the
data that's being downloaded when the user
switches to another tab. PAUL: So a little bit more
context here, I think, is that in the JavaScript code,
we download the full document, don't we? SURMA: Let's look at the header. So yeah. We basically would say
the route is this one. Oh no, actually
when we go to About, the sc-view requests
this website from the server,
the entire thing, and puts it into the Dom. Pulls out the part that it
needs and switches out the-- PAUL: But it downloads
the whole document and then query selects it
out, only that little bit. So you're saying what we
should be doing is just to send down that little bit. SURMA: Yeah. Since we're going to throw
away the rest anyway, might as well do it
on the server side. So look at our index.html
route, I guess. We could do something
really sneaky. What we could do is we could add
support for a curry parameter. So right now, if we do
something like this, you will get the entire site. Look at the entire site. What would be nice is that if we
add something like the partial, that we only get the
bits that are actually relevant for our server side
of or client side JavaScript. So what I'm going
to do is I'm going to check if partial is in
the request query dictionary, and now add logic. So as you can see
here, we have a lot of files that are being read. But if we have partial, we
actually only want to read-- PAUL: Typo. SURMA: We only want
to read this file. My copy-pasting is
off the charts today. PAUL: Still better than mine! SURMA: So we're going
to use let for once, we're going to use let because
we're going to change it. If we have partials, we're
just going to read one files. If we don't, we want
to read all the files. Boom. PAUL: OK. So you ask for partial,
we'll only give you the little bit in the middle. If you don't, we will give
you the header, the middle, and the bottom. OK. SURMA: So promise all
files, which sometimes might be an area with
just one item in there, and sometimes with three. PAUL: Somebody is asking,
what is the actual benefits of server-side rendering? SURMA: User experience mostly. I think I touched on it earlier
at the beginning, I guess. If we didn't do
this, and we didn't do rendering out all
the pages ahead of time and saving them as
individual files, the user would have to wait
for the JavaScript to spin up. PAUL: So you would download
all the JavaScript, build the page on the client,
and then inject that HTML. SURMA: And usually
in between, there's also a request to the server
to get the actual data that the user wants to see. Or you have to maintain multiple
copies of the same index HTML, which is probably
more developer experience, but also means it
was very error prone. Because the second you
do something wrong, everything can break apart. PAUL: Yeah. One of the superpowers
of the web, as well, it is really
worth pointing out, is its ability to stream. If you look at a
large document-- SURMA: You said it! PAUL: If you look at a
large document on the web and it's, like, several
megabytes large, it can still render after
the first few kilobytes have arrived. Because it has,
hopefully, potentially for some inline styles, it's
got enough to get something up on screen. If you're gated on
your JavaScript, you have to wait till--
it's like an entire kind of bulk download is finished
before you can get anything on screen. And so having something where
the server can immediately respond by going, look,
here's some HTML, and ideally some inline styles or whatever. Get that up on screen. And when the JavaScript and
everything else is ready, then you can progressively
enhance into other things. That's good if JavaScript
breaks, which is something that can happen in reality. SURMA: I was just on a
vacation in Indonesia, where internet is
noticeably slower than what we're used to here. You really feel the difference
of a website that do care about delivering the
right content on the very first response
versus the site that needs JavaScript and
everything to just get started. PAUL: Basically, performance
and resilience are at least two. SURMA: And since
it's performance, we're doing it on Supercharged. PAUL: Carry on, carry on. SURMA: I will. All right, so we
have files false, and the rest stays the same. So if it's just one
file, we're still going to turn it into a string. We're still going to
compile with Handlebars, even though nothing's
going to happen there. And then we're going to create
a hash inside the headers and send them back. So the pipeline stays
exactly the same, we just added one if
to handle partials. So let's see with Curl that
it's actually doing what I want. So I'm going to
reset-- syntax errors! PAUL: You got one. Oh, it's outside street mode. Now, didn't you
say to me earlier, I'm pretty sure that
ES2015 code is-- SURMA: I think that's the case. Apparently-- PAUL: Apparently you need
to put it in use strict. I think in new
versions of Note you don't need to put in use strict. SURMA: Maybe I was on Note 5. PAUL: Yeah, Note 5, 6, I think. So yeah-- SURMA: Let's give
this a try again. PAUL: That's a good start. SURMA: I can live with that. So let's start, does
the old version still work where we get
the entire document? Yes, it does. And let's add partial, and
we just get a single line. Which is awesome. And also it has a
different e-tag, because it's a different
document, which is good. So let's actually
go into your code. I get to patch up your code. This is so exciting. PAUL: What are we talking about? I write flawless code. SURMA: Is it? Lets look. Is it the router? PAUL: I'm not going to
help you know, am I? SURMA: No, it's the view. Because the view takes
care of actually loading the data for itself,
because, you know, that's actually sensible design. PAUL: Oh, now you recovered it. SURMA: Saved it! All right, so
somewhere in here we should have the XML HE requests. This is the on-load
event, there we go. So what you do is you
just use data, which is, I think, the part of the URL
that we've just navigated to. And the only thing we
have to do, really, is to append partial. I don't trust this. So I add a partial. That means that now our
decline site code should only load the data it needs
to refresh the views. Let's go back to the browser. Let's go back to
Home, and refresh. And because we have
the preload headers, we already loaded the
different-- I mean, it can go here. It should still work. So everything looks
right, that is good. So it's still working. And let's take a look at
what About actually loaded. PAUL: Somebody is asking
if, when you get a moment, can you disable
JavaScript and just show the benefits of side
rendering when this is done? Because we can just switch
off JavaScript, right? And then it would just show
that the links actually work without-- SURMA: Well, yeah,
yeah they will. Let's reload. Let's go back to
home, let's reload. Hide the console. I'm going to click on About,
reload it, About, partial. And that means within
response, we only load in a single line of markup
instead of the entire document. Which is, yay, winning for us. So we're going to
save a lot of data. So now let's do that, since
this seems to be working fine. We disable JavaScript. Home, Refresh. PAUL: OK, click
around, About, Contact. SURMA: See, there's no animation
because we have no JavaScript. PAUL: It still works, you're
still getting to the-- SURMA: This is progressive
enhancement done right. Because even with
our JavaScript, the site is actually
fully functional. PAUL: Winning. SURMA: This is
really, really good. And then we just enable
JavaScript again, we reload our website, and
we get our nice switchy animations. So in the end, we just wrote
a backend in about 70 lines. I want to give
this one more try. PAUL: OK. SURMA: Actually, I've been
on speedy the entire time, and we're still on HTTP1, right? PAUL: Yeah. SURMA: I have no idea
what's going on here. I'm going to blame Note 4. PAUL: OK. That seems like a
thing you can do. SURMA: If you want to
know why this didn't work, follow me on Twitter, @DasSurma,
and I will figure this out. Because this is bugging me. PAUL: Yeah, well, it's
the only issue you had, and you had it early on and
you recovered beautifully, I must say. Are there any other
questions on-- SURMA: Yeah, how
is the chat doing? PAUL: The chat is
doing brilliant. There've been so many
great remarks, so many brilliant chats in here. I've been really enjoying it. I see why you do this. SURMA: It is really rewarding. PAUL: It's really good fun. To SURMA: We have very nice
people out there in the chat. PAUL: Yeah, so I
guess, like you said, we got 70 lines of JavaScript. We're going to push
up to the gitHub repo so that you can
have a look around, and we'll probably add maybe
a few comments here and there. And I'm going to get you to
move that regular expression out from there. SURMA: Yeah, I'm
going to clean that one up, because it makes your
eyes bleed, and nobody knows. PAUL: No, it's not good. It's really not good. SURMA: Even the
comment doesn't help. PAUL: All right, OK. Are we done? SURMA: I think we're done. We got sorted rendering
in under an hour. PAUL: Perfect OK, well in that
case, I guess all that remains is for me to say thank
you to everybody who's joined us on the Livestream. Don't forget that
you can subscribe. You can follow us on
Twitter, I'm @aerotwist. SURMA: And @DasSurma. PAUL: And we will catch
you probably in the TLDW. TL-- yeah, too
long, didn't watch. It was TLDW. SURMA: It was TLDW. PAUL: Yeah, I got
it right this time. Well done me. Normally when they
record those videos, it takes me a lot of takes to
get even the slightest thing right. It's a bit like my typing, it
leaves a lot to be desired. SURMA: It's his life. PAUL: On that exciting note,
we'll catch you next time. SURMA: Thanks for watching.