Building a Hyper Fast Web Server with Deno

Rate this content
Bookmark

Deno 1.9 introduced a new web server API that takes advantage of Hyper, a fast and correct HTTP implementation for Rust. Using this API instead of the std/http implementation increases performance and provides support for HTTP2. In this workshop, learn how to create a web server utilizing Hyper under the hood and boost the performance for your web apps.

156 min
16 Jun, 2021

Comments

Sign in or register to post your comment.

Video Summary and Transcription

The Workshop introduces Beano and Hyper, a JavaScript runtime and a Rust library known for its fast HTTP implementation. The benefits of using Rust and Hyper over the standard TypeScript implementation of HTTP are discussed. Load tests show that Hyper is about 35% faster. The process of building a web framework called Hyperbole is explained, along with the implementation of request handling logic and middleware. The use of Deno for libraries and production is explored, and the importance of load testing and optimization is emphasized.

Available in Español

1. Introduction to Beano and Hyper

Short description:

Hello. I'm Matt Landers. We're going to build a hyper-fast web server with Beano. Beano is a JavaScript runtime that natively supports TypeScript. Hyper is a Rust library known for its correct and fast HTTP implementation. Excited about the Beano 1.9 integration.

Hello. I'm Matt Landers. I am head of DevRel at WP Engine, and I'm joined by… with Will Johnston. We're both doing this session together. We're going to be building something with Beano. So, we're going to build a hyper-fast web server with Beano.

What were you saying, Will? I said, hey, guys. Hey. Will, good morning. It's early for me. It's early for me. It's not… maybe not as early for Matt. And I don't know about the rest of everybody. It's early for me only because I'm on vacation, and I was… I drove 14 hours overnight. I have slept a little since then, but maybe a little delirious, so maybe everything won't go as smooth as it would have, but I'm excited to do this still.

I found a place with some good internet and should be… should be good to go. I'm really excited about Beano. Hopefully, everybody here has heard of Beano, but I'll just give a real brief intro. Beano is a JavaScript runtime that natively supports TypeScript, which is really awesome for me because I love TypeScript. So you can just create a TS file, run it with Beano, and you're good to go. You don't need a TS config or anything like that. It just works. Beano is built in Rust, which that is kind of a… plays into what we're going to be doing today. So when we say building a hyperfast web server with Beano, Hyper is a Rust library that is an HTTP implementation. And it's known to be very correct in its implementation, and it's very fast.

Yeah, memory-safe. So I've used Rust and Hyper before. So when this integration came in Beano 1.9, I was super excited about it.

2. Benefits of Rust and Hyper

Short description:

Will discusses the benefits of using Rust and the performance improvements of Hyper over the standard TypeScript implementation of HTTP. They plan to build a Hello World application and a router and server based on Express. They also mention using OAK, a web framework, and offer support for questions and provide setup instructions.

What were you saying, Will? It's memory-safe. It's memory-safe and Rust itself tends to be correct, like it forces you to do things correctly. It doesn't mean that you can't write bugs, obviously, like your logic can be wrong, but it forces you to handle error conditions, if you have enumeration, you have to actually match on all of them and handle every case. So there's lots of things in Rust that force you to do things well.

Well, and the nice thing about this is prior to Hyper, you had a standard TypeScript implementation of HTTP, which it works fine, but it's a little bit slower. Rust is a lower level language than JavaScript, so Hyper has the ability to be a little bit better performance. Right, and we have a much better performance for some things. Yeah, I mean, I'm actually really impressed with how fast the standard HTTP library has been, considering it's completely written in TypeScript. So we're gonna write a little Hello World application with that just to see what that looks like, and we'll use that to do some load testing so we can see the difference whenever we use Hyper.

And then once we go through a Hello World application for a Hyper, we'll build a router and a server around it. We're gonna model this after Express. My assumption is that most people here have used Node and probably have some experience with Express, so we're gonna do that. I mean, typically if I were gonna build a new framework out of the box with Deno, I probably wouldn't model Express. I would do something a little more idiomatic to Deno. Deno uses Web Standards across the board, so most of the things that are in Deno are also available in the browser, which is pretty cool. So you're using Web Standards most of the time. So there's things that I have learned because of Deno that are in Web Standards and available in the browser that I had no idea were there, which has been cool. Like requests and response objects and lower level things that I didn't think were available in the browser are, which is neat.

After that, so we'll create a little framework that does some routing and allows you to add some middleware and then if we have time at the end, we'll use OAK. OAK is more of an idiomatic web server approach written by somebody on the Deno core team. It's available to use, but it's a web framework. So when we're done with this, you're gonna have a little web framework that you've written yourself, which will be really neat to go through that process and see all the things that you gotta think about when you're building a web framework. But I wouldn't recommend it for production. OAK is pretty well supported in the community and I would check that out if you're gonna build your own server, but this will be cool to see some of the internals of how a web framework works.

And just before, or while Matt's getting started here, if you have any questions as we go along, you can post them in chat or there is a Discord channel for this workshop and we'll do our best to answer it as we go along. All right, and I'll try, if we're going too fast or something, just let us know or if you need explanation or more on what we're doing, definitely let us know. So I'm in a weird setup. So if you see me like moving my hand, I don't know if you can even see me, but you see me move my hand. I have my iPad up. That's my second monitor right now. So just have some notes to go through. All right, here we go. So right now I just have a folder. There's nothing in it. So you need to have Deno installed. You need to have at least Deno 1.9 or above. So hyper was added to the Deno API in 1.9. It's still in an unstable state. So we will use the unstable flag for this. I have this other file here. Hey, this is a load tester. I guess I can just pull that up. Yeah, I'm gonna link it in Discord and I'll also link it in chat here. Yeah, this is cool. It's a load tester. It's just a command line utility that you can use to generate load on a URL. And that'll allow us to analyze the performance of each of our solutions that we create. So if you wanna do that with us, definitely download HEI. They're the executables are right here. You just download them and run them. You might have to chmod, then mach1, chmod 777, then the filename. All right, let's build a little server with Deno. Oh, another thing you'll want. I'm in VS Code. There is an extension for Deno, if you search for Deno in your extensions. It's the one that's from Denoland. Denoland is the organization on GitHub for Deno, and they maintain this extension. It used to be third-party, and then they brought it in, which is cool. So get that extension. That'll allow you to do things like top-level awaits, get the typings for Deno, TypeScript and all that. All right, so the first thing that I'm gonna do is I'm actually gonna set this up for Deno. So I'm gonna do Command-Shift-P, and if you type in Deno, you'll get initialize your workspace configuration, and just hit Enter through those, and it'll create a VS code settings file to let the VS code know that this is a Deno project, and the extension will now be in use. All right, so I'm gonna create a folder, and I'm gonna call this one STD, and we're gonna create the hello world implementation in here for the standard library, and you can just create a TS file, and my iPad keeps going blank, so I don't remember the exact name of the module that we're gonna put in. All right. You don't need to clone Hey. Hey, if you want to, I just linked it. We're going to use it, but it's just a load tester. We're not going to write any code or anything like that. Right, yeah. We're just using that so when we write our server, we spin it up, we can point Hey to that and have it generate load and see how many requests per second we're able to get. All right, so the first thing we're gonna do is import serve, and this is the standard library implementation of Hello World, just to give you an idea of what it looks like. Deno uses ESM modules, so we don't have to use NPM install or anything like that. We can literally point this out on the internet, and it'll pull in these files, which is super cool. That's one of the big innovations of Deno as well is to get away from NPM and dependency management and allow you to do it in a more web friendly way. Yeah, Brian, we actually do have a GitHub link for the workshop that has the final version of what we're creating today, so I'll link that later so that people can follow along and not be looking through the final code while we're trying to code through it. We're starting with a blank folder, like you can see there, Matt just has his std slash hello-world.ts and nothing else, so that's what our starting point is today. Yeah, this is super cool. Deno is neat. If you haven't used it before, yeah, you don't have to do anything for a setup. I don't need to create an NPM. I don't have to do any package.json or anything like that. I can just create a TypeScript file and start coding and then we're just gonna run it. There's nothing more to it. The packages can be locally cached, so what you can do, if you're running the VS Code plugin, you can go up and right click on it, I think, and potentially cache the plugin. And that way it's just available for you. Yeah, one thing that's happened to me right now is like it's not finding these things. So sometimes after I initialize my extension, I need to restart VS Code, so I'm gonna do that real quick. So you might need to do that as well. If you're getting red squiggles under the import, then you'll need to do that. So now they went away, so I'm good to go.

3. Creating a Server and Running Load Tests

Short description:

We create a server and use it to listen. Deno supports top-level awaits and has built-in support for generators and iterables. We run the server on port 3000 and respond with a 200 status and 'hello world' body. We run a load test and achieve 17,000 requests per second. We're using Deno version 1.10.3. To use 'hey', download it from the GitHub repo and rename the file. Import 'serve' from the standard library in Deno to create a server.

All right, so right here, we're just creating a server and then we're gonna use that server to listen. So we can do top level awaits, which is cool. So we're gonna do for await. Grab the request of server. So whenever a connection is made, we're gonna get that connection and we're just gonna respond to it. So we can just do request.respond and it states an object, and you get your typings and everything in here as well. So just passing a status of 200 and we'll do a body just hello world. Yeah, this is nice.

Deno supports the top level await, which is nice. And it also has built in support for generators and iterables and that's what we're using here. Right, it's cool. Normally if you were in Node or something, you would need to create an ASIC function and then do your await in there and call that from the main file. But Deno supports top level awaits, so we don't have to do that. It just works, which is nice.

All right, so that should be it for our server. So this is our standard library, hello world server. We're just saying we're gonna listen on port 3000. We're gonna get a request. Whenever the server will sit here and wait for a connection to come in from a browser, whenever it does, it's going to go through this loop. We'll have an instance of requests and then we're just gonna respond with 200 and hello world. So we're not doing anything super fancy here, just a really basic implementation.

So now if we go over to our terminal, we can just say deno run, then we have to say allow dash net. So that's another feature of Deno is that it's secured by default. So you have to tell Deno what you want to allow your program to have access to. So I'm saying I want it to have access to the network. So you have allowed net, you have allow read for the file system, write plugins. There's a few different security things that you need here. We also need to turn on the unstable flag. So I'm gonna put dash dash unstable, and then I'll just give it the file. So standard library, hello world, yes. So if we run that, it'll start running and we'll pull up our browser, localhost 3000. And there is our hello world. No, probably can't see that because it's super small. Let me, let me zoom in. Let's see and well, that is not what I wanted to do. Not the right zoom. I don't remember the command, oh there we go. All right, hello world. What's up? It's running. It's pretty quick. Now let's run a load test against that. So I have this running. I'm gonna go over to another window. I'm gonna run hey. So I do minus Z. I'm gonna do 10 seconds and we give it our URL which is localhost 3000. And what this will do is it'll run and hit that web server as many times as it can in 10 seconds. And then it'll give us an output of what happened. There we go. I scroll up a little bit have it zoomed in a lot so I can't see it. You can see that we actually reran in 10 seconds a hundred and seventy thousand requests we were doing 17,000 requests per second. That's actually inacrodible to be honest. We're running on a TypeScript server HTTP server here. I mean, that's pretty impressive how fast that is which when we do the Hyperlinks it'll be even more impressive of how fast that is. But so that's quick. So this is our baseline where we're trying to take a look at 170 or 17,000 requests per second. All right. Yours might be different. It's a totally dependent on your hardware. I'm just on a MacBook Pro here. Yeah so if you're running into problems maybe look into what Dino version you're running. So we're running or I don't know what Matt is running. He can check. But I'm running 1.10.3 which is the latest. That's what I'm on too. If you have Dino, you can just run Dino upgrade and that's it. It'll automatically go out, get the latest version and update it for you. If you don't have Dino, you're gonna have to go download it and install it. I think there's like there's a curl command that'll run a shell script that'll download and install it for you. Yes so if you want to use hay, in the GitHub repo, they have some downloads depending upon your OS. But Mac. If you want to use hay, what I did, the reason mine is just hay is I renamed the file when you download it. It's like hay underscore mac OS architecture. Whatever. I renamed it and I copied it into my folder where we are. And I ran, if you run a chmod 777 hay that'll allow you to execute that file. And you might have to, if you're on a Mac, you might have to go into the settings and allow it, go to the security. Because Apple's going to block it because you downloaded an executable off the internet, which is nice, but sometimes inconvenient. Yeah, you might have to make that work. So now I can attempt. Yeah, so if you're hitting this requestAnimationFrame.respond, my guess is that you have... Yeah, it's not requestAnimationFrame.respond, it's Req.respond instead. Yeah, here's the code again. Sorry, I probably didn't leave it up long enough. But what we're doing is we're importing serve from the standard library in Deno. So, whenever you import from Deno land, and you just do slash, std, and then the version number of the standard library that you want, there's a lot of things that you can put at this level. Right now we're pulling in HTTP. There's things like for the file system, path, a lot of things look very similar to what you might find in Node. And in the file we're just pulling from server, which is kind of like a higher level implementation of HTTP. Which is why it's such a simple implementation to make this work.

4. Setting Up and Running a Basic Deno Server

Short description:

We create a server and use it to listen. Deno supports top-level awaits and has built-in support for generators and iterables. We run the server on port 3000 and respond with a 200 status and 'hello world' body. We run a load test and achieve 17,000 requests per second. We're using Deno version 1.10.3. To use 'hey', download it from the GitHub repo and rename the file. Import 'serve' from the standard library in Deno to create a server.

But we just say, we're just creating a variable constant for our server. We're telling the serve function that we want to run on port 3000. And then we're just, this loop allows us just sit and forever wait for requests to come in so that it never shuts down. And whenever we get a request, we just respond with hello world. And what that means is any path that I go to, I can just type anything in here. We're always just going to get hello world back. And so there's no routing.

Yeah, so. What's up? I can't see the questions. Yeah, I'm just going through some of these. It looks like you can install hey through homebrew, so that's cool if you have. If you're on a Mac, and you have homebrew. The way we set up this app. So we installed the VS code Deno plugin, and then you can see, yeah so Matt you can show that. 3.5.1. Yeah, it's not necessary. If you're in a different IDE, you might have a plugin for that IDE. We're using VS code. So the next step that Matt took there is he made a.VS code. I think there's a command. If you do Command Shift P or something, you can initialize. Yeah, if you do Command Shift P, and you type Deno, you'll see initialize workspace configuration. That will create the settings.json for you. What I have found, I don't know what this is, some kind of bug. But I have to restart VS code after initializing that settings.json. Otherwise it doesn't take up the fact that I'm in a Deno workspace. And then this is the code. Any more questions, Will, or can we go on?

I believe that's it. Somebody's having an issue with the cache, uncached or missing remote URL. Oh yeah, so whenever, I already had this cache, so I'm not getting the little yellow squiggly, if you're getting a yellow squiggly under this, don't worry about it, the first time you run it, it will, Deno will pull it down and cache it. So that's a that's a temporary. Yeah, so don't, don't worry too much about the IDE, Deno will still work, you can run it with the Deno command. Yeah, this one, if you're getting, if you're not getting typings, like if you have a request or something, you don't see a server requests there or, you know, server and it's just like, an any, then you probably need to restart BS code. But if you just see the yellow squiggly under the import, you either if it's a red squiggly, you probably typed it wrong. And then type in the right path. But if it's yellow, it just means that Deno is hasn't, it's not cached yet. So it doesn't have it might not have the typing. Oh, that's cool. You can also do Command Shift P and run Deno restart language service and that will work as well. Okay, cool. Yeah, right there language server, right? Yeah, that's all that might work instead of restarting us good. Yeah, so john is asking, can we add the start script in a file like a package JSON? I mean, I guess yes, you can. Deno doesn't have anything like a package JSON by default. So there's it doesn't have any scripts or anything. You could also create a shell script file and just have the command in there and then run the shell script. Yeah, you can use shell scripts. There's probably Dino things that you can install to actually support some scripting. I'm pretty sure there are. I think I've seen some things that support NPM compatible type scripting. But. That's true, you can also run it in VS code directly by just that you also can use, you actually can use NPM like, which maybe I'll do that real quick. So you could just do, I can create a package json here. Yeah, exactly you could create a package json and then just use it and run Dino instead of. In my script I'll say I'll call it std. Yeah, std. Yeah. Standard. Standard that's what it means. And then what was it? Oh yeah. So I can actually use npm here if I wanted to. Feels bad because I'm in Dino but now I can just do npm run std and we're good to go. So James asks, or he says, I ran, hey, against a small mode JS server and got 14 requests per second compared to Dino's 11,000 requests per second. You'd be interested to know if the for loop has anything to do. We may be able to, we might have time to get into this later, but when you're running web servers, things that seem like just one line of code, very small, can actually have a huge impact on the number of requests per second that your server can handle. Even something so much as a console log statement can reduce it a lot. Console log is way more expensive than you think. So you might have a very small node.js server, and I do believe that out of the box DNode server is slightly faster, but unless they are identical, it's hard to tell what could be slowing one down versus the other. Another question, since you're running the workshop, do you use Dno in production? If so, what for? I actually have yet to deploy Dnode to production, but I believe Matt has done it. Yeah, I've created some little sites with Dnode for sure. I haven't deployed anything with Hyper to production yet, but just because it's still in an unstable state, but I have used the standard library. All right, now let's do the cool part. Let's use Hyper. It's a little bit more involved, but not that bad. All right, so you can use the standard library. This is actually, I wanna show you one more thing about standard libraries. I think this is kinda interesting for people to see. Oops. All right, so I go to the standard library here. I'm in HTTP. Let's look in server, and you'll see that this is a TypeScript implementation. So it's doing things like, what's the content link and figuring that out. It has to create all the headers, like the HTTP headers. Like this is doing everything to support HTTP. The only thing that's coming from Deno that is native in this case, is the TCP connection. So it's using Deno to get a TCP connection so it listens on whatever port we tell it to. And then once that TCP connection is made with the browser, it hands that over to TypeScript and says, all right, do whatever you want. And TypeScript is responsible for reading the body, getting the headers, setting the headers whenever you send something like content link here. All of that stuff has been hand it's being handled in TypeScript, which is an interpreted language. So it's inherently not going to be as fast as a native language like Rust.

5. Using Hyper for Faster Performance

Short description:

We're going to use Hyper, a new Deno API introduced in 1.9, which is a Rust implementation of HTTP. It's well-written and handles errors and HTTP correctly. NGINX is recommended for serving static files due to its additional features like access logs, reverse proxy, and SSL handling. We'll create a Hello World implementation using Hyper. We listen for TCP requests and use Deno's serve HTTP to integrate with Hyper. The response is sent using the respond with function. The load test shows that Hyper is about 35% faster than the standard library's TypeScript implementation.

So it's inherently not going to be as fast as a native language like Rust. So now what we're gonna do is we're going to go use Hyper, which is a new Deno API introduced in 1.9. And I can show you Hyper too, before we get going. Somebody just brought up, can we use deno.listen? And we are getting to that right now. We are about to do it. The answer is yes you can. Yeah the answer is yes, deno.listen is what the standard library one is doing under the hood. But this is Hyper, so it's a Rust implementation of HTTP. It's very popular for being very well written, very correct, handling the errors, handling HTTP correctly. So that's one of the reasons I was really excited about this and showing it off.

So somebody said is deno server better at serving static files than Node? Or should we still use something like NGINX? I would still use NGINX because it has a lot more use cases than just serving files. There's a lot more you can do with it. And in general it's better to run JS behind NGINX. Yeah, I don't know that you would get that much of a performance benefit with hyper in front of it. I don't think that NGINX is gonna be any faster but NGINX gives you more than just serving the files. It can give you access logs. It's a reverse proxy. It can do SSL. Yeah, it can handle SSL for you so you don't have to worry about that. Yeah, there's a lot of things that it handles for you.

Alright, so now I created another file or another folder called hyper and we're gonna create a Hello World implementation using hyper. So I'll just name the same. Somebody asked, can you repeat what you said about standard and TypeScript? You showed some code and I did not get it. If you're talking about the standard server, I can paste some code into, I have that. I pulled up the standard library. Yeah. That was just to show you that what we're doing here, when we import this, I was showing this file, this server.ts file. When we import this, this is actually just TypeScript handling HTTP. So it's doing all the things to support HTTP and doing that in an interpreted language on a server. It's just inherently gonna be slower than having machine code. Although it does, it's still really fast. That's what's crazy. That's really shows the power of the V8 engine. It's just, is so efficient at running JavaScript. All right. Let's build the hyper server. The moment I've at least been waiting for. All right. So what we have to do now is we're gonna actually listen for TCP requests. So we're gonna deno.listen, deno, deno. I'm so used to saying deno. But, they've confirmed it is deno. And for those of you who don't know or haven't realized it yet, deno is just node but shifted around one letter. So that's how, or two letters I guess. Yeah. All right, yeah, the same person who wrote node wrote deno. You just fixing something he didn't like about it. All right, so we're gonna do is another top level await. It's gonna be slightly different. So we're gonna grab the request. So now this one, when we're doing this, this is only TCP. So it's just getting a TCP connection. There's no HTTP involved at this point yet. And then we're gonna create a function called handle HTTP. And we're gonna pass in that connection that we get. So down here, we're gonna create an async function, we're gonna call a handle HTTP. And that is of type dno.con, which bothers Will. Yeah, I don't know. I would say dot connection, but that might just be my Microsoft pass talking and thinking everything needs a full name. Yeah, your function names must be at least 50 characters. So we're gonna do now, this is the new API that was added in version 1.9 of dno, and it is dno dot serve HTTP. This is the integration point for hyper. So at this point is when we're using hyper. So we grab our TCP connection from dno, and then we pass that over to hyper like this, it's a function. We pass in the connection and now, we're deconstructing here and pulling out the request and respond with. Request has everything on it, like the headers and, you know, all the other things that come across with the request. I like the body, the URL, all of that. And then respond with is the function that we can use to actually send a response to the request. So we're just gonna call respond with, and we're gonna create a new response and pass in hello world. And there we go. This is our hyper hello world implementation. We're actually not using the request here, so I can just pull it out, probably. All right, I'll leave that up for just a second and then we'll go run it. But basically I'll run through this again. We create a TCP listener, so this is just straight network, internet protocol waiting for a request, when the browser hits, it first establishes a TCP connection and then once that connection is established, it'll use HTTP on top of TCP. HTTP is a higher-level protocol than TCP. It runs over TCP and then we pass that connection to Deno, serve HTTP and that is what under the hood is using Hyper and then we're just responding with Hyper and we create a new response and we pass in, hello world. Let's go run this and see how much faster it is. So I'm gonna stop this one and we'll do Deno run allow that optional net, unstable and then this time we'll do Hyper, hello world. Alright we're up and running, let's make sure that's running, refresh, it's all good and now let's run a load test against it. We use Hey, we run for 10 seconds and now we're getting 257,000 requests. So before we got 170,000 requests in 10 seconds, which was about 17,000 requests a second and now we're getting 257,000 requests over the course of those 10 seconds at 25,000 requests per second. So that's about 35% faster just using Hyper. That's the only thing that we did different of the standard library, which is a TypeScript implementation. Pretty crazy. I mean, that's a lot faster. You're talking about resource utilization and scaling out a very highly performant web application. I mean, that's a big deal. All right. I'll pull the code up one more time.

6. Unstable APIs and Contributions to Rusty V8

Short description:

We use unstable for the Sherift HTTP API because it is not considered stable for production yet. The Deno team may change the interface of new APIs, so they want to make it explicit that using unstable APIs comes with risks. They have also contributed to the Rusty V8 project, which provides Rust bindings for the V8 engine, allowing anyone to use the V8 engine with Rust. This contribution showcases their commitment to the community.

Are there any questions on this part? What do we have in the A file? Yeah, so we're showing the code again. Somebody asked the question, why do we use unstable? Right, so this API here, Sherift HTTP is not considered stable to use in production yet from the Deno team. I think it's pretty close. I think that by, you know, 1.11, it will be stable, which they're releasing pretty often, I think more than once a month. So I wouldn't expect that to be stable here soon, but that's the reason we use unstable. So we didn't have to use unstable for the standard library. And that's just an explicit way for Deno to say whenever they release a new API that they might change the interface to. So they don't want you to go run this in production and then, you know, in 1.11, they change the interface just a little bit, maybe they, you know, maybe respond with just becomes respond or something. They don't want you to deploy that. By making you explicitly put unstable, that's kind of them, the Deno team telling you like, hey, use us at your own risk. We may change this implementation slightly. And so it might break something if you push to production. Migrate something like if you were to update Deno in production without changing your code to take into account whatever interface changes they made. Yeah, so I'll paste the code here. Yeah. That's probably smart. Yeah. So if you're having trouble, you can see it there and paste it in if you need it. All right. Well, now, somebody says, let's take a moment to thank Ryan Dal. Absolutely. So he created Node, so that's enough for praise. And now Deno, he decided, let's do it again. And Deno is great. Deno is awesome. Another thing that they've done by creating Deno is really contribute to V8. I wanna show this off, this is cool. There's this thing called Rusty V8. And this is maintained by the Deno team. It's a Rust bindings for the V8 engine. So now anyone out there can use the V8 engine with Rust just by using these bindings, which is super cool. So part of the Deno project was they needed to, they wanted to build it all in Rust, so they needed to create bindings into the V8 engine because it's written in C and C++. So you can create Rust bindings to talk to C and C++ and they created this whole thing, open source, allowing people to now use V8. They've contributed a ton back to the community, which is makes me really excited as well.

7. Building a Web Framework and Using Hey

Short description:

We need to handle different things at different routes. We may want to log every request and run specific code for each path. We're building a web framework called hyperbole. We're using hey for stats and testing. Install hey with Homebrew on Mac.

All right, so we've created this web server, it's great. It's hella raw, we could go to production with that, I guess. But I mean, usually, we need to have different things happen at different routes and this is where it gets more complicated.

So this all has been pretty straightforward so far, but then it gets complicated because now we need to say, like, if I'm at a specific URL, if I'm a specific path, I want to serve something different than the home, than just slash or whatever, like we need to handle those cases. And there also might be times where we want to do something on every request. For instance, if we want to log every request, like create an access log, we would need to have some type of middleware that's gonna run on every request, you know, maybe measure the duration of that request and then output that, console log it out. But then we would also want to run this specific code for the path that's actually gonna respond to the server. So the middleware may not respond to the browser with anything, but you would want to in a different function potentially.

So what we're gonna do, sorry, this thing is. What we're gonna do is we're going to build out a little web framework. It's the greatest web framework that's ever been and you're gonna build it and we're gonna call it hyperbole. So hopefully some people got my dad joke there. To see these stats, we're using hey, so I've linked it up in the Discord chat. But for example, the command for hey, just a test localhost would be hey-z with 10s. So that's gonna run a 10 second test to see how many requests it can make in 10 seconds. So if you go out to that GitHub repo, is there some installation links you can download the hey file? You can add it to your path or whatever if you want to use it from anywhere. Or you just put it in your folder and call it like Matt has. Yeah, I did that so I didn't have to add it to my path or anything. You can also install it with Homebrew if you're on a Mac and you have Homebrew. Homebrew, I would recommend that because then you don't, and it'll just be in your path. You can just run, hey, I'm doing.slash hey because I haven't put it in my path. Yeah, it's a cool little tool for you to test out your code.

8. Building Hyperbole Web Framework

Short description:

We're going to build out Hyperbole, a web framework built around Denno's hyper implementation. It's the best web framework ever. The greatest web framework of all time. We'll define the types first and then implement the server function to get an instance of a server that can listen on a port and create routes. The server will have a listen function that takes a port number and returns unknown. The all function allows creating routes that respond to any method. We'll start with a basic implementation and iterate from there.

All right, so I'm gonna stop the hyper. Hello world. And we're gonna start building out Hyperbole our web framework built around Denno's hyper implementation. It's the best web framework ever. The greatest web framework of all time. This is hyperbole. It's truly, truly hyperbole.

All right, so usually when I get started writing something like this, I wanna define my types first. That just helps me kind of mentally prepare for what I'm about to develop. But so we'll get to that in a second. But what we're gonna do, the implementation that is gonna happen here is we're going to export a function called server. And this is what we're gonna call to implement the, I mean, to get an instance of a server that then we can use to listen on a port and then create routes off of. So I'm gonna create another file that's gonna import this, and then I'm gonna show you what we want, how we want it to work.

This server is gonna have a listen. Let me create a type. I'm gonna call it hyper Server. So I'm just creating an interface of all the functions that we want to be on here. We want listen, and that's gonna be a function that takes in a port, which is a number, and it returns a... Well, I think we did an unknown. Yeah, just return unknown for all those. Yeah, and then we're gonna have all, so this is one. All in Express allows you to create a route that listens for like get, post, delete, put. And it doesn't matter what the method is. It'll respond to that. So all is gonna take in a path. Has a string, so that's like slash hello slash world. That'll be our path. And then it also needs to get a function that's gonna run whenever a connection comes in that needs to be there. So for now, I'm not gonna fill this out yet. I'm just gonna put in something blank. All right. We're gonna, we're gonna take this like one step at a time because it's a decent amount of code that we're gonna be doing.

9. Naming the Handler and Cleaning Up the Code

Short description:

You need to name the handler variable and make it return unknown. Remove the colons after listen and all and replace them with a single colon. In live coding, it can get crazy, but you only need the functions in the interface, so use a colon instead of an equal sign. It's just semantics, but it's probably cleaner.

Yeah, you need to name it something right? Like handler colon. I actually have to make it a variable. Yeah. So this is our handler and it's gonna return unknown. What did I do wrong here? Wait. Why'd that not work? Because you have, you have listen colon. Listen colon and all colon and all you really need is listen and then colon instead of the fat arrow at the end. Oh, I didn't do the fat. Because you're doing an interface here right? When you're live coding man, it's like crazy. You have an interface with properties but you really only need them to be functions, you know what I'm saying? Yeah, I mean I just do that. Yeah, and then do colon at the end instead of the equal. Right, yeah. Man, same. It's semantics. It's semantics. Yeah. But yeah, it's probably cleaner I would say.

10. Creating Basic Server and Handler

Short description:

We're creating a basic server with a handler for middleware. The function will return a hyper released server. We'll create functions for listen, all, and use. Using a class is not necessary in this case, as the server function will only be called once. It's just a preference. A class will be used in another area later.

Another one that we're gonna do is use. So this one we're not gonna have a path, we're only gonna have a handler. And this is what we would use to, for our middleware. So I'm not gonna, we're gonna define what a handler is in a minute, but for now we're gonna create a very, very basic server so we can start to iterate on this. All right, so now this function is gonna return a hyper released server. So we need to create some functions in here for it to return, so we'll create a listen. Okay, it's in a port, the number, and it's going to, we're not gonna, I won't do this just yet. And the all, so I'm just gonna create all of these that we created up there. And we'll have to refactor some of this, but, so, and I use. Yeah, and if you're familiar with Express, this might look somewhat similar to how Express operates. Right. Then we're gonna return this, like all, listen, and use. So we're just trying to get this kind of set up at this point. Any reason you are not using a class here? No reason to not use a class here, but Matt decided not to use it. You could use a class, or you could use a function. In this case, it doesn't matter too much because you won't be calling this server function a bunch of times. You will call it once, so even if you created a class, you would only have one singleton instance of that class. So you wouldn't be getting all the advantages that you get with classes, with prototypes, and all that. But in this case, it doesn't make too much of a difference whether you use a class or you use functions like Matt has defined here. Yeah, it's just a preference at this point. We are gonna use a class in another area later.

11. Implementing Listen Function and Creating Handlers

Short description:

We start implementing the listen function and break it down into smaller functions for better code organization. We create the waitForConnection function to handle the Deno.listen process and the handleHTTP function to serve HTTP connections. We explain the concept of for await and its usage with async iterables. We test the basic server functionality and observe the response rate. Now we focus on defining the handlers and discuss the option of using classes in the server implementation.

All right, now we need to start to implement this. The main thing that we want to work initially is our listen. So let's try to get to the point to where we have a hello world on every route in our server, and then we'll start to break it apart from there.

All right, so our server needs to actually do a DNO.listen. So we're gonna break this out into more functions just to make our code a little cleaner. So one of them that we want is wait for connection, and this will do the DNO.listen. And we'll do a listener, and then we'll get the port here as well.

And then we gotta do for await, we're gonna grab that connection from the listener, and then here we're gonna break out this into, what did we call it? Just wanna make sure that that's right. No, we called it well. HandleHUP. Yes, that's what I thought but I just wanted, handleHUP, and then we just pass in that connection. We're just doing this to like break up our code, I don't like having large functions with tons of code in them. So now we're gonna create the handleHUP function. And it's gonna take a connection, let's say dino.con, and then it's actually gonna do the serveHttp. So we'll get an HTTP connection, serveHttp and pass in the connection, and then we need a loop over this, so for a wait. And this time we do need a request and a respond with because we're gonna use both of those. With, respond with. No, with. That's funny. And then, yeah, right now what we're gonna do is we're gonna create another one that is process, well, right now let's just respond, let's just respond right now, just to get this working and we can create some other code. So this isn't how it's gonna look forever but, we wanna get our hyperbole like in a state where it runs before we write hundreds of lines of code. It's not, we're not writing hundreds of lines of code. Like that, is that okay? Now you draw out the rest of the Owl. Is that size alright for everyone? Can you still read that well? Yeah, I can read it, fine. I made it a little smaller so I could fit more on the screen. Size is good. Alright, cool. So now in our listen, what we're gonna do is we're gonna call wait for connection with the port. Right? Oh wait for connection. You want to do dno.listen, right? I have that up here, I have that in wait for connection. Oh, okay, okay, cool. I guess that's fine, right? Yeah, you don't necessarily even need wait for connection, you could just put that code in wherever. But it's good to have a good interface around your listen function so that you publish that and that's what the user of your library uses and then what you do internally is whatever you need to. Right. Yeah. So this should work now. Well, we haven't implemented all in use, but we don't really need to yet. We're just gonna go listen and then it's gonna return on everything. So I'm gonna create a file at the root here called server.ts and this is gonna be our server that uses hyper-ability. So we're gonna import server from hyper-ability. index.ts because we're in Deno. And then we can just say server.listen. So somebody's asking to explain the for await. So for await is a concept in, it exists in TypeScript and in the node world as well. And in Deno, it's a first-class citizen, but whenever you have an iterable, an iterable is like an array or something that publishes an array of promises or anything that yields. It's called an iterable and you can do for await and it'll just sit and wait for the next iteration of whatever you are waiting for. All right, I have the dots up here on MDM if you wanna check it out, so you have async iterables and that's what we're using here. Essentially what's happening here is this is creating a object that just waits for connections. It's just sitting there asynchronously just waiting for a browser to hit it and when it does this for await loop hits off and it creates, it pulls that connection out of here into another object, which we're deconstructing and then we can use that to respond to that request. We wanna make sure that we're responding to these correct requests asynchronously, which is why in our HandoHTP, this, you know, because you don't want it to only be able to respond to one request at a time essentially. And, yes, those are non-blocking. So there can be other code running elsewhere while you're waiting here, it's not, it's not blocking all other code execution. Right. All right, so we created this and then in our server.ts, we're actually gonna use our server. So we import our server, we run the function. Hey, good point. I think your wait for a connection function needs to be an async so that, so that the way it works in there. Yeah, line eight. So make that in async function. And then where, is this one, this is in point, let's do two. No, this one needs two. Yeah. So right now we're just gonna respond to hello world but that's not what we're gonna do. We just wanna make sure that our code is working. You don't wanna code like too much before you try it out. So let's try it out and make sure it is working. So, dino run allow-net. I'm gonna add dash dash watch here, which will allow me to, whenever my file changes, Dino will restart and load it up so I don't have to keep coming over here and running this every time. And now it's passed this server.ts and now if we go to our browser, we should be getting the same result. All right, we are. We could even run a load test against that to see how this responds. Should be similar to the hyper one. Actually, it should be exactly the same. Something close. Yeah, so where at 25,000 requests per second, which was what we were getting with hyper. Now this is, as we implement this, you'll see that this number will go down over time and we'll talk about that. All right, so now we have a basic server. It's up and running. Now let's start to define how we want this to work. So these handlers, we need to be able to add a handler. So if we go to our server TS, what I wanna be able to do is I wanna be able to say server.allon-slash. So there is a class implementation of this server already linked in the chat here, so some people like classes more than that. Some people still like classes. Yeah, I've been doing React for so long that I just figured no one likes classes anymore. So that's why I made it a function. Well you thought wrong. I thought wrong. People want classes. The people have spoken.

12. Implementing Request, Response, and Next Functions

Short description:

We're implementing the request, response, and next functions in our hyperbole framework. We create a hyperbole request, a hyperbole next, and a hyperbole response class. The response class takes in the Deno.request event type. We add a send function to the response class to send a response with a body, status, and headers. We add fields to the request object for URL, body, method, and path name. We also create a request handlers array to track the handlers set up for the application.

The people have spoken. But that's good because coming up here soon we will implement another class. Yeah, so now here if you use Express, what you're used to doing is request, response, and next, right? We need this to work. We need to be able to get a request, a response, and a next function and decide what to do in our... In all, and we wanna be able to say respond dot send hello world. Ideally this is what our interface looks like. This isn't work because we haven't put this up yet, but this is how we want it to work.

And sometimes I like thinking about when I'm coding up a framework or something like this. I like to see what is the developer experience gonna be? And I kind of figure that out before I just start coding the framework because I'll probably won't end up where I wanna be if I don't really put some focus on the implementation or the interface that the developer is gonna use. So this is how I want this to work. So now let's make this actually work. So that means we need a request, a response and a next function. So I'm gonna go back over to our hyperbole. And we're putting all this in one file right now. You probably would wanna do something different. But so we're gonna create a hyperbole request. We're gonna create a hyperbole next. And then for the response, it's actually gonna be a class. So we're gonna say, as for class hyperbole response, we're not gonna, the reasoning that we're gonna use the class won't be readily apparent yet, but it will be in a little bit.

Alright. So let's see here. Okay, so for the response, I'm gonna create a public field, property field, whatever term you wanna use. We need a constructor and in the constructor, we need to pass in the respond-with as we need to be able to use this to respond on the servers. So I'm just putting in the type here. Will can paste this to you instead of you having to type it. It's a lot to be typing. And this is literally just me copying what respond-with actually is. Alright. Did I get the right number? Too many, there we go. Alright. And then in here, we just call it Super. That's it. You don't need Super cause you haven't extended anything yet. Oh, yeah. Yeah, see I'm getting ahead of myself. Alright. Yeah, you can definitely use that instead. Deno request respond width, that works as well. Say that again. You can use the Deno specific type, right? So, if you just do Deno.request event, it has the respond width function on it so you can just use that.

So, if you just use Deno.request event, it has the respond width function on it so you can just use that as the type in. I'll modify my response to include that. I don't think I can just do request event here though. Yeah, you can. And then you need a open bracket and respond width in quotes. No, bracket bracket. bracket. Yeah, and then respond width in quotes. Got it. Alright, well that's a slightly cleaner. Alright, so this is our response. So what we're doing here is when we create our response, we need to pass in respond width so that then whatever's using our response can, whatever, you know, whenever we call send, which we'll do now. We need send as a function. And we're gonna do, we're gonna take a body, a status, and headers. Alright. And then we're going to set our status equal to status or 200 if they don't pass one in. And then we're gonna say this.respond with a new response. Which is gonna take our body, and also our status, and our headers. Alright, and that's all we need for now. So now we should be able to call response.send, but we're still not doing any routing. Right, and that's what we have to implement now. So let's add some fields to our request object because we're gonna start to implement these now. And what we need to do is we need to start to create an object because we're gonna start to implement these now. For our request, we wanna get the URL and we'll make that a type URL with the browser. We wanna be able to get the body on the request, so the unknown in case it's an object, so it could be a JavaScript, like a JSON object that we can format for you. We'll get the method. So, I get post, update, delete and the path name. Now, it'll just be part of the URL, which is the slash whatever, and we'll use that to do some matching. And then for our next function, let's actually make this just a type. We'll say, export type hyperbole next. Next, and it's just gonna be unknown. All right, now, what we're gonna do is, instead of doing respond with here, we need to start to, we need to call some handlers. So, whenever we, over here, when we're creating this server.all, we are passing in a path. We need to have some way to track all the handlers that have been setup for the application. So, what we're gonna do is, in our code in server, we need to have an array that's gonna track this. Let me see, just wanna make sure I do the typing right. Alright, so we create a constant of request handlers. And that'll be an array. And we're gonna have a path on here, which is string. And then we're gonna have a handler, which we haven't created yet. So, let me do that first. I need to create a interface for that. So, my permanently requests handler. And this will be a function. Let me see. I think it might just be a type. Yeah, I'm gonna make it a type. It could be an interface, but we'll just make it type. And it's gonna be just what we had. We wanna have requests, which is a hyperbole request.

13. Setting up Handlers and Chaining Calls

Short description:

We want to set up a handler for the hyperbole request. We push the handler to an array that tracks all of our handlers. If we use 'all', we run the handler for every route. We can create a server variable that includes all listen and use and return it from the use, all, and listen. This allows for daisy chaining calls.

We want it to have a response. Hyperbole response. And we want it to have a next function, which is hyperbole next. And that's gonna return, what was it? Unknown, again? Yeah. All right. So this is the type definition for the handler that we have over here. So now we're setting this up. All right. So if we go down to here, here's our array. Our handler is gonna be of type hyperbole request handler. And then this is just gonna be an array that we can push to as we get them. All right. So this is gonna, whenever we call server.all or server.use, we need to push that handler that gets passed in right here to our array that tracks all of our handlers. So if we go to all, we wanna change this from this temporary thing to our hyperbole request handler. And we wanna do request handlers.push, our path, and our handler. All right, and for use, we want this to be our hyperbole request handler as well. And what we'll do here is instead of just pushing two request handlers, we can actually call this dot or we can just call all and we'll pass in a star here, meaning like run it for everything. And then our handler. I saw you sneak into this dot, that would be what you would do if you had started this as a class. Look, I appreciate it. Yeah, see, it's very, I didn't know, like I thought I would get chastised for using classes, so I'm like, I'm not gonna use classes. And now everybody wants classes. All right. I like classes, they're great. All right, so our use is saying, hey, we want to run this for every route. So we're gonna have to make sure whenever we decide what handlers to run that we look for the star saying, hey, if it's a wildcard route, just run this for everything. So the other thing that might be nice to do here is to create a server variable that includes all listen and use and then return it from the use, the all and the listen. And that way, you can kind of daisy chain your calls. Right, yeah, that makes sense. So then we'll return that from here. I'll move this up, just because this is kind of weird to have it below it. And it'll get hoisted but you know. Right, so we'll return server here and we'll return server. Yeah, there, and then do it on listen as well. Okay. Yeah, that way we can do like, use dot whatever dot whatever. Yeah, you see that sometimes in the express world.

14. Implementing Request Handling Logic

Short description:

Now we need to add logic to determine which handler to call. We loop through the handlers and check if the path matches. If the path is a wildcard, we run the handler. If it's not, we check if the URL path matches the request path. Finally, we call the handler. To process the request, we create a function called processRequest that takes a request and response. We instantiate the response and create a hyperbole request function to create the request object. We then call processRequest with the instantiated request and response. It's that easy to write a web framework!

All right, so now what we need to do is whenever we're doing the handle HTTP here, this has to get a little more complex because we can't just respond with hello world. We need to actually run the appropriate handler. So let's go ahead and implement that code and all of this will be working. Let me see here.

All right, so we're gonna create a new function called process request that handle HTTP is gonna call. And it needs to be async. Process request, and this is gonna actually take a hyperlinking post. And a hyperlink response. It's not gonna take a net function and you'll see that when we get to the middleware pieces. So you'll see why.

All right, so we're gonna get a request and a response here and now we need to start to add some logic of what handler to actually call. So one thing that we're gonna do, I'm gonna get like, I guess I won't do this at first, I'll show what happens if we don't. So we're just gonna loop through all of the handlers. Yes, these are not testing the request handlers, so we'll save that. And now we need to just check the path to see if it matches what's on the requests. So we can say, if rh.path, if it equals star, we wanna do this anyway, no matter what, so we'll add that first. If the path on the request handler is a wild card, we just say, we're gonna run this handler. Yeah, and so that'll work with if you use dot use, or if you were to call it dot all, then path and star is your path. Then next we wanna actually check the path and see if it matches. If it's not an all path, we wanna say, is this URL, does it match the path name of the request that came in. So remember this request, we're gonna have to instantiate this, or create it. I guess it's not instantiating, since I didn't make it a class, but we're gonna have to create this request and pass it in here. All right, so we're basically saying, if the path on the request handler matches the path on the requests that has come in, then we also wanna run this handler. All right, so now let's grab the handler out of our request handler, and then we want to call it. So we'll just say, await, handler. And for the next function, I'm just gonna make something up for now. Yeah. All right, so this should be good enough at this point to make something that works. We just need to call, instead of responding right here in our handle HTTP, we wanna call process requests, but we need to instantiate the request and response. So what we're gonna do is we're gonna create a function. For the response, we already have it, so we can do that. Const response equals new hyperbole response, and we pass in respondwith. So that's good. We have that. And now we need to create the request. So that's gonna look like this. Yeah, just make a function for it. So now let's go up closer to where we defined it. Defined it. Here it is, and we'll just create a function. It doesn't need to be async. We'll call it hyperbole request. And if you want to make this a class, go for it. And we're going to pass in the request, which is, what type is that? Request. Oh, yeah. It's just uppercase request. Yeah, it's the browser one. And then we're just going to return our URL, so we want to create that here. Consurl equals new URL. Let's request that URL. So the request.url just comes in as a string, and we wanna make that a little nicer for people that are using our server, where it's not just a string, it's actually an object. You didn't want to create the body here. Just no body for now. Yeah, I'll just create. I'll create a body variable for now that's just nothing, but we'll have to come process that later. The method is on the request, so we can say request.method and pull that off. And then we can get the path name from the URL. So now we can say URL.pathName, and that's just like a helper for us. We could do request.url.pathName, but just bringing it up a little bit. All right, so now down where we are processing the request, we need to pass in, not REQ, but request, and we should be good. Sweet. It's that simple. Yeah, it's just that easy. It's just that easy to write a web framework. I mean, why doesn't everybody do it? All right, let's see. This isn't getting my typing, so I'm gonna do the, restart language server. There we go. It doesn't like that I'm not using the request, you need to put underscores there, that's fine. Whoops, I didn't actually change. I thought I did change that, actually. It's not picking it up. Let me restart the TypeScript server. You have to change the interface because it's returning an interface. So you want to make sure that. I did not change it. Yeah. Oh, yeah, yeah, yeah. Got to change this. Hm-mm. How do you even work? It's still not picking up. I had this happen to me the other day, too. I'll try to restart the servers now. Whoops. All right, cool. It's there. I have a problem. Let's see what it is. I'm not sure what it's complaining about here. Hammers are incompatible? Go up. Where do you want to go? Start at the interface.

15. Developing a Router with Hyperbole in Deno

Short description:

We changed the server to return the hyperbole server interface instead of unknown. We encountered some compatibility issues with the use function but resolved them. We added a try-catch block to handle potential errors in the handler. If no handler is found, we send a 404 response. We discussed the importance of the next function for handling multiple middleware functions. The next part will be more challenging. We paused to allow everyone to catch up and address questions. The handle true flag indicates that the request has been handled by a middleware function. Sending a 404 response prevents memory leaks. The next function allows for calling further handlers.

What's the interface for server? So change your server to return your interface hyperbole server. Instead of return unknown, return hyperbole server in all those. So let's start with that. Now we've been doing it this way before. Yeah, well, we'll just start with that. Just to get it caught up to how we're doing it in here. And it's still not liking it. And then the use use are incompatible. Yeah, so go down your use function. I didn't put handler here. Whoops. Ah, there you go. I wanna go back to the unknown. Oh, well I would put server there that it makes sense because you are doing that. You are returning hyperbole server. There is a question saying, can you show the hyperbole request one more time? Ah, yes. So basically we have a function here that we pass in the request from Dino from the serve HTTP, whenever we get that request and then we wanna do something to that to make it more usable for users. Wrap it. So grab the URL we're going to process the body and a little bit. Grab the method path name. All right. Alright. Will if you want to copy that over the chat. Yes. So I have the code. It might be too much to paste into Discord but I will try. It is definitely too much to paste. Alright. So our server is now up. Let's see if this works now. Alright. So we've been running over here because we've been watching. So we should just go to refresh. And it's working. Alright, so we did, do we do slash I think? Yeah, so we did slash. So now if I go to another URL that's not the home one, it's just going to hang. Nothing's happening here. You can see my browser is hanging, you see a little blue line. The reason is that what happened, we come over to our server and we go to process request. We didn't find a, we didn't find a handler because we don't have anything for the path that I put in. And so it loops through all the handlers and says, I don't have any. And I'm just sitting there waiting. We don't have anything to tell the server that it should send a 404 back. So it just hangs forever, which is not an ideal. Not good, not good at all. So what we're gonna do is we're gonna change this process request just a little bit. And we're just gonna create a let handled equal false. And when we call our, when we do this loop, we're gonna wrap this in a try catch. Just in case, like the handler crashes. We don't want it to crash everything. We don't really care necessarily what the error is, although we'll put it in here. And then in the, after we call the handler here, we wanna set handled equal to true. And then below here, we'll say if not handled, we want to do a response that's in, pass undefined for the body, because we're not going to pass the body, but we're going to pass 404 for the status. So now if we hit, if we, when we hit a request, so we don't have a, we don't have a handler for, that's going to do a response. It's going to loop through here. It's going to handle it. It's going to stay false. So when we get done with the loop, we're just going to respond with 404. So let's go back over to the browser. We'll hit that and you see that it came right back. And if we look at the, the network, we should see a 404 now. There. And if I go back to the homepage, should still work. We have a router. It does something. Any questions on that so far? Sounds like a critical piece. Now what we've done so far here is we're, we're only able to handle one. We're only able for one handler to work at a time. Cause we're not implementing that next function. So we need, this is going to have to get, this is where so far it's been relatively straightforward. The next part that we're going to do is a little bit. Trippy, I guess, like to get your mind around. Are there any questions, Will, are we good? Looks like we're good. I'm trying to answer them as they come along, but I put the code out there that I have so far in my index.ts. Yeah, just if you just want the hyperbole next, that's the type for that. We'll take a, take a brief moment to pause and get everyone caught up. Yeah, as much as we can. I'm going to run to the bathroom and get more water real quick, because this next part is going to be... So, there's a question, Should we break the for loop when a handler is used, meaning the handle is true? So we are going to get into this in a second, but if you think about if you've ever written an express server, you can have multiple middleware functions that respond to the request. So we don't want to limit it to only a single handler. Now, we're not building all of express today, but we will get into this part. The general way that it works is when you create a middleware function in express and same with hyperbole, you will have a decision to make. You can either do something with the request or the response object and then call the next function, which would mean go to the next middleware. Or you can actually end the request, meaning you send a response. If you send a response, then we don't want to call any further handlers. But if you call the next function, we want to be able to call further handlers. So what that handled true means is that we assume that if any middleware, any one middleware is called, that you are handling this request. So if at the end of the day, we don't match with any middleware functions, we'll send a 404, but if we match with a single one, then we assume that you've handled it. And like Matt was saying, the reason that we're doing that handle true and sending the 404 otherwise is because otherwise we might have connections that are just held out in memory and we can get a memory leak and actually what the Deno server will try to do is it will try to reuse the connection, but it can't reuse a connection that already has some code running in it. So you'll get an error.

16. Processing Request Bodies and Adding JSON Response

Short description:

We add a route for '/hello' that responds with 'world'. We discuss the importance of load testing to ensure framework performance. We introduce the concept of processing request bodies and adding a JSON response function to the response object. We explain the need for parsing the body of a request and demonstrate how to do it using the text decoder. We mention the performance impact of creating the URL variable and the benefits of creating a more approachable interface in the framework. We conclude by adding a logger as a future improvement.

So handled true, it doesn't necessarily mean that the response has been sent. It just means that we have called a handler and we will make the assumption that it has handled the response.

All right, I think we're all caught up. Like I said, I put all the code in there. I put the code for the server in there, as well, the server file. And I think we are ready to go onto the next part.

So what were you thinking for the next part? Well, the first thing I'm gonna do, I'm load testing this so we can see what's happened so far. So remember, we were getting like 200 and something thousand requests. Look at where we're at now. We're at 176,000, 17,000 instead of 25. So that's per second, 17,000 requests per second. So what has slowed this thing down? Well, it turns out that as you start processing these requests and doing things like creating the URL here, this right here, is enough to slow it down that much, just creating the URL variable. Or instantiating that class, right? So, say that's why when you're building these frameworks, you have to continuously load test them to make sure you didn't do anything crazy. Now, this is on purpose, like we want to do this. We don't, there's nothing we can do about it. We want to create an interface in our framework that is more approachable than just the raw, here's a string for URL and making people handle it. So that's something that we're willing to deal with. We want to keep track of this over time to make sure that we're not degreating the performance much more than that.

All right, so now, let's add another route just for fun. We can just add another one and say, server.all. So this is cool. Now we have like a functioning server. I'll put, I'll do slash hello. I'll grab a requests response next and we'll just respond with just world this time instead. So say response.send a world. So I got a slash hello now. I should just get a world. So let's see. There it is. That's pretty cool. I mean, it's not an insignificant amount of code but it's also not that much code now build out a router that works for a single handler. That's kind of neat.

So what won't work right now is what we want to be able to do is have a logger. Like the main, one of the primary things that you end up doing with middleware is to have a logger. Another thing that we'll do before that is that that's gonna get the most complex. So why don't we go ahead and process the body? So let's support a post request. Yeah, that makes sense. Now that's probably a better thing to do first. So what we'll do is we want to do something like this. We want to go to like slash JSON and then we wanna be able to get our request response next. And then we want to be able to just respond with that request body. So we want to be able to say like dot body and then stringify that or parse it. Or we actually want our response obviously have more than just send. We want to be able to say response.json and pass in the body. Right? So we want, it's this right here, when we do that send, we're just sending a string in the body. But we want to be able to take an object and send that as a response. So you could think about like, or creating a REST API. We don't want to have to make every route that we have, like, take our object and stringify it and send it back. We just want and we don't, then we'd also have to set the headers for application JSON. So let's go ahead and add to our response object a JSON function. So we'll go down to response right here, or class. I really should have put all these more files. That's like, crazy now. So we're gonna pass in an unknown object. So we were expecting an object at this point. And then we can pass in a status and headers. Keep me honest here, Will. I'm not looking at any notes, so.

All right, then we'll, from here, we'll call this.send. And we need to do json.stringify on our object. And then we're just passing the status and the headers. So now we've created a JSON function on our response object, where we can pass in any object. We'll stringify it and send that out. And part of the headers we want to add is headers.built. I think to add... Yeah, so for this one, I don't think we take in headers. Let's not worry about that for now. You just, yeah, just send the content type through. Yeah, so here we'll just do content type, application.json. So you want to tell, we're gonna add this header that says that, hey, we're sending you JSON back. That way, the browser knows and everything. It's not just a string. It's actually JSON data. And then we use our send method to do it. So that way, anytime we want to change how we're sending data, we only have to do it in one place, right? Yeah, but for this, we want to change the requests so that the request can parse the body. That's right, yep. So now we're expecting on this, whenever this request comes in, we're expecting that we're gonna get JSON or something there. So we need to process the body. It may not be JSON. It could just be a string but we need to process this. So we're going to say, let me pull this up because I always get this wrong. Yeah, it's gonna turn into an async function and then we're gonna have to use the text decoder to decode the body and then parse it. And we can be, just make assumptions for now that you're always gonna send a parsable JSON text in your request. But this is where, in Express, if you've ever used the body parser, that's what we're coding here. And the body parser can accept all different types, body parser can do all different types of request bodies, but we are just going to process JSON request bodies. So now we're gonna say body equals decoder dot decode raw value, yeah, and then we want to try to JSON parse this essentially, and if it's successful, fine, if not, we don't really care, it's fine, we're just gonna return as a string, essentially, so person ignore this error because it's not a real error, we just wanna give it a shot and say, hey, this could be JSON, let's see if it is or not, if it is, we're gonna parse it, if not, we just leave it as is and it might just be undefined depending, and I wanna show you what this does to the performance too, so before we do anything, Oh, let's have an error somewhere, let me see. Yeah, so now you have to await it down below. Okay. So, await on line 83. Wait no, but I want, I want, oh wait.

17. Adding Middleware and Implementing Access Log

Short description:

We added the ability to process the body and send an object back. The performance decreased by 30,000 requests per second, but it's still at 14,000 requests per second. We optimized the server by checking if the request is a Git request and skipping body parsing. The load test showed an improvement to 19,000 requests per second. We discussed the importance of load testing and optimizing over time. We shared the code so far and the GitHub repo will be linked later. We explained how adding more functionality can slow down the server. We discussed the daisy chaining feature and how it can be implemented. We introduced the concept of middleware and the need for the next function. We explained the importance of handling a response and the use of event emitters. We demonstrated the implementation of an access log using the response and request data.

On line 83, you have to await creating the request. Correct. Oh, and I need to, no, that does exist. You need to modify your interface, I think. I guess that fixed it somehow. All right. Okay, yeah, you're good. Yeah. All right. So, now we're gonna do this one in postman because we wanna actually send some JavaScript or some adjacent object. So, open postman. And then we'll go to localhost, wow. I'll go 3000. Slash JSON. And we're gonna pass in a body. A body. We'll pass in JSON as well. Let's just do like, hello world. All right, we're gonna need to post that now. So, we post it, we send it and then we get hello world back. We see our headers, and that is application.json. And now, we actually support processing the body and sending an object back, which is cool. That wasn't that bad to add that in. Pretty easy. All right, but now, what I want to show you is what just happened. I think that we, my guess is that we have destroyed our performance. All right, now our performance is at 14,000 requests per second, so not as bad as I thought it was gonna be, actually, but it did go down by 30,000. So the, right now when we're hitting this, we're hitting our Gitter for the homepage. We're not passing any data in it and anything like that. So we affected by adding the, parsing the body, we've got the body of our body. And then we, we affected by adding the parsing of the body, we actually affected our Git request. So what we can do is Git requests don't have bodies, so let's check in our, whenever we're creating our request object here, let's check and say, and only do this if request that method is not equal to Git. So now we're saying, hey, let's go try parsing the body if it's not a Git request, because if it's a Git request, there shouldn't be a body anyways. We're not even gonna parse it. Now if we go back and we run our load test again, it should come back up. All right, cool. Yeah, now we're at 18 or almost 19,000 requests per second from where we were at 14. So you can see that these little changes that we make are important, so you gotta keep load testing and optimizing over time so you don't completely destroy your performance. All right, is there anything else we can do before we get to the?

Yeah, so let's take a moment here. So first of all, we will all link a GitHub repo with all the final code once we get there, but for now, I have our Hyperbole file here, so code so far, and I'll link that file. Yeah, so the longer the body is, the less it will handle. It'll handle fewer requests. So yeah, I mean, now you can see how we started with just an insanely fast server, but as you need to do more things, your server will slow down. So that's the expectation you have to set, but you can see how just little things will affect it a significant amount. Now after a while, it's like exponential decay, so like you start affecting it slightly less every time you add new things because you're already doing a lot. Right. All right, okay, are we caught up? Yep, all caught up. All right. So something interesting, Matt, that you can do, that I linked out, but I will link again. Here's my server. My server.ts. And so I'm doing the daisy chaining there, which is what we had set up. So I just have server and then.all and then.all and.all. Right, so it's like.all. And.listen at the end. Yeah, so you can do it that way if you like to do it that way. Yeah, because somebody had asked a question about that, and that's kind of... Express supports this, so for now, you know, we're gonna support that. Right. So it's the same difference essentially. All in preference. All right. So now, let's go ahead and put in our server file what we want to be able to do with our middleware. So we want to be able to say, we want to go add some middleware here that runs on every request. And what that means is we need to implement our next function now. And we also need to let, so with that, we need to let our framework know when a response has actually been responded to because you can't have two, you can't have two responses to run requests. So if we're going down through all of our handlers and something calls that respond with and responds to a request, we can't keep going through the handlers and have another function trying to call respond with and respond to a request because that'll fail. Once one handler actually responds to the request with some data, we have to end that chain and be done with it. So we have to really kind of think about how we're going to handle this now. And we get to the point to where the reason that we created that response object as a class now, is we're going to use event emitters for this. But here's what we want to be able to do here. In our use, we want to be able to say, we want to be able to do like a duration, I mean, let me get this exactly how we want it and then we'll implement it and make it work. One, you're right, you don't need unknown or strength, string body is just unknown in this case, we had it as string and then we just added unknown but once you use unknown, unknown takes precedence, you are right. All right, so what we want to be able to do here is something like this. So I wanna be able to create a variable that's start and I'll just make it equal to data now. So we're going to create a logger, you can access log. And then we wanna be able to say like response that on end. So we want to say, hey, when this response is over, I want you to call this function and this is where the event emitter is going to come in. And then we want to get the duration which is a data now minus start. And then we would want to console log this out. So we could do, what can we do here? We can do response that status. We don't have this yet, or we do have this. We did add that already. So, and then we want, what was the request method that came in? I'm trying to create, you know, this is your standard access log, essentially. I don't think. And the path name and probably the duration. Duration. And we'll make that milliseconds. Cool. And then here is when we actually need to call next cause this logger isn't responding through the requests.

18. Implementing Next Function and Event Handling

Short description:

We import the event emitter and extend our hyperbole response class. We emit the 'end' event when the response is done. We create a wrapper function called call handler to implement the next function. We listen for the 'end' event and resolve the promise if it's called. If the next function is called, we resolve the promise to indicate moving to the next handler. We call the call handler instead of the functions directly. The call handler returns a promise that evaluates if the response is ended or if the next function is called. We create a boolean to track if the response is ended.

It's just doing some processing. It's basically getting a timestamp. So this is going to run first and say, all right, when did this request start? And then when the response is done, we want this event to fire when the response ends so that we can then log out the duration. This is a really common thing that you would end up doing in like express. So we want this to work. So now let's go back over to our hyperbole implementation and we're going to have to do some work here. Yeah. We're going to want to first import the event emitter. I will link that in the chat. So that's... So you're going to add that import at the top of your file and that's what we're going to use to create or to expand on our hyperbole response class. Yeah, event emitter is a third-party Deno library. It's hosted on the Deno land site. But it's just... Node has an event emitter and it follows the same general interface there. Yeah, it was probably ported from Node. Yeah. All right, so now that we have the event emitter, we want to actually extend our class. That's why we're using the class here for the response. So you want to say extends event emitter and then we need to tell it what events we're gonna support so let's create another type here and I'll call it... I'm gonna call it hyperbole events and it's just gonna be end. I need to change this to hyperbole event. All right, cool. Yeah, and you need to do super. Yeah, super so I got ahead of myself. Yeah, and the event emitter takes in, in its constructor, it takes in a number and that number equates to how many listeners there can be to a single event, or maybe total, and zero just tells it to add, allow as many as we need. I think the default is 200. Now, we don't know how many people might want to listen to these events, so we have to just let it do all of them. And now what we wanna do is, any time that we respond, we need to fire that event. So here, we want to say like this.emit. So now, we have this on here because we extended the event emitter. And we want to emit the end of that. Right, so we're seeing, all right, we respond. Yeah, so this is cool. So we get all the, just by extending the event emitter, we're going to get.on,.off,.once, and.emit out of the box here without having to worry about the code for that. Right, so if we come over here, this should start working. Guess I gotta restart the server again. All right, yeah so now we're good. So now our implementation is working, but this isn't gonna work yet because we haven't done anything with our next function. Like we have a next function, but we're not doing anything. So we need to actually implement this. So whenever we call process request, we're gonna create another wrapper called call handler that's gonna implement the next function for us. So just function call handler and we're gonna pass in the request, call response and the handler that we're gonna use. and then here we're gonna return a promise. And it's gonna be a boolean and we're doing that for the next. The next function is basically gonna return if it's it either we wanna go to the next function that's been assigned like in the next handle, next request handler, or the next function is gonna not, is gonna come back as false and say, hey, there's either no more handlers or we've already responded. Yep. I like how VSCode is just very premature with its. Right? Right. That's pretty good. So, there's a question, hyperbole response class has not defined a method where it respond with. We actually did define the method respond with. Matt took a tricky route there and if you notice, I don't know what line it is in his, but if you scroll up to the class, Matt, you can see it really quick. In his constructor, he says private respond with. So, what that does is it puts respond with on Matt's class. Yeah, it's the equivalent of being like this. It's in with. Since we have to pass it in the constructor, if you add private here, it'll put it on the class for you. Yeah, so respond with is provided to our hyperbole response when we instantiate the class and we just immediately set it on private so we can use it later. And that's down here, we instantiate it right here, see how we pass in respond with that comes from the serve HTTP. We pass that in when we instantiate our response and then that's available to us in the send method to respond. Send method, I guess it's methods is a class. All right. What was I doing? Oh yeah. We want to have this call handler. All right so, we're gonna say it's not resolved yet and this is where it gets kind of like trippy. Yeah, we need to do two things. We need to do, Matt, you can work on kind of typing this out but there are two things we need to do. We want to listen for that end event. If the end event is called, we just want to resolve this promise immediately. Otherwise, we want to also call the handler and if the handler, we pass in that next function, if the handler calls the next function, we want to immediately resolve as well. So, we need to know two things. We need to know if the handler we call ends the request by sending a response or if it calls our next function and that's what we're gonna work with here. And this is, these are done in potentially an asynchronous call, so that's why Matt wrapped this in an asynchronous, immediately executed asynchronous function so that we can try to await and wait for the res.once. So, res.once end means, you know, don't listen for multiple calls to the end method, just listen for one and once it's done, we resolve and you'll probably wanna, you can set resolve to true in that in like line 112 as well. You don't need to, technically when you're, when you're working with promises once you call the resolved function once or reject function, you can't then further resolve so it'll just work but we'll just do it for safe measure, I guess. It makes it a little more readable, I guess. Yeah. This is where we're implementing the next function. So we're calling the handler, right, and then we're passing in the next function which is going to resolve to true, so we're saying hey, call the next method please, essentially. Yeah, so this function call handler returns a promise, and when the promise evaluates, it's going to say was the response ended or not? And if it was ended, or really do we need to call, move to the next handler or not? So if the response was ended, we don't wanna go to the next handler, so we resolve with false. If the next function is called, then we resolve with true to indicate hey, let's go to the next, let's call the next handler that we have. Yeah, so the first one is like if you were handling that, if response.send is called, the second one is handle the next function. Right, yep. So when we process our request, instead of actually calling these from here, we're going to call the call handler. So we need another thing here too. We want to know like if it's ended or not. So let's create another Boolean here. Yeah, so in here we need to know like if the response has ended, we don't want to continue to call handlers no matter what. This is just like a catchall.

19. Handling the Result of Call Handler

Short description:

And we also want to know the result of our call handler if we need to go next or not. So if the event emitter events the end event, we want to say, let's say the request has ended. We've responded. Stop all processing, right? So once listens for the event that you pass in, and it only listens one time. So it's not like you're subscribing and trying to listen multiple times. It's just like a one time listen. All right. All right. So once we, when in our response.send, we're doing this.emitend, then it'll call all these once listeners. Exactly. It listens until it catches the first end event, which we only ever trigger one. But in a real world scenario, you may trigger multiple by accident. The end event could be triggered multiple times, and we don't want to handle it more than once. And what we're going to do here is we're going to, what did I do? What did I screw up? You just haven't, I haven't used the wait method. Yeah, you haven't done the wait yet. We're gonna create a next variable that's false. And then now when we await call handler, that's gonna get populated based on if they called next or not, and that'll let us know if we should keep looping or not. Nice. Yeah, and we probably just call the console log the error here. So we don't really, I mean, if we call these handlers and they fail, we don't really want to, to not continue. I mean, you could continue and something else could handle the requests. So we just want to keep going if we can. Yeah, a lot of times you'll see in an expresser like a global error handler as well. Yeah, yeah, I mean, we don't, we don't have time today to go through all that, but. We could. You can take it further. Take this all the way, it turns out like, it's a lot of work to build these things.

And we also want to know the result of our call handler if we need to go next or not. So if the event emitter events the end event, we want to say, let's say the request has ended. We've responded. Stop all processing, right? So once listens for the event that you pass in, and it only listens one time. So it's not like you're subscribing and trying to listen multiple times. It's just like a one time listen.

All right. All right. So once we, when in our response.send, we're doing this.emitend, then it'll call all these once listeners. Exactly. It listens until it catches the first end event, which we only ever trigger one. But in a real world scenario, you may trigger multiple by accident. The end event could be triggered multiple times, and we don't want to handle it more than once. And what we're going to do here is we're going to, what did I do? What did I screw up? You just haven't, I haven't used the wait method. Yeah, you haven't done the wait yet. We're gonna create a next variable that's false. And then now when we await call handler, that's gonna get populated based on if they called next or not, and that'll let us know if we should keep looping or not. Nice. Yeah, and we probably just call the console log the error here. So we don't really, I mean, if we call these handlers and they fail, we don't really want to, to not continue. I mean, you could continue and something else could handle the requests. So we just want to keep going if we can. Yeah, a lot of times you'll see in an expresser like a global error handler as well. Yeah, yeah, I mean, we don't, we don't have time today to go through all that, but. We could. You can take it further. Take this all the way, it turns out like, it's a lot of work to build these things.

20. Implementing Oak Framework and Linking GitHub Repo

Short description:

We see the console logs when hitting the server. The load test shows a decrease in requests per second due to console logging. We discuss the Oak framework for Deno, which is production-ready. We link the GitHub repo and explain the use of underscores in Dino's linter.

All right, so now I need. Yes, this is good. Yes, is it? Right? Yep. So now we should start to see our console logs come through whenever we hit the server. Let's go just hit our hello one. Oh. So I will link our... We're hanging. I'll link the code to this point that I have. We're not ending. I missed something somewhere. Oh, I called next here. Yeah, that all looks good. Now go into your code. The code, but. That looks right. Call handler, did I miss something in the? What about in the response, did you do the emit in there? I think you did. Yeah, you mean, yeah, I did. Where is it? Yeah, this. That emit in. Okay. So that should be right, right? Yep, and then call handler. Yeah, call handler. I returned a new promise. Resolver get. You didn't call the async, the second async iterable function. You didn't do an immediately executed function there. That is a problem, isn't it? The URL does contain the path name already. Matt just did a easy path name variable there to make it easier for you. Yeah, I just brought it up a little bit to just do request.pathname. You don't have to add that, obviously. You could just do request.url.pathname. I say, add that. Yeah, so you would have received a response in the browser already, but what that logging is doing is it's actually logging to the server console. So it will log the request information and the response information. Yeah, so now when I'm over here, you see I'm getting. It's a 200. It was our response. It was a get method, and it was at the root path, and it's two milliseconds. Now if I go to like slash hello, and we come back, now we can see we responded with the 200, the get slash hello. Zero milliseconds super fast, and then we can call our post. Now we see that it's a post at slash JSON, zero milliseconds. Bam! And now whenever we call our. Now this is interesting too. Now lets go do our load test. And this will give you some insight into why you should remove your console logs when you go to production. I remember we were getting... Well, I think it was, we were getting 18,000 requests per second, yeah, 19 almost. And now we're getting 15,000 requests per second. That's purely because we are console logging. That is what console log does. It's a pretty expensive function because it's having to write it to standard out. Yeah, and it does date, some date math stuff. Yeah, and now if we come over here, look how many responses we have. Yeah, there's some date math too. That's cool. It responded to all of those. All right, are there any questions? That's all the coding that we had for the day. We can show oak and its stuff too. What's that? We can show oak and stuff too. Oh yeah, we can do oak real quick. But I mean, for a workshop worth a coding, we have a pretty useful framework now for building something out. Now there's a lot missing that you would want in a full-fledged framework, which is why we're going to show oak. Oak is a really great web framework for Deno, and it also supports hyper, as long as you run it in the unstable mode. So let's create a little oak Hello world. Yes, so the information you use is buggy, though when you do stuff like await underscore next res dot send, yes, you can, it will end up not calling the second handler. So we haven't taken this so far, so that's why we're demoing oak right now. We just wanted to give you an example of how you might start writing a web server like this. But in practice I wouldn't necessarily take this and push it into production. Oak is a- It's a production ready server framework for Dino. So if you're going to go use Dino, I just thought it would be cool for a workshop for you to kind of like look under the hood of what these frameworks you're doing, because it's pretty cool. I mean, it's nice to know like when you're using these frameworks what's actually going on. So I'm going to, now's the time I think I'm going to link the GitHub repo with our, all of our stuff here. Oh, of course a little different. So it doesn't have request response, it has this context object. And then the context object actually has response that body that you can set, you set that. It'll respond with that body. Yeah, when you prefer something with the underscore, this is a, it's a way of saying, hey, I'm not going to use it. It's actually a Dino. So the error you get is based on Dino's linter. Dino has a built in linter. And it lets you do the underscore and then you won't get the lint error. Yes, if I don't put the underscore, I get a lint error that says, hey, you declared next, but you never used it. And there are common patterns in other languages where underscore means, I'm going to get this but I'm not going to use it. Specifically in Go, that's another thing I can bring up. Like in Dino, it's modeled after Go. So if you, like if we run dino-help, you'll see that there's a lot of things in here that are also in Go. There's a formatter already for you, there's a linter. A lot of the stuff that you see in here is actually from Go. And if you go look at the standard library, like a lot of the implementations will have the Go references to Go in it.

QnA

Using Oak Framework and Q&A

Short description:

We ran Oak, a framework built on top of Hyper, and observed a slightly slower response compared to our framework due to its additional functionality. Oak is recommended for building websites and APIs with Deno, as it is a well-supported and widely used framework maintained by the Dino maintainers. We built a functional framework in this workshop, but there is more to be added to make it production-ready. We discussed the flow of a request from the browser to the response, and the JSON example of decoding the request body. Thank you for attending the session and feel free to ask any questions.

Like, hey, we're porting this from Go, which is cool. All right, so now I'm in Oak. It looks like, did I type that right? Dno dot land slash x slash oak, seven at five dot zero. Let me try to run this. The import failed so I got something, what did I type wrong here, I'm not seeing it. Dno land slash x, I think it's v, v seven dot five dot o. Oh nope, that's it. That's like what's happening. All right so now we're running Oak and you can see it's got a different interface than we're used to. But you'll notice, so it is using Hyper under the hood. So if we go load test this. And it'll be similar to our, what we were getting before we did the console log. The console log is totally messes you up but, yes, we're getting 13,000. So it's a little slower but this framework is doing a lot more than our framework is so, it's understandable. Now I'd say, so I'd go check out Oak if you're gonna build a website or API or whatever with Dino. I would definitely recommend using Oak. It's a really great framework and it's maintained by some of the maintainers of Dino as well so, very well supported and very widely used so check it out.

All right. That was fun. We built a whole web framework in a workshop. Hopefully you learned something or just had fun playing around. I think that's what this was. I don't think that we expect anyone to leave here and go create a web framework but we thought it was a good way to show how Dino uses Hyper, how it's a lot faster and then how you could build something around it and hopefully give you a little bit more respect for these framework developers and what it takes to go build them because it's not, there's a lot to it. And we built a relatively functional framework but there's a lot more that you would want to add to this to make it production ready. So, all right.

We got any questions before we wrap up? I'm linking the repo, the correct repo. I linked a different one in there. Oh. Linking the correct repo in the chat right now so you can grab that to use later. Yeah. All right. Well, thank you for coming to this session. Again. We have a couple questions coming in so we can sit around and answer some questions. We'll answer question. Sure. If what we created as a framework, there are no DoDeno frameworks that do this boiler plating for us. Correct? No, so there's no built-in Deno framework for this. However, OAK is kind of the de facto standard that people use for building web applications with Deno. So it implements a similar interface, slightly different to what we did today but it also has a lot more full featured functionality, so it's a production ready server framework. We just created, you know, we gave you kind of a glimpse into how that might be written, but in production I would suggest using something like OAK. You know like this one right here that we're looking at is OAK. So the second question, could we go through the flow? Like what happens when the browser hits the server and asks for the request all the way to the response? I guess. Yeah, all right. I'll walk through that. So if we're in hours, let's go to hyperlink. The first thing that happens, so if we go to the, you start from the listen function and work from there. So we call listen, that goes to wait for connection. Wait for connection. This is where it starts to listen on the TCP port, right? So this is listening on whatever port we say, which is 3000 in this case. So the browser, you hit enter on localhost 3000 and then it starts a TCP connection. That is where this gets picked up. This says, all right, hey, I got a connection. This loop is worth waiting for the different connections. Once it gets the connection, it hands that connection over to the function handleHttp. And handleHttp is where we invoke hyper, is where we say, hey, we wanna use the TCP connection, and on top of it we wanna use HTTP. So we pass in the connection and then that returns a request event, I think, which has respondWith and request on it. We instantiate our request and response. So these are ours, the ones that we created the hyperbole request and the hyperbole response, we instantiate those, which is where we do some processing of the data. Like we process the body, we process the URL, we make that a little easier for people to work with. Then we pass that into process request, which this is where we start to figure out what handler should process this particular request. We wanna listen for when the, or when it ends, like whenever we've responded to the request, we wanna stop all processing because we can only respond to a single request once. We don't wanna have a bunch of handlers firing off and trying to respond. We fire the handlers one at a time synchronously, which is why we have the await here. We come down and we look for a match. We look for something that matches the path. If we go to our server, we see that we have these paths. We have slash JSON, slash for the home page, and slash hello. Those are the only paths we're listening for. When we come back over here, if the path matches, then we wanna call the handler that we passed in for that right there. And then if none of those matches, that means we didn't handle the request, we need to send a 404 back because that means we don't know about this. There was no handler created for it. So we wanna send a 404. And that's pretty much it. So the wait for connection is constantly running. This doesn't run just once. It's sitting here waiting for as many listeners as it- there's many TCP connections from the browsers. That's why it keeps the server open and running is it's waiting for TCP connections. When it gets a TCP connection, we say, hey, we know that we only care about HTTP. So we pass it over to serve HTTP. And that's where hyper comes in and processes all the headers and body and all that for us. And then we take it from there. The connection will close when you do respond with. So it'll end up closing the connection for you because it recognizes that all of the data is sent and it's done. You go through the JSON example one last time. Go through the JSON piece of that like creating the JSON. Oh, on the request. Yeah, so when you send up a JSON request, we go and we look at the body and we say, we use a built in text decoder which is just built into Deno and we decode. Yeah, and we just decode the value, the raw value and then we parse it as JSON. This isn't what you would do if you were going to write, like a full fledged server framework, you wouldn't necessarily do this. You might decode based on the content type that's being passed through in the request but we just took a shortcut for this.

Decoding Raw Bytes

Short description:

It's a readable stream of UN8 array, which means it's a byte array. We create a decoder and run it on the value from the reader to get a string. We can then use that string to parse the data.

It comes through if you look at the type here. It's a readable stream of UN8 array which means it's just a byte array. So it's like raw bytes. So you have to say alright, I have bytes. What are they? What do we do with them? Well, we're assuming that it's gonna be text that's coming through like a JSON string or a form from a request which we would need to decode that differently here. So we create this decoder and then we run that on the value that we get back from the reader. So this gives you a reader that reads the raw bytes into a variable. So now this is a stream, a raw stream of bytes and then we pass that into the decoder and it gives us a string back and we can use that to try to parse it.

Deno as a Replacement for Node

Short description:

The node community does not see Dino as a replacement for node. Dino is too early in its life cycle and lacks the extensive third-party libraries and utilities that Node has. However, Dino offers benefits such as being a single binary, cross-compilation, and the ability to turn JavaScript into an executable. It provides security out of the box by restricting network and file system access.

Yeah, so I'll bring up one, a controversial. Well, I guess it's not a question it's a more of a statement and then a question, but it says the node community does not see Dino as a replacement for node, what is your opinion? Note, this is an opinion, this is not. Yeah, this is not endorsed by anybody. I don't think it's like right now, there's no way Dino can be a replacement for node. It's just too early in its life cycle. The node community is very mature and robust. There's a lot of third party libraries and utilities and stuff that's in node that you just don't have in Dino right now. So what the Dino team is doing is they're creating like polyfills or stuff so that some node packages will run in Dino, which will be helpful to kind of build, accelerate what you can do in Dino. But, right now, I definitely wouldn't call it a replacement, but to me, it's a much better platform for building on in the future. Like, first of all, Dino is a single binary, like node is a big install that you gotta install with a bunch of, it's a large install. And you need a package, dedicated package manager and all of that. Yeah, Dino is literally just a binary. That's it. You can take Dino and it's one file, you can move Dino anywhere you want and just run it. I don't have to install anything. And it cross compiles, it's all written in Rust. Another benefit is you can do Dino compile, and that will take your JavaScript and turn it into an executable. So, then my JavaScript file that I create, like we can do that to the server we just created, and we can take that. We'll get one file out of that one executable. And we can run that on any platform. I don't even need to have Dino on that platform. I only need my executable. So, there are scenarios where Dino actually provides things that Node doesn't. Also, if you care about security. So, it's like security out of the box. If we don't put that allowed net, it doesn't work. Our code won't have access to the network. And by default, our code doesn't have access to the file system either. So, we can't read files. So, you get some security that way as well. There are-

Deno's Future and Use of Rust

Short description:

Now, many people run Node inside a container or in production without file system access. However, Node's unlimited file system access can be dangerous. Deno is not a replacement for Node yet, but I'm excited for its future and the reimagination of libraries. Deno's use of Rust ensures faster and safer code, making it ideal for security-sensitive applications. Rust is widely used and is being adopted in various projects. I'm a big believer in Rust and its potential. No further questions at the moment.

Now, many people nowadays run Node inside of a container or something in production and that you might not get access to the file system. But you can really get bitten if you're not careful in Node because it has just unlimited access to your file system. And there have been cases in the past where somebody has written a rogue package and it has taken over your computer. And I'd say that I am of the opinion in the camp that Deno is not a replacement right now either. I think that I am really excited for Deno and what it brings to the table now as well as the future of Deno. And what I'm really excited for is for people, you know, obviously they're creating kind of like one-to-one polyfills for a lot of popular Node libraries right now. I'm excited for the day where people have kind of, just like Deno was kind of taking the essence of Node and re-thinking, re-imagining, imagining it in a different way that kind of fixed some of the pain points with Node. I am excited for the day where people have ported libraries in such a way where they're re-imagined. So now you kind of get rid of some of the technical debt and the architectural decisions that just didn't pan out, but you have to support everything. So I'm really excited for that day, when we get new libraries for DNO that are kind of like more DNO's essence and less of Node's essence. Right, another thing that I like about DNO too, as far as like the future is that it's written in Rust, and what we're starting to see with Rust is that it's being used to go rewrite lots of code it's lots of low level code out there. Just because it's memory safe, it forces you to write correct code and because of that, you end up with faster code in the end like can C in Rust, are they technically going to be? C might be slightly faster if you write perfect C code, but nobody's writing perfect C code. So usually code that you rewrite in Rust is actually faster, safer. I'm a big believer in Rust, that we're going to see lots of code that's very security sensitive being rewritten in Rust. And you're seeing Rust now being used in the Linux kernel in Android. It's all over the place. So I'm a huge fan of Rust and that's one of the reasons that I really gravitated towards Adino is that it is written in Rust. We got anything else, Will? I can't see anything.

Using Deno for Libraries and Production

Short description:

There is currently no good solution for writing libraries primarily for Deno while also compiling them for Node. However, there are polyfills available for Node that can make certain functionalities work. For web apps with Node, Express, and React, you can use the alfjs framework, which is a React framework for Deno. TypeScript is highly recommended for use with Deno due to its numerous benefits in terms of productivity and scalability. However, you can still use JavaScript with Deno if you prefer. When it comes to using Deno in production, it depends on your specific application requirements. Deno is ready for basic web servers, but for more complex APIs and extensive use of the Deno API, you may need to consider polyfills or continue using Node for now. The Deno community offers a great opportunity for early adopters to contribute and build projects that are more idiomatic to Deno. The goal of Deno is to be as web-compatible as possible, utilizing browser APIs whenever possible. Additionally, there is a Decode podcast that covers various topics, including TypeScript and headless content management systems. WordPress can function as a headless CMS, and plugins like WP GraphQL make it easier to use WordPress for data while hosting the front end elsewhere.

We got anything else, Will? I can't see anything. Yeah, so there was a question, do you know if there is a good solution for writing libraries primarily for Adino while also compiling them for Node? I am not aware of any. I don't know if there's a library but there is something out there where they're writing the polyfills for Node. So like if you try to read from the file system using Node, you know if you import fs and you read from the file system, things that'll make that work. But I think there's gonna be a little bit of work that you have to do specifically just for importing. Like Adino uses the esm-modules so you import using URLs and like the full name like.ts and you don't do that in Node. So there's gonna be some things there that you probably have to do from that perspective but they're trying to make it easier over time.

Someone says, I'm used to create web apps with Node, Express, and React so like boilerplate projects. Do you know of anything like that with Deno? Yeah, so there's alfjs. So this is a nextjs type framework for Deno. So it's a React framework for Deno. It's got like the file system-based routing. A lot of the stuff, it's got SSR, SSG, like all the things that you might be used to in Next and React. All in one framework. So check it out.

Do you need to use TypeScript with Deno, why not JavaScript? JavaScript is an intermediate language not to be modified by human hands. So. I would not use TypeScript. I mean I would not use JavaScript directly anymore. I mean TypeScript saves you so much. I know that if you're not used to TypeScript that maybe there's like this slight inertia that you got to get over to really be comfortable with it. But once you do the benefits are just enormous. It just doesn't make any. It's like C versus Rust or not really those aren't trying to type but it's just getting that strong typing really improves your productivity over time. I know when you first start using TypeScript you probably don't feel that way cause you feel like you're just fighting the type system all the time. But eventually you're not and you have code that you rely on and then when you get better intelligence you get a better linting like everything just gets better when you use TypeScript. So I would highly recommend that if you've been averse to TypeScript that you just bite the bullet and picked it up. However, to explicitly answer your question you can use Javascript. You can just write the Javascript file and you can Deno run Javascript files and it will work fine. Yeah I can just create a new file here like test that. The only reason we did this is Deno treats TypeScript as a first class citizen, so you notice we don't need a TS config or anything like that. And we get some typings and it's kinda nice. But you don't, if you are hell bent on I don't like TypeScript or I don't wanna learn this language right now and I just wanna use Javascript you can totally use Javascript with Deno. Right. So there I just did a quick Javascript file and ran it and there you go. I think they should add like a warning. Yeah I mean if you changed if you changed your server.ts to server.js you could run the servers. True. JS file. Cause you don't have any explicit typings in there. So it, Right. So now it's a JS file. This was already running. So I can server.js and we're up and running. Yeah it's either or but I would recommend you can type through it. Yeah. Anything else? There are some people typing so there could be some stuff. Will you use a Dino server in production now? Actually so I think that now is probably the time to if you are creating a new application you may make a decision about what's involved in the application, what do I need to do? And you can do a little bit of research and determine if Dino is right for you for a production server. If you're just creating like a basic web server and you don't have to do a ton of API and use a lot of the Dino API, it's totally ready right now. It really depends on what you need to do. Like we said, they're working on polyfills for a lot of the node libraries to make it nicer but depending upon what you need to do Dino may already support it and if it doesn't, you might use node for now. And I'll say that too, like there's a huge opportunity for everyone right now to get in on the Dino community really early like build some of this stuff out that's missing. And I imagine like people who got in on the node community early, that's where we're at right now with it. Like there's a lot of opportunity to join the community, give back, build out projects that are more idiomatic to Dino. You know, importing something from node isn't necessarily the right answer because Dino does things differently. They have top level weights, and it's not based on callbacks like node. It's very much based on like asynchronous, you know, using a weight, a single weight so- And you and iterables and stuff like that. So I mean we wrote this framework to give you the essence of Express but with Dino. I mean, you might reimagine a framework using more Dino level stuff. That's kind of what Oak does. Right. And I think that's what you should do. We wanna see that pop up in the community because that's you wanna see people taking advantage of all the things that Dino has like most of the things that are in Dino are also in the browser, which I think is really freaking cool. Like it tries to use browser APIs as often as possible so that your code you could write something for Dino. If you don't use Dino dot somewhere and you just use a lot of the things that are available to you, like the text decoder and request response like all of the different objects that are also in the browser and I'll run in the browser or on Dino, which is really that's part of the design decisions of Dino is that it's as web-compatible as possible. Juan we also have a podcast, a Decode podcast where one of our episodes is exclusively about TypeScript and so we've been downplaying it so far but both Matt and I are huge TypeScript fans and have been for a very, very, very long time before it was public and it is definitely better for scalability. So I've been a part of a number of JavaScript projects that ended up in hundreds of thousands of lines of JavaScript code and while I will admit that tooling for JavaScript today is way better than it was six or seven years ago and it's getting better all the time. The tooling for TypeScript was way better five years ago than JavaScript and it's still better today and the things you get like being able to safely refactor variable names and add functions and change function signatures and understand how it will affect your code, that just leads to a lot better scalability in a TypeScript project. For sure. Yeah, I will share the episode, so I'll share it on Apple, Spotify, Google. The Spotify. Google. Those are probably the big ones here. So, here is, here's the episode for TypeScript on Apple. Here's where you can find it on Spotify. And here's where you can find it on Google, if you're, if you use Google. Yeah, and our Decode podcast, we talk about a lot of stuff like this. We also talk about headless content management systems, other front end cool features and things coming in the future. We also interview people in the field about their experience, so we've had a few guests in the past. A lot of that podcast is centered around WordPress as a headless CMS, which is what we are working on making, improving, as part of WP Engine. All right, so when WordPress becomes a headless CMS, it's just an API for your front end, so a lot of our content is about those front end applications. Yeah, so a lot of people don't realize this, not to get too far into it, but a lot of people don't realize that WordPress, you know, I have not been using WordPress for a really long time. I think 10 years ago I used WordPress and then I kind of took a hiatus and I've come back in recent years. And WordPress actually functions pretty well as a headless CMS. And what that means is you kind of shut off the front end of WordPress and you host your front end somewhere else. And then you use WordPress just for the data. A lot of people don't realize that that's possible. But there are a few plugins for WordPress that are making that better today than it ever has been. So the big one is WP GraphQL, which is a GraphQL API built on top of WordPress, which makes it really nice for querying and getting content and all that sort of thing. Right, it allows your users to still log into WordPress, which they're probably used to and create all their content, but then you don't have to use WordPress and build out the website.

Choosing CMS and Templating Languages

Short description:

You can use whatever you want. Deno, Node, React, Vue, whatever. Depending on the site's purpose, WordPress may be preferred over Markdown. Alternative CMSs like Contentful, Strappy, and Firebase offer different options for backends. Templating languages like mustache and EJS are available for Deno. Modules and plugins similar to Node's ecosystem can be found on deno.land/X. Deno and Node both run on V8, but Deno can also run browser-based modules. Thank you all for attending. Visit developers.wpengine.com for more information and join our Discord community for support and discussions.

You can use whatever you want. Deno, Node, React, Vue, whatever. Yeah, yeah, so typically I've made the assumption in the past that, hey, I'll just have some Markdown files hosted on GitHub or somewhere else and people can edit Markdown and that will be good enough for them. And then, I've come to realize that certain publishers and marketers and different people, they're not very interested in editing Markdown files or even in a nicer editor editing Markdown. They prefer the experience they get in WordPress and it's just a slightly better publishing experience. So that's why using, it's always a consideration for me, depending upon who I'm building a site for, whether I am going to use WordPress or if I was just to build a personal site, maybe I would say, I'm fine with Markdown, that's fine. Right, agreed.

Yeah, so there are alternatives. There are also alternative CMSs. So I mean, besides WordPress, there's like Contentful and Strappy if you really want to have a developer focused headless CMS. There's also Firebase if you're building more of an application that's a little bit nicer to spin up an API. And yeah, and there are lots of different backends. So even if you're using headless WordPress, that doesn't mean you need to use Node on the front end or Dino or anything. You could have a Python server on the front end, you could have a C Sharp server, you could have Java, you do whatever, which makes it nice. So if you have a headless CMS, where you just have your API for your CMS, and then your front end is whatever you feel like, whatever you're comfortable with, and whatever makes the most sense for what you're doing on the front end. Yeah.

Are there any templating languages like pug, mustache, Hamel or EJS for Deno? Oh, that's a good question. I'm sure there are, if you go out to, if you go to deno.land slash X, whoops, not all the way to Oak, but if you go to slash X, this is where all the modules get posted. So, let's see. I know that there is, there are a number of different templating ones. There's mustache there. EJS templating engine for Deno, D-E-J-S. So yeah, they are out here. You can just come out here and search if you want to find stuff. Yeah, you'll notice that there are a fair number of plugins or modules that you're used to seeing in Node that also exist for Deno already. Yeah, things like Fuse are cool because it was already only using web standards. So it'll work on Deno by default because it runs in the browser. If it runs in the browser, it'll run on Deno. So, I mean, assuming that you're not using the DOM.

Hey, I appreciate everybody coming out. It was fun. We managed to get through it without too many stumbles. That's always great. I was worried about that on this one. Yeah, and they both, all the Deno stuff runs on V8 and so does everything on Node. Well, not necessarily in the browser, depending upon what browser you're using, but yeah. Cool. All right. Well, thanks everybody for coming out. I really appreciate you spending the time with us, and if you have any questions or anything, go to developers.wpengine.com, and we have a Discord server that you can join, and you can ask questions and chat with us in the community and hang out, and we'll be there.

Watch more workshops on topic

Node Congress 2023Node Congress 2023
109 min
Node.js Masterclass
Top Content
Workshop
Have you ever struggled with designing and structuring your Node.js applications? Building applications that are well organised, testable and extendable is not always easy. It can often turn out to be a lot more complicated than you expect it to be. In this live event Matteo will show you how he builds Node.js applications from scratch. You’ll learn how he approaches application design, and the philosophies that he applies to create modular, maintainable and effective applications.

Level: intermediate
JSNation 2023JSNation 2023
104 min
Build and Deploy a Backend With Fastify & Platformatic
WorkshopFree
Platformatic allows you to rapidly develop GraphQL and REST APIs with minimal effort. The best part is that it also allows you to unleash the full potential of Node.js and Fastify whenever you need to. You can fully customise a Platformatic application by writing your own additional features and plugins. In the workshop, we’ll cover both our Open Source modules and our Cloud offering:- Platformatic OSS (open-source software) — Tools and libraries for rapidly building robust applications with Node.js (https://oss.platformatic.dev/).- Platformatic Cloud (currently in beta) — Our hosting platform that includes features such as preview apps, built-in metrics and integration with your Git flow (https://platformatic.dev/). 
In this workshop you'll learn how to develop APIs with Fastify and deploy them to the Platformatic Cloud.
Node Congress 2023Node Congress 2023
63 min
0 to Auth in an Hour Using NodeJS SDK
WorkshopFree
Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.
We will enhance a full-stack JS application (Node.JS backend + React frontend) to authenticate users with OAuth (social login) and One Time Passwords (email), including:- User authentication - Managing user interactions, returning session / refresh JWTs- Session management and validation - Storing the session for subsequent client requests, validating / refreshing sessions
At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.
Table of contents- A quick intro to core authentication concepts- Coding- Why passwordless matters
Prerequisites- IDE for your choice- Node 18 or higher
React Summit 2022React Summit 2022
164 min
GraphQL - From Zero to Hero in 3 hours
Workshop
How to build a fullstack GraphQL application (Postgres + NestJs + React) in the shortest time possible.
All beginnings are hard. Even harder than choosing the technology is often developing a suitable architecture. Especially when it comes to GraphQL.
In this workshop, you will get a variety of best practices that you would normally have to work through over a number of projects - all in just three hours.
If you've always wanted to participate in a hackathon to get something up and running in the shortest amount of time - then take an active part in this workshop, and participate in the thought processes of the trainer.
TestJS Summit 2023TestJS Summit 2023
78 min
Mastering Node.js Test Runner
Workshop
Node.js test runner is modern, fast, and doesn't require additional libraries, but understanding and using it well can be tricky. You will learn how to use Node.js test runner to its full potential. We'll show you how it compares to other tools, how to set it up, and how to run your tests effectively. During the workshop, we'll do exercises to help you get comfortable with filtering, using native assertions, running tests in parallel, using CLI, and more. We'll also talk about working with TypeScript, making custom reports, and code coverage.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Node Congress 2022Node Congress 2022
26 min
It's a Jungle Out There: What's Really Going on Inside Your Node_Modules Folder
Top Content
Do you know what’s really going on in your node_modules folder? Software supply chain attacks have exploded over the past 12 months and they’re only accelerating in 2022 and beyond. We’ll dive into examples of recent supply chain attacks and what concrete steps you can take to protect your team from this emerging threat.
You can check the slides for Feross' talk here.
Node Congress 2022Node Congress 2022
34 min
Out of the Box Node.js Diagnostics
In the early years of Node.js, diagnostics and debugging were considerable pain points. Modern versions of Node have improved considerably in these areas. Features like async stack traces, heap snapshots, and CPU profiling no longer require third party modules or modifications to application source code. This talk explores the various diagnostic features that have recently been built into Node.
You can check the slides for Colin's talk here. 
JSNation 2023JSNation 2023
22 min
ESM Loaders: Enhancing Module Loading in Node.js
Native ESM support for Node.js was a chance for the Node.js project to release official support for enhancing the module loading experience, to enable use cases such as on the fly transpilation, module stubbing, support for loading modules from HTTP, and monitoring.
While CommonJS has support for all this, it was never officially supported and was done by hacking into the Node.js runtime code. ESM has fixed all this. We will look at the architecture of ESM loading in Node.js, and discuss the loader API that supports enhancing it. We will also look into advanced features such as loader chaining and off thread execution.
React Day Berlin 2023React Day Berlin 2023
30 min
Javascript Should Come With Batteries
Setting up JavaScript projects is no fun. Getting started involves installing and configuring node, tsc, prettier, eslint, a testing framework, a database driver, and more. Why is JavaScript not batteries included? In this talk we'll talk about how Deno fixes this, letting you focus on building stuff. We explore what benefits full tooling integration unlocks, and remember how fun it is to program if your tools help you, rather than requiring your babysitting.