惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
V
Visual Studio Blog
博客园 - Franky
N
Netflix TechBlog - Medium
WordPress大学
WordPress大学
小众软件
小众软件
Jina AI
Jina AI
L
Lohrmann on Cybersecurity
罗磊的独立博客
I
Intezer
W
WeLiveSecurity
T
Tenable Blog
Cyberwarzone
Cyberwarzone
P
Privacy & Cybersecurity Law Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Securelist
Recent Announcements
Recent Announcements
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Engineering at Meta
Engineering at Meta
Hacker News: Ask HN
Hacker News: Ask HN
F
Full Disclosure
S
SegmentFault 最新的问题
S
Security Affairs
L
LINUX DO - 热门话题
C
CERT Recently Published Vulnerability Notes
博客园 - 叶小钗
Stack Overflow Blog
Stack Overflow Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Secure Thoughts
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Register - Security
The Register - Security
月光博客
月光博客
Microsoft Azure Blog
Microsoft Azure Blog
Last Week in AI
Last Week in AI
PCI Perspectives
PCI Perspectives
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
T
Threat Research - Cisco Blogs
B
Blog RSS Feed
L
LINUX DO - 最新话题
F
Fortinet All Blogs
雷峰网
雷峰网
Security Latest
Security Latest
大猫的无限游戏
大猫的无限游戏
P
Palo Alto Networks Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
V
V2EX
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 【当耐特】

Deno

Deno 2.8 | Deno Claw Patrol: an open-source security firewall for agents | Deno Fresh 2.3: Zero JS by default, View Transitions, and Temporal support | Deno Deno 2.7: Temporal API, Windows ARM, and npm overrides | Deno Build a dinosaur runner game with Deno, pt. 6 | Deno Build a dinosaur runner game with Deno, pt. 5 | Deno Deno Deploy is Generally Available | Deno Introducing Deno Sandbox | Deno Build a dinosaur runner game with Deno, pt. 4 | Deno Build a dinosaur runner game with Deno, pt. 3 | Deno Build a dinosaur runner game with Deno, pt. 2 | Deno React / Next.js Denial-of-Service Vulnerability: Deno Deploy users protected | Deno Deno 2.6: dx is the new npx | Deno Build a dinosaur runner game with Deno, pt. 1 | Deno React Server Functions / Next.js Vulnerability: Deno Deploy users protected | Deno My highlights from the new Deno Deploy | Deno Deno's Other Open Source Projects | Deno How Deno protects against npm exploits | Deno Help Us Raise $200k to Free JavaScript from Oracle | Deno Deno 2.5: Permissions in the config file | Deno Fresh 2.0 Graduates to Beta, Adds Vite Support | Deno Deno 2.4: deno bundle is back | Deno JavaScript™ Trademark Update | Deno What's coming to JavaScript | Deno A brief history of JavaScript | Deno Reports of Deno's Demise Have Been Greatly Exaggerated | Deno An Update on Fresh | Deno How Plaid migrated 100 services to a new database platform 5x faster with Deno | Deno Deno 2.3: Improved deno compile, local npm packages, and more | Deno Add JSR packages with pnpm and Yarn | Deno Zero-config Debugging with Deno and OpenTelemetry | Deno Exploring Art with TypeScript, Jupyter, Polars, and Observable Plot | Deno Deno v Oracle Update 3: Fighting the JavaScript Trademark | Deno Build a custom RAG AI agent in TypeScript and Jupyter | Deno How to get deep traces in your Node.js backend with OTel and Deno | Deno toranoana.deno #20 登録受付中(2025年3月14日) | Deno Node just added TypeScript support. What does that mean for Deno? | Deno The Dino 🦕, the Llama 🦙, and the Whale 🐋 | Deno Publish a lint rule, get a prize | Deno Deno 2.2: OpenTelemetry, Lint Plugins, node:sqlite | Deno If you're not using npm specifiers, you're doing it wrong | Deno How Deno's documentation is evolving | Deno Oracle justified its JavaScript trademark with Node.js—now it wants that ignored | Deno Introducing the JSR open governance board | Deno Intro to Wasm in Deno | Deno Announcing OpenAI on JSR | Deno Deno in 2024 | Deno Goodbye WinterCG, welcome WinterTC | Deno Build a SolidJS app with Deno | Deno Run your Next.js SSR app on Deno Deploy | Deno Solve Advent of Code 2024 with Deno and Win Prizes! | Deno Deno v. Oracle: Canceling the JavaScript Trademark | Deno Deno 2.1: Wasm Imports and other enhancements | Deno Build a Typesafe API with tRPC and Deno | Deno Self-contained Executable Programs with Deno Compile | Deno Build a Database App with Drizzle ORM and Deno | Deno Introducing your new JavaScript package manager: Deno | Deno Announcing Growthbook on JSR | Deno Build an Astro site with Deno | Deno How to convert CommonJS to ESM | Deno Announcing Deno 2 | Deno The Final Touches: What’s New In v2.0.0-rc.10 | Deno Announcing Stable V8 Bindings for Rust | Deno Deno 2.0 Release Candidate | Deno Secure, efficient private npm registries with Cloudsmith and Deno | Deno Painting the Plane as We Fly It: Designing JSR | Deno Introducing Web Cache API support on Deno Deploy | Deno Deno 1.46: The Last 1.x Release | Deno Protect your cloud spend with new Deno Deploy spend limits | Deno What we got wrong about HTTP imports | Deno Benchmarking AWS Lambda Cold Starts Across JavaScript Runtimes | Deno Announcing Supabase on JSR | Deno Deno 1.45: Workspace and Monorepo Support | Deno Introducing KV Backup for Deno Subhosting | Deno A Gentle Intro to TypeScript | Deno Announcing Hono on JSR | Deno How We Made the Deno Language Server Ten Times Faster | Deno How the Guardian uses Deno to audit accessibility and performance across their 2.7 million articles | Deno Introducing More Flexible Domain Association for Deno Subhosting | Deno The stabilization process of the Standard Library has begun | Deno Deno 1.44: Private npm registries, improved Node.js compat, and performance boosts | Deno How we built a secure, performant, multi-tenant cloud platform to run untrusted code | Deno The Deno Standard Library is now available on JSR | Deno How to document your JavaScript package | Deno Your Low Code Solution Needs an Escape Hatch | Deno Deno 1.43: Improved Language Server performance | Deno How Slack used Deno to save months of engineering effort in launching their new platform | Deno JSR Is Not Another Package Manager | Deno Announcing the Hookdeck SDK on JSR | Deno Announcing the Neon Serverless Driver on JSR | Deno An intro to TSConfig for JavaScript Developers | Deno How we built JSR | Deno How Netlify used Deno Subhosting to build a successful edge functions product | Deno Introducing Simpler Project Creation in Deno Deploy | Deno Deno 1.42: Better dependency management with JSR | Deno Introducing deployctl, the command line interface for Deno Deploy | Deno Introducing JSR - the JavaScript Registry | Deno How to add Monaco to a Next.js app and securely run untrusted user code | Deno Survey Results and Roadmap | Deno Deno 1.41: smaller deno compile binaries | Deno
How to publish Deno modules to NPM | Deno
Kitson Kelly · 2022-03-01 · via Deno

I wrote oak, a full featured HTTP middleware/router framework. It is the most used HTTP framework for Deno and powers many sites; for example doc.deno.land.

A lot of people want to use Deno as their primary development platform but also want to be able to share code with-in the Node ecosystem. This is easily possible using dnt. In this article, I will show you how I published Oak to NPM module in a way that it is useable in Node.

  • Overview of oak
  • dnt
  • Getting oak to run on Node.js
    • Web standards
    • ESM and Node.js
    • Supporting HTTP on Node.js
    • Other tidbits
    • Building, testing and CI
    • Publishing to npm
  • Conclusions

Note: This article is accurate at the time of publishing, Deno, oak, dnt, and Node.js will continue to evolve and specific technical details and statements may not be accurate in the future.

Overview of oak

If you aren’t familiar with oak, it is a koa inspired HTTP middleware framework along with a router. It’s main purpose is to provide a structured way of handling HTTP requests. While the fundamentals of oak have been consistent since it was released in December of 2018 (which Deno was v0.2 at the time), it has continued to evolve as the Deno CLI evolved, including migrating to the native HTTP server and ensuring the Deno Deploy is supported.

Basic usage in Deno is straight forward:

import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello from oak";
});

app.listen({ port: 8000 });

But it was always designed with Deno in mind, leveraging built-in Deno APIs to be able to not only handle HTTP requests and responses, but also do advanced features like generating ETag signatures for static files and complicated form data body handling. Running it on Node.js was never a design consideration.

dnt

dnt provides a build pipeline to transform Deno code into a Node-compatible NPM page.

The pipeline does the following at a high level:

  • Transforms Deno code to Node.js compatible TypeScript code:
    • Rewrites Deno’s extensioned module specifiers to ones compatible with Node.js module resolution.
    • Injects shims for any Deno namespaces APIs detected, as well as other globals which can be configured.
    • Rewrites remote imports from Skypack or esm.sh as bare specifier imports and adds them to the package.json as dependencies.
    • Other remote imports are downloaded and included in the package.
  • Type checks the transformed TypeScript code with tsc.
  • Writes out the package as a set of ESM, CommonJS and TypeScript type declaration files along with a package.json.
  • Runs the final output in Node.js through a test runner which supports the Deno.test() API.

This means you can develop and test all of your code in Deno and when you want to publish it to npm and make it available for Node.js, you use dnt to export it and validate that it works as expected, all with minimal or no changes to your source code.

Once you have it setup, you can continue to iterate on your package, easily publishing it for both consumption in Deno as well as via npm.

Getting oak to run on Node.js

There were a lot of lessons learned in getting it to work, and not everything is done yet. Support for HTTP/2, web sockets and handling for FormData files need to be added, but the main functionality works.

Web standards

Deno fully leverages web standards and the web platform. Node.js is increasingly adding web platform APIs, but they are often not exposed globally and some still require dependencies. These were ones that needed to be addressed with oak:

  • Web compatible Streams are globally available in Deno, but only available via built-in module "stream/web" on Node.js. These needed to be exposed via the dnt configuration:

    {
      shims: {
        custom: [{
          package: {
            name: "stream/web",
          },
          globalNames: ["ReadableStream", "TransformStream"],
        }],
      }
    }
  • Web crypto is globally available in Deno, but only available via webcrypto symbol via the "crypto" built-in module on Node.js. This needed to be exposed via the crypto: true option in the shims section of the dnt configuration.

  • While not used directly in oak, some of the dependencies needed the Blob global made available from the builtin "buffer" module. This is exposed via the blob: true option in the shims section of the dnt configuration.

  • Deno exposes the web standard fetch() and Headers which are used by oak. Node.js currently doesn’t have them available built-in (though that is changing) and so they needed to be exposed via the "undici" package, which dnt supports via the undici: true options in the shims section of the dnt configuration.

  • oak extends the web standard ErrorEvent for internal errors which is available globally in Deno. Node.js does not have this and so I had to create an ErrorEvent class which extends the globally available Event in Node.js and is loaded by dnt as a global shim.

ESM and Node.js

Deno was built around ES modules and while Node.js has un-flagged support for ES Modules for an extended period of time, it is still a complicated issue. For many workloads dnt can easily down-emit your code to CommonJS and ESM to make it possible for a consumer of your package to choose how to load it in Node.js. For oak, this was a bit more complex. There is one situation where Node.js cannot support ES modules, and that is the situation of top-level await.

I had a usage of top-level await to initialize a variable that I needed to refactor in order to support CommonJS.

Supporting HTTP on Node.js

The biggest difference between Deno and Node.js is on how they handle HTTP requests. Deno has evolved, where initially it only supported HTTP via the Deno std library, it now provides a native implementation built around the web platform fetch() APIs. Node.js’s builtin "http" module as a very low level API that has large remained unchanged since the early days of Node.js.

When oak migrated from the std library HTTP server to the native one, I implemented an abstraction layer for handling the requests and responses. Even when the support for the std library HTTP server was dropped from oak, the abstraction was retained. This abstraction, with a bit of refactoring and improvement, allowed support of the low-level Node.js "http" server with very little changes in the rest of the code base.

If you are interested, the code is in the http_server_node.ts module.

After some refactoring, I was able to use the dnt and the mapping feature to “swap out” the Deno abstraction with the Node.js abstraction. This is a minimal amount of platform specific code (about 160 lines for Deno and 220 lines for Node.js).

Other tidbits

One of the initial test failures I had when running the tests under Node.js was for the custom inspect logic that oak has for many of its classes. In Deno, the custom inspection method is defined by the "Deno.customInspect" symbol, but in Node.js it is available as "nodejs.util.inspect.custom" symbol and also has a slightly different API. So I added a "nodejs.util.inspect.custom" symbol method for each of the classes I had a Deno custom inspect upon and adjusted it to better align to the Node.js API.

Even with all that, there are still subtle differences in the output of inspect, and so it was one of the few places in oak where I actually had to branch test code based on if it running under Deno or Node.js

Also, the undici Headers class varies from the Deno Headers class. Deno’s version passes all the WHATWG tests, and undici’s was designed with Node.js first in mind. There is a fundamental problem with the web standard and server side JavaScript though and the use of Headers, specifically it has to do with the special treatment of Set-Cookie header, which is only set server side. Both Deno and undici independently solve this problem, and so this results in different behaviors when running on Deno and Node.js. I think this behaviors won’t impact people directly, but they may, and I might have to make changes in the future to accommodate for that in oak.

Building, testing and CI

dnt a build pipeline you would typically integrate into a build process. For oak I chose to create a _build_npm.ts script, which in addition to running the dnt pipeline, does some other build steps that fall outside of the scope of dnt.

Generally you only need to write tests once, and by default dnt will run your tests under the integrated Deno like test harness under Node.js for you. With oak, I had a few tests that were specific to Deno or Node.js, as well as some tests results that varied between the platforms. I tried my best to avoid branching or ignoring tests, but in some cases they are unavoidable. I created a simple utility function for detecting the environment:

export function isNode(): boolean {
  return "process" in globalThis && "global" in globalThis;
}

Integrating it all into CI was very straight forward, as all that needed to be done was to run the build script as part of the CI process, which will build, type check, and test the package for me.

Publishing to npm

Once you have dnt building your package for you, it is ready for publishing to npm. All I need to do with oak is change to the output directory of my build script followed by npm publish.

Conclusions

I am biased obviously, but I think Deno is a great development platform as well as a great runtime for JavaScript and TypeScript. With a rich set of modern built-in APIs, lots of support for web platforms APIs, and built-in support for authoring code in TypeScript, it is a great runtime. Add in the “batteries included” testing, binary redistribution packaging, debugging, and IDE language server, it is hard to find a better experience.

One of the big blockers for adoption though has been how to develop code in Deno but share it with Node.js without duplicating lots of code, or having to do lots of work yourself to make things work. I feel dnt goes a really long way of taking that barrier away, and taking something like oak and being able to write code once in Deno but share it easily with Node.js is a really good example of this.

We would really love to see more projects start to follow this path. Inevitably we will discover things that need to be changed or improved with dnt, and we would love to hear feedback and experiences with it.