
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
In this advanced TypeScript workshop for backend development, participants will explore advanced backend concepts and TypeScript features for API development across different runtimes. The focus is on using TypeScript in backend environments, writing type-safe tests, and building modern, maintainable APIs. The workshop caters to senior developers, covering essential backend patterns and discussing the 2024 change of out-of-the-box TypeScript support in Node.js. The session aims to provide real-world examples, templates, and practical knowledge for attendees to apply in their projects. The workshop emphasizes more code and less talk, ensuring a focused and informative learning experience for all participants.
How do I remove the blur effect from my CSS?
I removed but the blur is still there. Any ideas?
filter: blur(5px);
Does work for removing blur from modals?
backdrop-filter: none;
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:16 Hi there, nice to have you all here and welcome to the advanced TypeScript for let's call it bun or no development, or simply put,
00:00:26 backend development.
00:00:27 This is a workshop where we'll dive into some of the most interesting backend development concepts, some advanced ones, which you might not have heard
00:00:36 about before.
00:00:38 Starting off, we'll discuss about widely used TypeScript features, specifically some patterns in API development that are created for different runtimes.
00:00:51 And by the end, you'll walk away with the ability to use TypeScript in any backend environment, which is something that people mostly use on the front
00:01:01 end so far, and some backend APIs.
00:01:04 You'll also get the skills needed to write high quality type safe backend tests.
00:01:10 So yes, we're going to look into some backend testing as well.
00:01:14 You'll also walk away with real-world examples and templates to apply in your own projects, as well as knowledge of building modern maintainable and performant APIs.
00:01:26 So if you are a beginner, I recommend bookmarking this talk or conference for later on and returning to it in the future,
00:01:35 as it is a bit on the advanced level.
00:01:38 I may skip over a few beginner concepts or some installations so I can cover some specifics in detail.
00:01:44 And I want to use this opportunity to ask you guys, what is your current level of development?
00:01:49 What is your position right now?
00:01:51 Is it a junior, mid, senior, team lead, or something else?
00:01:55 OK, this is interesting.
00:01:56 I see Andre is a senior front-end developer.
00:01:59 We have Alyssa, who is a senior back-end developer.
00:02:03 Rob is a senior dev as well.
00:02:05 And Meredith is a senior full stack, but not confident in TypeScript.
00:02:10 So we have people from all different parts of web development stack, be that frontend or backend, most leaning toward the senior side with some being mid-level.
00:02:21 But again, I'm glad that all the seniors are here because that's who this content is prepared for.
00:02:29 With that said, let's dive right in.
00:02:31 More code, less talk.
00:02:33 That's going to be the point of today's conference.
00:02:36 We'll try to not have too much stuff that is unimportant and we'll only focus on the things that I actually want to share with you.
00:02:45 The things that you can take away and walk away from this workshop and then know something more.
00:02:52 after seeing that, okay?
00:02:53 We're not going to overload it and overwhelm it with a lot of stuff.
00:02:57 We simply want to make sure that we cover some essentials when it comes to advanced TypeScript backend patterns, okay?
00:03:06 So first of all, talking about a big change, which is going to be, well, you know, there's always some changes happening in the web development world,
00:03:15 but there is a big one that happened in 2024. And here, the one I'm talking about is out of the box, Papstool support in Node.js,
00:03:26 right?
00:03:27 Have you guys heard about it before joining this conference?
00:03:29 Yeah, not much.
00:03:30 There is ts-node for that.
00:03:32 Yeah, that's right.
00:03:33 That's right, Andre.
00:03:34 There is ts-node.
00:03:36 But now, Node.js is kind of switching a bit and introducing out-of-the-box TypeScript support for Node.js without using ts-node,
00:03:44 okay?
00:03:46 Specifically, there was a PR created in July and that has now been fully merged.
00:03:52 So if you scroll down into that PR, you'll even find a code sandbox link where you can play an experiment with this latest TypeScript support on end features
00:04:02 of Node.js.
00:04:03 So you can install the latest version of Node.js directly.
00:04:08 We're talking about the latest unstable version, I think, right now.
00:04:11 Or not the long...
00:04:13 No, it's stable, but it's not long-term support.
00:04:16 Version 23. So if you want to test this out on your local device, some of the features which we'll be talking about, you can download version 23. Or you
00:04:25 can use Docker or any kind of an environment server to experiment with these new features.
00:04:31 Or you can use this code sandbox link provided in that PR.
00:04:35 I can even drop a link to that over into the chat.
00:04:39 So you can explore it if you don't want to install the latest version of Node on your device.
00:04:43 But yeah, simply put, to start off with this workshop, I'll provide a link into the chat.
00:04:51 And this link will be for the final code of the workshop that we'll go over together today.
00:04:58 You can see it here.
00:04:59 I just pasted it into the chat.
00:05:00 So if you have some time while I'm speaking, while I'm introducing concepts, feel free to clone it.
00:05:06 Just do a git clone, git pull, and you'll have the latest part.
00:05:12 Of course, I have to make it public.
00:05:14 So I'm very happy that Andre immediately pointed that out.
00:05:18 Let's see how fast I can do that.
00:05:20 There we go, change visibility.
00:05:22 It's going to ask me for my second and triple OAuth validation to make that happen.
00:05:30 I will do that for you right this moment.
00:05:32 So now you get the repo, and now you can explore the code base.
00:05:37 You'll see that we have a couple of different examples here.
00:05:39 We have advanced node API, we have bun hono, which we might or might not explore at the end.
00:05:45 If not, I'm going to give you some instructions how you can check out that code base on your own for the bun part of this workshop.
00:05:52 Other than that, we have some examples and Inversify demo.
00:05:55 No need to fork it.
00:05:56 You can just clone it.
00:05:58 You can just clone it on your device.
00:06:00 That should be enough.
00:06:01 But if you want to, sure, go ahead and fork it as well.
00:06:04 Either way works for me.
00:06:06 Perfect.
00:06:06 Now that most of you have gotten this repo, if you haven't gotten yet, that's totally okay.
00:06:12 You can just follow along and see what I'm seeing.
00:06:14 But of course, it's often very helpful to have something to be able to move around different files and folders and test it out.
00:06:22 Yes, for now, we're only on one slide.
00:06:24 I'm just going to move to the code base very, very soon.
00:06:28 With that in mind, let me actually do that right now.
00:06:32 There we go.
00:06:33 This is what you should be seeing.
00:06:35 That's great.
00:06:37 One thing I want to say about the approach that I'm going to take in this workshop is that it's a bit different than to what I typically do on YouTube.
00:06:47 If you don't know that, I also educate people on YouTube through a channel called JavaScript Mastery, and it's more beginner-oriented.
00:06:55 But I'm happy to say that now I get the opportunity to speak with all of you who are more advanced, so I'm going to take a bit of a different approach.
00:07:05 Right now, I'll focus on taking a final repo.
00:07:10 In this case, for me, it's going to be a starter, but for you, it's going to be a final repo.
00:07:15 And I will break it down feature by feature with small focused examples.
00:07:20 So in this case, we'll have isolated lessons based on the starter code.
00:07:25 will take an already existing fully working API.
00:07:29 But because you're advanced, we're going to take that base stuff to the next level together.
00:07:33 So just wanted to say you have to start your final code right here.
00:07:37 Perfect.
00:07:38 With that in mind, we can start with a very, very simple example just to test whether all of this works.
00:07:45 You can create a new folder called examples if you don't have it already, but I think you will.
00:07:50 And within it, you'll also have a hello.ts file.
00:07:54 What are we trying to test here?
00:07:56 Well, we have a basic JavaScript object called a developer, which has an interface of developer attached to it.
00:08:04 So it's of a type developer.
00:08:06 It has a name BuxyFixwell with a caffeine level of infinity and different skills with a debug mode of true and a method which we can call when we try to
00:08:16 deploy something.
00:08:17 Now we also see an interface for that.
00:08:21 If we try to call this method developer.deploy, will it actually work?
00:08:26 Keep in mind, we're not using node.ts here.
00:08:28 We're simply using a regular old node, but we have a hello.ts file.
00:08:34 So if I open up my terminal and if I head over to CD examples, let's do that right now.
00:08:45 And let's see, oh, there we go.
00:08:47 I have to see the outside of this, so see the examples.
00:08:50 And I can run just a single command.
00:08:53 Let's actually run a node, hello.ts.
00:08:57 If you try to run it like a regular node file, you will notice that it throws an error, unexpected identifier developer.
00:09:05 Of course, it's not working.
00:09:07 But what if I run it with a node V right here to check which version of node do I actually have?
00:09:14 You can see that I'm running node 20, but don't worry, just switching your current terminal to node 23 is super simple.
00:09:23 Let me show you how you can do that very easily.
00:09:26 So if I head over right here, And if you go to nodejs.org and you go to download, you'll see a package that allows you to download a specific version of Node.js.
00:09:38 I'll head over to 23.3 and I'll just copy these commands to my clipboard.
00:09:43 This is work very easily for Mac OS and Linux devices, might be a bit harder for Windows.
00:09:50 But yeah, as Sam said, you can also just run MVM install 23 and that's gonna do the job.
00:09:55 I just wanted to show you where I got it from.
00:09:57 So I'm going to run these commands and very easily and quickly, I'll be switched over to node 23.3. Now, if I head over here and if I run node hello.ts
00:10:10 one more time, it's not going to work again, even on version 23. That's because we have to provide an additional experimental flag before running this command.
00:10:21 that flag is going to look something like this, dash dash experimental dash strip dash types hello.ts.
00:10:30 Now, if I run it, you'll actually see what we were trying to console log, deploying, oh no, forgot to remove console logs,
00:10:38 which in simple words means that we have run a TypeScript file within regular node, which is amazing.
00:10:47 So there's nothing to compile on configure on your own.
00:10:51 And I think that's it.
00:10:52 The workshop is done.
00:10:54 You now know how to use TypeScript within a Node.js environment out of the box.
00:11:00 Of course, I'm just kidding.
00:11:01 This is cool and all, but there's so much more stuff we can explore.
00:11:07 It still has its limitations, right?
00:11:10 Even if you look at the official documentation, you'll see that there's currently a warning at the top that says that all content in this article uses
00:11:18 Node.js experimental features and use them if you wanna use those features, but remember that those features can change in future versions.
00:11:27 So they're really protecting their ass right here trying to say, this is all experimental.
00:11:33 For that reason, for our next demos, we'll use something known as TSX.
00:11:41 And TSX is just a TypeScript execute command, which is a Node.js enhancement to run TypeScript.
00:11:50 Think of it as TSX alias to Node, just like this.
00:11:53 You can pass a specific CLI flag, and that file will be TSX.
00:11:59 Simple node enhancement, okay?
00:12:01 This is just one of the approaches in which we can create or use TypeScript within Node.js in a bit more stable way.
00:12:09 Yeah, and TSX already is a thing.
00:12:11 Yeah, there might be a connection between the two, so it can be a bit misleading.
00:12:16 But yeah, I get what you're saying.
00:12:18 I had the same thoughts, Rob.
00:12:20 Thank you for mentioning that.
00:12:22 But now I want to talk about Node Advanced API.
00:12:27 How can we create an advanced API using Node, one that follows industry best practices in building robust APIs?
00:12:36 In the provided final code that I shared, you can already find a completely built API made using Node, Express, MongoDB,
00:12:46 Mongoose, and TypeScript.
00:12:48 Let me show that to you right now.
00:12:50 So if you head over here, you will be able to see a Node API.
00:12:54 This is a totally good API, right?
00:12:57 If you check it out, it has different routes, like routes for users and routes for events.
00:13:04 You can see them here.
00:13:06 Think of this as an API for an application like Eventbrite or Meetup, or in this case, Git Nation that allows you to host different events.
00:13:15 It is a simple CRUD application where we can get users, create users, and search users.
00:13:22 And we also have different routes for events to enable the CRUD functionalities.
00:13:27 Now, we also have Mongoose or MongoDB models for events and for users.
00:13:33 As you can see here, they're all typed safe.
00:13:36 So we are exporting an interface of iEvent, and we have created a new Mongoose schema for it as well.
00:13:43 And the same thing goes for the user.
00:13:45 We have created a model schema as well as gave it a TypeScript interface.
00:13:53 We also have controllers right here that handle the logic of our functions.
00:13:58 So we have a function that gets the events, we have one that creates them, that searches them, and we have absolutely the same thing for the user.
00:14:06 The reason why I'm going over this so fast is that you most likely already know this, right?
00:14:12 Super simple so far.
00:14:13 If anything is not clear, you can let me know, but just wanted to show you an example of a basic, fully working Node.js API,
00:14:21 something that you would create very quickly.
00:14:23 And upon a quick glance, everything looks great, but it's nowhere near production ready.
00:14:29 So in this workshop, we can go back to the slides, I'll teach you how to improve this code base by adding something known as the dependency injection adder.
00:14:42 We'll also add unit testing and similar techniques with TypeScript.
00:14:47 So I want to ask you a question right here.
00:14:50 How many of you have already heard or already know what dependency injection is?
00:14:56 I can see Meredith knows about dependency injection for Java, and I can see some people have emoted to that as well.
00:15:03 Alyssa, Jacob knows it as well.
00:15:06 Andre says it's widely used in Angular and Nest, he thinks.
00:15:11 Well, if you think that, Andre, you are completely right.
00:15:14 Yes, it's used in Angular and Nest and for that matter is used in a lot of different frameworks and many huge companies use it.
00:15:25 And Meredith said that she knows what it is for Java, implicating that she might not know what it is for Node.
00:15:33 But dependency injection is just a regular programming pattern, right?
00:15:39 And programming patterns are just concepts that once we understand how they work, That's the most important thing, to understand what they are and how
00:15:48 they work in theory.
00:15:49 But then of course, it'll be easier to learn their specific implementation in another programming language or technology.
00:15:57 So yeah, we're going to go over all of that together today, Meredith, so you'll know what it is within the Node.js ecosystem as well.
00:16:03 If you haven't heard about it, or if you forgot what it is, or if you want to learn it for Node.js, let me give you a quick recap.
00:16:11 Dependency injection is a design pattern used in software development to manage dependencies between components, making it easier to test,
00:16:22 maintain, and scale your applications.
00:16:26 It's used by many big companies from Google to Netflix, Amazon, Microsoft, and it's also a core principle of frameworks like NestJS,
00:16:37 exactly what Andre suggested.
00:16:40 It's also being used in Spring Boot, and here we have Java, And it's also used in ASP.NET through the .NET core.
00:16:50 So if you open up the NestJS documentation, you can see a paragraph about dependency injection first thing at the top.
00:17:00 That's how important it is for NestJS.
00:17:03 And if you're having trouble understanding what the heck DI actually is, well, think of it this way.
00:17:10 Imagine you have a car and the car needs fuel to run.
00:17:15 Now, the car can either find and get the fuel itself.
00:17:19 Well, maybe Tesla can do it with this weird rod charger.
00:17:23 Or someone else needs to give that fuel to the car.
00:17:27 OK, so there's two ways.
00:17:29 Now, if the car has to go out and find the fuel for itself, it's actually a lot of work for a car to do.
00:17:37 And it's harder to change the type of the fuel that it's getting, like switching from petrol to electricity.
00:17:43 But if somebody feeds fuel into the car, like a fuel station, then the car doesn't need to worry about it.
00:17:52 It's much easier and much more flexible.
00:17:55 So how does this relate to programming?
00:17:58 Well, a class or a component often needs other things to work properly, like the fuel for the car.
00:18:06 These things are called dependencies.
00:18:09 We have all heard of them and use them in our day-to-day life.
00:18:13 So with dependency injection, what you can do is something like this.
00:18:20 Instead of a class creating its own dependencies, they are provided or injected into it from outside.
00:18:29 This way, the class doesn't have to manage it on their own, making things easier to change, test, and maintain.
00:18:38 And if I had to run this concept through code, then I can give you a very simple example.
00:18:44 Imagine you have a class called Engine, and you also have a class called Car with a constructor that takes in the engine and creates its own engine.
00:18:55 So it's a direct dependency on the specific engine right here at the top.
00:18:59 And then we can actually drive the car using the drive method.
00:19:02 Pretty simple, right?
00:19:03 And this is tightly coupled, which means that your classes or objects don't have to know the specifics of how other parts of the code are built.
00:19:14 as the car class depends directly on the engine class.
00:19:20 So you cannot easily replace the engine without different implementations, or sorry, with a different implementation.
00:19:27 And the same thing can be achieved but better with dependency injection.
00:19:33 So if you look at this interface right here, where we have the engine, we have a class of V8 engine, which implements the engine.
00:19:41 And then we have a class that takes in a constructor of engine where the dependency is just injected right within it.
00:19:48 So that is the difference.
00:19:50 then if you want to use it, it's going to look something like this.
00:19:53 You can create a new engine out of the V8 engine constructor, and then you can create a new car and pass the engine right into it,
00:20:03 or in other words, simply inject it.
00:20:06 This I hope makes sense.
00:20:08 Of course, Of course, it is a very simple example, and you have to keep in mind that in about two hours or hour and a half that we're here,
00:20:15 we won't be able to dive into super complex examples, but you will take that initial node API that we created, and you will actually beef it up to make
00:20:23 it much more robust, error-prone, type safe, and all of those good things.
00:20:28 But what I need from you right now, based on this car example, is just to understand what dependency injection is on a basic theoretical level.
00:20:38 We have a dedicated nest.js framework, which is completely built on dependency injection, In this workshop, I will teach you how to implement DI using
00:20:51 a library called Inversify, which will allow us to add it to Node.js, which doesn't use it by default.
00:20:57 Inversify is a lightweight dependency injection library for JavaScript and TypeScript apps, which allows you to, and do just that,
00:21:06 implement dependency injection right into your Node application.
00:21:13 There are many other libraries, such as TypeDI, there's also BottleJS, and there's TySringe, which allow you to do the same thing,
00:21:23 but I found Inversify to be the best for this demo, and specifically for tab script based projects.
00:21:29 So some examples that we have been talking about right now, basically this car example, I want to show you how it would look like with Inversify.
00:21:40 You would simply have to import a couple of things from Inversify, such as inject, injectable, container, and so on.
00:21:46 And then we do almost the same thing as before.
00:21:50 You create a new engine, but then you add this at injectable decorator, which tells Inversify this class can be injected whenever needed.
00:22:01 Okay.
00:22:02 And then you also pass it over to the car, which can also be injected.
00:22:07 But in its constructor, you actually use the add inject part right here that injects an instance of an engine.
00:22:15 And then you can actually call it right here at the bottom where we bind the engine interface to the V8 engine implementation.
00:22:23 And we also bind the car class.
00:22:25 And finally, we resolve the car instance, which is going to look like this.
00:22:29 And then you can drive your car.
00:22:30 So it's similar to the last example, but with some extra steps.
00:22:35 But now let's go back to our code.
00:22:39 Okay.
00:22:40 We have explored our simple example where we have seen that now we can run Node.js within, that we can run TypeScript within our Node.js applications.
00:22:49 And we have also explored a simple node API.
00:22:53 Now, let's explore how Inversify works on this same exact example.
00:22:57 In your code, we have a demo, which is fully working, where you can see that this is the same code that I showed you through slides.
00:23:04 But in this case, you can also see an additional package right here at the top.
00:23:08 This package is called Reflect Metadata.
00:23:12 And we basically needed for Inversify to work.
00:23:17 Now, what is this?
00:23:18 Well, TypeScript metadata reflection allows you to attach and retrieve runtime metadata, like types of properties, method parameters,
00:23:29 and return types.
00:23:31 It is used in this code because Inversify relies on TypeScript metadata reflection capabilities to implement its dependency injection features.
00:23:39 That was now just a part of all of this in code.
00:23:43 And this was just a slice of everything what you learned today.
00:23:47 But for now, in the same shared starter repo, which for you actually is the final repo for me, it's a starter because I want to code some things live in
00:23:56 front of you.
00:23:57 We'll see how that goes.
00:23:58 We know how demos typically work.
00:24:01 But for you, I have actually provided to you a final repo.
00:24:06 So for you, it will not be a starter.
00:24:08 It'll be a final.
00:24:10 Why?
00:24:11 Because I don't want you to run and type as fast as possible and just try to implement things on your own while following what I'm doing on the screen.
00:24:20 I want you to have the final code on your device so you can actually listen and try to understand the concepts.
00:24:27 Just so you know, you don't have to rush or type anything.
00:24:30 You can just explore the code base on your device.
00:24:32 Okay.
00:24:34 So now let's actually head back over to the code base and let's open up advanced node API.
00:24:44 This for me is a starter for it's a final version of the modified node API, which is a simple base API.
00:24:51 But now we want to actually improve it.
00:24:54 What we want to do is add robust and scalable functionalities implement the dependency injection pattern, implement some testing,
00:25:04 and do all sorts of things.
00:25:08 I'm happy to see in the chat that Andre is good with that approach because the last thing he wants to do is type quick after a long working day.
00:25:16 So yeah, I know that most of you maybe have already worked today.
00:25:20 It's Tuesday after all, right?
00:25:22 Not a weekend in sight.
00:25:24 So I'll try to keep everything brief and just try to give you an overview of what dependency injection is and how we can make our APIs in TypeScript and
00:25:34 Node.js more scalable and safe.
00:25:36 I have been repeating myself, but now you get the gist of it.
00:25:39 So we are ready to dive into this big portion of this workshop, which will be me actually taking the starter and beefing it up a bit.
00:25:48 First things first, you'll notice that we have a lot of packages in this starter repo or the final repo.
00:25:56 We have different things like .env, which is used for environment variables.
00:26:01 We also have Express and Mongoose.
00:26:05 We also have things like Winstun for logging.
00:26:08 We have Zod for validations and some other, including Jest for testing purposes.
00:26:14 You'll also notice that we have the mentioned Inversify, as well as Reflect Metadata, which I talked to you about.
00:26:22 And then we also have the Inversify Express Utils, which is the Inversify package for Express Utils.
00:26:29 This will not be only about dependency injection.
00:26:33 Don't get me wrong.
00:26:33 We will dive into that, but I'll teach you how to make this API more robust with just general good programming practices.
00:26:41 First things first, we'll be implementing some checks for our .env.
00:26:48 You know how when you have some environment variables, it is very easy to just not provide some or wonder what's happening with the app once deployed,
00:26:58 but you forget to add your environment variables in production.
00:27:02 So for that reason, let's create a new env.ts file.
00:27:07 And within it, we'll want to implement some type checking for our envs.
00:27:12 We can do that by saying const env schema is equal to z.object, which is referring to Zod.
00:27:21 So right here, I can import z from Zod.
00:27:25 And we're going to give it a node env, which we most likely need, which is going to be of a type z.enum.
00:27:32 And it'll be an array of either development, production, or test.
00:27:37 And the default will be set to development.
00:27:39 We can also add a port to this ENV schema of a type z.coerce or translate from a string to a number with the default value being a port of 3000. And finally,
00:27:55 we'll add a database URL, which will be of a type z.string.url.
00:28:02 just like so.
00:28:04 Let me know if these things are jumping in front of the code too much.
00:28:07 If so, I could actually make this maybe a bit smaller so you can see it.
00:28:12 Is this too small or is it still visible?
00:28:15 I'm going to switch depending on the file, how much code there is.
00:28:17 If I can, I'll keep it on the larger side just so you can see it a bit better.
00:28:21 After that, we can export a new type.
00:28:25 called env, which will be equal to z.infer type of env schema.
00:28:31 So we're simply inferring the schema from above and exporting a new type called env.
00:28:38 Now, right below, we can create a new result by saying const result is equal to env schema.safe parse.
00:28:47 We're using safe just so that something doesn't break.
00:28:50 And to it, we can pass the process.env.
00:28:53 What is this doing?
00:28:54 Well, it's taking a look at our.envs to make sure that we have all the necessary things in there.
00:29:00 If something goes wrong, so if result is not successful, we can console log that error and simply quit the process.
00:29:08 Simple as that.
00:29:09 But if everything is right, we can just run export const env of a type read only, ENV is equal to result.data.
00:29:20 So what this allowed us to do is to now head back to somewhere where we're using this ENV.
00:29:27 For example, in our, let's go to our index.ts right here.
00:29:34 And you can see here that before, we were using process.env, that database URL like this.
00:29:40 But what we can do now is we can actually use the ENV coming from dot slash ENV.
00:29:46 Do you get the idea?
00:29:47 Now this will be type safe and actually less error prone because you can see exactly which variables are there.
00:29:54 And if they're not there, you'll be able to see the errors in your console.
00:29:59 That's the idea.
00:30:00 Have you guys done something like this in your own code bases?
00:30:03 Maybe not this exact implementation, but something regarding checking for ENVs that are in there.
00:30:08 It's a common practice, right?
00:30:10 Yeah, yeah.
00:30:11 Android used Zod, but more for form validation.
00:30:13 But as you can see, it can actually be used here as well.
00:30:18 Who would have said, right?
00:30:19 You never know until you see it.
00:30:21 This now is just one of the steps in which you can make our code base more scalable and less error prone.
00:30:27 Now, let's dive into creating the models.
00:30:30 If you head over into the user model.ts, we'll do something special here.
00:30:36 Considering that we have already seen a user model in our simple node API example, we can see that here, we'll do something similar,
00:30:44 but I'll teach you how to make it more type safe.
00:30:47 We can do it like this.
00:30:49 I'll paste the actual code just so we can go over it together.
00:30:51 We'll import the schema document and the model from Mongoose.
00:30:56 And we'll also create an interface called iUser with a name and email, as well as an enum of role, which could be either admin or user.
00:31:06 Pretty straightforward right here, right?
00:31:08 But what we're doing in this case is exporting an interface of iUser document, which then extends both the interface of user,
00:31:19 but also extends the interface of the document, which refers to the Mongoose document.
00:31:26 What does that mean?
00:31:27 Well, that means that we'll also give it things like underscore ID and different things that documents typically have.
00:31:34 Then we can define the user schema like we normally would by passing the I user right here.
00:31:39 So now when you create a new user, it is completely type safe with all of the properties from the interface, as well as all of the properties from Mongoose.
00:31:49 This is something that just works incredibly well.
00:31:53 I have seen this GitHub post.
00:31:56 right here on Mongoose, where I think the creator of Mongoose talked about this.
00:32:02 Let me try to pull it up in the browser right here so you can see what I'm telling you about.
00:32:06 It's going to be this thing right here, a closed PR saying remove support for document interfaces that extends documents.
00:32:16 So before, it was just immediately being extended, but now they disabled that as it had some performance issues.
00:32:26 So now we can do it just like what I have done here by extending the iUserDocument and the document without any performance issues,
00:32:34 but you have to perform this additional step.
00:32:37 Somebody is asking, would this work with SQLite database?
00:32:44 Good question.
00:32:47 You're asking about TypeScript, I'm guessing, and ENVs.
00:32:49 Yes, it would.
00:32:50 You could add Zod and TypeScript validation to SQLite, but I'm not sure.
00:32:56 And not this part about Mongoose, right?
00:32:58 Because this stuff that I'm explaining right here is about Mongoose and MongoDB.
00:33:03 So this would be a bit different.
00:33:04 But yes, the previous concepts I explained can be used on SQL and non-SQL databases.
00:33:09 Now that we have this the model for the user, we can actually head over and go to services.
00:33:17 So let's head over to services and let's head over to user services right here to implement that service, which will handle the business logic of the application.
00:33:28 A service can be anything from user, event, auth, logs or search.
00:33:33 It is just a simple convention to handle all business logic in one place with separation of concerns.
00:33:39 So in this case, what we can do is very, very similar to the initial theory example with a car that I have showed you.
00:33:50 It's going to look something like this, where for the first time, we're using injectable from Inversify, where we also import the models we have created.
00:33:59 We specify injectable right here at the top, and then we export a class user service As I told you, it contains the business logic for user actions,
00:34:09 such as methods, getUsers, createUser, and updateUser, as well as deleteUser.
00:34:16 And we have all of these neatly decoupled in separate functions, but still belonging as a part of a larger class.
00:34:24 We are using the injectable decorator right here at the top.
00:34:28 to indicate that this class can be managed and injected by Inversify's container, which means dependency injection container.
00:34:38 This class can be injected.
00:34:41 This is what this decorator says.
00:34:43 Now, I'm going to teach you an additional TypeScript thing to make your code more type safe.
00:34:49 and that's gonna be the use of symbols, okay?
00:34:53 So let's head over to, let's go right here to, let me see, I think we can do it even in this file right here.
00:35:01 If I head over to services, user, right here at the bottom, I wanna declare some new types, okay?
00:35:08 And those types are gonna look something like this.
00:35:11 I'm just gonna paste them in this case, and it should look like this.
00:35:16 You can see const types is equal to, and here we define a symbol for each different service.
00:35:23 I think we could have put this in the generic as well, but for now, let's keep it here.
00:35:28 We can move it later on.
00:35:29 Where we have a symbol for user service, log service, which we'll have later on, logger middleware, generic service, event service and logging middleware.
00:35:40 As you can see, there's a lot of types here, so maybe it's better to put it into constants.
00:35:46 If I head over into constants, right here under types, we can define all of these different symbols for different services that we're offering.
00:35:55 such as the user service, which we have created so far.
00:35:59 What this means, once again, is that we have a symbol for it so you can directly refer to the user type and know what it belongs to.
00:36:08 It belongs to the user service coming from services user.ts.
00:36:13 We'll explore how that can be directly used within the code very soon.
00:36:17 But of course, a user service wouldn't be super good without a controller.
00:36:24 we use controllers to create an inversify controller.
00:36:28 So let's head over right here into controller.
00:36:34 That's going to be the user controller.
00:36:35 And we can create that Inversify controller.
00:36:38 So let's do that together.
00:36:40 Or you can just watch me do it.
00:36:44 Let me see.
00:36:44 Yeah, this one will be a bit longer so we can explain it together.
00:36:48 I will just paste it here to save us some time.
00:36:50 I won't be coding it, but I will be explaining exactly how it works.
00:36:55 So you can see that here we're importing or declaring a controller.
00:37:01 This right here is a decorator which maps all of the routes in this class to the base route of forward slash API, forward slash user.
00:37:12 So once again, all the routes in this class will be mapped to a base route of forward slash API, forward slash user, which is how we'll be able to call
00:37:22 these controllers.
00:37:23 later on.
00:37:24 Then we have a constructor right here, which injects the user service into this controller so it can be used within below methods.
00:37:34 Let's take a look into that.
00:37:36 Constructor, inject.
00:37:39 Types, user service.
00:37:42 And these types are coming from the constant types which we have created not that long ago.
00:37:47 This one right here.
00:37:49 And we're declaring a private user service right here.
00:37:52 Then we're declaring a first HTTP GET method under a forward slash, and we're defining a public async GET user's functionality where we can request and
00:38:06 response something.
00:38:07 In this case, we inject the request object, which means we inject the incoming data, and we do a similar thing for the response.
00:38:16 we also inject the response object right here to send the data back.
00:38:21 And then we have just a regular function that returns a promise.
00:38:25 This promise tries to get the users from the user service.
00:38:29 So we're saying this.userservice.getUsers If there's no users, return noUsersFound, but if there are some users, we can simply return the status of 200
00:38:42 with the users right here.
00:38:43 Similar thing happens for a second method, which is newUser or recreated.
00:38:49 Keep in mind these are injected requests and response.
00:38:52 and we create a new user.
00:38:55 We also have one for the update user.
00:38:58 And in this case, it's very similar to what we have seen above, right?
00:39:03 So we have just the request and the response, where we have the updated user, where we call the user service, updated user,
00:39:11 has the necessary programs, and we go ahead and update it.
00:39:15 Absolutely the same thing happens for delete.
00:39:18 I hope this makes sense so far.
00:39:20 I'll go ahead and repeat it one more time.
00:39:23 We have a user model.
00:39:26 So we can see it right here.
00:39:28 It is type safe, corresponds both to Mongoose and general TypeScript interface types for that user.
00:39:35 Then we have also created a service for this user.
00:39:41 where we have allowed this specific class to be injectable.
00:39:47 This is what a dependency injection means.
00:39:49 By using this injectable decorator, we have specified that this user service can indeed be injected.
00:39:57 Here, we define some public methods, such as getUser, createUser, updateUser, and delete.
00:40:04 And then we inject those methods within the user controller.
00:40:09 where we define that all the methods from this specific controller will start from API users.
00:40:18 And here we inject the types user service so we can actually use them.
00:40:23 And then we define the logic for the controller of how that works.
00:40:27 I hope that makes at least some sense so far.
00:40:31 So now we can move into creating an Inversify server.
00:40:36 So for the first time to actually spin this all up, we have to create an Inversify server.
00:40:42 So let me actually close these current files.
00:40:45 And let me head over to user service, which is going to be right here.
00:40:51 Oh wait, I have multiple folders.
00:40:53 So let me make sure to open one specific one, this one here, and go into the types.
00:41:00 Here, you can see that we have a symbol for, and we have different types, but have you seen how this symbol was actually being used?
00:41:09 Why are we importing these different symbols in the user service file?
00:41:13 Well, let me show you.
00:41:14 Let's head over to the user service, and here you can see one thing that I wanted to point out, which is the partial TypeScript type.
00:41:23 And this makes all properties in the T, which means given generic TypeScript type, optional.
00:41:32 So what does that mean?
00:41:33 That means that to this create user, we can pass partial parts of this T operator.
00:41:40 That way, we won't be able to create new types.
00:41:43 We can only pick types from partials.
00:41:47 So for the create user, we can take partial types of this iUser interface.
00:41:52 Why?
00:41:53 Because it also contains different things.
00:41:56 I think it also contains things like the underscore ID and stuff like that, which are also generated for us by Mongoose.
00:42:03 But here we're saying that it can be partial, right?
00:42:06 We don't need all of that stuff when creating it.
00:42:08 We simply need to get its name, email, and role.
00:42:12 Hope that makes sense.
00:42:13 Just wanted to clarify that.
00:42:14 But now, to put all of this to use, we can actually create an Universify server by heading over to, well, let's do that right here.
00:42:24 I'm just pulling this up right here.
00:42:27 We can go to our index.ts.
00:42:29 And here, you can see that we are just running a regular Express server.
00:42:35 We have Mongoose, where we connect to a database URL, which we used from our process ENV before, but we later switched it over to the .ENV and we're connecting
00:42:46 to a database of advanced node.
00:42:49 Now, what we can do here is actually add another import statement by saying import user controller, right?
00:42:58 Coming from controllers, because we want to actually use it, but not that one.
00:43:03 We actually want to import at forward slash controllers, forward slash user dot controller in this manner, okay?
00:43:12 We want to do it like this.
00:43:14 This one contains the API routes for user management.
00:43:18 And importing it here like this will ensure that the controller is registered with the Inversify Express server.
00:43:27 So let's actually now bind them together, similar to what we have seen in the car type.
00:43:33 or the car example.
00:43:34 So if I head over below the port, we can say container, and this container we need to declare.
00:43:42 So I'll do that right here by saying let container, or we can do it as a const, but we will change it later, is equal to new container,
00:43:52 just like this.
00:43:54 And this container is, I believe, let me see, this container should be coming from Inversify, of course.
00:44:00 There we go.
00:44:00 So now we have it.
00:44:02 And if we do that, we can say container.bind where we bind a user service like this.
00:44:10 And we can call it and pass types dot user service and bind it to a user service, which is going to look like this.
00:44:19 Of course, we have to import the user service from types and then we won't have any errors anymore.
00:44:26 This means that we bind the user service to the symbol in the container.
00:44:32 So if we head over to dot types, this is now binded to this user service.
00:44:39 And this means that any code that requests types.userservice will get an instance of user service.
00:44:48 Okay, now let's head over down and let's actually create this server.
00:44:55 Currently, we're simply saying something like mongoose.connect, but what we can do is define the server by saying let server is equal to new Inversify
00:45:09 Express Server coming from Inversify ServerUtils.
00:45:13 And to it, we can pass the container.
00:45:16 If you do that, we can also apply some additional configuration by saying server.setConfig, which takes in a callback function.
00:45:27 And to this callback function, we can pass the app.use And here we can run the regular express dot URL encoded, which if you used and created many express
00:45:39 servers is what you're typically used to.
00:45:41 Okay.
00:45:42 So here we also want to pass it.
00:45:44 The extended is equal to true.
00:45:47 We often add that type.
00:45:49 You can see that right here, which allows us to choose between parsing the URL encoded data with query string library when false or QS library when true.
00:45:59 So in this case, we'll be using the QS library.
00:46:02 Perfect.
00:46:03 And let's also scroll a bit down.
00:46:07 And right here, let's see what I wanted to do here.
00:46:11 Oh yeah, I actually wanted to use the app that use Express JSON.
00:46:15 So now if I scroll below here, below this app use, we can now say app.use express.json, because we want our payloads to be in JSON format.
00:46:30 Perfect.
00:46:31 Finally, once we have created the server, we can now say something along the lines of let app is equal to server.build.
00:46:40 So now we have built an Inversify server.
00:46:43 Let me just make sure that this is good, because in this case, I believe we have an app from Express before, But this time,
00:46:50 we'll have an app coming from Inversify.
00:46:54 So that is how you run an Inversify server.
00:46:56 So let me actually switch this over.
00:46:59 Let's see if I forgot something.
00:47:00 I think we can also define a port.
00:47:03 Right now, I believe it's either hard coded or no, it's coming from Process ENVs.
00:47:07 But what we can do is define the port like this.
00:47:11 is equal to env.port.
00:47:15 And specifically, I'm referring to the env coming from .env, which we created not that long ago.
00:47:21 After that, we simply connect to the database and then run a console log and listen on that port.
00:47:26 I don't think I have missed anything, so we should be good.
00:47:30 But what I will do is just explain this part in a bit more detail.
00:47:35 So what is this inversify server?
00:47:38 What does it do?
00:47:40 Well, Inversify server uses dependency injection for a cleaner, more modular, and more testable code, making it easier to manage complex applications compared
00:47:52 to a regular Express server.
00:47:54 So that's why we had to switch it below when we said app.
00:47:57 Now it's based on this specific server.
00:47:59 So in this case, We build it not using the express app, but we build it using the server build, which then with our configuration uses the express server.
00:48:10 Okay.
00:48:11 Hope that makes sense.
00:48:12 So we're no longer using a regular app coming from express, like what we have done just a bit before.
00:48:18 right here.
00:48:19 Now the app is coming from Inversify, but basically it's building a regular Express app behind the scenes.
00:48:25 Think of it just like a wrapper, okay?
00:48:28 So now that we have the Inversify server, we can actually test the API routes by making requests to API users.
00:48:38 That's another thing that we want to do.
00:48:39 But make sure that you have, well, in this case, you won't have the ENVs, but if you want to test this out later on, you can basically create your own
00:48:49 MongoDB Atlas server and port specified here, and then you can run the application locally as well.
00:48:56 But in this case, you can see what's happening on my end, considering that I already have some ENVs right here.
00:49:01 It shouldn't be too hard for you to just run your just Mongoose Atlas to quickly spin up a server.
00:49:08 With this in mind, let's go ahead and test out those API routes.
00:49:13 I'll do that right now by running our application.
00:49:17 Let's head over here.
00:49:18 Let's CD into advanced, let's see, CD out of examples, and then CD into advanced node API.
00:49:30 Let's run API, MPMI, sorry if we haven't already, and then MPM run dev.
00:49:36 This will run TSX watch, and you should be able to see it right here.
00:49:40 Now we do immediately get a Zod error saying that there is a database URL required, but it looks like it is undefined.
00:49:48 So if I head over to ENVs, hmm, That's weird.
00:49:52 It seems like my database URL is here.
00:49:55 So let's try to debug this together.
00:49:57 It says it's an invalid type because it's getting undefined, even though I have it right here under that ENVs.
00:50:05 Okay.
00:50:07 We have some issues right here while testing in production, but that happens often.
00:50:12 Let me see if I restart it again.
00:50:14 Nope, the same thing happens.
00:50:17 Let me include you into debugging here.
00:50:20 Any ideas why this might happen?
00:50:22 I am under advanced node right here and the .env file.
00:50:27 Oh, but I have to check this ENV.
00:50:30 Maybe it's talking about node ENV.
00:50:31 No, it's talking about database URL, saying that it's of a type z.string.url.
00:50:37 Let me just make sure that I have done that in the right way.
00:50:40 I believe it has been done.
00:50:42 So we should be good.
00:50:43 Oh, it looks like I had a typo.
00:50:45 Okay.
00:50:45 That's my bad.
00:50:47 Actually it should have been z.string.url, but most likely I had something else typed wrong right here.
00:50:53 could have been the safe parse thing, but now we're good.
00:50:56 So if you got everything right, the server should be running in development mode on port 9000. And now we are ready to test it out.
00:51:05 What we can do is just make a regular GET request.
00:51:10 Where was the typo?
00:51:11 That's a great question.
00:51:12 If I go back and try to compare the two, because they have two versions of the code base here, we can try to find it.
00:51:18 What I often do is those situations is I use the diff checker online.
00:51:26 So if you really want to know, I can head over to the diff checker right here, and maybe try to paste both versions of the code.
00:51:33 And then we can figure out where the difference was.
00:51:36 This is a pro tip if you ever need it.
00:51:38 But so now I'll answer your question, Sam.
00:51:41 It looks like the issue was I didn't import the config from .env and I forgot to import the expand config.
00:51:49 Without that, we cannot know.
00:51:50 So that was the issue that I had.
00:51:52 I forgot to do the imports right here.
00:51:54 I had just the types, but they can't work without actually importing the config from .env and expand from .dvd expand.
00:52:02 So now we know where the issue was as well.
00:52:04 With that in mind, we're running on localhost 9000, so we can try to make a get request.
00:52:09 In this case, you can use any kind of a checker for API requests like Postman.
00:52:14 You can do it in the browser as well for GET requests.
00:52:17 But in this case, I'm using, I believe this is Thunder Client, allowing you to make a very simple request right within your Visual Studio Code.
00:52:26 So if I head over to localhost 3000, forward slash API, forward slash users, and make a GET request, you can see connection refused by the server.
00:52:38 Oh, sorry, it was 9,000. too used to front-end development.
00:52:43 So we should be good now, but the connection still gets refused.
00:52:46 Let's see why that is.
00:52:47 Oh, it could be because they had some headers right here, but no, they should not interrupt what we're trying to achieve right here.
00:52:54 Let's see why that is.
00:52:55 Localhost 9000 forward slash API forward slash users.
00:52:59 And is our application running?
00:53:01 Let's see.
00:53:01 I might have to rerun it now.
00:53:03 Oh no, nevermind.
00:53:04 I still get the issue right here.
00:53:06 Even though we, oh no, nevermind.
00:53:07 My bad.
00:53:08 I brought the ENV when testing it back to the version without the imports.
00:53:12 So now we're up and running on localhost 9000. Every time that I do a demo live, I tell myself, next time, let's not do it live,
00:53:21 let's prep the code.
00:53:22 But then again, I always want to do it live with you, so some issues happen.
00:53:26 But that's completely normal.
00:53:27 I'm sure that most of you have gone through that as well.
00:53:30 Now, with that in mind, let me actually head back to the new request and let's send over that request to localhost 9000. And would you look at that?
00:53:39 We indeed do get a 200, 545 bytes.
00:53:45 By the way, this is just a regular extension for VS Code.
00:53:47 If you don't know about it, it's called Thunder Client, allowing you to make API requests within your code base.
00:53:53 So what do we get back?
00:53:54 Well, we get back some users that I have created just before.
00:53:58 seeing their ID, name, and all of that.
00:54:01 And we can also try to make a post request.
00:54:05 but we don't really have to type all the things like name, email, and role.
00:54:09 I guess you can trust me.
00:54:11 If the GET works, the POST will too.
00:54:14 But that's basically how dependency injection works.
00:54:19 It's intimidating at first, but you can see that this GET request worked.
00:54:24 And the fact that this GET request worked actually means that we have successfully implemented dependency injection into our application,
00:54:32 believe it or not.
00:54:33 Um, so if you head over into our services and if you head over into our user service, you can see that's it, right?
00:54:43 We have injected this user service.
00:54:45 We have hooked it up with different types, with a symbol for user service.
00:54:49 And then within controllers, we have told it, we told it basically to expose itself under API users.
00:54:58 And here we have injected that user service, as you can see.
00:55:02 And that's it.
00:55:03 The fact that we have successfully gotten back a 200 on this API request means that it works.
00:55:10 There are many more benefits, but currently it's very hard to see the benefits of doing all of this hard work just to be able to see or just to be able
00:55:20 to do a request that you would be able to do in a much simpler way.
00:55:25 I mean, looking at our node API right here under controllers or routes, If we make a call to router.get forward slash users,
00:55:36 we would immediately also get all the users, but with much less code.
00:55:41 So currently this dependency injection thing seems like just an overkill for something that's not really doing anything,
00:55:49 right?
00:55:50 Well, it's hard to see its benefits for small projects.
00:55:56 But as the projects grow, trust me, it has huge impacts on those projects.
00:56:02 But still, I still want to show you how this helps.
00:56:07 Another thing that we can focus on right now is talking a bit about unit testing.
00:56:13 Unit testing is something that I strongly advise you to implement within your projects.
00:56:19 It is actually one of the biggest benefits of dependency injection.
00:56:24 Now, how?
00:56:26 What do the unit tests have to do with dependency injection?
00:56:31 Well, the DI helps unit testing by allowing dependencies to be easily replaced by mocks.
00:56:40 This enables isolated testing of individual components without relying on actual implementations, ensuring more focused and predictable tests.
00:56:51 So let's go ahead and create some unit tests together.
00:56:55 We can do that in the advanced node API folder under tests, under user.test.ts file.
00:57:04 Within here, we can try to describe our first test.
00:57:08 Have you guys used testing before?
00:57:10 you can let me know in the chat, specifically with Jest or with some other library as well.
00:57:15 I can see most have used Jest, so you should be at home right here.
00:57:19 What we can do is describe a new test.
00:57:22 That's going to work something like this.
00:57:24 Describe a test where a user service tests without separate repository.
00:57:34 I'll explain what that means very soon.
00:57:37 Right here, we can create a new callback function within it.
00:57:42 So let's head over here.
00:57:44 Let me properly close it.
00:57:47 And we can start describing test.
00:57:51 First, we'll create a new container of a type container coming from Inversify.
00:57:59 Then we'll create a new mock service by saying const or let mock user service of a type Jest.mocked user service.
00:58:13 So Jest provides you with mocked functionalities like this.
00:58:17 And what we're doing here is simply mocking the version of a user service.
00:58:23 You can see how simple is this when you have access to those user services, which can be injected.
00:58:31 Super simple.
00:58:32 Next, what we can do is say something along the lines of before each, what we can do is create a new container.
00:58:44 That's going to look something like this.
00:58:48 Container is equal to new container.
00:58:52 And we can define a mock user service.
00:58:55 That's going to look something like this by getting the users.
00:59:01 So let's say mock user service is equal to an object.
00:59:05 where we can try to then.
00:59:08 create a mock implementation of what the user service is doing using Jest.
00:59:13 That's going to look something like this.
00:59:15 Get users is going to be equal to a Jest function.
00:59:19 Get or something like create user can also be a Jest function.
00:59:24 We can have a update user, which will be a Jest function.
00:59:28 And we'll have a delete user equal to Jest function.
00:59:31 And all of that together will be as Jest mocked user service.
00:59:37 So having services makes it super simple to implement.
00:59:40 Now we can bind this user service to a constant value of the mock user service.
00:59:46 Let me show you how that looks like and how that behaves.
00:59:49 Container.bind, and we want to bind the user service symbol coming from types by saying something like types.userService to constant value of mock user service.
01:00:07 This bind mocks the implementation of the user service to the DI container, allowing us to test to inject the mock wherever types.userservice is required.
01:00:22 So wherever we're using this user service, we can also be running these tests, which is the beauty of how dependency injection makes your tests so much
01:00:34 more easier to implement and so much wider, let's put it that way, because you're immediately using it in many places within the code.
01:00:42 After that, we can very simply say that it should Right?
01:00:47 Return users.
01:00:49 That's the only thing that this function should be doing.
01:00:51 So we can create an async function.
01:00:54 And within here, we can declare some mock users.
01:00:57 That's going to be equal to an object where we have some arrays, where we have an underscore ID, maybe something like one.
01:01:04 We have a name equal to John Doe.
01:01:07 We have an email, something like, let's do JohnDoe at gmail.com.
01:01:14 And we also have something like role equal to role coming from models user dot user.
01:01:21 And this will be as iUserSubset.
01:01:26 So this will be a subset of the user.
01:01:29 And that's something that we have to define right here.
01:01:33 Check this out.
01:01:34 Right here at the top, I'll say type iUserSubset is equal to pick.
01:01:40 So we're using a TypeScript pick property right here, which allows us to to pass two arguments to pick from.
01:01:51 So from the past type, pick a set of properties whose keys are in the union k.
01:01:58 A bit complicated explanation, but it'll make so much sense very soon.
01:02:02 What we can do here is say pick properties from iUserDocument and which properties we want to pick.
01:02:09 Well, it's going to be the underscore ID, maybe name, maybe email, and a role.
01:02:15 So we want to pick those properties specifically from the iUserDocument, which we have access to right here.
01:02:22 We can also define something like a second user, maybe by duplicating the first one and just changing this to two, JohnDo two,
01:02:32 two, and this can be good.
01:02:35 The second one can maybe even have a role of an admin.
01:02:38 Now we can go below those mock users and say mock user service, dot getUsers dot mockResolvedValue to which we can pass mockUsers as iUserDocument,
01:02:56 specifically an array of user documents.
01:02:59 This is what we're trying to mock as the result value.
01:03:05 Finally, we can also retrieve the mocked user service from the dependency injected container by saying const user service is equal to container.get,
01:03:18 where we get that specific user service, and we can define the types dot user service.
01:03:24 So we retrieve the mocked user service from the dependency injected container.
01:03:30 Once we do that, we can now finally try to fetch those users by saying const users is equal to await user service dot get users.
01:03:41 And then we can expect mock user service dot get users.
01:03:49 Finally, to have been called number of times is gonna be set to one.
01:03:58 And we also expect users to equal mock users.
01:04:05 So this is one quality type or gist test, right?
01:04:12 It works something like this.
01:04:13 This is just a part of the test where it should actually return the users But we can also do another test right here where it can actually maybe create
01:04:25 the users, right?
01:04:26 So for this one, I'll actually copy it and paste it right here below.
01:04:30 So you can see how the tests for creating users would look like.
01:04:34 It should create a new user.
01:04:36 We can create the same mock user or a different one.
01:04:39 In this case, Alice.
01:04:40 The mock user service.createUser.mockResultValue to which we pass the mock user.
01:04:49 We, again, get the user service from container.getUserService.
01:04:54 We then create a user using a real createUserService.
01:04:58 And then we expect the mockUserService.createUser to have been called with name Alice, an email example.
01:05:07 And we expect that one to equal the mockUser.
01:05:12 So what we're doing here in simple words is implementing the tests, for both returning the users and creating the users,
01:05:22 but the process is much simpler than if we wouldn't have had access to user services or to symbols or to the dependency injection.
01:05:33 Because here, we can just very easily compare what the mock user should look like when compare or when equaled to the actual user created by the mock user service.
01:05:47 And let me answer some questions here.
01:05:50 Alyssa asked, why does the type need to be just that mocked user service instead of just user service?
01:05:57 Let's see where that is.
01:05:59 So we're referring to the just that mocked, right here.
01:06:05 And that's a very good question.
01:06:07 And I think we already have a response right here saying that basically we're creating a mock service based on a user service.
01:06:15 So we have to specify that the mock type has to be of a type user service.
01:06:20 And this is the part of unit testing to check how logical flow of the services works.
01:06:25 Yeah, that's basically the perfect response.
01:06:27 So in this case, that's the reason why we have to say just.mocked and then put the type of the user service right within it.
01:06:34 And now what do you say that we actually run that test?
01:06:37 I'll open up my terminal.
01:06:40 I'll actually split it and we can run npm test right here.
01:06:45 But of course we have to be into within this advanced node API and we can say npm test.
01:06:54 If we do that, Jest is running.
01:06:56 We have two test suites, one failed, and we can say reflect.getMetadata is not a function.
01:07:05 I think that's because I forgot to import a part that I told you is absolutely needed, which is the reflect metadata.
01:07:13 So if I head over to the top, what we can do is say import reflect metadata.
01:07:20 Okay.
01:07:23 And now we can try to rerun it.
01:07:25 So let me actually do that right here by saying npm test.
01:07:29 And you can see that now the tests have successfully ran.
01:07:33 And it says your test suite must contain at least one test.
01:07:37 We have two test suites, one failed, one passed.
01:07:41 Now, why is that?
01:07:44 Well, let's see.
01:07:44 Oh, but regarding the actual tests, it says two passed, two total.
01:07:50 So it's possible that I maybe missed something in the code.
01:07:53 Let me see.
01:07:54 If I run it once again, yeah, same thing happens.
01:08:00 Maybe I have like an empty test suite somewhere.
01:08:04 That's definitely something I could check.
01:08:05 But in this case, it seems to be good.
01:08:08 Yeah, we have one where we're testing the user service tests without separate repository.
01:08:14 And I can see that Andre also asked the question, are we testing a mock right now?
01:08:20 I believe we can mock the user service when testing, say a controller, which calls a user service.
01:08:26 And yes, we are creating a mock test.
01:08:29 And that's exactly what we did.
01:08:30 We mocked a user service and created a mock service service.
01:08:35 So we are basically creating two services, one for mocking and one real one.
01:08:41 Okay.
01:08:44 Oh yeah.
01:08:44 And I think the issues are happening because there's an additional test right here.
01:08:50 Let me see.
01:08:50 We have a user spec.ts.
01:08:53 No, that should not be it.
01:08:54 This is for integration tests.
01:08:55 And we have a user.test.ts.
01:09:00 Interesting, not sure where this second suite is, but that's totally okay.
01:09:04 We tested what we wanted to test and one test suite is there.
01:09:09 But yeah, since we don't have any tests here, it's possible that that's why it's throwing this one fail.
01:09:14 But that's not what we care about right now.
01:09:17 What we care about is that we have successfully implemented the tests within our application and we have successfully mocked a service and compared it
01:09:26 with a real service.
01:09:28 What we can do next is get back to the slides for a second and just discuss how easy it was to mock whatever service we're creating and perform unit testing
01:09:41 in that way.
01:09:42 So in this case, what was the takeaway?
01:09:46 What did we actually learn by taking a look at this?
01:09:49 Well, the first thing is mocking dependencies.
01:09:54 Dependency injection lets you replace real dependencies with mocks or fakes during testing.
01:10:03 This allows you to simulate specific behaviors or dependencies without relying on their actual implementations.
01:10:11 So Andrea asked the same question.
01:10:14 Why did we actually test mocks?
01:10:16 Well, yes, we simulated specific behaviors without relying on their actual implementations.
01:10:22 The second thing that it helps us with is isolated testing by injecting only the necessary dependencies You can test a single component in isolation,
01:10:34 focusing on its logic rather than worrying about other parts of the system.
01:10:40 So we also have isolated testing.
01:10:43 Another thing is flexibility.
01:10:46 By injecting only the necessary dependencies, you can test a single component in isolation, focusing on its logic rather than worrying about other parts
01:10:57 of the system, so it is completely isolated.
01:11:00 And we have improved test reliability.
01:11:04 Since tests don't depend on real systems like databases or APIs, they are less error prone and prone to failure due to external factors,
01:11:16 ensuring that we always have consistent and reliable results.
01:11:22 In short, dependency injection enables focused, flexible, and reliable testing by decoupling the components and allowing for controlled dependency injection.
01:11:36 But that does not mean that you cannot do integration tests with DI, of course.
01:11:41 You can, but with this way, it is much simpler.
01:11:47 Let's actually look into creating one of these integration tests.
01:11:52 If I head over here, we can head over into user.spec.ts, I believe.
01:11:59 In this code, I think you should already have the code there.
01:12:04 I'll just paste it here.
01:12:05 We're going to implement an integration test for the users right here.
01:12:11 It's going to be very similar, at least the basic gist of it, but I'll show you how much further we can get by doing this.
01:12:20 What you can see is that we have a before all declaration right here, which connects us to a database URL.
01:12:27 Again, we're using the same environment variables right here.
01:12:31 After each one of these tests, we want to delete the users.
01:12:35 Super simple.
01:12:37 And after all, you want to just disconnect.
01:12:39 These are just some specific just properties.
01:12:43 Very simple to find their explanations in the docs, but I think you get the gist of it.
01:12:46 Then we describe our first integration tests, where we say that user service integration tests and we create a container,
01:12:55 same as before, we declare a user service, same as before, and then before each one of these tests fires, we create a new container and we bind the user
01:13:08 service to the user service type, or rather to where we declared the symbol for that user service.
01:13:16 And then we try to get that user service right here into this test.
01:13:22 very simple to get it once you actually have the types and you bind it and you inject it.
01:13:28 That's the whole thing.
01:13:30 The whole part we talked about is just injecting different services and controllers right into your code.
01:13:35 And we can start with the integration testing.
01:13:39 First, you should create a new user.
01:13:42 We create a mock user, Kevin, for example.
01:13:45 We call the service to create a new user.
01:13:48 We say const user is equal to a weight.
01:13:51 user service.createNewUser.
01:13:54 And now we can have different expectations of what we expect for this new user.
01:14:01 We expect it to have a property of underscore ID.
01:14:05 We expect the user.name to be equal to new user.name.
01:14:09 We expect the user email to be equal to new user email.
01:14:13 And we expect the user role to be equal to new user role.
01:14:17 And which user are we actually comparing it with?
01:14:23 Well, we're directly comparing it with the user we're creating using this user service.
01:14:29 That's the integration test that we're doing right now.
01:14:33 Perfect.
01:14:35 Next.
01:14:36 we should also be able to fetch the users, right?
01:14:39 So we need to add a test directly using the service by saying const user one is equal to await user service dot create one user,
01:14:48 create a second user.
01:14:50 And then finally, we can try to get all users and that's it.
01:14:55 We expect the users to have the length of two and we expect the users to contain equal object basically to have their exact IDs.
01:15:06 If they have the IDs, that means that we have successfully created two users in the database.
01:15:13 So in this case, what we're doing is testing the main services through the database.
01:15:20 These are real tests, no mocking.
01:15:23 And that's exactly what a real integration test is.
01:15:26 Testing the main services, no mock services here, Andre, everything is real here.
01:15:33 As you can see, because we actually have to connect to the database, we have to create a container, and this time we are using a real user service,
01:15:42 not a fake one.
01:15:43 So the same service we created, we're just testing whether it works successfully.
01:15:48 real test, no mock data.
01:15:51 Again, we're checking whether the update service works as well.
01:15:55 And yes, it does, or at least I hope so.
01:15:59 We can know for sure if I actually run the tests.
01:16:02 So now if I run npm test one more time, let's see what happens.
01:16:06 This time we got that all of the tests have passed.
01:16:10 So there we go, six tests passed.
01:16:13 Perfect.
01:16:14 The integration tests took some time because it had to make actual database calls.
01:16:19 So yes, it's good to use dependency injection with integration tests, but with caution.
01:16:26 Dependency injection can help you manage dependencies more easily, set up tests more effectively, and also swap real components with test-friendly versions
01:16:36 like a mock database or a fake API.
01:16:40 But for integration tests, the goal is to test how different components interact with each other in a real setup.
01:16:48 So while DI can be used, try not to mock too many things, because it could reduce the test's effectiveness in simulating real-world scenarios.
01:17:00 Okay, makes sense.
01:17:01 So try not to mock too many things.
01:17:04 Some tests do have to remain real and have to be connected to the real database.
01:17:10 So I hope you're convinced on the benefits of dependency injection so far, but if you're still not convinced, DI has many more full benefits.
01:17:21 Let's say tomorrow you might want to change a part of the code base to use another completely different database.
01:17:28 Maybe SQLite, maybe Redis or something else.
01:17:33 that you want to introduce with the user service, but not MongoDB.
01:17:39 The only thing that you'll have to do is install the needed Redis package and create a new service or modify the user service.
01:17:49 It is super simple.
01:17:51 Let me show you how that actually works.
01:17:53 I'll copy the finished code and override it here.
01:17:56 And check this out.
01:17:58 This is a completely different implementation of this application using Redis, right?
01:18:06 We're having a completely different way of connecting to it.
01:18:10 We have ways to get users, to get user documents and many more stuff, right?
01:18:16 It's all here, all implementations that we've had before.
01:18:20 The only single thing that we had to do is just replace the user service within a user services file.
01:18:30 And the code will still run exactly the same.
01:18:34 I mean, that's maybe the biggest takeaway here is that your app works fine.
01:18:40 You won't have to modify the controllers or anything like that.
01:18:44 It almost feels like a dream come true where you can just swap things.
01:18:50 You know, when this is super useful, I used to work on a payment provider where we had to connect so many different payment gateways,
01:18:58 right?
01:18:59 Imagine if you could create a service for each different gateway.
01:19:03 one for card payments, one for PayPal, one for Stripe, one for some other country's currencies and country's payment processors.
01:19:12 It's super simple to do that with controllers.
01:19:14 Okay.
01:19:15 Hope that makes sense.
01:19:19 Now, because the controller is agnostic, as you can see it here, it is completely agnostic.
01:19:25 Everything it is doing here is gonna work with no matter which database you're doing.
01:19:29 It is just calling the user service.
01:19:31 And I think that only now you might better understand what this whole thing is about, right?
01:19:36 You now understand that we have a agnostic controller and a service which is completely hot-swappable with other stuff.
01:19:44 The controller relies on this abstraction.
01:19:47 it relies on injecting different user services, but not on the specific implementation details, like whether it's MongoDB or Redis or something else.
01:19:58 Since the DI container is binding the user service to an implementation, like Redis backed in this case, as long as the interface or the methods of user
01:20:10 service remain the same, the controller doesn't need to change.
01:20:14 That is a huge takeaway.
01:20:19 You can completely hot swap different user services, and it's still going to work without having to modify any other part of the code if it has the same
01:20:30 methods that the controller has.
01:20:33 Now, I want to show you something else.
01:20:36 But first, I want to create a logger service.
01:20:40 I'll go back to the user service.
01:20:41 I'll bring it back to MongoDB.
01:20:44 There we go.
01:20:45 I think we're good.
01:20:46 And we'll head over to additional services under log.
01:20:51 And I'll teach you how to implement a logger service.
01:20:55 So I'll paste the code right here.
01:20:57 You should already have it.
01:20:59 It is injectable, which means that we can actually use the, we can inject it as an additional dependency to other pieces of code.
01:21:07 We define a Winston logger and we just create some format for logging different kinds of logs.
01:21:13 And we give it different methods like log info, log error, and so on.
01:21:18 Now we can head over to the main index.ts.
01:21:22 That's going to be right here under index.ts.
01:21:27 And let's see where we're binding the user right here.
01:21:31 What if we try to bind another service?
01:21:33 Like, let's say that we bind the log service to the log service, like this, coming from types log service.
01:21:43 And now you can modify the user controller to use the log service.
01:21:48 So let me head over to user controller TS.
01:21:52 That's gonna be right here.
01:21:55 And right here at the top where we're injecting the user service, we can also inject this second service.
01:22:02 That's gonna be happening right here within the constructor as a second parameter.
01:22:08 We can define add inject types.logService and we can make it a private logService that's going to look like this of a type logService.
01:22:26 Okay, that makes sense.
01:22:30 And now you can use it right here at the top of your methods.
01:22:34 So if I see the HTTP get, you can go into the promise and maybe you can say something like, Well, let's simply do this.
01:22:43 I'll do this.logService.logInfo.
01:22:50 And let's say that I'm going to log some pieces of info like getting all the users.
01:22:56 Or maybe we can do something more specific, like what you have in the final code, rec.originallyurl, get fetching users.
01:23:04 So we know exactly what is happening.
01:23:06 So now if you try to make a request to the users, that's going to be right here.
01:23:13 Let me go back to this terminal.
01:23:16 And actually, let me go to the Thunder client and make a new request to get the users.
01:23:23 See our open terminal.
01:23:25 and let's see what happens there.
01:23:28 If I head over to user controller, it looks like I have a typo.
01:23:31 No, we're back running and I'll send the request.
01:23:35 And would you look at that?
01:23:36 We can now see the info get users.
01:23:40 So what does this mean?
01:23:41 Well, we see the log in the terminal as well as the log folder in the project structure.
01:23:47 How is that gonna look like?
01:23:48 Well, here it is.
01:23:49 We have a new log that was created for us by the logger functionality, which we create we specify that right here, which means that you can inject specific
01:24:05 services into other services as well.
01:24:06 And they can all work together seamlessly, making your code super scalable.
01:24:13 The same thing happens with the middleware, right?
01:24:16 You can turn the same service into a middleware injection, but how?
01:24:19 Well, first, you should create a separate middleware class.
01:24:24 So let's do just that.
01:24:25 I'll head over to advanced node.
01:24:27 I'll head over to, well, maybe let's go to middlewares.
01:24:31 Let's see if I have it.
01:24:32 No, it looks like I don't.
01:24:35 So I'll create a new middlewares folder.
01:24:40 And within it, I will create a new logger.ts.
01:24:46 Within this logger, I think you already have that code on you, but basically we want to create a middleware that logs whenever a specific request is made.
01:24:55 And now the only thing you have to do is modify the index.ts to use this middleware.
01:25:01 So if you head over here, we don't want to necessarily inject the log service, but what we do want to do is say container.bind.
01:25:13 Oh no, we're going to bind it like this.
01:25:15 This is smart, right?
01:25:16 We can say container.bind log service, but we're going to bind it through middleware.
01:25:22 Let me show you how.
01:25:23 Container.bind logging middleware.
01:25:29 And then we can call the types logging middleware like this.
01:25:34 Now we can add that middleware right here to our server setup.
01:25:38 And let me inject the log service as well, because it's being used in the user service.
01:25:44 So I'm going to say log service.
01:25:49 and let's not forget to import it just so our app doesn't break.
01:25:53 And now we can discuss this middleware right here by saying const loggingMiddleware is equal to container.get loggingMiddleware where we actually call
01:26:05 the types loggingMiddleware.
01:26:07 And we can say app.use loggingMiddleware.logRequest.bind loggingMiddleware.
01:26:15 And now, Basically, whenever you make a request, you should be able to see some logs happening.
01:26:21 So if I head over here and try to make a couple of requests, you can see that...
01:26:29 Well, we cannot see them here.
01:26:31 Potentially, if I go into the logs...
01:26:35 Oh, here we still see only one, but we need to change the get URL in a Thunder client.
01:26:41 Let's see.
01:26:42 What am I going to change it to?
01:26:45 Oh yeah, sorry, I'm making a request to the GetThunder client.
01:26:49 We should actually be making a request to localhost.
01:26:52 I thought it kept it.
01:26:54 There we go, incoming get request, and we can make more.
01:26:57 And you can see that now immediately, I'm doing it for only one, but you get the idea, it's gonna actually work for all the different routes.
01:27:05 And it's super simple to implement by using the same log service that we had before.
01:27:11 So you can use it within unit tests.
01:27:14 You can use dependency injection within middlewares.
01:27:17 You can use it in general for your application.
01:27:19 It is super cool to use.
01:27:22 And now what we can do is, I also want to show you another thing that you can do, and that is creating a generic service with dependency injection.
01:27:34 Let me show you how that works.
01:27:36 If you head over to advanced node API and head over to services, we have a generic.ts file.
01:27:43 We're going to use advanced TypeScript with dependency injection to create a generic service.
01:27:50 I want to explain it to you by actually showing you the final code.
01:27:55 Does anybody have the final code for generic TS?
01:27:58 I believe it's in the final repo, but I don't have it here on me.
01:28:02 Let's see who's the first one to pass it over if the Zoom chat limit actually allows it.
01:28:08 There we go.
01:28:09 Who was first?
01:28:09 Let's see.
01:28:11 Oh, it was Gina.
01:28:11 Okay, that's amazing.
01:28:13 If I'm not mistaken, you actually joined later and you were able to just pass it in such a quick time.
01:28:19 That's great.
01:28:21 Perfect.
01:28:21 Even though my formatting is not working, what matters the most is that I have the code right here.
01:28:27 So what we have is a generic service.
01:28:30 I know it's a bit hard to read right now, but bear with me.
01:28:34 We're creating a new generic service with get all, get by ID, create, and so on.
01:28:40 These are basic crowd functionalities, which are going to work for any kind of an API, be that users, be that events, or be that whatever else.
01:28:51 Now that you have a common generic service with generic types, we can use that in any service, okay?
01:29:00 Because these are generic TypeScript types.
01:29:03 I know this might seem like a lot of generic types, like T array and then promise T and T and T.
01:29:09 What does this T mean?
01:29:10 Well, if you know a lot about TypeScript or a little, you will know that T stands for generic TypeScript parameter, which means that it is completely interchangeable
01:29:20 depending on what you pass into it.
01:29:22 So if you inject this into, for example, user, it's going to be I user.
01:29:28 But if you pass something like an event into it, it's going to be the interface for the event.
01:29:33 So it is interchangeable.
01:29:34 So now we can create an event service derived from this generic service.
01:29:40 Let's see.
01:29:41 That is right here under event.ts.
01:29:47 And who can pass me the code for that?
01:29:51 Let's see.
01:29:51 I don't think I have it here, but I want to teach you as much as possible without wasting so much of your time.
01:29:57 I just want you to get the base idea.
01:30:00 So we're under Advanced Node API, event.ts.
01:30:05 Andrei was first this time, so perfect.
01:30:08 As you can see, this code is pretty simple.
01:30:12 We're trying to inject using Inversify, and we're injecting the event service in this case.
01:30:19 But for this to work, we also, of course, need an event model.
01:30:23 So let's go ahead and go to the models for the events.
01:30:26 And somebody can pass that over as well.
01:30:29 Now, not on purpose, but I'm making this experience interactive.
01:30:35 There we go.
01:30:36 That's good.
01:30:37 This time, we have it.
01:30:39 Oh, no, I think I have it this time.
01:30:41 There we go.
01:30:42 So I can just paste it right here.
01:30:44 This is the event.
01:30:46 model, right?
01:30:47 It's very similar to the user model, but this time we have it for events.
01:30:53 But I want to show you how the events work using the generic service.
01:30:58 Let's make sure that we have everything needed here.
01:31:00 I think this is going to be good.
01:31:04 What else do I need?
01:31:05 We need the event controller as well.
01:31:08 So if I head over to event controller under Advanced Node API, we can implement that controller right here.
01:31:19 Let me see if I have it.
01:31:22 If somebody can pull it for me, that will be great as well, just so I can quickly get it into the chat.
01:31:28 Oh, I think it's crossing the limit of the chat.
01:31:30 So let me get it maybe from the final repo.
01:31:34 I'll do that right away.
01:31:35 Sorry about this, guys.
01:31:37 I expected to have the code here, but I didn't end up having it.
01:31:41 It's going to be advanced node API right here, and it's going to be under models, event.ts right here.
01:31:49 Perfect.
01:31:50 I have it from GitHub.
01:31:52 It's going to be the event controller.
01:31:55 Okay.
01:31:56 So the controller, of course, for the event does what a controller typically does, which is it implements the business logic of that controller.
01:32:04 So if you see it here, we basically are specifying that these methods can be called under API events for getUsers and so on.
01:32:14 Wait, I think I pulled users, right?
01:32:16 I should have pulled the events.
01:32:19 I think I actually have it here.
01:32:21 No, I think we're good.
01:32:21 Yeah, I think we're good.
01:32:22 Perfect.
01:32:23 This makes sense.
01:32:24 So we're trying to get the events from here by saying this.eventService.getAll.
01:32:30 Yeah, this makes complete sense.
01:32:32 But of course, we have to implement the event service.
01:32:35 So if we head over into event service, that's going to be right here under services, event.ts.
01:32:42 And this part also has to be implemented.
01:32:45 Even though it is injecting just the generic server, sorry, generic service, we have to have the service for the event as well.
01:32:54 So if we check it out, that's going to look something like this.
01:32:58 And with that in mind, What we can do is find the event service to inversify controller and import the event controller as well.
01:33:09 I think we are doing that.
01:33:10 Let's see if we're doing that in the index.ts.
01:33:14 And here we are importing and binding all of these containers, but we have to do the same thing for the event container.
01:33:23 So let me actually refer to this code that I have here.
01:33:27 I think it's, yeah, it's a similar thing for what we did with the users.
01:33:31 or for the logs.
01:33:32 So I'm just going to go ahead and duplicate this below.
01:33:35 And I'll replace this with event service.
01:33:41 So if I say event service, it should look something like this.
01:33:49 And of course I can import it from event service.
01:33:51 Do you guys get the primary idea behind this?
01:33:54 What's happening is we have just created a generic service, which can now be used both as events or our users or everything.
01:34:02 So it's a one level up of the abstraction.
01:34:05 Even though the event service doesn't have anything here, the generic service actually acts for it.
01:34:12 The event service or later on the user service or other services just extend the generic service.
01:34:20 And you have seen how generic the generic service is.
01:34:24 It is so generic that you can simply define the routes and you're calling the model.find.
01:34:29 So whenever you have users or events or whatever else, you can simply call the generic service and then make the other services extend that generic service.
01:34:40 Then we have created the event service to use that event service.
01:34:44 And we can now use all the methods from the generic services.
01:34:48 How many times have I said service in a short time?
01:34:52 Many times, but this just saves us from just recreating all of these CRUD methods over and over and over again.
01:35:01 We'll have to do it only once and it'll work seamlessly from now on.
01:35:06 So if I head over to our Thunder client and I try to make a request to events, if we have done everything correctly, It should work.
01:35:16 Let me just make sure that I have my application running.
01:35:21 It looks like there's an issue with our symbol for the log service.
01:35:27 Let's see where the issue is coming from.
01:35:31 Maybe I have a typo somewhere.
01:35:32 That can totally happen.
01:35:35 In the types, we're looking good.
01:35:36 We have the log service in the index.ts.
01:35:40 Let's see if we have binded things properly.
01:35:42 We are binding the event.
01:35:44 Oh, I'm binding it to a log service.
01:35:45 The event should have been binded to the event service.
01:35:48 So if we do this, The server is running and in the Thunder client, if we make a request to local host 9,000 API events, we get back,
01:35:59 cannot get API events.
01:36:02 Let's see why that is.
01:36:03 The logger at least is working properly.
01:36:07 But now we have to make sure that this 404 turns into a 200, and that's most likely going to be within the event controller,
01:36:17 the logic for implementing all this.
01:36:20 The event controller is trying to get the event, but here we have the method for getUsers.
01:36:25 Let's see why we have that, even though it's calling this.eventService.getAll.
01:36:31 Maybe the issue is somewhere else, maybe in the routes.
01:36:37 Let me think about it a bit.
01:36:38 Anybody knows what could be the issue here?
01:36:40 Our application is running.
01:36:42 And if I try to make a request right here and go to localhost 9000 API events, yeah, we unfortunately get a 404. Could be an issue somewhere in the code.
01:36:54 What if we move the scheduling slash?
01:36:56 Oh, it could be the slash.
01:36:57 Oh, so you have experienced those issues as well.
01:36:59 Yeah.
01:37:00 Often removing the slash helps, but I doubt it'll help us in this case.
01:37:04 It was a good try though.
01:37:05 Always with live demo, something goes wrong.
01:37:07 It happens, but no worries.
01:37:10 What I care about the most is ensuring that the concepts that we covered so far are stuck with you.
01:37:17 Okay.
01:37:19 Can you do a quick check if you open up the controller one more time?
01:37:23 I think I saw that it was binding to the API users instead of API events.
01:37:27 Yeah, I also see that.
01:37:29 Oh, you think in the controller here?
01:37:30 Up at the top.
01:37:32 Okay, I see API events.
01:37:33 Yeah.
01:37:34 Oh, no, it's events.
01:37:35 It's events.
01:37:35 Yeah.
01:37:36 Another nice try.
01:37:39 We'll see.
01:37:39 We'll see.
01:37:40 Great, great tries, though.
01:37:43 But again, in the final code base that you have, everything should be working perfectly.
01:37:47 Most likely have just a minor typo, which, again, totally happens to all of us.
01:37:53 But what I care about the most is making you understand that you can use injectables to with controllers.
01:38:01 You can use them with services.
01:38:03 You can use them with middlewares, with different loggers.
01:38:08 You can do it in many different places.
01:38:10 Let me just see if I actually imported this in the index.ts of the Node Advanced API.
01:38:17 The controller.
01:38:18 Let's see.
01:38:19 Are we using the controller right here?
01:38:21 We have the logging middleware.
01:38:23 We have the event middleware.
01:38:27 Where do we have the controller here?
01:38:29 Oh, yeah, there we go.
01:38:30 Okay.
01:38:31 See this?
01:38:32 We imported the user controller, but now we also have to import the what?
01:38:37 Well, that's going to be the event controller, because without this, we cannot actually get the controller functionalities.
01:38:44 Oh.
01:38:45 Hopefully one last try for the end of this great webinar.
01:38:48 Let's see.
01:38:48 How does it go?
01:38:49 Okay.
01:38:51 It failed.
01:38:52 Let's see.
01:38:52 What do we get this time?
01:38:54 We have a typo.
01:38:56 We have a typo.
01:38:57 That was a bit underwhelming, but hopefully it's going to go now.
01:39:01 There we go.
01:39:01 Status 200. And we actually get back the events right here.
01:39:07 Perfect.
01:39:08 Let me actually close this so we can see it.
01:39:10 Advanced TypeScript workshop, Git Nation event on dependency ejection right here.
01:39:15 Well, perfect.
01:39:16 What a way to end the workshop right here.
01:39:20 There's a lot of stuff that we discussed, right?
01:39:23 We learned about advanced concepts and patterns.
01:39:27 So far, so good with a couple of obstacles on the road.
01:39:31 But TypeScript is super powerful, right?
01:39:34 It is the industry that allows us to use some of these robust APIs, but you may not need it all at all times.
01:39:42 You mean, what am I saying?
01:39:45 Well, but if your user base is not too big, if you don't have millions of people and you have limited features, you seriously don't need to implement all
01:39:54 of this.
01:39:55 But you can just go serverless, not with Resell.
01:39:58 You can choose any service you want, but always make a choice depending on your application's complexity, size, and requirements.
01:40:07 That's just my opinion, at least.
01:40:10 And if you're a part of a big company or a big application, go with frameworks using dependency injection behind the scenes or the ones that have support
01:40:19 for it.
01:40:19 And if you're an indie hacker or someone who wants to develop a quick SaaS, you can go with frameworks that speed up your process and get your work done.
01:40:29 And that's it.
01:40:32 That's the final part in this workshop of implementing a type safe backend.
01:40:39 This was a great one, but as you know, there's always so much more to learn.
01:40:45 But if I had to summarize this workshop, I'm gonna put it this way.
01:40:50 You now learned what dependency injection is.
01:40:53 You now learned that you can use TypeScript natively out of the box with Node.js applications.
01:40:59 And you'll learn how we can implement dependency injection within Node.js application using Inversify.
01:41:07 Are we all good on that?
01:41:08 If that is actually the case, if you know that now, then I'm going to consider my job to be done.
01:41:14 That's the only thing I wanted to let you know.
01:41:16 Of course, you can dive significantly deeper into all of these functionalities now that you understand the base concepts.
01:41:24 What we can do is As I told you, even though you know about implementing dependency injection from scratch, most frameworks and libraries or big applications
01:41:38 already have it implemented, so it's going to be super easy for you to just understand it because you will understand how it works behind the scenes.
01:41:50 As a final thing, I want to give you some homework if you want to have it.
01:41:53 In the final repo, you'll see that there's another application done with Bun and Hono, right?
01:42:00 This was done with Node.js, but nowadays more and more people are actually using Bun and Hono.
01:42:07 Bun being a Node.js runtime environment and Hono being a new way to produce APIs.
01:42:13 How many of you have tried them or at least heard about them?
01:42:16 If you can give me a yes in the chat.
01:42:18 There we go.
01:42:18 I see a couple of people that have heard of Bun, which is amazing.
01:42:21 So if you want to explore that as well, you have the final code base in the repo.
01:42:26 So just go ahead and take a look and kind of see the differences between the implementation of Bun and Hono and Node.js.
01:42:34 With that said, hopefully I didn't bother you too much with this workshop.
01:42:38 It was just under two hours.
01:42:39 So I so hope you were able to get some interesting insights out of it.
01:42:43 Thank you so much for being here and listening to me.
01:42:46 Appreciate it guys.
01:42:47 Once again, Alyssa, Andre, Jacob, Meredith, Jason, Rob, Abby, Alejandro, Gina and Jacob.
01:42:54 Thank you much for participating.
01:42:56 If you have any questions, reach out to us on contact at jsmastery.pro or just head over to jsmastery.pro or hit me up on Twitter,
01:43:06 LinkedIn, or whatever you're at.
01:43:09 I'll see you guys soon.
01:43:10 Have a great one.
01:43:11 Bye-bye.