The State of Node Compatibility in Deno

Rate this content
Bookmark

Deno is a next-generation TypeScript and JavaScript runtime from the original creator of Node.js. Deno is fast, secure by default, and provides a great developer experience, with TypeScript, JSX, testing, linting, and more all included out of the box. And best of all - you can bring many of your favorite to Node modules with you to Deno. In this talk, we'll cover the current state of backward compatibility with Node.js projects and modules in Deno. We'll demonstrate what works with zero configuration, and the available mechanisms in Deno that will let you bring the best of the Node ecosystem along for the ride.

David Sherret
David Sherret
Kevin Whinnery
Kevin Whinnery
23 min
04 Apr, 2024

Video Summary and Transcription

Today's Talk introduces Deno, a next-generation JavaScript runtime with native TypeScript support and improved server-side development features. Deno offers granular permissions for sensitive APIs and includes built-in tools like a test framework and linter. The Talk demonstrates how to use Deno with an existing Node.js project, showcasing compatibility and import features. It also discusses upcoming features like bringing Node modules to Deno and handling file extensions. Overall, Deno provides a seamless transition for Node developers and offers a range of powerful tools and features.

Available in Español

1. Introduction to Deno and Node Compatibility

Short description:

Today, we are here to talk to you about the state of node compatibility in Deno. Deno is a next-generation JavaScript runtime with native support for TypeScript and a number of features that improve server-side JavaScript development. We'll give you a quick demo of Deno and then focus on node compatibility features. We'll show you how to apply these features in an existing node project. Let's get started with a Deno demo, where you'll see the native support for TypeScript and how to set up a Deno server.

What's up, everybody? My name is Kevin Wendray. I'm part of the Deno team. And I'm joined by David Sherritt, who is also part of the Deno team, working on the Deno runtime. And today, we are here to talk to you a little bit about the state of node compatibility in Deno.

So for those of you who haven't heard of Deno before, it's a next-generation JavaScript runtime with native support for TypeScript, lots of nice built-in tooling that we'll get into here in a second, and a number of features that we think kind of improve the state of server-side JavaScript development. But a major thing that you might not know about Deno is how much work has gone into backwards compatibility with Node.js as a kind of a key component of server-side JavaScript development. So in this presentation, we're going to give you just a quick demo of Deno to give you a sense of kind of how it works, how it might differ from Node.js. And then we're actually going to spend the majority of our time digging into some node compatibility features to let you know how those work, how you might apply those in the context of an existing node project.

So we're actually going to be taking a simple Node.js project that uses Express and making some changes to it using some of the node compatibility features that exist in Deno. So let's go ahead and get started. And my friend David, by the way, will be joining us kind of as our color commentator. David's actually the implementer of many of these compatibility features we're going to talk about. So I'm going to ask him some questions, and he's going to be my pair programmer as we implement some of these things. So what do you think, David? You ready to do a quick Deno demo? Definitely. All right, let's do it. So I'm going to jump over to just a sort of a blank Visual Studio code project where I'll do a very quick Deno demo just to give you a sense of how the runtime works.

So one of the things that you'll notice in Deno right away is that there is native support for TypeScript. So without any dependencies to install or anything like that, you can very quickly start writing TypeScript code. And I'll implement sort of a hello world Deno server. And in Deno, that's going to be done using the Deno namespace. And there is a Deno serve command, which is going to allow us to set up a handler function that will take a web standard request object and allow us to handle incoming requests. If you had a Deno JSON, then you'll get the type checking. Yeah, exactly. That's definitely true. So I was going to point out you might notice some of the squiggles and the lack of completion. And the way that I can fix that, as David mentioned, is by adding a deno.json, which will kind of enable my VS Code plugin for Deno to start to give me some hints about Deno serve and the built in Deno namespace, things like that. So as the first argument to Deno serve, again, I'm going to pass in a request object. And in this case, I'm just going to return a new response, which is going to be hello world. And to make the TypeScript linter happy, I'll just add a leading underscore there to indicate that I'm not actually using this request object. So this is kind of, once again, without any third party dependencies, I can write some TypeScript that's going to serve HTTP requests.

2. Deno Runtime Features and Permissions

Short description:

Deno has a unique feature of granular permission to sensitive APIs like network access, file system access, and system environment access. You have to explicitly opt into these permissions as a developer. Deno also offers built-in TypeScript support, an excellent Visual Studio Code plugin, and runtime permissions.

And if I run this with Deno run, you'll notice that I'm actually challenged for permission. And that's another feature of the Deno runtime, which is unique to Deno and doesn't exist in Node in the same way, is granular permission to sensitive APIs like network access, access to the file system, access to the system environment. Those types of permissions you have to explicitly opt into as a developer. So in this case, I'm going to say yes, I'm happy to allow network access. And now I have a server listening on localhost 8000. So if I spin up a new Chrome browser, which I will here just momentarily, and go to localhost 8000, there is my hello world response. So that's just a really quick demo of a couple of things. So we have built-in TypeScript support. We have an excellent Visual Studio Code plugin, which gives you some IntelliSense and other hints in the editing environment. And we have some of these built-in runtime permissions.

3. Deno Development Experience and Tools

Short description:

Deno provides a batteries-included development experience for server-side JavaScript. It includes tools like a test framework, linter, and formatter by default. Deno FMT cleans up formatting, and Deno compile generates a portable, self-contained executable for running a local server.

One other thing that I wanted to show you is some of those features that I talked about before. So Deno tries to provide sort of a batteries-included development experience for server-side JavaScript. So a lot of tools that you would normally have to install yourself are actually included in Deno by default. So oftentimes when you're starting a new Node project, you'll have to set up a test framework, like a linter, a formatter, all those types of things.

In Deno, those are actually just provided for you. So let's say that I make some mistakes here and I have strange formatting inside my files, or I'm looking at a pull request from somebody that's new to the project. I can just use Deno FMT in this directory, which is going to... Oh, I didn't save those changes. My fault here. Let me save those first, overwrite. And now if I run Deno FMT, that's going to clean that up to a standard format that you can expect to see across all Deno projects. But in addition to testing and formatting, there's also a few really cool things like Deno compile.

So if I say denocompile index.ts, what that's going to do is actually give me an executable file that is portable, that is completely self-contained, that I can just execute to have a local server running. So if I do ./.denobasic, now I have that same program. I'm now running a server on localhost 8000 from a single binary. So again, with zero tools to install, zero dependencies, I can format my code. I can compile it to a single executable. There's a lot that Deno kind of does for you right out of the box.

4. Using Deno with an Existing Node.js Project

Short description:

Learn how to take advantage of Deno compatibility features in an existing Node.js project. Refactor the project to use Deno's ESM module system and inline import dependencies from npm. Run the application in Deno and address potential problems.

So that's just a quick overview of Deno, kind of what Deno does. But we are not here just to talk about all the cool stuff that's built into Deno. We want to learn a little bit about what you can do in the context of an existing Node.js project and take advantage of some of the compatibility features that exist in Deno.

So let's jump back over here. This project, I have a very simple kind of pre-existing Node project, which if you've used Node before, it'll look pretty familiar. It uses Express as the sort of lightweight web framework that handles some routes. We have a body parser Middleware, which will allow us to parse incoming URL encoded post bodies or JSON post bodies. And then we have a couple of routes set up for a fictitious user REST API.

And what I thought we could do for starters is, let's take a look at kind of how this works in Deno kind of unchanged, and then we'll sort of incrementally refactor it to use some of the Deno compatibility features to make this application actually run. So number one, we can just kind of satisfy ourselves that this project works by running Node index.js, and it does look like we have the server running on port 3000, so let's just take a peek at that. Oh, yeah, it looks like there isn't a route there, but if we do like users, it just kind of gives us this empty JSON array. So we're in a working state with the Node server, but now let's try to run it in Deno and kind of see what happens. So let's start by running Deno run, and I'm going to use the dash A option here, which gives the running process permission to all underlying APIs. We'll do that for the sake of expediency here. It's generally best to only use the set of permissions that are required to execute the process, but we'll run it with dash A, and then pass in index.js. And it looks like we're already seeing some potential problems. It says reference error require is not defined.

Why is that, David? Why is require not defined? In this case, Deno only supports ESM, to be simple. It's like we have the one module system. It's the standard module system that JavaScript has standardized on. We need to update that from CommonJS to ES modules. Yep, absolutely. So in this case, we have our two requires up at the top of the file here. So if I want to update these, I'm going to make this import express, and then it's going to be, instead of using require, it's going to import express from npm express. And this is an npm specifier, which is the way in a Deno application that you would, you know, inline import your dependencies. So this is something that will work. There's actually a few different ways to approach this, which we can take a look at here in a moment. But we'll sort of incrementally get there by again, saying, you know, import body parser from npm body parser. And that I think is probably our first hurdle. So let's run the application.

5. Deno Compatibility and Importing Node Modules

Short description:

Learn how to import the process global from the node process module in Deno to read environment variables. Explore the compatibility between Deno and Node.js by making minor changes to run Node code in Deno. Discover the upcoming bring your own Node modules feature.

Oh, and then there's another error here. Okay, so type error cannot read properties of undefined. So process.env.port. So in a lot of node applications, you know, you'll read in, you know, whatever port you want to listen on from the system environment. And that's slightly different here. So what's this error all about, David? Why can't I do this?

Yeah, so there's no process global in Deno. So we have to import it from the node process module.

Okay, so yeah, that's probably the quicker way here. So we could do that. There is also a Deno API for getting an environment variable that we could use in the Deno namespace. But we can grab the import. Is it, you know, help me out off the top. I think it's just process. It is just, okay. So it is. From, this time it's going to be a node process. So this, you know, this node specifier, this actually allows you to use node built-ins like FS, HTTP, all the other parts of the node standard library, which have polyfills in the, you know, in the context of a Deno application. So now we should have process.env defined and let's try to take a look at that.

So that's great. So we now have the server listening on port 3000. And if we reload it, we can see that we're starting to get the same responses. So with a couple of tweaks, we're starting to make some progress, but I thought it would be good to take a look at some other compatibility features beyond just, you know, these basics. But, you know, it is kind of cool to see that with just a few minor changes, you can take some code that was running in Node and is now running in Deno. So one thing that you might want to do, especially if you're migrating from a node project is rather than using, you know, NPM specifiers and having a Deno, what's actually happening in the background here is that Deno is automatically downloading and installing and caching versions of these NPM modules locally. You might not want to manage dependencies in that way. You might actually already have like a package.json, for example, which already has all of your dependencies installed in it. So there is a way, David, right, to use the dependencies that are in your package.json and installed in your Node folder. So what do we like to call that? So there is like just out of the box how it is right here. You can actually not specify NPM colon and Deno will resolve through the package.json. But the one thing, like say you want to bring your own Node modules, use your own package manager, that's the upcoming bring your own Node modules BYONM feature.

6. Using Node Modules and the Sloppy Imports Feature

Short description:

Understand how to bring your own Node modules to Deno using the upcoming feature. Learn about configuring Deno to use Node modules installed by a package manager of your choice. Explore the sloppy imports feature in Deno for importing files in other directories.

Oh, did that not work? Yeah, I would have thought that, you know, is this body parser is yeah, you know what, that's actually part of, I think that this is actually part of Express. There is a body parser module on NPM. Is it on package.json if it's not on package.json? Yeah, it's not on the package.json. That would be why. So it's looking for that. That's why, yeah. Yes. So we would have to NPM install on that. I'm sorry, I cut you off. What were you going to say?

Oh yeah, we just have the upcoming bring your own Node modules feature and that will allow you to use package manager of your choice with Deno. Yes, so that's often useful to like bring your own Node modules if you have dependencies that kind of depend on the layout of the Node modules folder in some way. So like you would want to install those dependencies through, you know, say YARN or PNPM maybe and take advantage of all the features and specific behavior of an NPM client. Deno can pretty happily work with a Node modules folder after it's been set up by the package manager of your choice. And there's a few ways to configure that behavior. But I think one of the nicer ways is through, you know, adding a deno.json to the folder or to the project that you're working on. And within the deno.json, there is a property called unstable. And right now, the bring your own Node modules feature is currently behind an unstable feature flag. In Deno 2.0, a lot of these compatibility, you know, flags are actually going to be just enabled by default and you won't have to configure them in Deno.json. But in this case, we can enable unstable. It is, I think, literally called bring your own Node modules, right? No, BYONM. Oh, it is just BYONM. OK, so this is going to enable Deno just using whatever Node modules are installed to the Node modules folder when it runs that, when it runs this application. So if I run here, this is actually going to be using the version of Express that's already been installed to the Node modules folder. And you can see we can refresh here and the server continues to work just fine.

So another thing that I thought would be useful to look at is the sloppy imports feature, which sounds a little funny. But one of the other differences between Node and Deno that you're going to notice is for imports of files in other directories. Let's say that in my Deno project that I set up before, I'm going to create a handler.ts. And in this file, that's where I'm going to create the handler function that I want to use here. So I'm going to grab this handler function and I'm going to export it as the default in an ECMAScript module in this handler function. And then if I wanted to use that in Deno, I would need to import handler from and then I would specify a handler.ts and you can use that here instead.

7. Imports and File Extensions in Deno

Short description:

Understand the behavior of imports in Deno, which closely resembles browser behavior. Learn how to import external files without a specifier in a Node project. Configure Deno to handle imports without file extensions using the 'sloppy imports' flag in Deno.json.

And if I ran that, of course, that whoops, I wouldn't want to use the compiled version. I'm just going to Deno run and then I'll do dash dash, allow net to allow network access specifically. And then that's going to basically do the same thing. But one thing you'll notice right away is that this import uses the file extension. And in Deno, we tried to make the Deno runtime, you know, as closely as we can to browser behavior. The only globals except for the Deno global that are in a Deno application, you know, are globals that you would have in the browser. So there's, you know, fetch and a lot of the other browser based globals are available in Deno. And the way that you specify imports is usually by specifying, you know, a fully qualified URL. And, but no, it doesn't actually work in the same way.

In a Node project, you'll usually will, you know, import, you know, external files without a, without a specifier. So let's do the same thing. We'll create a new file called, you know, handler.js. And here we'll have to actually do something slightly different. So in Node, if I'm going to create an ESM module, you know, I would call it MJS. And I guess, since we're running it in Deno now, I don't have to do that. So I'll just leave it as a .js extension. And I'll do kind of the same thing. I'm going to, you know, export default. And then I will essentially make this the same handler that we would have over here. So it'll just be an async, you know, fat arrow function. So it's going to do about the same thing. And then over here, I could say, you know, import handler from .slash handler. But again, in Node project, I'm probably going to just say .slash handler, which is the, you know, which is generally the convention in Node. You don't use the, you know, the extension when you're trying to import. We can actually configure Deno to handle this, again, by using one of those unstable flags. So if we go to Deno.json, this one is called what, David? It's like sloppy imports, like with a hyphen. So we'll say sloppy imports. And if we enable that and go back to index.js, you can see the red squiggly has sort of downgraded to a blue squiggly because, you know, we're not using the extension. But other than like web standards, why else, like, don't we use that in Deno generally, David? Like what's the cost of not having the extension? Yeah. So when you don't have the extension, then it needs to do a lot of probing.

8. Compatibility Features and Conclusion

Short description:

Recommend using explicit extensions for better performance and simplicity in the runtime and tooling. Deno provides a layer to help incrementally refactor node code. Run existing node projects in Deno and convert common JS modules to ESM. Utilize node imports for built-in features and explore unstable node compatibility features. Refer to the Deno documentation and node compatibility guide for further assistance.

So every time the runtime is resolving that file, it has to go, okay, is this a directory? Okay. Is there an index.js? Is there an index.mjs? And go through all these possibilities, do this probing, and, you know, imagine timesing that by many, many times. There's a performance cost. So yeah, we try to recommend using explicit extensions. And it's also much simpler in the runtime and it's simpler when you're implementing any tooling.

Yeah. So we'll generally, like when you're doing this in a Deno project, we'll give you some gentle nudges that will let you know, like, hey, probably you should use explicit file extensions. But because a lot of node code won't have that sort of by default, we do have this sort of layer that's going to help you incrementally refactor in this direction. But once we, you know, once we run that file, again, we get some warnings letting us know we have sloppy module resolution happening. But at the end of the day, Deno does, you know, helpfully allow us just to, you know, run the same code within the context of the Deno runtime.

So that is kind of all the time we have for today to get into these compatibility features. But what we would recommend that you do is if you have an existing node project, to actually try and run it in Deno. And you might actually be surprised at how far you can get without having to make very many changes. But generally speaking, what you're going to look at doing is converting any usage of common JS modules to ESM. You'll want to, you know, use node imports for any built in node features that you're going to be using. And you may have to take advantage of some unstable node compatibility features. So in the Deno documentation on docs.deno.com, there's actually a fairly complete listing of unstable feature flags, several of which have to do with node compatibility. So we also have like a node compatibility guide on the docs, where you can look at some of the things that you are likely to have to change in order to get a node project running on Deno.

So, again, I think that's all the time we have for today. But David, thank you very much for hanging out and for helping me pair program this. And thanks to everybody else watching the presentation today. And I hope you enjoy the rest of the conference.

Check out more articles and videos

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

Node.js Compatibility in Deno
Node Congress 2022Node Congress 2022
34 min
Node.js Compatibility in Deno
Can Deno run apps and libraries authored for Node.js? What are the tradeoffs? How does it work? What’s next?
Javascript Should Come With Batteries
React Day Berlin 2023React Day Berlin 2023
30 min
Javascript Should Come With Batteries
Setting up JavaScript projects is no fun. Getting started involves installing and configuring node, tsc, prettier, eslint, a testing framework, a database driver, and more. Why is JavaScript not batteries included? In this talk we'll talk about how Deno fixes this, letting you focus on building stuff. We explore what benefits full tooling integration unlocks, and remember how fun it is to program if your tools help you, rather than requiring your babysitting.
Deno 2.0
Node Congress 2023Node Congress 2023
36 min
Deno 2.0
Top Content
Deno 2.0 is imminent and it's bringing some big changes to the JavaScript runtime. In this talk, we'll introduce the new features including import maps, package.json auto-discovery, and bare specifiers. We'll discuss how these improvements will help address issues like duplicate dependencies and disappearing dependencies. Additionally, we'll delve into the built-in support for deno: specifiers on the deno.land/x registry and its role in providing a recommended path for publishing. Come learn about how these updates will shape the future of the JavaScript ecosystem and improve backwards compatibility with Node applications.
Bun, Deno, Node.js? Recreating a JavaScript runtime from Scratch - Understand magic behind Node.js
Node Congress 2023Node Congress 2023
29 min
Bun, Deno, Node.js? Recreating a JavaScript runtime from Scratch - Understand magic behind Node.js
Bun, Deno, and many other JavaScript runtimes have been hyped, but do you know why? Is it that easy to make a runtime from scratch?

I've been researching the secret behind Node.js' power and why there are so many new JavaScript runtimes coming up. Breaking down each key component used on Node.js I've come to interesting conclusions that many people used to say whereas in practice it works a bit differently.

In this talk, attendees will learn the concepts used to create a new JavaScript runtime. They're going to go through an example of how to make a JavaScript runtime by following what's behind the scenes on the Node.js project using C++. They'll learn the relationship between Chrome's V8 and Libuv and what makes one JavaScript runtime better than others.

This talk will cover the following topics:
- What's a JavaScript Engine - V8
- Why Node.js uses Libuv
- How to create a JS Runtime from scratch
Writing universal modules for Deno, Node, and the browser
TypeScript Congress 2022TypeScript Congress 2022
25 min
Writing universal modules for Deno, Node, and the browser
This talk will walk you through writing a module in TypeScript that can be consumed by users of Deno, Node, and browsers. I will walk through how to set up formatting, linting, and testing in Deno, and then how to publish your module to deno.land/x and npm. I will also start out with a quick introduction on what Deno is.
Fresh: a new full stack web framework for Deno
Node Congress 2023Node Congress 2023
24 min
Fresh: a new full stack web framework for Deno
Fresh is a web framework based on Deno and Web standards built to run on the edge

Workshops on related topic

Building a Hyper Fast Web Server with Deno
JSNation Live 2021JSNation Live 2021
156 min
Building a Hyper Fast Web Server with Deno
WorkshopFree
Matt Landers
Will Johnston
2 authors
Deno 1.9 introduced a new web server API that takes advantage of Hyper, a fast and correct HTTP implementation for Rust. Using this API instead of the std/http implementation increases performance and provides support for HTTP2. In this workshop, learn how to create a web server utilizing Hyper under the hood and boost the performance for your web apps.
Writing Universal Modules for Deno, Node and the Browser
Node Congress 2022Node Congress 2022
57 min
Writing Universal Modules for Deno, Node and the Browser
Workshop
Luca Casonato
Luca Casonato
This workshop will walk you through writing a module in TypeScript that can be consumed users of Deno, Node and the browsers. I will explain how to set up formatting, linting and testing in Deno, and then how to publish your module to deno.land/x and npm. We’ll start out with a quick introduction to what Deno is.