Intermediate React, v5

Node Streams with Express

Brian Holt

Brian Holt

SQLite Cloud
Intermediate React, v5

Check out a free preview of the full Intermediate React, v5 course

The "Node Streams with Express" Lesson is part of the full, Intermediate React, v5 course featured in this preview video. Here's what you'd learn in this lesson:

Brian creates an Express server in Node.js to server-side render the React application. The HTML document is split into two parts. The first part is sent back to the user immediately while the remaing part is returned once the React application is hydrated.

Preview
Close

Transcript from the "Node Streams with Express" Lesson

[00:00:00]
>> All right, let's go on node server. This is gonna be outside of source. So here in the root directory, right, a server.js We are gonna be doing this in the E6 modules which can be kind of a pain with node but it is fine. Import express from express.

[00:00:26]
Import fs from fs. Import path from path. Bear with me there's a decent amount of typing here. Import fileurlToPath to path from url, and import renderApp from source slash, sorry, we actually need ./dist/server/serverApp.js. Now, this doesn't exist yet, right, we need to build it. Before we run the server, we have to run npm run build : server and then we can run npm run start, but we can't import the jsx file directly, it has to be built.

[00:01:18]
Okay, const dirname, because we are in ESM modules with node dirname doesn't exist, which is a constant annoyance to me. So we're gonna say past der name fileTourlPath, import metadata.url. This is a very weird thing, but basically if you're used to writing node, this is how you generate what was .__dirname before.

[00:01:48]
If this is unfamiliar with you, just know that this dirname is now whatever directory we're in, right? So it's gonna be, yeah, whatever directory in our project that we're in Const PORT, this is what the port we're gonna listen in. Either process.env.PORT, this is just a convention for cloud computing.

[00:02:13]
So if something passes in a PORT, we'll use that for our PORT, otherwise we'll use PORT 3001 by default. const html=fs.readFileSync. Path.resolve, dirname and ./dist/client /index.html.toString. So what this is gonna do is our project is going to build out an HTML file. We're gonna go read that HTML file because we need all the paths in there, right?

[00:02:59]
We need Where the CSS is, where the JavaScript is cuz they have like all these hashes attached to them. And so we don't know what they are in advance, so we have to do this part to go get the proper paths for all those different things. In other words, we can't just use the source file, because it's not gonna be clientApp.jsx at the end, it's gonna be some hashed named JavaScript, Okay, I'm gonna split it into two parts cuz when a user makes a request I'm gonna give them the head first.

[00:03:32]
So looking at my index.html again. When a user loads my page, immediately we can flush out to them all of this, right? Which is good because then they can start loading all the CSS. And spoiler alert, they actually move the JavaScript up here as well for you. So they can start loading all the JavaScript, which is great, right?

[00:03:55]
So while they're parsing all this, they're loading all this stuff. And then we can be running all of our server side rendering in the background. So multiple things are happening at once. So we're giving them the best experience that we possibly can. So that's why I'm spreading it.

[00:04:07]
I'm just gonna split it into parts. The first part, this part and the second part, this part. So I can split it on what? Not rendered, right? So I'm gonna just literally split it on that. Html.split, Not rendered. Now, typically, I would imagine most people would probably do something like, That's probably a more robust way of doing it, but I'm lazy and I'm not gonna do that.

[00:04:41]
I'm just gonna do not render cuz it's already there. Okay, so that's parts. Now let's make a really basic expressApp. If this is not familiar to you, it's totally fine, this is not an Express course. App.use, /assets, express.static, (path.resolve). This is just serving all the static assets that we need to do, dirname./dist/client/assets.

[00:05:23]
This will serve all of our images, our CSS or JavaScript, all that stuff. Did I import express there? I didn't write, yeah, sorry, that's app=express or the other way around, Okay, And then here's the magic, what you've been waiting for, app.use. We're basically saying on anything that's not served by a static asset, let react handle,okay?

[00:05:59]
Request and response. So what did I say? The very first thing is we're just gonna flush them the header, res.writeparts0. Immediately flush them everything that's in the head. Then const stream = renderApp, Again, this is the app that we got here from dist serverApp.js. We're gonna give it req.url, And this object of onShellReady, Stream.pipe res, Okay, onShellError, Do error handling here.

[00:07:04]
This would be, Log it out to your error service, log it out to your, I don't know, whatever logging service you're using. I'm not gonna do anything here because we're probably taking too long on this anyway. So anyway, that's what onShellError you would do there. And then all onShellready, this is basically everything has happened, so we''re done.

[00:07:29]
So this is last thing to write, res.write parts[1]. So this basically mean everything is been out, cuz remember React is gonna give it to incrementally. So it gonna give you one piece, and another, and another piece, and another piece. onAllReady is basically all right everything is done now, right, so we're good to go.

[00:07:50]
We can write the tail of it and then we can close the request with res.end. And then on error like an on an actual error, We can console, log that out. Okay, so that's all of that. Lastly thing, we'll say is console.log, Listening on http://localhost:, PORT. I like to do this so then I can command click in my vs code terminal and it'll take me directly there.

[00:08:42]
And app.listen (PORT). Okay, so stream.pipe. This is the important thing here, stream we're getting back from a renderToPipeableStream which is a known datatype streams. When we put this together basically saying, the response object and the React stream are being connected to each other so that whatever is coming out of React is going straight to the user.

[00:09:13]
That is exactly what this does right there. If you have more questions about stream datatypes, I'm pretty sure Scott's course has got to talk about it, so watch Scott's course on Node.js. There is one thing that you might consider doing if SEO is really important to you, which I mentioned to many of you it is, rendering things to stream can be a problem because to Google, it looks like you're slow.

[00:09:38]
Cuz it's worried about, as a crawler, getting everything all at once. So what you might do here is, if it is the crawler, do nothing here. And then onAllReady, which basically means everything is already, you would say something like, if it is the crawler, Then you would do this here.

[00:10:04]
Cuz once you have the, whole thing here on, onAllReady, this would just dump everything all at once cuz everything would be ready, right? So it'll maintain that back pressure and then you can just dump it out all at once. General gist of how you would handle that. Okay, so let's make sure I'm not out of my mind.

[00:10:28]
That's always a possibility npm run. You wanna run buildclient. You can see here I got a bunch of stuff output there. I'm gonna do npm run build-server. Got a bunch of stuff coming out here for the server assets, cool. And now I can say npm run start, and I have a server running on 3001.

[00:11:00]
Might have an error, no. There we go. I don't know what's going on there. So, one of my favorite things to do here is to look at the page source cuz that'll tell you what it's getting on first load. You can see now it's getting a bunch of this stuff on first page load, like all of the various different animals and the select and everything like that.

[00:11:32]
But notice on the first load we're getting no pets found, right? That makes sense it's not doing any Ajax on our server, right, it's just gonna render out the very first thing. So if I refresh here, Notice that we get no pets found immediately. There's no loading once it's there, right?

[00:11:51]
We're getting an immediately loaded out the first set of HTML. So this is what I was talking about. You're gonna get that first date loaded immediately for that user, and then it's goinna start making all the AJAX calls, right? Then it's gonna start loading all that kind of stuff.

[00:12:10]
It's make sense. It's a lot of math like machinery to plug together, but it's pretty cool, I think. It's always really hard to tell because we're doing it all local development, right, and so there's no round trip, our little round trip is just electrons in our laptop, right?

[00:12:26]
But this can make a big difference when if someone's coming from India to US East, right, that can make a big difference.
>> Is there a way that you can simulate that?
>> Yeah, I mean,
>> Your servers, Docker containers.
>> Certainly you can do that. You can certainly slow it down that way.

[00:12:42]
The easiest way always is if I go to the Network here, you can always choose throttling here and you can put yourself on, I'm on Firefox, again. But you can put yourself on good three or a regular 3G, right, and I refresh. Now Firefox is gonna make it seem like everything's going a lot slower.

[00:13:08]
S o I use that quite a bit. And if you don't use it, you should look at your app on on various different speeds. You should also look at your analytics to see what devices and what speeds people are accessing your computer on. So then you really know who to target.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now