Fastify is an HTTP framework for Node.js that focuses on providing a good developer experience without compromising on performance metrics. What makes Fastify special are not its technical details, but its community which is wide open for contributions of any kind. Part of the secret sauce is Fastify plugin architecture that enabled developers to write more than a hundred plugins. This hands-on workshop is structured around a series of exercises that covers from basics "hello world", to how to structure a project, perform database access and authentication.
Fastify Workshop
AI Generated Video Summary
The Fastify Workshop covers the basics and advanced topics of the Fastify web framework, known for its speed, security, and developer experience. It has a strong and welcoming community and is highly performant, extensible, and supports JSON schema. The workshop consists of multiple modules where participants build a small app together, adding more features at each step. Topics covered include logging, serialization, testing, code coverage, validation, authentication, decorators, hooks, and auto-loading routes. The workshop emphasizes the use of TypeScript and provides tools like TypeBox for generating JSON schema and defining types.
1. Introduction to Fastify Workshop
Today we're going to talk about the Fastify workshop. We'll learn the basics and some advanced topics. If you've used the framework before, please help your fellow learners.
So, today we're going to talk about the Fastify workshop. So, we are going to learn the basics of this framework, and maybe go a little bit further along on more advanced topics. Have you used this framework before folks? If you do, you can write in the chat, you can respond, you can to wrap plus one. I don't know how to pronounce your name. So, anyway, thank you. Yeah, a few people have used the framework before. So, yeah, nope. Ah, okay. Somebody has not. Nice. Cool. Okay. The people that have used the framework before, you're probably familiar with the concept. So, if you've used the framework before, please this is more or less the first few exercises are more basic. So, help your fellow learners.
2. Fastify Framework and Workshop Introduction
Fastify is a web framework known for its speed, security, and developer experience. It has a strong and welcoming community and has gained significant adoption. Fastify is highly performant, extensible, and naturally supports JSON schema. It provides logging by default and has good support for TypeScript. Many companies, including big names, use Fastify. It has a wide range of plugins, both core and community-contributed. Fastify's benchmarks show it to be as fast as Node core for certain tasks. Let's start with the workshop by ensuring you have the recommended versions of Node and NPM.
So, why Fastify? So, a few bits and bobs. So, I created this framework long time ago. By the way, hi, I'm Matteo Corlina. I created Fastify down in 2016. So, there's a framework, there's a workshop with the creator of the thing. If you want to take some time to open Twitter, go to Twitter slash Matteo Corlina, hit the, go here, look Matteo up. And then it follow, it's a nice button here. Okay, please do. Thank you very much. You can also follow Fastify itself. It's actually a nice framework to follow. Anyway, I work for a company called NearForm. We are a professional services company based in Ireland. By the way, we are riding. So in case you're looking for a job, that's where we can hook us up. This is the company NearForm. Again, you're probably familiar with it. If you follow me, if not, it's loading. Okay, here we go. It is NearForm. So if you want, we are over here and check us out. Anyway, let's keep going. Whoa, somebody has arrived. Ooh.
So why Fastify? Fastify is a web framework to build APIs and really nice servers for Node. It's fast. It says in the name it's fast. How fast? Well, we'll see. But the other bits is that it's very secure and also it provides some, has a very good developer experience. One of the nicest part about Fastify is that it has a very nice community. Fastify was actually born before, the Fastify community was actually born before Fastify actually even existed. I started Fastify when I convinced Thomas De La Vedova, the other creator of this library, to join me in this journey. So, happy days. And, well, we recently gained a lot of adoption, how much adoption? Well, you can actually look it up by yourself, but in the last year, the number of downloads have been slowly growing. So I suspect there is some Sunday band being filled up and it's going, it has steadily but surely grown. Nice use it in January, beginning of January, end of January, December, this is always the case. So, happy days.
So core features, first of all, it's highly performant. Fastify adds almost no overhead compared to NorthCore. It's very extensible, Fastify, it has plugins, hooks, decorators and other things. And it has a lot of, it's naturally supports JSON schema. So, it's not mandatory, but you should be using it. It also provide logging by default. And we were recently chatting, checking another, it uses Pino under the cover. So I wrote that same 2016, early 2016. And it's a great logger, so check it out. It's developer friendly and it has a good support for TypeScript, even though it's actually improving. Who is using it? A lot of companies. So including some big names or small names, whatever. Enough people are using this. This is probably increased from the last time with the numbers. So, there are, oh, well, not by much. So we have 46 core plugins and 162 community plugins. You can see the list. There is a lot of things happening here. One of the things is, the Fastify community, as I said, is very welcoming. In fact, we have a contribute flag, a contribute tab on our website, and you can see those are all issues that are free for people to do. Very interesting enough, we have even some in Chinese. So I don't know what even this is written. Hopefully this makes sense to somebody that speaks Chinese. I don't know if any of you does, but if you do, there is some. Please help on the Chinese document. So there's a lot of things that are possible. Benchmarks wise, Fastify's been built to be as fast as possible. So you can check the benchmarks, but the result is that Fastify is, for all intents and purposes, as fast as Node core would be for a certain task. Good, bad, I don't know. That's what it is, so you can check it out. Let's start with the workshop, okay? You can execute these commands, I'm going to pass this command down here. Know that you need to use, I recommend to use Node 8, and sorry, Node 16 and NPM 8, so please make sure that you have those two things. How do you do, node-v will tell you which version of Node you have, and npm-v will tell you what version of NPM you have.
3. Workshop Introduction and Fastify Setup
To start the workshop, ensure you have Node 16 and npm-8. If Docker is not installed, don't worry. The repository is public for you to check. The workshop consists of multiple modules where you build a small app together, adding more features at each step. Start with step 01 L Word, check the ReadMe, and run NPM start. Petr Dixten asked about the starting command for Fastify, which involves creating Fastify, adding a route, and listening. Fastify allows extending functionality with plugins, which are private contexts. Split server.js into server setup logic and index.js for Fastify instantiation and product registration. Create a new file in routes/users and export it as a Fastify plugin.
You want to have Node 16, and it should come by default with npm-8. We, at the end of the workshop, we might use Docker, but if you don't have Docker installed, don't worry. And the repository is public, so you can just go around and check it out. You can just... You can... Rafaela posted the instructions here. I'm going to pass it... Pass the link also in the Discord group. It is... Depend about alerts. Who the heck does not have the event about alerts?
Workshop structure. The workshop is... It has multiple modules. You can build. Like, if you start building these application, we build a small app together. If you start building this application, you will build on top of the previous one. And we will... At each step you will add more features to this. And you will have the solution in the folder. In the next folder, okay? And yeah, there are bonus features and hints.
First of all, how do you start? Well, there is a step 01 L Word and you should check out the ReadMe and run NPM start. Okay? So what's written in there? Just to give you a hint, near form the Fastify Workshop, and you can go as a C and L Word and server, well, and sorry, and ReadMe and just run NPM start. Okay? That's the command. So if everything works, this command should run and not error anybody. Let me know. I'm giving you, I don't know, three minutes actually. Yeah. Three minutes to do it. So let's see. I'm going to take the time here on my clock, on my watch. Feel free to interrupt me anytime, by the way.
Oh, hello. Hey folks. How is the workshop going? Were you able to run the command? Hello? Any answer? And do to streaming. I believe my computer is really slow, so the packages are still getting installed. Okay, let's wait for that. Just trying, a little more to save some text. Okay, so, how are everybody? So Petr Dixten is asking what's the stalling command for Fastify. So what you need to do, let's go back here, what you need to do is these two lines, okay. So let's go and let me walk you through your first Fastify program, okay. You have already built it, we've wrote it for you essentially. The program is actually very simple which is create the Fastify, and add a route, and then listen. Okay, so let me show you something that might be interesting. So if you go into server, you're doing things this way, what you could also do is this. So... Can you increase pounds? There is a way to just show the code screen and not the presentation. Yeah, okay. Okay. So what you can do is you could also write things in this way. So basically relying on top-level await and not having a function that drops it. Okay? However, I would recommend to do the function because we will keep working on it later on. So, you might just want to do time detail await. You know I'm fond of a top-level await. So just in case you have any doubts. Okay? Let me go back to the slides.
Okay so next step. Fastify allows the user to extend its functionality with plugins. So plugins are a key fundamental construct of Fastify. And you can actually read up into this link with the document. I'm passing it in the chat, okay. In the chat and the Discord channel, okay. In Fastify everything is a plugin. So you can do fastify.register and your plugin name. And you are basically creating a new context which is private. So what I'm asking you to do is to split server.js into two files. One it's a server.js file that contains only the server setup logic. And one index.js file that contains the code to instantiate Fastify and register products. Then you need to create a new file inside routes, routes, the last users and export it as a Fastify plugin. Don't want to jump too quickly. So keep working on your folder and let's, I'll leave these up.
4. Creating a Plugin in Fastify
Let me know if you have any questions. We left off at step one, where we split the code into two files. Now we can create a plugin by creating a folder and file. Register the plugin in the index file. This is an example of how to do it. Plugin is just a regular function that needs to be exported and registered. You can use type definition for auto-complete in Fastify with plugins.
Let me know if you have any questions, okay. You know, this is more like baptism of fire type of situation. Okay, so let me start doing these exercise a little bit myself, okay. So, wait a sec.
Okay, so we left you when we were at this step here with this kind of with this thing. This is step one, okay. So what you can do, yeah, what you can do, let's repeat it loud, Rafael. In case you have any questions, please share in the chat or open your mic. Make sure, okay. So the first thing is to split this into two files. So how do we do that? Well, we open our index.js file and in the index.js file we can copy this partially, okay. And we can just adjust to function build. Oh, okay. And then export default build. In this, in this one, I would do build from oh, index.js. Then I will just do, okay, does it make sense? Hopefully so. You guys are very scientific. Yeah, definitely, definitely. It's making much sense.
Okay, so please interact a little bit, otherwise I'm talking to the wall. So now you can do node server. Oh, did wrong, can't read properties are fine, defined. Well, did, oh yeah, of course, that this is an asymptotic, so I need to await it. Amazing, okay, so now this is starting. Our localhouse 3000, and it's printing a little work. Okay, so now we have separated this, so how do we create a plug-in, okay? So in order to create a plug-in, what you can do is, we have work here, what we can do is create a folder routes, and then users, and here we have this file, and then export default app, users sent. We can just do return users. Recover. It's nice... GitHub copy that is nice to create, fix your data. So here we go, we-recreated two users and these are sick. Now, how do you register this? Well, we go back into, we go into our index file and then we, you can actually do, well, I call this app, call this app, and then I would do app.register and you can even use dynamic import if you want to, routes-users.js. And node-server, and we do QUIRL, oh, it did not start. Seems about okay though. Ah. Yes. Okay. Let's try again. Where did I get something wrong? Let's see if this... Yeah, it's not loading something. A drop in my facts. Ah yeah, this is a sink. Yes. Thank you. Here we go. Thank you. Here we go. Now it's still, it's Elo Word is working and it's Users is working too. Cool, okay. Cool. So this is an example of how you do this. It's let me share my screen. How are you doing? Have you been following along? Are you stuck? Are you able to, nice, okay. Are you able to work on this? Okay, so.
One question. Yep. So Plugin is essentially any module, right? It is not a linear program. It is just a regular function that needs to be exported and we can register. Yeah. Thank you. Yes, it's just regular function. Yes, it's not, there's nothing. There is something special about it in the sense that the way it's loaded. So something that is useful, and I'm going to show you this is, okay, you see this line, long line, okay? So what you can do in, let me share again my code. It's, if you like code completion and you probably do, okay, what you want to do is you can actually, okay. What you can do, this is not needed. Actually, this can be just an empty object. What you can do, you can define it's type, at the top, even if it's JavaScript, because then you could do, you see that it has all the methods already filled up, so you have auto-complete on the thing. Note that I'm using VI with TypeScript and auto-complete, so anyway. This is a trick that you can use to get auto-complete done already in Fastify, so when using plugins.
5. Logging and Serialization in Fastify
In this part, we discuss logging in Fastify using the Pino logger. Pino is a fast logger embedded in Fastify. You can enable logging and configure it using the logger option. PrettyPrint can be used to log human-readable output. We then move on to serialization, which is an important part of Fastify. Fastify uses the AJV library for schema-based request and response compilation and validation. We do a quick exercise to validate user responses using Fluent JSON schema. We also mention the TypeBox library and declare a schema for a route that returns a username. The response is defined with an HTTP 200 status code.
Now, something for the future, like this is buried down at the end. Rafael, if you want to open an issue in the meanwhile, this is buried down at the end of in the middle of the TypeScript reference, okay? So maybe we might want to move it to the getting started. Yeah, actually, doesn't have a question about it because some ports works, but when you encapsulate with the clear module, it works. Here, essentially I would just put it here. Okay, yeah. Okay, so it might be useful just to add it so that, anyway, cool, okay, so. Sorry here, it was, let me show you, like let me, I forget that I don't have both screens showed. So if you look at the docs, Rafael, you see that it is, we have the, this is your first plugin and we can actually add it here. Sure, sure thing. Anyway, cool. So this is the first, the second exercise, okay? How many of you were able to do it? Hopefully, all of you, I don't know, a few of them, a few of you're not telling me how you're progressing. So hopefully you will be doing this very well.
Logging. So next step that we are going to cover in this, in today's workshop is logging. Classify shifts by default with a logger called Pino. What is Pino? If you haven't heard about Pino, check it out immediately. Pino, it's a really fast logger, so it has a nice logo. There's a pie in tree, happy days. And you can just use it this way, but it's naturally embedded in Fastify. And Fastify is a logger option that you can use to enable logging and configuring. So you can open up your logging documents, logging docs, and you can just turn on logger, okay? You can also configure PrettyPrint to log stuff. What is PrettyPrint? PrettyPrint is, you know, for something nice for humans, not just for people. Pino by default logs in JSON so that you can easily process the things, okay? Give you three minutes. Like, folks, turn on the logs. Okay. Working, okay, great. Yeah, okay. Cool, done, okay. Have you turned on PrettyPrint? How does it change with PrettyPrint? Yes, okay. Cool. So you can turn on PrettyPrint with the logger. You can log from the Fastify instance. You can also log from the route itself. And you will get something like that, but with colors. I hope you like colors. Do you? Yes. With PrettyPrint, you will getting... Note that you are getting a lot of charty values. So, you will see multiple lines, you will see that you had incoming requests with this payload, which has a request ID, then the same request will say all the logs for that request, and then when it's completed, it will tell you how much time it took to respond to that request and the request ID for it. Okay, so pretty good, yes. Let's move to the step four, serialization. That is one of the most important part in the Festify. It will really enable you to be performer, to increase your performance, your service. Also, because Festify use a schema-based approach under the hood that is AJV, is a library that compiles your request and your response to be able to answer fast and serialize it pretty fast as well. And also it validates your input by just passing just couple of lines of code. So, I will pass here uh, the, the, the, in the chat here and also in the score. I have friends for the validation and serialization that is pretty important. It will enable you to validate your code and validate your response. And it will enable you to reach that a hundred percent of performance using Fastify, okay? So my code, please go to the next one. So let's do a quick exercise, let's validate the response of the users the users wrote using the I schema created with the fluent JSON schema you can use others schemas, for instance how many folks have used that joy for validation? I don't know if you heard about it, but it's also a pretty cool library. So that's it, I think that I will share my screen at this time. So you need to stop Matthew and start my library. And for today, we're gonna go with a Drupal, Drupal-24 Pro libray, I will be using two libraries, because they are a different library and it's a Python library. And so we are also using a Mac. So we're gonna start with Python, because it's our largest library. And the reason, is because we wanna be using C morning to do the tests for this example. So all the members of the team are going to be doing this, since they are so small, and this realization that is... Okay, TypeBox, is a good one as well. I mean, I have used a TypeBox for almost my project, so it's good to know as well. So let's go to our source code. We have added the pretty print here, and it's important the role, but right now, we show a goal to the role and it what the answer we should provide to the users, and it will enable to answer fast in order to compile it. And the first thing that you do is likely the installing of FluentJSON schema, but with the NPM CI command, it will be installed by default. So, go to your work here file and to your users folder, and then let's declare the schema. It's already declared, but I would just explain it a bit. Festify uses some receivers as a second parameter of each route, couple of options, and one of them is the schema. Throughout the schema, you can combine some responses and some requests that you want to receive. For instance, in this route, we are returning the username and the type of the username is a string and also is an array of objects, right? No questions so far, I assume. And going further, we shall define that this is our response. So we create our response object and inside of this response object, we define that for this route, when everything works fine, we will return HTTP 200. 200 is the default response of specify when you don't specify anyone, okay? So if you return, for instance, just an empty array, it will be an empty array with 200 of response code.
6. Testing and Type Definitions in Fastify
When testing Fastify, if you get a 200 status code, the response format and schema are defined. Changing the schema can lead to unexpected results. The validation will not kick in if the response is a string. Fastify's runtime type definition differs from TypeScript's compile-time definition. Fastify's approach is faster and allows for dynamic data sources. TypeScript provides compile-time guarantees, but cannot validate at runtime. Using both approaches is possible. Fastify's runtime validation is faster and provides more flexibility. TypeScript is easier for development, but requires additional tooling. Fastify will support both approaches in the future.
You can also test it using core, okay? So for instance, if you go to the step four, you can test, and do a core with the variables to the localhost 300 users, it will return you HTTP stats code that is 200 is the full of a good response, okay? So yes, when we answer at 200 stats code, we want to define hold the responses format, hold the response is I don't know the exactly word, but how is the response schema, okay? So right now we are assuming that the response will contain an array, and it's items will be an object with a property cal it username and its type will be a string. And also it will be required. So it was the final. In case of if you change the username, for instance, to one that is not a string anymore, and you try to run your code again, no server and then you do a curve. It is working. Okay. Not as expected I assume. Let me see. For instance, if you. Okay, yes, it's this one. If you change the return, for instance, here we are returning an array, but you want to return just rel word. But your schema was defined as an array of object. Okay, it is not what it's supposed to do I guess. One second here. The schema was defined, it was asynchronous. Okay, I think that it would be... Any idea, Mattheo? So you are? I just trying to answer a different answer than the schema. So you're not schema. I think it's not. Let me just check the example. Okay. Organization. It's 200. Okay, I think that is this one. You're missing the required. You need to specify required in the items. No. I have to read. Let me check. So node CD. Step 04 serialization. Okay. Okay, this works. Then let me try duplicating this. Okay, yeah, that's it. I have found that the schema is this line. Yeah. It's yeah, the property. No, the difference is that if you pass in a string, it will bypass the schema. Yeah. So if you are returning a string, okay you are, if you look at the curl dash V, you will see the content types, it's text, and therefore the validation will not kick in. Yes, yes, I forgot this, we have discussed it, but just for example, here we have the name, we have a specifier that the username, the username is required, is a property required for a HAN server. So for instance, if I changed the property to test, it will return me a non 200 code, internal server error. So it also, it also prevent you to answer bad things, to expose much more than expected. So the question for Jacob is, why not use TypeScript for type definitions? You can, but there are a lot of folks with doesn't like the TypeScript approach because it bundles, it's do a lot of things, but FATFI is fully supported by TypeScript by the way. Okay, but also is important to mention that this is completely different than a TypeScript, type definition. Okay. Here, we are defining thing in runtime. For instance, if you are using TypeScript and define a type here, my amazing response in TypeScript, it will not work, okay? Because it's type, when you compile. Differently, as a result of the function, I would there define a TypeScript, the proper types of definition, and I would get at edit time or compile time already at error instead of getting them at runtime. They differ, there is a fundamental difference between those two things, okay? So, first of all, if you specify it in this way, get the rendering of the JSON is faster. So we can use, instead of just simply editing it, you will get it immediately, okay? So then, the other problem is, so one advantage is speed, this is actually significantly faster than just returning the JSON. The second one, which is, to some extent, even more important is that you probably don't know what you're returning, really. Like TypeScript is not getting it, so TypeScript is all removed at compile time, right? So, essentially, you just don't know, okay? Because something else might be... In this case, it's simple, but the data might come from another library, okay? Okay? For instance, if you have a response like this and you return and you have a property that is any and you define anything here and you return A, it will work in TypeScript, you know, and it's not expected to work. Yeah, but you should not use any. Yeah, you should not use any, but sometimes you could not trust the source of your data. For instance, it comes from the API, okay? The API can answer to anything, okay. Okay, but then you still, still the problem comes from API, but then from the schema, you also don't know it. You also don't know, but you compile it and you, instead of returning 200 stats code, you return up 400 stats code, because it's validated during the runtime. In TypeScript, you can only have the guarantee during the, when you are writing the code during the compile time, but not in run time. But it's a lot, as far as I can see it, if I would write it in TypeScript, the development is a lot easier for me because I don't have to play around with some kind of other program to generate things where points, function calls might be missing or whatever. And I'm directly getting the error inside but they are doing two different things. Yes, I understand there are two different things. This one is at run time and the other one is at compile time helping me writing the correct code. There's nothing that bars you to do both. Yeah, except for a bit more work but. Well, we will fix that into the next release of Fastify will enable you to do the both. So that will simplify a lot of things. So anyway.
7. Handling Validation Errors and Writing Unit Tests
Fluent JSON Schema provides auto-completion and simplifies writing JSON Schema. TypeBox is recommended for working with schemas and TypeScript. Handling validation errors in Fastify is explained in the documentation. Testing is important, and Fastify provides flexibility with the inject function. Let's write a unit test for the index.js module. Use Tap for testing and import the beauty server. Test the GET users route and assert that it returns users correctly. Use Fastify's inject function for making requests in the test. Ensure the response status code is 200. Run the test using NPM test.
There's a question of is there a difference between using fluent JSON Schema versus just a regular JSON Schema object, no. No. Okay, so fluent JSON Schema is just us not wanting to do. Like the point is with fluent JSON Schema you get auto-completion. It's very simple to write JSON Schema with it. Okay. If you use something else, if you use JSON, normal JSON Schema, I typically forget some stuff and get it wrong all the time. Okay? It's... Yeah. Yes. And this is very good information, typebox can provide you everything that you want if you want to work with schemas and TypeScript. It works really good. Okay? So. Well, I think that the serialization was explained. If you have any questions, please share, let's move forward. Okay? Yeah, I have one. Yeah, sorry. The workplaces at the moment, so that's why I have my camera turns off. But anyway, so I have a question about what is like the recommended like fastify-ish way of handling validation errors, right? So as far as I remember, like my TO is not a big fan of using like instance-of to check something, right? But if you need to check that specific type of error is exactly like validation one, right? So as far as I remember again, in fastify-docs, it says that you should check that validation field is like present. Let's cover validation during validation. So we have a validation example out. Yes, here. Okay. So two steps down the line. Okay, okay. Great. Okay. So that's it. The solution pretty simple as we have seen. So we have tried out, so they make the response wrongly. It will return a 500 that's called internal server error. Pretty good. And the step five is let's go to the testing. Testing is pretty important and specify, make it very flexible. Throw with the dot inject function that is thanks to like my request. So let's make a test for your index.js. Js module try to use no tab and use specify inject for that. Okay so let's move forward. Let's write a unit test for the in index.js module or index.js is modular is that so you can, we are returning the best file with the rote. So let's create the index.test.js but firstly you should make sure that you have installed Nodes tab, okay. So you install it, create a folder. You can also, after installing tab, you create a new script that test your code. Okay so going to the test file we need to import. Tap, we need also to import our beauty server, okay. It is in the index dot j s, great and also tab provide this utility that you create suite of tasks. So let test the GET users and we can do it asynchronous or not but synchronous will be more easy. So let test that we should return users correctly. Correctly. In this function, in this callback, we will assert it. So let's create the Firstby instance, telling our build server. And then all our server is created. We just call the festify inject that will make the request to users just simpler. You don't need to do a festify listen or festify ready. Doing the fastify inject it will automatically request the festify route. So pretty good. And then we showed, make sure that the answered, the status code of the answered in the exercise says that we should check if the answered is 200. So the answered should be 200. Good. Let's test this one. NPM test. Okay. We have a passive. But also we show the task that we expect. We returned the expect the Hey of your users. Okay. So let's do the design. I'll wait. We can do it to a chase on the response is equal. I'll wait. Rest on chase on that will bring you the body of the request of the response actually.
8. Code Coverage and Validation in Fastify
In this section, we discuss code coverage in Fastify using ESM and the challenges it presents. We explore the solution of using C8 instead of Tap for code coverage. The difference between the C8 implementation and the Tap native one is explained. We also address the question of why not use Jest for testing in Node.js, highlighting the issues with Jest overriding node globals and causing errors in perfectly valid code. We provide a link to a major issue in the Jest library for further reference. Finally, we introduce the next topic of validation in Fastify using AJV.
And then you you don't need it. You just check if the chase on the response is the same as username equal. Let me check what is our answered is Alice and Bob, right? So Alice and Bob, if we run the test again Okay, we have our route. That's it, okay. Pretty simple, no? Now you might wonder why the code coverage is all reported to zero. If you look at the code coverage report is all 0%, okay. Yes. It's because we are using ESM, ESM and code coverage do not play well together these days. So that's life right now. If you want a solution to that, okay. One solution is to disable tap internal code coverage and instead use something called C8. I'm going to share my screen. So what I've done is you're in step, you're testing. So in step five test, whatever what I've done, get diff, what I've done here, I've just started this thing. Dependency is C8 and you can run it with C8 app. And when you run it, it will automatically so the NPM test will not, but if you do, NPM run cov will give you the right results. Well, it's not like the first one is the one of tap and the second one is the one of C8. So then you do that per C and as you can just do no coverage. So that's rely on C8 to do your thing. And it's actually working fine, okay, what is the difference between the C8 implementation versus the tap native one, tap uses an old library called Istanbul and while C8 uses, it's a new code coverage tool provided by V8 itself. Two different options and one is the old school one, Istanbul and it's Istanbul does not play well with ESM unfortunately that's life. Note that I think you can, now it has improved the situation so I think you can do this now. It's working, okay? So you can actually just do the one liner import if you want to. There is a very good question. Why not tap instead of something like just? Okay now you have asked the right question. So this is one of the things that you should not be using you should not be using test to test your node systems. That's the TLDR, why? There are a few long bugs about it. Okay, a few long discussions about it. It's a lot of conversations, but the end result is that with Jest, Jest overrides all the node globals, so you instant soft checks would not work. That's the first one. Then we have, then it also tweaks stuff on the handles that are created by the system. So perfectly valid code would start to error in Jest. Perfectly valid code for node will just error in Jest. And you need to do, we need to do, as maintainers we need to do certain shenanigans to make sure that Jest still works, okay? While it doesn't in some cases, which is very weird, okay? Or very, very weird, unfortunately, okay? I will pass you one of the major links to Jest, it's an old problem of the library, okay? I am just always finding time to find the right links. So... Here we go. And Facebook. Jest. Think this is the issue. Yay. 2549. If you want to know, this is partially the reason. This is going to untue in any possible turn whenever you don't expect it. So here we go. I'm passing it here. Okay, so this is essentially... There are a few workarounds to that problem, but to be honest, it's not worth it. There are other problems about it. You see, this still is a bug. It's considered a bug, okay? And it has a few hundred comments about it from all over the place. Okay? And you see, look at this. Timers, it's just, you know. Anyway, just, from my point of view, if you're using Node.js, you're actually looking for problems if you're using testing Jest. Jest works greatly for working on the front end, by the way. I'm just, I am pretty happy about it on the front end side of things, okay? Oh, yes, so I, somebody's saying, I missed what you do to get your tests to do the stuff. So you did C8.tap, so instead of tab, you just do, you can just do C8.tap. And then, however, then you need to, you can do this, but then you also need to have in your Tapper C file. Oh, it's, oh yeah. You need to have a Tapper C file. And in this Tapper C file, you need to specify the no coverage true. Or alternatively in here, you need to put no coverage. So tell Tap not to use its own coverage stuff and just use C8 for coverage, that's it. So, this is nice. Okay, we have, I'm going to cover the next step, which is validation. So we are down to validation now. So let me open up the next screen share. Okay, here we go. Share, share. Okay, validation, Tap. Sorry, AJV.
9. JSON Schema Validation in Fastify
In Fastify, we use AJV for JSON schema validation. AJV is one of the best validators and follows the JSON schema standard. The exercise is to create a new plugin in routes/login.js and define a post route with a schema for the request body. The schema should ensure a suggestion object with username and password fields. Give it a try and we'll review the solution together.
So what we use, we use, in Firstify we use AJV. There's a long documentation about validation in, in the, in our doc. It's a long reference list. It explains a lot of things. AJV is one of the best validators for a JSON schema. So it's, it's what I would recommend people to use for validation.
You want to ask why JSON schema? Well, it's a standard, okay? That's, that's it. Other alternatives might be good enough, but it's way better to have a standard and to be compatible, for example, with Swagger and make sure things work automatically.
So the exercise is to create a, this is a document you can check out, the documentation to solve the exercise. I would recommend you to create, I will ask you to create a new file, a new plugin in routes slash login.js, in which you create a route, which is a post. And in that post, you can specify a schema for the body of that post, that ensure a suggestion object containing two fields. One is username and the other one is password. Both add strings. Cool? Give you five minutes for this. We'll see where we are at in five.
Okay, let's walk you a little bit through the solution and the exercise, okay? Oh, does anybody needs more time to figure it out? Okay, so, that me. Share my screen, okay? So, what is it? Need to change my share, and just here. Okay, so I'm still in the previous folder, so you should be able to follow along. So, the question is to create a new file called a login.js. So, I'm opening it up my editor, doing login.js, and then I am here, and I need my previous, like, step. Why it's not moving forward, okay. So, I need actually this from my previous one that I added, well, not the plugin. Okay, nope. Where did I put it? Work here. Okay, so I'm copying in my nice sentence. Rafaela, if you have some moments, can you maybe open an issue on the workshop to create the, to add the types everywhere, so people, would. Yeah, I have mentioned that you, in your past issue, take a look at that later. Ah, okay, perfect. So, we are here, I'm logging, so I'm passing this here, so I can export default. Let's create, okay. I tend to prefer C in calling this app. So, we want to, this is, we want to create a post. So we do app dot post. Well, it's not working. Yeah, it did work, okay. Now, this is goes to mongo. We don't really want to go to mongo, okay. But what we want is we want to accept a schema. So, let's say return some. And now, what we want to create, we want to import S from when there's schema. And then we can create our schema here. So, this is our schema. And for the schema, we need to specify our body. And it has a username and password. Now, it's really handy. I don't know, like using Copilot for doing live coding seems like magic most of the time, okay? So, if you see lots of smarts of the complete is Copilot doing his thing, okay? So, now, this requires a username and a password. And then we want to validate it. Now, I'm going to cheat a little bit and then going to copy some of the code. So, we have our response. Oh, well, it's just the username and password. Copilot is a game changer. It is. So, we are not actually validating anything here. We're just returning the username. And the password. Now we can also say the response. And we want to say it's the username and the password. Okay. So, cool. Mm-hmm. Let's see if I can run this. Node server. It's starting. Okay. So I can do curl-x-post-h. We need to specify the content type. Application.json. That's what we are sending. The body, which is dash-d. And we are saying, like, username.
10. Debugging and Validation in Fastify
I need to use double quotes. Username is foo and password is var. The login is not found because I have not added it. Now it's working to some extent. The returns are empty, but we'll see why in a moment. Let's try to validate our stuff by curling with a different password, and we'll get an error saying that the body should have a required property password.
Oh, I need to use double quotes. Username. And it's foo. And password is var. Very strong username and password combo. localhost2000. Oh, bad JSON input. Where did I put my JSON in from? Oh, yeah. I did not put the last quote. Cool. Okay. And the slash is not found. True. So we want the login. And it's not finding the login. Why? Oh, yeah. Because I have not. I added it here. So what I need to do is add login here. Oh, no server. And now it's working to some extent. It's already picking it up. Nice. So instead, but the returns are empty. Why the return is empty, we'll see that in a moment. So, and let's go to three. Let's close this up, it's too many things. So routes, login. So it's an object. Let's see. Ah. It's going well. Returning. Let me, let me cheat for a second. Ah, yeah, true. That's why I did it wrong. You see it from time to time. The auto-completion is too smart. By the way, that syntax is actually pretty good, but the actual syntax is this one. So you can see that. If you take the justification from the results, that's actually all of the system's outputs. So you don't know how many outputs you wrote that first. It has basically the same value that the system has when you use the autocompletion, which is 256, and then it has 24, which is the number of inputs that were given and how many were taken. So let's try. Here we go. Okay. Now it's working, okay, great. Perfect. Okay, now what we can try also, if we can try to validate if our stuff is correct. So I can curl, and let's try to specify a different, let's call it pass. You'll see that we get an error, and the answer says, well, body should have required property password. You will get a much more detailed error in your output.
11. FastFi JWT Plugin and Authentication
Let's move to the FastFi JWT, a widely used method of authentication. FastFi provides a plugin called FastFi JWT that uses JSON-WebToken internally. In the next exercise, we'll register the FastFi JWT plugin in index.js using a hard-coded string as a secret and return a JSON-WebToken. I'll present the solution, and then we can work on the task together.
Okay. Okay. Let me start, the share, and let me start sharing again, the slides. So here we go. Here is the slides. So this was the exercise. This is the solution. As you can see, we have implemented our schema. Okay. Right credential, wrong credentials. So let's move to our important part of every API. I would say that every API will need to have an authenticate or if it exposed publicly. So let's move to the FastFi JWT. That is a JWT is a method of authentication widely used across the globe. So, FastFi provides a plugin called, FastFi JWT that uses internally JSON-WebToast. And in the next exercise we'll change the index.js so that it registered a FastFi JWT plugin using a hard coded string as a secret. And you can create a return code for that, return a JSON-WebToken for that. I will present to you the solution and then we can write the task together.
12. JWT Token Generation and Decoding
We import the HTTP errors library to handle unauthorized errors. We test the login route by sending a request with a username and password. If the login is successful, we return a JWT token. The token can be decoded to retrieve the username and can include additional properties. We register the JWT plugin with a secret and update the response schema to accept the token. The login route now returns the JWT token as a response. We can decode the token to access the username and any additional properties.
Okay, so let me just share my entire desktop. So, right here we have our last example and then we have the loging that we just returning the username and password. But now we should check that for instance, if the username is totally different from the password, so we should throw an error. We should tell to the requester that it is not allowed. So, errors unauthorized. But firstly we should import the error. So import errors from HTTP errors. That is a nice library. It provides the response schema to you. Pretty good. Just it. Let's try. So, if we go to the node index, no server actually, and then we try to do a Core D, if I remember correctly, passing the username. Let me open better here. Username is Rafael, and my password is Rafael as well. It's a local host. 3000 login. Okay, we should also specify the header, content type, if I remember, just to specify that it is a JSON, okay. Content, content type. Content type, yes, application, JSON, okay. Let me see if that works. Okay, am I missing something? Should work. Great. But here we are just returning the username and password when a success happens, okay? But when I perform a login, I should return my token, okay? I token of authentication. So let's use the first five JWT for that. This five JWT sigin. And then you can specify any object that will be decoded to the, encoded to the token. So let's pass the username, for instance. Okay. So if you run it again, I think that they should import the, yes, it's this. I should register firstly my app, okay? So import, specify JWT. And to register this, this plugin you should provide a secret. My secret will be this. Okay, I can, I can put anything here, okay. So now we are getting that the username is required, mostly because I might send something wrongly. Let me see here. One second. It should work. Okay, yeah, yeah. It is not working because our latest respond buddy is username and password. Now we should change it to accept the token as a required the string. Okay. So I hope that it works. Great, it is working fine. So now when we do a request, let me clear for you guys, and then I have a JWT token as an answer. So for instance, if you go to the JWT.io and paste this code, it will bring you the token decoded data, that is the username. If you want to add other property here, you can. Okay, for instance, if I add hello, it will bring me hello world here, and then I perform a new login, I'll get the new token, I'll get the new token, and if I decode this token, I will get the hello world. Okay, all good so far? Any question?
13. Testing the Login Route and Changing to Auth Check
Let's add a test for the login route. Create a server and perform a request to the login route with good credentials. Check if the response status code is 200 and ensure that the response token is correct. Use the Fastify JWT mock method to fake the response from the token. Decorate the JWT method with a stub from Sinon. Return the example token and check that it works. Run the test and verify the result. Let's continue with the second exercise of step seven, which involves changing the login route to the auth check. When the check fails, return a 401 unauthorized status code. Share the solution for the test, which involves passing a wrong password and checking that the status code is 401. Let's keep going and feel free to ask any questions.
So let's add a test now for this route, okay? So let's, or latest code was trying, was testing when I value the post is tested. And also, we should now test when we send credentials, good credentials. What means credentials is when a password is the same as the username, okay? So let's remove everything here. Let's rename it to test token with the correctly credentials. Okay, so let's create our server, build server, okay? And let's do a request, quest res equals await testify inject. And we showed a performer request, we will do the request to the login route using the post method and our body will be the username the username as Rafael and the password as Rafael as well. It means that it is a good, with the request it should works, right. So we should check if the response that's called is 200. And also we should make sure that, the JSON the response of their server. JSON the response token, right, is the same as, well, here we have a problem. How we can fake the answer, the response from the token. The Fastify JWT provide a mock method that you can return a stub, for instance. When you are creating a server, when you are creating a test for your server, you are registering the routes that you need to test. In this file we are testing the login route, okay. And here we are importing the login route that basically create the post route. But also we are making use inside of our logging route, we are making use of JWT plugin. So here we showed, register the JWT token, but we still have the same problem that the JWT provides a dynamic token. How we can assert that this token, this token is correctly. We can use this Synon stub, Synon is a test helper. It will help you to create mocks for your request. Okay, so you have this Synon from import, Synon. And then here, you can decorate the JWT by yourself. The JWT method, because in the login route, you are using the Fastify JWT SIGIN. So here, we show the mock it. We create the SIGIN method, and it will be a stub from Synon. Okay. And here, the only thing that we should do is Fastify JWT SIGIN... returns, let me see if I remember it correctly. Return. Yes, returns the token that they want. And now, okay, it is a kind of standard for plugins in Fastify system to mix property and methods to request object. There is a lot of plugins in the Fastify system. There is no standard plugin, I mean, you can, for JWT requests, we have the Fastify JWT that is officially maintained by the Fastify team. Most of the plugins are maintained by the Fastify community, but the core ones like the JWT, the MySQL, the PostgreSQL, is maintained by the Fastify core team, okay? So here, let's return the example token. And then we can just check that example token will work. Okay, so it should work. I guess, let me see. Let me run the test. So NPM run test. It was failed, let me see why it should be equal. Okay, it is not before the route that we have created. Okay? Tap also provide a good way to test a single route that you can just specify tap test login test Jadon,.js. Post login with an invalid post payload. Okay. Let me shoot that bit. Okay. Removing the used, that it's yes, is the other one. So it's working fine. Okay. So we have created our test. We have mark at the answer and then you can keep going. By the way, the test, all the tests is available in the step seven, but don't shoot. Okay. You should just look at it when the steps is done. Otherwise we will not get any insights from it. Okay. Let's keep going. Any questions so far? The solution pretty clear. Okay. So let's do the second exercise of day step seven that is change the routes login to the outs check. And when the check fails, we showed the return up 400, one authorize it, okay. The first example here we did together, okay. The second one is up to you. I will just share the solution for the test. It's pretty simple. You just need to pass a wrong password and it will basically enter inside this zip and it will throw an error. And then you can just make sure that the status code is 401, okay. This is just it pretty simple. We have seen the most complex stuff in the, when you are using sign-on to mock stuffs and so on, it is pretty useful. And I think that we can keep going, other, if you have any question share, please share.
14. Storing Configuration in Environment Variables
Today we'll talk about storing configuration in environment variables using the Env Schema module. We'll create a config.js file to load a JWT and secret environment variable. Then, in server.js, we'll import the config and use it to instantiate the JWT project. It's important to separate the configuration from the server implementation and pass the config down from the top file. This allows for easier testing with different setups and avoids the need for mocking. Decorators are used to protect routes that can only be accessed by authenticated users.
And I think that we can keep going, other, if you have any question share, please share. We'll move to the next talk about config. I think that there is no questions, right? So. Okay. Let's move then. Config. So this is one of my favorite topics. It's config. And one of the things that most people can get wrong in how they structured their code and applications using Fastify, but not just Fastify, kind of almost everything.
So one of the biggest, one of the recommendation can be to use environment variables to configure your app. What you don't want really is to have your JWT secrets encoded in the source code of your application. Very recently I was a, I found it plenty of time, these types of secrets embedded in the code. Also, you don't want your AWS keys passed in your code base, okay? There's a lot of things where you don't want to put those stuff. So, usually you don't want to, genetically you don't want to commit config values straight into a repo and you might want to manage them using environment variables and injecting them into the system, into your process, at runtime, right? When you deploy it. You could potentially also fetch them, for example, from some sort of secret storage, for example, all vault. Or AWS key storage, I think or AWS secrets, anyway. We have a few module in the Fastify community for that to check out, so. But today we are going to talk about storing your configuration in environment variables. So what I, the module that we're going to use, there's always a module, right? You have learned this in this workshop. Every single, at every single turn, there's a new module that you can use. Okay? Every single, every single step of this workshop, there's a new module that get introduced. There's a module for that. It's called Env Schema. I really like the Env Schema module because you can have schema. And it's actually pretty nice. What you do in a schema, You specify a JSON schema for your environment variables. You are saying for example, that you want a port and stuff. Okay? And then you actually create your Env Schema. Okay? And you have your conflict. Note that it also automatically supports.env by default. And.env is actually awesome because you can just put your, locally when you are developing, you just create a.env file and it will automatically pick up your environment variables from there. It's pretty handy. I really like this. Note that you can also use, like with ajv, you can actually automatically cast things automatically. So, it's pretty handy. Also, fluent schema is supported and all those things that, you might want to use also TypeScript, like lots of stuff. So, what I'm asking you is, to create a new config.js file that use N schema to load a JWT and the scores secret environment variable. Using the dot em, like looking at the dot em for development, essentially development purposes. And it validate its value. And then, you want to, your server dot JS, you want to import your config.js file and provide the outputs to the parsed environment stuff. Sorry, the parser config to the build server function. Inside. In the dot JS, then use that configuration provider to actually currently instantiate the JWT project. Okay, is it clear? Yeah, okay. The enviable is always a headache. Yeah, I know it's always a headache for folks, I know. So that's why we are covering this here. So, and also, the important, part of the important part, the important here is that we are separating the starting of your application, so and parsing of the configuration from the actual build server implementation, okay? So we are, don't, a mistake that a lot of team do is to have a config file and from every place on your code base, you load up that config file and you just take stuff from there. Or randomly in the middle of a request, or processing a request, you are accessing the environment process dot M to check the environment content of configuration, okay? That's not cool. So instead, try using this pattern where you are passing, you are actually having your config flowing down from the top to the value step. I know it's a little bit more verbose, but it actually helps in compartmentalize and encapsulate and scale your code. Yes, so the question is to use it, to use it in other files, is it better to pass it to VR property? Yeah, my main recommendation is to always do something like that. Okay, so from the top file, you import your config and then you pass it to your FASTA file instance to boot it up. Why this is better? Because when you're testing your application, you can actually test it with multiple versions, with multiple setup and multiple configuration values without having to rely on mocking to set up your configuration. Which is way better overall. So you can just avoid a mock and just pass it down. It's also more clear because it's clear where things come from. It's who is responsible for a value. So yeah, you know, that's my, that's the way I would implement this, so. And here you just do your configs and your stuff. Does it make sense, folks? Great. Do we want to do this? I'll do this and then you do the next one. I can do nine and 10, Rafael, and then, or you want to do nine and 10? I can do the 11 and 12. You can do 11 and 12, okay, perfect. Yeah. Okay. Amazing, okay, let me start with nine, okay? So let's start with talking a little bit about decorators, okay? What are decorators? So, in the previous tab, you used a JWT token. And with this JWT token, you can use to access protected routes. In this tab, what we are doing is to protect a route that can only be accessed by an authenticated user, okay? How we are doing that? Well, first of all, we need to create a decorator, okay? Fastify extensibility model is dividing it. It is provided by plugins.
15. Using Decorators and Hooks in Fastify
In Fastify, decorators are functions or objects that are added to the request, response, or main server. Hooks are used to populate decorators. Fastify does not use a middleware pattern like Express. Instead, it uses hooks to add functionality at different stages of the request lifecycle. The onRequest hook is the first hook encountered, followed by pre-passing, parsing, pre-validation, validation, pre-handler, handler, pre-serialization, on-send, and on-response hooks. Fastify.authenticate is a decorator used for authentication. It is an asynchronous function that verifies the JWT token. To require authentication, use the onRequest hook and the Fastify.authenticate decorator. Hooks can be passed globally with Fastify Hadoop or specified as options for individual routes. This allows for selective execution of hooks for specific routes.
Everything is a plugin Fastify. And the decorators are essentially functions or objects that are functional properties in general that are added to your request response or your main server. Those are typically populated inside hooks. What are hooks? Well, we'll talk about hooks in the step 10. So for now we focus on the decorator. So what you're going to create is create a new plugin slash authentication. Wait a second. Is a new plugin slash authentication JS plugin that essentially it's the one that it's using the inclusive fastified JWT module. Okay. Is not you move the register of the JWT module inside this new plugin. It also create a new authenticated decorator to the fastified instance. Which essentially validates the token and respond with an error if it is invalid. Actually, let me just show you. It's probably become more clear when we are going to do the next step, okay? So this is the example of what you need to do. So basically create a plugins authenticate file and you just move the fastified register inside using the option. Okay, and using creating a decorate function here. Have you ever encountered this? Are you familiar with this type of declaration, okay? I don't know if the people that have used Fastify or not. You might just want to copy that file to use the next to the next one. I don't know if you want to, you totally can. It's something that is worthwhile doing from my point of view, okay? So if you want totally, go ahead, okay? You can go ahead and copy it. Note that we need to use the skip overwrite, okay? Why? Because it's a... With skip overwrite, it's otherwise, you want to be able to expose the plug-ins, okay? So you'd want to be able to expose the declarations. All the plug-ins are right now filtered. Sorry, are completely encapsulated. So when you are creating a plug-in, you want to be able to use the decorator outside of the function, okay? You'd still need to register it and import it. In the next step, folks, we are going to use the decorator that you just have created to create the protected route. Now this image is probably not great. It's not rendering great, Rafael, so you might want to talk, change something here on the code, okay? Anyway, what happens is that in... Fastify is not based on a middleware pattern. You probably are familiar with the concept of middlewares in Express, right? Middlewares enables you to add a lot of functionality. Fastify instead offers, basically you have your incoming requests, then you do routing, then you do, we create a logger. Okay. Then it's our passing through a few hooks. So the first one is an onRequest hook, and onRequest is actually pretty, it's the first hook that you're going to encounter. Not that routing happens at the very beginning. So in Fastify, whenever you are, so there's no dynamic route. Whenever a route is decided, that's the route, okay? There's no, no other route is going to be called. Then you do the instance logger. Okay. You create the logger, then we create the hook. Then we show the onRequest hook. Then the pre-passing hook, pre-passing in different from onRequest because it can be used to modify the body if you want to. Then there is a parsing. Parsing, so it's a content type parsing. So if there is a body, this is where the body is parsed, then there is a pre-validation hook, which you should almost never use this because you have not validated your input yet. So, but it's useful in certain cases. Then there is a validation hook. Then there is where the validation happens. The IJV, then a pre-handler and then the handler. This is where you, what you write. This pre-handler is useful before then you issue a reply. Okay, and before the reply there is a pre serialization hook. And then serialization happen. And then there is the on-send hook before sending. Then we have the, the responses sent and then we have our response on-response hook after the response is sent to the end user. This is different from express where everything's just a middleware and you just add middleware on top of middleware on top of middleware on top of middleware, okay? Using this is what also gives you a lot more throughput than express. So, it's a fundamental part of Fastify. So, it is the exercise, okay? So, you get your, you have your decorator from the preview, your previous function, okay? And you want to require authentication using the on-request Fastify hook. Using the Fastify.authenticate decorator. Now, the authenticate decorator, it was defined like this. So, it's an asynchronous function that take a request and a reply and you just call request JWT verify on it. In here, what you would do is you need to look at the on-request hook and essentially require authentication for your stuff, okay? Note that you can pass the hooks, can be passed globally with Fastify Hadoop, but also you can... You can also specify it as options to your routes. So, you can actually specify only one route, I want to execute our hook only for one specific route. This is very important for how Fastify works, so you don't need to execute this long chain of meters all the time, but you only execute what you need at every step. Hopefully, it's great. Can you please clarify why it's better to register off the curator by using onRequest hook, not the Prev validation one? Just because we will be able to skip few unnecessary hooks until if the off fails. Let me see. Yes, for me it makes sense to register in the Prev validation, yeah. Let me just check if the request body is already parsed in the Prev validation.
16. Authentication Token Validation Implementation
It's better to put the authentication token validation on the onRequest hook to avoid waiting for the request body. If the request body is large, parsing it before the necessary steps can cause performance issues. However, if you need to validate authentication tokens passed in the request body, you should do it before passing them to the subsequent steps. Now, let's discuss the implementation details.
The request body is not. So I would, so the out is better on the onRequest because onRequest happens immediately so we are not waiting for the body. So basically, instead of waiting for, let's imagine you're sending a five megabytes file. If you're an authenticated user, and you're sending it on the five megabyte file, you might be able to cause a denial-of-service attack if you are parsing the file, if you are receiving the full file and then parsing it. If you move it on pre-validation or pre-handler, what you are doing is you're actually having the full file parsed before you actually do your thing. This is problematic because the moment you are doing that, you are causing a certain problems and you are actually exposing yourself to do unneeded work in case the workshop is, in case the file is, in case the payload is very big, okay? So that's why I prefer to say it's better to put it on our request. However, you might want to receive your authentication tokens for whatever reason because the JWT, you pass it as a, as a better token inside the others, but let's say that you're passing your JWT or your whatever means of authorization inside the body, then of course, you need to put, to do that work before passing, okay? Oh, sorry, before, after you have done all the parts and before the wraps. Does it make sense? Yeah, okay. So let's go and talk a little bit about the implementation of this, okay?
17. Fastify Hook and Authentication Strategies
The Fastify hook supports arrays of functions, allowing you to combine different hooks in interesting ways. The Fastify Oath module enables you to specify multiple authentication strategies using an 'or' syntax. Another popular authentication strategy is Fastify Passport.
Yes, the hook supports array. The question was that, yeah, so basically you can just, yeah, an array of function. Yes, you can specify it as an array of functions. So one single function or an array of functions. So whatever you want, whatever you need. If you want to combine those kind of hooks in an interesting way, like for example, say, oh I want to support one strategy versus another strategy. You can also look at this module, this Fastify Oath Module. Fastify Oath enables you to specify multiple one to use in or, as an or syntax. So that first validate if there is a JWT and then verify there is a username and password. And it will do it's job essentially. So it essentially will try them all up until it find ones that actually, so it's actually good. There are other authentication strategies. One is fastify passport. A lot of people like passport, so here you go.
18. Fastify Hooks, Encapsulation, and Auto-load
Is it safe to use prevalidation hook for case transformation? It's not recommended to manipulate data before validation. Prototype poisoning protection is an important concept. Fastify hooks can be encapsulated within a plugin. Fastify auto-load can bootstrap an entire folder, automatically configuring routes based on the folder structure.
A lot of people like passport, so here you go. So there is another question about is it safe to use it, prevalidation for case transformation? Ooh. So okay, is it safe to use prevalidation hook for something like case transformation? Like when we have schemas defined for fields in camel case but input is underscore, run time is run underscore time versus run type put as a camel case. I would not do that. Like I would not do any data manipulations before having validated what it's in there. There is one case where you can do that is for instance, when you, I have user the Google Cloud PubSub and Google Cloud PubSub sends the data to you in coded and you show the coded before parsing. So in that situation, you can do it because in the prevalidation, because you have a check at the, if it is safe to decode and then you decode and then you validate after it, you know. But in case of just changing the fields for camel case, I also would not do it just for, you know. Yeah. I would list all the things that I want to list in my JSON schema and just support them, okay. It's a part of the problem is that is actually one thing that we do when receiving a JSON that's actually very handy. It's the prototype poisoning protection. Okay. I'm not indexing it. Updates. Yeah. Prototype poisoning. Okay. You can read it up. Okay. It explained it all. It's an article, whatever, very important. So let's go forward in step 10. Let's try step 10 and let's see how it works. Okay. So we are in, let's go and see how all of this work. So let me share my screen. And so we go into step 10. Step 10 hooks. Okay. So first of all, we do run node server. Then what we need to, we need to, first of all, login. You probably remember how you would log in. So it's a day, you need to send the curl syntax. So you have your curl and then you specify your body and then you specify, you send it to log in. Okay, and we also want that V to know the exact response. It is our JWT token. So this is the important one, the token. Okay. Then what you will do if, in order to test this, you would need to curl your HTTP slash slash localhost 3000 user passing a better token. So authorization error, and then you take the value above. You passed it and you close. Okay. And then you see that it returns the token. Now, if I put a wrong token here, you see that I'm getting an unauthorized message. Works beautifully. It's very simple and it's a very effective way to add authorization to your app. Now, something that you can do is, for example, if you want to, let's say you want to protect the multiple routes, okay? Like right now, we have the your user here. It's one route, okay? But you have the on request. What you can do is you can fastify adhook. On request, okay? And then here, fastify authenticate instead. Okay? And then let's say that you specify another route. You can say, put it here. Now that the hook will keep working for all... The hook will keep working for all routes defined in the same plugin. So it's actually pretty neat so we can have like two routes. So let's run this code, and we can, the first one is still working, and then if we are moving to user two, which is our second round, and also sort of second route as well. So you can see that you can actually create the CDR to find a CDR route with a common authentication defined. And if we can't try, if we try another strategy, like another stuff, like the log-in route, the log-in route still works as expected. So it's not protected by authentication. So the Hadoop is encapsulated. We call it, we define it as encapsulated within the plugin then defined. When is the stuff going to happen when they're not encapsulated? Well, if in order to have something not encapsulated we need to specify either using the skip override or using the fastify plugin utility to define them. In that way the hooks will always be executed. So let's talk about fastify auto-load. Fastify auto-load bootstrap an entire folder. This is pretty cool actually. It loads all the plugins found in the directory and automatically configures the route matching the folder structure. Which means that if you auto-load the route routes it will automatically for every file that you add to this route it will be automatically bootstrapped. It means that if you create the route users.js and you create the route orders.js it will be automatically loaded into the fastify structure even if you don't add anything in the index.js.
19. Fastify Auto-load and Route Registration
Fastify auto-load allows automatic route registration based on the folder structure. No manual route registration is required. Simply add a file to the appropriate folder, and the route will be registered automatically. The Fastify outload plugin is used to enable this functionality. By specifying the directory to load, routes are registered without the need for manual intervention. Additionally, the routes can be printed for verification. This feature simplifies the route registration process and improves development efficiency.
It's important to mention that before any work once we have created tasks for everything we can do this refactor easily. So don't worry about breaking our application because we are moving forward but adding tasks for everything.
Let's move to the Fastify auto-load. So for instance, if you register a folder users slash users it will automatically add your prefixed path. It means that, let me try if I can draw here. You have the route users. I think that this is user. And then you add a new file to it, the file 1.js. When you load your application using Fastfile to Load, automatically if you register a route here using Fastfile, okay I can't write, but Fastfile get, it will add the one as prefixed route, okay? So let's go to better example. Here in your best example we had the routes, oh let me increase the font size. We have the folder house, okay, and inside the house we have the login.js, we have the users.js. Inside the user folder we have index.js that we registered as route slash user. But using specified out load we don't need the specified user, we can just remove it. We can just remove it because the specified out load will automatically understand that this file is inside the user folder so it should be prefixed by user. It should be prefixed by user. If you add the route one here, it will when the specify load it will be in the end. User one. If you register the route two it will be user two. You don't need to change anything, just adding a file. This is pretty good. This enable a lot of good stuff to do, to be honest. Let me go back.
So also if you take a look in your application, we are registering the route here. A new route for plugins is actually a new plugin. We register here. When you route for users is here and logging and the users in general. If we need to register a new route, we should do it. For instance, foo.js, but also have registered, specify, out to load, you don't need to register it manually. Manually, it will be generated automatically. So let's go back to the step 11 as an exercise. Let's do it together, then we move forward. Let's remove out the manual route registration. Let's remove it, and also the plugin, instead of removing, let's comment it, okay? You can specify the register and the FASTFI outload. FASTFI outload is also a, a plugin, officially supported by FASTFI, okay? So which parameters does it receive? Actually, it is pretty simple. You register the outload route, and then you specify for which, which directory it showed loads automatically. The first one should be the plugins. So let's specify that. So we are import meta url because we are using ESM, okay? And then we merge with plugins. Basically, these join, just we return the full path to the plugins folder. Okay? The full path for instance, home, file, and so on, okay? Okay? The second one is we should register automatically our routes so let's just move plugins to routes. Just it, we don't need to do anything more. Just one thing actually. When you start the Festify server, for instance, in the server.js, you can also print the routes to register it. So if you do a console.log Festify print routes and let's go to the step 10 hooks and do server, let me see if it'll work. Okay, not working. Let me see why. It's the build server, yes. Ah, we should send the options I guess. Let me see if it works. No. Okay, one second. server.js. Let's see the server.js here is the same and then we can test it using the index file. It should be the same as we have registered. No problem so far. Okay, let's try to do it. Here we have the route using user. Okay, just to fix that. Step 11. Go to server. Okay, it's working and let's register just or let's just print the routes registered, okay? Console.log specify print routes. Great, it is printing for house. Okay, let's and it is important to mention that here we are registering just two folders two folders and we are not registering manually any route. So for instance, if I create a new route for instance here called ZOOM. The ZOOM will return the same thing but here will be hello and here will be word. ZOOM was called and ZOOM was called. Okay. Note that I haven't registered this file in any place. I just created the file. Okay.
20. Database Connection and Authentication Validation
We are going to use database connection to validate our authentication. Change the config.js to support the PgConnectionString variable. Register the Festify Postgres plugin with the connection string. Verify if the username exists in the database and return a 401 status code if not. Use fastfy.pg.query to select data from the user's database. Perform a request using the near form SQL to check if the user exists. If the user doesn't exist, throw the same error as an authorizer.
And then the ZOOM route is automatically registered. Pretty cool, no? So it will enable you to do a lot of fancy things, a lot of good steps, you know. But there is an issue yet. What is the issue? If you check the structure, you see that we have a route called user, slash user. But in fact, what we want is to have just user. But as I mentioned before, when you create a folder, it will add a prefix. So here, you don't need to specify user. You just need to have the slash. So when you move, when you change it, you'll have the correct routes. You have the users and you have the user. And this one. So it's working perfectly fine. And well, I really enjoyed this tool to be honest. So the only thing that you need is these couple of lines of code. Actually these two lines of code, okay? So here we did, and then let's go to the step 12, okay? It's one step to the end. But before going to it. I would like to hear your thoughts about the last two and also what you think about this... What is your feedback about it? Did you understand or you have any question? Everything is good so far? Good so far. Okay, very clear, thanks. That's one, good. Yeah. Let's go to the Step 12. It's more, it's one of the most difficult, I would say, because you need to deal with database, but it's more real, okay? Probably you'll do it in your applications. Okay? So, Fastify Post Procegress is also official plugin for Fastify, but before any work, you should make sure that npm run db up and db migrate were executed. So, try it on your machine, if you are following the steps, it will create a docker container. If you don't have a docker, I'm not sure if you'll be able to run it, okay?
So, in this step, we are going to use database connection to validate our authentication. The first step, we will do together, but the last one, I will give you some minutes to finish and hope to check it out, okay? So, have you run the db up and db migrate? It is working. Okay, thanks, Jacob. So, let's do the exercise together. Let's change the config.js to support the PgConnectionString variable. It's an environment variable that is widely used to connect to your database, okay? So, this is the configuration I will send you right here. Okay, so let's change our config.js. We have our config.js here and we are expecting to have one more property. And this property will be the connection, eg, connection string that will be required as well. Okay? Just it, this is pretty simple. And we should register our specifyPushGrids to accept that connection. So, but also we should define the PG connection string in our M file. Let's paste the, let me see. Okay, our connection is here. And then we showed make use of this environment variable. Let's go to our index file. And then before registering the plugins, before registering the route, you can register the Festify plugin. Okay, so let's import the Festify plugin, Festify Postgres, sorry. Festify Postgres from Festify Postgres. And then you'll have it. And it requires a option that is the connection string. So the connection string is basically what we have in the config file. But actually it is from the environment variables. So it will be pg connection-string. Because the options here, let me see, yes, yes, inside the config is the same. Great, yeah. We are importing the configuration here and we are passing to the server and then we are able to use options, pg connections string. The first part, pretty good. All good is very easy but the next slide, well also we have registered our FAT-fy PostgreSQL plugin, we are able to make requests using the Neo4m SQL. So your objective is to verify if the username providing the request body exists in the database. If not, you should return a 401 status code, okay? A tip here is use fastfy.pg.query to select the data from the user's database, okay? The solution is pretty simple, to just import the near form SQL and perform a request. So here we have the check, the simple of check, okay? But we show the check if it exists in the database. So we can use fastfy.pg.carry using this SQL. Near form SQL will prevent you to do a SQL injection, you know, it is common. So let's select the ID username from users table. from users table where the username is equal to username passive. So it will be a promising and it will return rows, okay? So it will return some object that contains rows that is the response of the database. Okay? So inside the rows, we want to get the first client, the first user, actually, and check if this user exists, okay? If this user doesn't exist, you should throw the same error as an authorizer. And then, we are good. This is the solution. Let me just refactor a bit. Okay. This is the enough thing. Let me test it with the step 12, no server. And then, if I perform a login, let's qrcode with the password.
21. Fixing Authentication and Modifying User Handling
The username and password authentication failed. To fix this, change the request to use a valid user from the database. Additionally, in step 12, move the existing routes users.js to routes users index.js and change the response schema to require an array of objects with the properties username and ID. Finally, modify the handler to load all the users from the database instead of using static data.
Okay, the username and password have failed. Password authentication failed, to the, let me see here. Okay, I think that I have... It is working, it is returning 401. Let me do the request again, this returning 401 because my user doesn't exist in the database. Let me get an user that exists. Let me see here. In the test, I don't remember exactly the user that exists in the database. Is the Alice, okay? So let's change the request to use Alice and the password is the same Alice. And then you have the token, okay? It is authenticated because you have checked in the database so it's working, okay? So the solution is pretty simple and then let's move to them to more one exercising the step 12 that is move the existing routes users.js to routes users index.js to make use of the specify output. Second step change the response schema that so that it requires an array of objects with property username of that string and ID, okay? Right now we are just returning username and password. So now you should return username and ID, okay? And the third part is modify the handler to load all the users from the database instead of static users, okay? Basically you showed, the same request that you did here you showed to the users but instead of do a wire you should only remove it. It should give you the ID and the username properly. So the solution is pretty simple. Basically you should just, yeah, I think that we will provide the slides as well. Actually this repository is public so you can run it locally without an issue. The solution you should change the response schema because in the first version you are returning the password and now you should return the ID and the type is integer, okay? And the users when you have moved to the RouteUsers, index.js, you should instead of returning the static users you can do the carry to the database just as I mentioned before and then you return to the application. This is simple, okay?
22. Adding Custom Authentication with TypeBox
Let's go to step nine and add custom authentication using TypeBox. TypeBox provides tools to generate JSON schema and define types. By using TypeBox, we can define the request and response schema, which improves performance and validation in Fastify. The TypeScript approach includes declaration merging to solve issues with TypeScript not recognizing certain properties. Fastify 4, coming soon, will introduce a new feature where the type is specified in the schema. Overall, the workshop covers the complete process of building with Fastify, whether using TypeScript or JavaScript. Feel free to ask questions, provide feedback, and follow on Twitter for updates.
Let's go to the step nine that you did, okay? The step nine, the repository and use the declaration measuring to add in a custom authentication by decorator to the Fastify. You can use TypeBox as was mentioned before here, okay? Let's do it together. So let me close this. The steps go to the step nine. Actually, I can go to the step 30 because... 30? Because we... I showed you just the important part of the TypeScript, okay? Firstly, you are creating... The creation of the Fastify is the same as in the JavaScript. The difference is that you can return the FastifyType, the FastifyInstance, for instance. You have the server.js that is the same as the finite. You just have some types, types definition, okay? And the most important part, as was mentioned, I guess in these step five, I guess, we should define the route schema, how we can do that with TypeBox. TypeBox provide a couple of notes to you and couple of tools to identify, to generate your JSON schema, okay? We were using the fluent JSON schema, but it doesn't provide us the TypeScript type, okay? The TypeScript type, okay? For instance, we are receiving the username and password in the login route, okay? So, the TypeBox provide some types like the fluent schema to define your type and you can use it in the schema. But the most important part is that you can also define a type from your JSON schema, okay? This is the syntax to generate the JSON schema and then you can use this type inside the Festify request. So it means that, for instance, if I do, I tell to the TypeBox that I have username and password, right? And if I do here request.buddy.fstb. I can tell it complete automatically. I can use the username and password. It automatically completed, okay? And this is very good actually because you can define the request, you can define the response schema here and it's also validated in runtime, is validated in compile time and also it enable you to achieve the more performance in Fetchify. If you don't provide a response schema, you basically you lose 20% of theıl of the throughput in the Fetchify. You can achieve much more with the responses schema because once you have compile schema for the response, you are be able to answer way more fast, okay? We had a lot of benchmarks in Fetchify, you can check the GitHub, Fetchify benchmarks, okay? You can check all the benchmarks with a lot of other frameworks like Express or Happy or Koa.js. We are still there the latest. Yes. And well, this is the one part of the TypeScript approach, okay? Any questions so far about Typebox or how or which usage? Very clear? Okay. Okay. So there is one point here that, for instance, we have generated, we are decorating our Festify or Festify instance with the Authentication, right? So once we do it, we are able to do Festify Authentication. It is available here in this scope but we are using the Festify plugin to enable it to run in every place as Matthew said previously. But we can't do it, actually, the Festify will not, you will not understand that. Let me go here. Here, for instance, the Festify will not understand, the TypeScript will not understand that Authenticate is part of the Festify. Here, it's working because we have added the types, but if you don't have the types, it will not work. It will throw an error, because Authenticate doesn't exist in the Festify instance. How we can solve it? We can make use of declaration merging in TypeScript, how it works. Basically, you define a folder that contains a.d.ts, and then in Festify, you declare a module of Festify, just to add some property to the Festify instance. So here, we have added the Authenticate property in the Festify, okay? For instance, if we don't have it, it will not work. Property Authenticate doesn't exist in the type Festify instance. And if you run, if you try to compile it using TypeScript, it will not work, it will throw us as well, the same error, okay? It will only work if you use, if you make use of the declaration schema, declaration merging, actually. You can check declaration merging TypeScript. This is the reference that we will send also in the Slack here. Okay, and in the Festify TypeScript documentation, there is a couple of notes about how to create your plugin using the declaration merging approach. You can also, for instance, Festify provides some in the authenticate. Here we are decorating the Festify instance, but you can also decorate the reply instance, that is the Festify request. And how we can add this property to the Festify instance in the TypeScript land. The thing that you need to do is just export interface, Festify request here, and then you add my amazing property that is a string. I think that this is the most important thing when you are dealing with TypeScript and Festify. One thing that will come in the Festify 4, it will be released in the next coming weeks, is the, you don't need to specify the type right here, you just need to send the type in the schema. It is still a feature for the next major version. It will work. So it's amazing to be honest, there's an issue, I don't think I remember, let me see, pull request. I forgot the name. Yeah. Yeah, I won't be able to find it because it's too much PR, but it will work very good. It will be amazing. So this is what we have planned for the TypeScript section. I know that this section of four hours of duration is tiring. You should be tired. Do it as your lesson, right fast for it. It's complete, easy to write test. And it doesn't matter if you are writing TypeScript code or JavaScript, it will be very easy to do. So that's it. I think that I, I hope to do, I hope that you have you understand. Did you like it? What do you think about this workshop? I would like to hear. Follow me in the Twitter. If you have any question, yes, you can ask in the discord. You can ask for, if you have find some bug, open up issue, and if you are able to solve, open up here, okay? Thank you, everybody.