Choose Your Own Adventure with Astro
Eric Carlisle the benefits of Astro's islands architecture and support with other popular framework components.
Decorators are a new feature of JavaScript and TypeScript that enable you to wrap functions with new features, without modifying the underlying implementation of the function. In this lightning talk, you’ll get a whistlestop introduction to decorators as we implement a light-weight web framework, inspired by the Python Flask web framework.
Kevin is the Head of Developer Relations at Deno. Kevin is a long time JavaScript developer, educator, and open source contributor. In addition to authoring a handful of popular Node.js modules on npm, Kevin created the open source educational PC game TerminalQuest (formerly TwilioQuest) using web technologies, which has been enjoyed by over one million players. On the Deno team, Kevin is focused on documentation, userland tools, developer experience, and community programs. Kevin and his family live just outside Minneapolis, MN, USA.
Kevin Whinnery 0:00
Hey what’s up everybody, my name is Kevin, and I’m part of the Deno team. And for those of you who haven’t used Deno before, Deno is a JavaScript runtime for the server that natively supports TypeScript without a compilation step. And one feature of TypeScript that I think is really neat that actually shouldn’t be coming to a browser near you very soon, is decorators. And in the next 10 minutes, I’m going to tell you a little bit about what decorators are what you would use them for, and then show you a little bit of code that uses decorators, so you can get a sense of how it works, and how it might differ from similar code that you’ve written in the past. So let’s go ahead and dive in. So we’ll start off by talking about what decorators are at a high level and how they work. So decorators allow you to customize the behavior of classes in a reusable way. Now, you might have already sort of done this type of thing before by extending classes with other super classes, using the prototype chain, things like that. But decorators provide a slightly different means of doing this by providing ways to customize classes that are sort of agnostic from the inheritance chain that a class might be using. So let’s, let’s consider this in the context of a simple class like this one, which is a person that has a name, and then you know, a instance method which greet would prints out a greeting method, excuse me, it prints out a greeting message to the person whose name is stored in the class. So if we wanted to change this, change this method to logout messages when we enter that function, and then maybe when we exit it, which is a pattern that’s reasonably common, when you’re adding like debug logic, you could change the code to look something like this. But that’s a little nasty, and you wouldn’t be able to reuse it in other classes that need that same logic where you want to print a message at the beginning of a method and then print a message when the method exits. So one way you can clean that up is by implementing this logic as a decorator, and a decorator in your code is going to look look a little bit like this. So you’re going to prefix the decorator with an at symbol. And then that’s going to be actually a function call that gets invoked when your class is loaded, to sort of change the behavior and functionality of the method that it’s decorating, before it gets called for the first time. So the implementation of that debug logging decorator takes in the sort of original function in this case, or the original class method, which can be used to call that same functionality using the dot call syntax that you see here, which you can use on JavaScript function objects today. And it’ll pass in a context object, which contains information about the entity that contains that contains that method. And using this signature, you can, you can wrap the functionality of that original method by executing some code before it executing some code after it and doing lots of different things to possibly mutate the value that’s being returned from that function, or, you know, take some kind of action, maybe change the visibility of data within that method call elsewhere in your program. And what the decorators can do at a high level, as I said, you can change the decorated entity itself. So the entity being anything from like a class property or a method of some kind, and you can actually change any data associated with it, you can replace that replace the decorated entity entirely with something that’s semantically compatible. And by that we mean, if you’re replacing a function, you need to return a function that can be called in the same way, if you’re replacing a property, you need to replace that with a property that can be accessed in much the same way. So generally, you can’t sort of take a function and replace it with a string say, the other thing that you can do within a decorator is expose access to data within the class outside the context of the class. So maybe in a browser scenario, you wanted to have some data in the window scope that you would update when something happens in the context of a class. So changing the the scope of or the access of data that’s in scope of the class is another use case for decorators. And you can also kind of process the entire decorated entity in some way after, after the instance of the object has been initialized. So there’s a hook that happens kind of after all of the different decorators have been called after the instance has been created. And you can do some some amount of modification of the decorated entity after that. I’d notably decorators in JavaScript, at least in this iteration of the Decker API can only be applied to classes. So you can use decorators on class declarations, methods, fields and accessors. And those accessors can also be the new accessors that use the accessor keyword in ACMA script. So decorators as a specification is relatively new, although the specification I think it was in 2022 Got to stage three, which means that it’s ready for implementation by browser vendors. And it hasn’t landed in many browsers yet. But in TypeScript, TypeScript generally implements stage three features of ACMA script, so decorators in their current iteration are available in TypeScript five. And in Deno, the stage three decorators are going to be available in version 1.40, which isn’t out yet, but should be in the next couple of days. In fact, when you’re watching this video, it’s very likely that this feature will be available in Deno, so be sure to check it out. So if that’s what decorators are, what generally would you use them for. And for the most part, you’ll see decorators used by framework or library authors to augment the functionality of classes that you right, so this is an example of using a class component decorator in the context of a Vue js application. So you can, you know, take those class properties and bind them to, you know, templates in the in the UI. There’s also a, you know, state management library called mob x, which is relatively popular, and it makes extensive use of of decorators to create like reactive state that you can use within the context of user interfaces as well. But one of my favorite usages of decorators doesn’t actually come from the JavaScript world, it all comes from the Python world. So flask is a lightweight way, excuse me, a lightweight web framework for Python, very similar to express or Hono, which is a framework that we’ll see here in a moment that lets you declare, you know, routes for your web applications without necessarily having to change the implementation of your underlying Python functions. And this, I think, is pretty neat. And exposes one of the secret use cases of decorators, which is keeping your, your code that is unrelated to the framework are pretty clean, and mostly having to do with data and business logic instead of, you know, lots of boilerplate to render HTML, or JSON or something like that. So what I’d like to do is show you a quick demo that uses decorators in a similar context. So I’m going to create a sort of class based, you know, extension, to the Hono framework, which is a lightweight web framework, again, kind of in the tradition of Express or Sinatra, and we’ll use decorators to kind of clean up our code from our route handlers a little bit in a way that I think is generally pretty useful. So let’s pop out to our, our text editor here. Um, so I got Visual Studio Code open. And I this is a simple Deno application that, again, uses TypeScript by default on so I don’t have to kind of configure any tooling in order to be able to use TypeScript. And I’ve done a couple of things here. So this is kind of the entry point for my application. And I’ve created two other files here. One is this Home Node decorators.ts file, which is kind of my framework code that’s going to implement my decorators. And then I’ve also created a to do resource class, which is going to consume those decorators, and define some routes within my application that are actually going to handle incoming HTTP requests. And over here on the right, I have an HTTP client that I can use kind of see this in action. So I can, you know, get a list of to do to do items. And then I’ve implemented a couple of routes along the way. So if I, you know, go visit slash to dues, slash that ID and send that I can get, you know, a single to do list item back. So I have like a little bit of a lightweight REST API on top of to do list items. So let’s take a look at how it works.
Kevin Whinnery 9:20
We’ll start with the actual sort of implementation code for the for the to do list and that’s going to use i the decorators that I declared in this other file, which we’ll take a look at here in a second. It also for persistence uses Deno KV, which is a key value database that’s built into Deno we will go super deep on how KV works but each of the I you know that we have a class here called to do resource and each of these instance functions, uses a decorator to declare what route it handles what HTTP verb it expects to handle and within the implementation Each of these functions is a code that’s very specifically scoped to just like data access. So I use the Deno KV API to grab a list of items from the database. And then I just render a list or I just return a list of items in response to each of these method calls. So I’m not actually doing any logic to render JSON or HTML or anything like that. My logic here is mostly focused on data access. And within my decorators file, this is where we see the implementation of actually stage two decorators, which is what you can use in Deno today without any additional configuration. But again, in the upcoming version of Deno on, you can use the stage three version of this API as well. And once again, because this is an older version of the API, we won’t go super deep on how these decorators are implemented. But I have a couple of wrapper functions, which will take a route and then return a decorator, that is set up to create a hono endpoint that handles traffic to those to those URLs. And one thing that I think is pretty useful about decorators as an API, as an API pattern, especially as a framework author, is that you can stack these decorators to do multiple things. So if I uncomment, this, template decorator, and save, save my server, now, instead of generating just JSON, by default, for my to dues, I can, you know, hit this endpoint, and excuse me, I’ll hit Go localhost 8000. And if we say slash to dues and hit send, now, I can actually render HTML in response to that, to that request, as well. So this is actually similar to a pattern that exists in flask where you can specify a template that gets rendered every time a route is requested, instead of just relying on the logic within the function to actually find that template and and render it on behalf of the of the user. So as a framework developer, you can kind of stack these, stack these decorators together, and end up with a nice API that is more composable versus having to have your implementers consume an API with multiple and actually having to change the underlying implementation in order to use different framework features. So that’s it again, just a super, super fast introduction to decorators. I hope that you can check it out. Again, in the future. Here are some of the sources that I used in the preparation of this presentation. So please check those out to learn more about decorators. And once again, you can try decorators in Deno 1.440, which is coming out here very, very shortly. So once again, my name is Kevin and I hope that you enjoyed learning a little bit about decorators and I hope you use them in your own code very soon.
Eric Carlisle the benefits of Astro's islands architecture and support with other popular framework components.
TheJam.dev is a 2-day virtual conference focused on building modern web applications using full stack JavaScript, static site generators, serverless and more.
Chad Stewart will help front-end developers address that complexity by building your front-end Architecture with intention by leveraging component-driven design.
In this session, Harshil Agrawal will explore Remix and how to use it to build a localized site using Contentful.
TheJam.dev is a 2-day virtual conference focused on how to build modern web applications using Jamstack, serverless and more