1, 2, 3... Fastify!

Bookmark

In my journey through nodeland, I always wonder about the cost of my abstractions.


I started a journey to write an HTTP framework with extremely low overhead, and Fastify was born. With its ability to reach an astonishing 90k requests/sec, Fastify can halve your cloud server bill.


In this talk, I will walk you through the basics of the framework: how to route requests, write tests, and use the plugin system.



Transcription


Hi everyone. I am Matteo Collina and I'm here today to talk to you about fastify. Please take a moment to follow me on Twitter at Matteo Collina. I tweet about node.js development and a bunch of other things. So yeah, maybe you can find it interesting. Anyway, we are here to talk about fastify. This is, you know, we are all remote. It's all distributed around the globe. So this talk, I'm going to try something that I normally don't try on stage. So maybe let's see how it goes. So first of all, fastify is a web framework that myself and Thomas De La Vedo started back in 2016. And now it's coming up to be the major version 3, which is going to be released soon-ish. Anyway, how do you install it? npm install fastify. And you can, you know, it's actually very easy to use. However, we are not going to talk a lot about it, but we are going to actually use it to actually build a very, very simple app. So first of all, we can just start by, you know, having our own server and we can import fastify. By the way, folks, I am going to use a Node 14. Why? Because I want to use the new ESM features of Node Core. So you know, so that's what we are going to use. So we can do import fastify from fastify. Then I can create my app. And then I can do app.listen on port 3000. Whoa. Okay. So maybe this server, oh, NVMUse14. Hey. So this server I started and then I can curl it and then, oh, well, root not found. Yeah, we didn't add any. So that makes total sense. However, we also, if you look at it, it also didn't log anything. So maybe we just want to add a little bit of a logger here, which is probably useful. So we can do logger true and we can start it back. Oh, now it says server listening. It logs in new line delimited JSON. It uses another library called Pinot to log. Still we are getting some output out of it. However, if we are in development, what you can actually do is do pretty print true only in development. And if we do this, we will get a little bit more nicer output out of it. Note that it just logs twice for each request. Logs when a request comes in and when a request comes out. Pretty nice and handy. Okay. So that's the basic of our app. We have started fastify. So what we are up to it now is what we want to do is we can actually split it up because we can't just code on one single server. So what we are going to do is we are going just to do another file called app.js. So what we do, we do as per default, and then we create what we call a fastify plugin. So and my app. So app. And we can add our routes here. For example, get slash and then a sync. And then return a low word. Yay. I'm a single quote person. So anyway, so that's it. We are in here. So if I restart this and then I go, oh, still don't go. I need to actually load this. So what I can do, I can do app register. I can use import because we're doing ESM, right? So app.js. And then here we go. Okay. Now it's working. Again, yeah, we have moved this to one other file, but this is still a little bit bespoke and a little bit more complicated. So one of the things that we can do is we can actually use a module called, instead of putting our routes in our file, in our single file, we can actually do something a little bit better. So first of all, I'm going to comment this up because we're going to use it later. And I'm going to register one module, which is actually very handy. It's called fastify autoload. And I need to tell it what file, what we want to load, in fact. So I'm going to get a utility that I have prepared, which is called dsm. And it will actually give me a join method, which I can just pass in the URL of this current file, which is start from file, columns, slash, slash, whatever. And then I can concatenate it to routes. Note that if I want to do something different, it will be a long, long list. So I don't want to really do that. So I'm going to create a routes folder. And then in the routes folder, I'm going to create a hello.js file. In my hello.js file, I'm going to copy this block here. Again, I still need my plugin here. So here we go. And then we can do this. And then I can close it up. Here we go. So essentially what we are doing here, we are registering fastify autoload, which is then pointed to a directory, our routes, and then all of those routes will be loaded. So let's see if all this still all work. It will crash. Of course it crashes. Must be a function or a promise. So what did I did wrong? Because that's typically very common that I do something wrong in here. So I have my export default here and routes. Where it is? So this file is here. Server import app.js, my app. I am importing my file. Here we go. Plugin. Oh. Let's try this. Yay. Okay. It wanted a function name. So here we go. We have started this and now we still all working fine. Okay. So what we can do now is we can create a little bit more stuff. So for example, you can create a b c dash d dot js, for example. So in this file here, you see we have created a little bit of a directory structure and then hello from d. Now if we go here and restart, you can actually see a b c d, oh, a b c, and then that's d dot js. So as you can see, with using fastify autoload, we can actually, oh, just. By using fastify autoload, we can open up this file and down to, and load it up from the a b c d, from the a b c folder. Okay. So let's move one step forward. Now if, what if I want to write a unit test, for example, let's create a new test dot js file. Now I'm going to use a very simple, a very simple utility that, here we go, a very simple, a very simple utility that it's actually very, very handy, which is called tape. I am a fan of very easy test tools. So I don't know. I am, hopefully you are like that too, but if you don't, you can use whatever you want. So I need tape, I need fastify, and then I will need my app. So note that I'm actually not creating the server, but I'm actually loading my apps in a separate way. So what I can do is, for example, load the hello world. Well, I can test hello world. So I want to test my hello world function. So I'm using an async function here, and then I'm using deep equal. Here we go. And then we can do create a server, which is my fastify server, and then we can do register. Guess what I'm going to do? I'm going to pass up in here, which is our fastify plugin. So I'm going to put that plugin in. Then what I need, what we can do now is we can await, we can get, we can call our server using our internal testing utility. So we can do await server.inject and slash, which is our route. And then we can do deep equal, press JSON, and then specify the fact that we want hello world. Here we go. So, and then what we need to do, we need to remember to close our server. So we are doing this. Now we can run no test and, oh, where did I put this? Test. Oh, yeah. So if you're using ESM, you need to pass an extension, right? Remember that. Okay. So you see our test is passing. That should be deeply equivalent. So it's actually working fine. Now we can actually also add one more thing here. And about ABCD as we wanted, test ABC. So what we can do, ABC and then hello from D. And this should work the same. Okay. So we have added a little bit of things. So now we want to create some block of routes that are protected. So what I'm doing, I'm going to create a folder called protected and an index.js in here. And in this index.js, what we are going to do is export default async function, admin, maybe whatever, and then app, which is our fastify app. What we are going to do now is in here, we want to add some folders, some endpoints. But before that, we need to configure our authentication system. In here, what we want, we want to add another import called JWT, for example, from fastify JWT. So now what we're going to do, we are going to register this plugin and pass in secret. And I'm calling it change me. Hey, this needs to be changed. Now we are adding this bit. We're going to need to add a couple of other things, which are pretty, pretty, pretty handy. The first one is decorator because we want to add an authenticator helper. So we call decorate and we call this authenticate because this is how we're going to authenticate our system. So this is going to be another async function, which is rec and reply. These are fastify objects. And then what we're going to do, we're going to have a nice try, catch block and do await rec.jwt verify, which is going to verify our JWT token. And in case of an error, I just want to reply.send that specific error. Okay. So that's actually very simple. Now we can change the error if we want to in here. It's fine. So we have added this. Now how are we going to log in? Well, in order to log in, I'm going to create a user called login.js here. Again, export default async function, login app, ops, hey, then app.post because this is a post. And then I'm going to say, call login. And we're going to need a little bit of stuff in here. So I am leaving this a placeholder. So we're going to do rec, reply. Then we need to authenticate our app. So sorry. Yay. So we get to authenticate our app. So what we're going to do in here is I'm going to create to get a username and password from our body. Then I am going to do, here we go. If the username is Matteo and the password is not Polina, we are going to, poor man authentication system never do this in real life. So we need to throw a new error. We need to throw an error. So I'm going to do unauthorized error. Now we need this errors module. This is import errors from HTTP errors. So pretty handy. And then in case we just need to get our token. So with JWT, you get a JWT token and you do app.jwt.sign. And we put the username in and then return the token. Okay. So this is our system. Now we need one little bit more because we need to validate a little bit more our input. So input S from fluent schema. Here we go. So what we're going to do in here now is we're going to open up the schema and do body S.object and then.prop and you pass in the username. Then this is a string and this is required. And then the same goes to password. Okay. So we do a little bit, tad bit of data validation beforehand. So I also demo this to you. Now what we need to do now is we need to have in our protected route, what I want. I want to have in our index here, I want to make sure that all the routes defined in this file and the file that this one imports and loads are automatically authenticated. So what I can do, I can do add hook and I'll call on request. So I'm adding it to the on request lifecycle hook. This is called every single time a request comes in independently on the function. And we are going to pass in app authenticate, which is the method that we have just added. Now I can do, for example, app.get as something and well, app.slash and just say a single rank reply. And just say return this is authenticated. And then do. Here we go. Okay. So that's it. Now we need to test to test if all of this is working, which is, you know, demo gods. Hey, demo gods are not with me as usual. Add is not. Oh, where did I put add? Add. Nice. Here we go. So demo gods are not with me. So if I do protected now, it says unauthorized error. Okay. So in order to log in, here we go. Hey, I'm cheating. So I am sending in a POST request with a JSON as a body and my username and password. Now the answer to this is a long, long bit of token. Okay. So what we are going to do is take this token, which is actually pretty handy, and then pass it in as an authentication to the authentication. So what we're going to do, we're going to say header, which is authorization. It's authorization for a. Okay. Authorization. So we are going to just change this up so I don't mess it up. I messed up enough during this talk, essentially. Here we go. And then protected. Hopefully, all of these would work. This is authenticated. Okay. Now, this is actually pretty interesting because what we can do is we can also add a test for this code. So in order to add a test for this route, what we can do is protected. So here we go. So what we can do, we can actually do something very similar to what we were doing before. And await and call protected. Now we need to call it as a login first. So first we call login, but we need to call it as a post. So we have this URL, and then the method needs to be post. Here we go. And that's it. And we need to specify a body, and we need to say that username. Username, it's Matteo. And password is Polina. Here we go. Which is pretty handy. And then what I want is I want to do this. I want to call this token because that's our JWT token. Then I close this, call JSON. So I got my token. Now I can actually call, here we go. And I am going to go in here, and so URL, here we go. Protected. And then I need to specify another. So that is actually pretty important to, yeah. Error. And then we specify our token. Here we go. And then it's, now let's see if all this work. So this is authenticate, just a second. Oh, authorization, sorry. Authorization, here we go. So this is actually working. Format is authorization bearer token. Bearer token. Interesting. OK, so just a second, folks. And this is not working as it should be. Bearer token, so here we go. Sorry, I need to understand why this is not working. And this is the problem. You know, here we go. OK, so now this is, here we go, REST body. Here we are. Yes, this is authenticated. So what we're going to do is call deep equal. This is authenticated. Here we go. OK, so I just wanted to finish this demo, this long demo, saying that our fastify server is, fastify can be used to build applications really quickly using a combination of several utilities, such as, for example, fastify JWT, the decorators, and the lifecycle methods of fastify. Note that I'm currently using a few, all of this is using bleeding edge stuff. So the new fastify v3 releases, Node v14, and the new native ESM support. So this is not on Spile. It's just running native ESM. And it's actually look pretty fresh to be seen. So I just wanted to point out that this is a URL where this system, this application is going to, this code is going to live. So if you can check it out and play with it, if you want to. The website for fastify is www.fastify.io. So you can look it up on npm and also on Google. If you have any questions about node.js, please reach out to me at Matteo Colino on Twitter or matteo.colino at nearfrom.com. And thank you all. Hi, everyone. Hey, Matteo. How's it going? Really good, really good. It's been a blast. That was an amazing demonstration on the speed of fastify. Well, you know. Our attendees seem to agree because we have a number of questions flooding in. But I, obviously, being a demonstration, I wanted you to ask to kind of fall back and focus on what inspired you at the very beginning, you and Thomas de la Vadova back in 2016, to say, you know what? Let's build this thing called fastify. How did that come about? Yeah. That's actually one of the great starting points. So at the beginning, I thought, well, I think there is space for a new web framework for node.js because I was having some problems with the client with both Express, api, Restify. They all had certain things that were not working well in production for some of the company I was working with. So I thought, well, maybe there is a space in the industry for something that combines having good performance characteristics in production and a good developer experience at the same time. And it needed to solve a certain class of problems that fastify ended up solving. And at the beginning, however, at the beginning, I thought, well, writing and maintaining a new web framework is a massive endeavor. It's really, really a lot of work. And I'm not going to do it unless I can find another human being to start doing this in the first with, to start doing this with. And if I can convince somebody else that this is a good idea, then we probably have a chance. If I cannot, because there's no way I can do, I could have done all of that thing alone. So essentially, it was starting from the point of, this is something that is going to be owned by the community. All the community will have a say on how all these things is built and how this thing is maintained. And so that's why it has very open contribution policy, for example, from the very beginning and so on. And it's fully on open governance and a lot of those things. So it's essentially, it's really focused on getting a very good experience for the contributors as well as the users, because the users are the contributors, essentially. The typical tagline of fastify is, well, when somebody reports a bug, it's would you like to send a PR to fix it? Because it's everybody should be maintaining it. The maintenance is prized to all users, essentially. Nice, nice. And with there being a large number of frameworks within the javascript ecosystem, I was just curious to find out a little bit about what makes fastify stand on its own and its own light as compared to the rest of them. Sorry, can you repeat for a sec? How does fastify differ from the other? OK, there are a few things. So first of all, it's a respect compared. So it takes the several parts from most of the frameworks. So it, first of all, it fully support async await compared to, for example, Express 4. I know Express 5 is going to support async await, but it's still alpha or something. And so first of all, it supports async await and all the latest things. We are also adding, as what I've demoed, the new fastify v3 adds some very good support for ESM, for native node ESM that you can use with Node 14 and Node 12.18, which is great. So we are really keen on adopting the latest and the greatest of the javascript spec. However, we adopt, we do not follow a strict middleware pattern. We follow a lifecycle-based pattern, which means that there are hooks that are triggered at any point in the stages where the request comes in. And at various part, you can inject code. But if you're not injecting code, these things are not triggered, which is really, really powerful because we can, one of the key thing, one of the key problems, for example, when writing large Express app is that you have a lot of middleware where you do, if this route do this. And then if not, call next. And then you pile them up one after the other. With fastify, you can avoid all of that by having this kind of lifecycle. And you can set those lifecycle methods only for a class of routes or only for some specific routes, which is one of the best way to lay down an app, an application. And that cause also the minimal overhead because those things are only triggered for the routes that require them, essentially. Nice. And we do have a question from Michael Zelensky in the audience. He wanted to find out what IDE does Matteo use. So I am a huge fan of Vim and TMAX. I've been using Vim and TMAX for the last maybe 10 years, maybe 11, 12, something like that. On Vim, I recently switched to the space Vim distribution. I was trying it out. And I've been trying it out for a few weeks now, which is kind of interesting, to be honest. I like it. Slightly different from the Vim config that was coming before. I wanted to try something a little bit more modern. Yes, nice. And just because we don't have that much time, which is very, very unfortunate, I wanted to let everybody who's tuning in know that Matteo is not going anywhere. He is going to be in his Zoom room. So all the questions that everybody is asking will be answered in the Zoom room as well. I wanted to kind of wind up before that by being, this is a pseudo-chef related discussion. I wanted to find out what your comfort food is. OK, my comfort food is pizza. And by the way, I'm Italian. So I'm talking of Italian pizza. Pizza is kind of a religion here. So let's not confuse whatever you can eat outside of Italy to what you eat in Italy. And it's very important that I'm talking about Italian pizza. Now, you can get really good Italian pizza in a lot of other places in the world. Most of the time, though, you're just getting something that's, it's just another type of dish that's called pizza. No, I'm not referring to that. And also, if it has pineapple on it, it's definitely not an Italian pizza. But if you get a really good Italian pizza, that's what I would call it. That's like my comfort food. It's also a carb bomb that is going to get straight into my belly. So whatever, so that's kind of, that's kind of it. Definitely. Well, I'd like to at this point, thank Matteo for his time this afternoon for sharing his knowledge on fastify. Like, as I did mention, Peeps, he is not going anywhere. It's just more, he will be in his Zoom room to answer all the questions that have been asked. So and they are quite a number as you're talking about pizza. They kept, the list just kept on growing in terms of more technical stuff. So he will be around to answer those as well. And please do keep those questions coming. Matteo, it's been a pleasure speaking to you. I feel I have learned so much more about, you know, server-side javascript development, javascript ecosystem, and pizza. And I'm sure we will be speaking again, sure. Bye bye. Bye bye.
36 min
18 Jun, 2021

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

Workshops on related topic