Qwik for Instant-Loading Websites & Apps

Rendering Data on the Server

Miško Hevery

Miško Hevery

Qwik Creator (Previously Angular)
Qwik for Instant-Loading Websites & Apps

Check out a free preview of the full Qwik for Instant-Loading Websites & Apps course

The "Rendering Data on the Server" Lesson is part of the full, Qwik for Instant-Loading Websites & Apps course featured in this preview video. Here's what you'd learn in this lesson:

Miško uses the onGet method to load a list of contacts on the server. This method is a server method and is never sent to the client. The useEndpoint method pulls the data into the client component, which renders the results in the onResolved handler.


Transcript from the "Rendering Data on the Server" Lesson

>> All right, so let's go back to index.ts. And let's say we want to actually render something of use, right, like individual data. So we need to have a list of things we wanna render. And so I don't wanna set up a whole database, so I'm gonna cheat a little bit and just set up a very simple database.

So I'm gonna copy this file. So inside of the contacts, I will create a new file called fake-db.ts. And inside of here, all I'm doing is setting up a type saying a contact will have an ID, name, an avatar, a GitHub URL, Twitter URL, and LinkedIn URL.
>> And I'm setting up a few of these items, so that we can have a list.

And I'm gonna delete the rest, right? So all we have is basically mock data for us to print. And so let's say, we want to use this data to do the rendering. Now, the way you think about it is, typically, what happens is inside of your component, you will go and make like an XML HTTP request to kind of fetch the data that you want.

But that means that you can't really do streaming. In an ideal world, you want to be able to get a request. And as soon as you want to get a request, you want to be able to go and start talking to the database. And then while the connections database is happening, you want to start rendering as much as you can without the data.

And then once the data shows up from the database, then you wanna render that particular bit. So if I put a HTML fetch operation inside of this component, I will not execute that fetch until I get to that component which might be way down the line. So what I really wanna do is I wanna calls the fetching of the data as rigorously as possible.

And so you can declare a well-known function name on Git, which is of top RequestHandler. And let's just make this function do nothing for now. And of course, we have to import it. And let's just say console.log. Okay, so we're gonna print this. Notice what's happening, every time I refresh this page, I will get this call on a server, right?

So this function here is executing on a server. And it's always guaranteed to execute on a server. And so the thing we would like to do here is we would like to respond with the data. So we can just say, let's talk to a data. This function can be async, right?

But let's go and talk to our, let's just return the fake data from the database. What you would normally do here is you would talk to an actual database or make an HTTP request to somewhere else. But this is happening on a server and so you're basically fetching the data.

So at this point, this is where you would put your pretend. We are pitching data from the database, I love autopilot, wow, it's amazing. Okay, so pretend we are fetching data from the database here, right? So you do whatever you do to talk to your database, fetch the data, this function can be async so you can have await.

And at some point, you return the dataset here. There's a more advanced case where we can start rendering without the data is actually returned, but I'm not gonna go into that just yet. But basically, the mental model is as soon as the request comes in, the thing you start doing is you start talking to the database before you render anything.

And then as soon as you return, which in our case is gonna be pretty synchronous, we can start rendering the page, right? So we start at the root component and start visiting all the layouts. And as we go through the layouts, we eventually get into the index., over here.

And so at this point, we would like to get hold of the data that we've gotten on a server, yes?
>> You're saying, do we need to await contacts?
>> You could, but in this case, it's synchronized, you don't have to. But because it's a async method, you could put awaits in there, you can put whatever you want.

Actually, just to prove a point, I can say, Right, we can do something like this. It doesn't really add anything in here. One thing that's actually kind of different about Qwik is, with Qwik, you can use asynchronous methods and awaits just about anywhere. A lot of existing frameworks basically have all these restrictions on you cannot have await statements inside of the rendering pipeline.

In Qwik, you can put it in just about anywhere, yes.
>> What's the motivation behind where you would want to include it?
>> Include what?
>> Async and await.
>> Well, if you're talking to a database, it would be a asynchronous operation, right? So I'm just pointing out the fact that this method is asynchronous, so that you can do asynchronous operations inside of it.

>> Got you.
>> In our case, we are not doing asynchronous operation, right? But when you have a real database, then it must be asynchronous, right? But it's very nature, and so if you have a framework that doesn't allow asynchronicity at this location, you'd be in trouble. Okay, so now we've gotten the data, and so we wanna actually use it inside of the component.

And so what we can do is we can say const, we call this the endpoint. And we can say, useEndpoint, But before we do that, let's give it some type. So what we're returning here is we're gonna be returning a contact array, right? So we're basically specifying it that the thing we're returning is array of contact, right?

And so this endpoint, and then say, type of, oops, This is just complaining that it's not used. And so we are basically connecting this component's data to what came out of the server. And notice all of this is typesafe, right? So the fact that we declare to this as a contract array, now, this endpoint knows that, okay, I'm gonna be getting this, over here is a array of contact, right?

Now, the thing to do, So we have a component called resource and I'll explain this in a second. Let me just write it out first. So what resource does is basically says, let me back up a second. What you get here, what the endpoint is, is kind of like a promise.

In other words, we're saying like, look, at this point, we can give you this data, but we don't know whether the back can actually return the data or not. And so think of this as a promise. And the reason we do this is because we wanna be able to stream the output as soon as possible, right?

So we wanna start streaming everything in the UI that we can, before the data actually returns from the database. And so in the UI, we basically say resource, and this has to be imported. And so we say we have an endpoint. And so now, we can say onPending, we wanna say something like loading.

So this is the loading indicator. And onResolved, we're gonna get a contacts. Let me just do it this way, okay, and why is this complaining? Yeah, because this has to return a JSX. And so let's just return, Contacts, okay, right, I was right, sorry. Let's just do JSON here for now, stringify.

Okay, so request comes in for this URL, that triggers us talking to the database. Concurrently, while we're talking to the database, we can then start fetching, start streaming the response to the client. We can get as far as the resource is, and the resource basically says in order to continue streaming, you have a choice to make.

Either render the pending or render the actual data, just to kind of make the point. Let's see, this should, yep, see the data actually shows up inside of the contact right here, right? So the resource has a choice. It either can say I'm still loading the data, or this is what you need to render.

Now, Qwik is kind of strange in that it runs both on a server on the client. And so on a server, we will never render the onPending, right? Because we know that we are on a server, and so we know to just wait for the data to show up, and then continue streaming.

And so on a server, the onPending gets ignored, and then only the onResolved gets executed.
>> As you said, this onGet will not get bundled into the client-side library, but how? Because is there any route in the server site which will hit or how?
>> So when we navigate to this particular page, right?

It hits this file, which then executes this code, which then fetches the data, which then starts rendering the component. The data is returned as a promise here. When the promise resolves, then we execute the onResolved function. And, by the way, all of this thing happens on a server.

So if you look at the Network tab, there is no JavaScript here. Actually, we added the Partytown, but besides the Partytown, there's no JavaScript here. I'm gonna remove the Partytown just to kind of keep things neat, if that's okay. Sorry, that was inside of route.
>> So while deploying this app, are we going to deploy in say NGINX?

And then I mean, create some route or then the bundler will, I mean, I'm not able to visualize the-
>> Yeah, so using the, here, I'm not gonna run this. But if you do NPM click add, it gives you a list of things and you can for example add the Cloudflare, Netlify resell, Node js, static site.

So we have adapters, so that allows you to deploy it into different places. Or you could write your own hand coded, kind of answer to this particular thing. So when you're ready to deploy, into one of these technologies, you could just do the integration, or if you have a custom Node js system, you can do that too, or you wanna run on a Deno, or whatever.

So that's a deployment thing that we can get into later once we're finished building the basic things, but yes, good question. Okay, so we've got here, okay, let me run the server again, the server is running. My only point here that I wanna show is that, look, there's no JavaScript being downloaded in here, everything.

We build this page in JavaScript. We did everything we need to be fetch the data, we inline it, etc. But nothing actually in terms of JavaScript is being downloaded here because the system correctly identified that none of this stuff is interactive. There is nothing to do here, and so no JavaScript.

If you look into the data that got serialized, notice it's empty, right? We're not serializing any references, any objects, or any subscriptions. So let's keep on going here. So let's do an ordered list here, all right? And so let's wrap this resources unordered list and this creates an li, but we need one li per each contact.

So at this point, we wanna say something like contacts map. And then in here, we wanna do something like that. And then in here, we wanna say contact, oops, I cannot type, contact name. Oops I don't know why it is, but when other people are watching, my typing skills disappear.

I don't know if other people have the similar experience. Okay, why is this complaining? I see, the li should be here, on our list, and this, and, There we go, okay, so we start fetching immediately. We start streaming, once we get to this point, hopefully, the database is already resolved.

We continue streaming over here and we iterate over individual items. And so now, we have our names, right? The way to think about it is, this URL produces some data. In this case, it produces a list of contacts. And there is two ways to look at the data.

You can look at it as JSON or you can look at it as HTML. It's the same data, just different format, right? And so what's actually happening is if you go to the Network tab. And if I go to All, and I look at this thing, because this thing here says accept HTML, because it says accept HTML, the system knows to send you the HTML.

If this said, accept JSON, then assistant would return to you the JSON. So the same URL is used both for rendering HTML, as well as for fetching the data.
>> So remember, intercept the header, and then act accordingly.
>> Yes, so that's exactly what happens. So if you're on a client and for example, you need to refresh the data, the URL fetch you would do is the same exact URL, but you just say I want it in the form of JSON.

And so you get back JSON rather than HTML.

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