Giới thiệu Deno – một lựa chọn thay thế cho Node.js

Giới thiệu Deno – một lựa chọn thay thế cho Node.js
Photo by Gerd Altmann from Pixabay

Deno là môi trường để thực thi JavaScript và TypeScript tương tự như Node.js. Deno là công cụ được tạo bởi Ryan Dahl – cũng là tác giả của Node.js – để khắc phục những thiếu sót của Node.js. Nó vẫn được xây dựng dựa trên V8 JavaScript Engine, tuy nhiên nó được viết bằng ngôn ngữ lập trình Rust.

Deno còn chạy được cả WebAssembly nữa 😀.

Những vấn đề của Node.js đã được tác giả đề cập trong buổi phát biểu tại JSConf EU năm 2018. Những vấn đề này thường được biết đến là “10 Things I Regret About Node.js”. Một số vấn đề nổi bật là thiếu sự bảo mật, cài đặt dependency thông qua node_modules chưa hợp lý, khó làm việc với promise và một vài vấn đề khác. Và để sữa chữa những vấn đề đó, anh ta đã phát triển Deno.

Cái tên Deno này có nhiều ý nghĩa khác nhau. Người thì bảo đây là đảo ngữ của Node, người thì bảo đây là viết tắt của DEstroy NOde. Tất cả đều là tin đồn thôi, tất nhiên là không có tài liệu chính thức nào nói về vấn đề này.

Trong bài viết này, tôi sẽ giới thiệu một vài thông tin cơ bản về Deno, và sẽ có sự so sánh nhẹ giữa Deno và Node.js.

Tại sao lại phát triển một công cụ mới mà không nâng cấp Node.js? Lý do là vì Node.js đang được dùng quá rộng rãi. Việc đưa tính năng mới mà vẫn đảm bảo tương thích ngược với phiên bản hiện tại không phải việc đơn giản. Đó là chưa kể những tính năng này cũng cần thời gian phát triển và sửa lỗi nhất định. Nên phát triển một công cụ mới là lựa chọn hợp lý hơn nhiều.

Cài đặt

Việc cài đặt Deno hoàn toàn không khó, và đã được hướng dẫn chi tiết trong tài liệu này. Sau khi cài đặt xong thì Deno CLI sẽ có sẵn trong máy (thường sẽ tự động thêm vào PATH luôn), sau đó thì việc sử dụng cũng dễ như Node.js vậy.

Để kiểm tra việc cài đặt Deno, có thể dùng lệnh sau:

$ deno --version
deno 1.30.0 (release, x86_64-apple-darwin)
v8 10.9.194.5
typescript 4.9.4

Nếu Deno đã cũ và muốn cập nhật lên phiên bản mới hơn thì có thể dùng lệnh:

$ deno upgrade
Looking up latest version
Local deno version 1.30.0 is the most recent release

Tiếp tục viết thử một chương trình đơn giản để kiểm tra mọi thứ đã hoạt động chính xác. Ví dụ như sau:

const hello = (str: string) => {
  return `Hello ${str}!`;
}

console.log(hello('Deno'));

Đây là một đoạn TypeScript, có thể lưu nó với tên index.ts và chạy thử bằng Deno.

$ deno run index.ts
Hello Deno!

Như vậy là mọi thứ đã hoàn thành. Để biết thêm các lệnh của Deno, có thể dùng lệnh sau:

$ deno --help

Deno hỗ trợ TypeScript

Một trong những điểm nổi bật của Deno so với Node.js là nó hỗ trợ TypeScript (hơn thế nữa, TypeScript là first class language của Deno, giống như JavaScript). Như trong phần trước, tôi đã ví dụ một file TypeScript và Deno có thể chạy nó trực tiếp mà không cần phải cài đặt hay biên dịch gì.

Deno cũng tương tự như Node.js, vẫn sử dụng V8 JavaScript Engine để thực thi code. Thế nhưng Deno đã tích hợp sẵn trình dịch của TypeScript để có thể thực thi code trực tiếp.

Về cơ bản thì V8 JavaScript Engine chỉ có thể thực thi code JavaScript. Do đó, code TypeScript sẽ được kiểm tra và biên dịch, kết quả sẽ được lưu trong file system và được dùng để thực thi code. Tập tin đã biên dịch này sẽ được lưu lại để sử dụng về sau nếu code không có gì thay đổi. Có thể sử dụng lệnh deno info để kiểm tra việc Deno dịch và lưu các tập tin này như thế nào.

Deno không yêu cầu bất cứ một thao tác nào để có thể làm việc với TypeScript. Mọi thứ đã được thiết lập mặc định. Nhưng nếu người dùng muốn thay đổi những cấu hình này, thì hoàn toàn có thể làm được. Thông thường với TypeScript, file tsconfig.json sẽ được sử dụng, nhưng khi làm việc với Deno, file deno.json thích hợp hơn cả (bởi vì ngoài những cấu hình cho TypeScript, thì có thể thêm những cấu hình khác nữa).

Lưu ý rằng, Deno có thể không hỗ trợ tất cả tùy chọn của TypeScript. Có thể xem danh sách những tùy chọn có thể thay đổi ở đây cùng với cấu hình mặc định của Deno. Dưới đây là một ví dụ cấu hình đơn giản cho Deno:

{
  "compilerOptions": {
    "checkJs": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noUncheckedIndexedAccess": true
  }
}

Deno có thể tự động kiểm tra và thực thi theo những cấu hình của file deno.json. Trong trường hợp người dùng muốn lưu tên khác, thì có thể dùng tùy chọn --config trong lệnh:

$ deno run --config deno.json index.ts

Khi Deno kiểm tra và thực thi code TypeScript, nếu gặp lỗi nó sẽ ngừng chương trình và trả về exit code lỗi (khác 0). Nếu muốn bỏ qua những lỗi này thì có thể dùng một trong 3 cách sau:

  • Thêm comment //@ts-ignore hoặc //@ts-expect-error vào dòng gặp lỗi
  • Thêm // @ts-nocheck vào đầu file và tất cả lỗi trong file đó sẽ bị bỏ qua
  • Sử dụng tùy chọn --no-check trong lệnh deno để bỏ qua toàn bộ việc kiểm tra lỗi
    $ deno run --no-check index.ts
    

Permissions khi chạy Deno

Deno được phát triển để khắc phục nhược điểm về bảo mật của Node.js. Vì vậy Deno được cho là môi trường thực thi JavaScript và TypeScript bảo mật hơn. Việc bảo mật của Deno được quản lý thông qua permission. Để minh họa permission hoạt động như thế nào, tôi lấy ví dụ một script đơn giản như sau:

const getCovidStats = async () => {
  try {
    const response = await fetch('https://disease.sh/v3/covid-19/all');
    const data = await response.json();
    console.table(data);
  } catch (err) {
    console.error(err);
  }
}

getCovidStats();

Đây là một script đơn giản, nó sẽ lấy dữ liệu thống kê về Covid từ trang disease.sh và hiển thị. Khi thực thi script này, Deno sẽ thông báo rằng nó cần quyền truy cập mạng:

$ deno run index.ts
⚠️  ┌ Deno requests net access to "disease.sh".
   ├ Requested by `fetch()` API
   ├ Run again with --allow-net to bypass this prompt.
   └ Allow? [y/n] (y = yes, allow; n = no, deny) >

Có thể ấn y (yes) để cấp quyền truy cập. Hoặc có thể sử dụng tùy chọn --allow-net trong lệnh để cấp phép truy cập:

$ deno run index.ts
✅ Granted net access to "disease.sh".
┌────────────────────────┬───────────────┐
│ (idx)                  │ Values        │
├────────────────────────┼───────────────┤
│ updated                │ 1675295362627 │
│ cases                  │     675353102 │
│ todayCases             │        145019 │
│ deaths                 │       6763254 │
│ todayDeaths            │           750 │
│ recovered              │     647737351 │
│ todayRecovered         │        177872 │
│ active                 │      20852497 │
│ critical               │         41995 │
│ casesPerOneMillion     │         86642 │
│ deathsPerOneMillion    │         867.7 │
│ tests                  │    6921828647 │
│ testsPerOneMillion     │     871225.32 │
│ population             │    7944935131 │
│ oneCasePerPeople       │             0 │
│ oneDeathPerPeople      │             0 │
│ oneTestPerPeople       │             0 │
│ activePerOneMillion    │       2624.63 │
│ recoveredPerOneMillion │      81528.34 │
│ criticalPerOneMillion  │          5.29 │
│ affectedCountries      │           231 │
└────────────────────────┴───────────────┘

Với tùy chọn --allow-net, không chỉ cấp phép cho cập mạng nói chung, Deno cho phép chỉ định truy cập đến những domain (hoặc địa chỉ IP) nhất định. Điều này dễ dàng thực hiện bằng cách thêm danh sách những domain đó vào --allow-net (ngăn cách bằng dấu phẩy). Nếu script muốn truy cập vào những domain chưa được cho phép, Deno sẽ báo lỗi:

$ deno run --allow-net='disease.sh' index.ts

Đây là điểm cải tiến của Deno so với Node.js. Ngoài việc truy cập mạng, thì việc truy cập vào file system cũng cần được cấp quyền. Nếu một script cần đọc hay ghi file, cần phải cấp quyền khi thực thi nó bằng --allow-read và/hoặc --allow-write. Ngoài ra thì còn rất nhiều hành động khác cần phải được cấp quyền, và tùy chọn rất mạnh là --allow-all để cấp toàn bộ quyền cho script.

Tương thích với những API của trình duyệt

Một trong số những mục tiêu của Deno là tương thích với trình duyệt web. Điều này được thể hiện rất rõ thông qua việc sử dụng như API của web thay vì phải sử dụng những thư viện hay API được xây dựng riêng cho Deno. Một ví dụ rất điển hình là Fetch API đã được sử dụng trong ví dụ phần trước.

Fetch API được dùng trong ví dụ đó hoàn toàn giống với Fetch API của trình duyệt. Với Node.js, trước đây tôi thường phải cài đặt thư viện node-fetch mới có thể sử dụng được (phiên bản Node.js gần đây đã thêm fetch vào rồi thì phải).

Danh sách những API trên trình duyệt có thể được sử dụng ở Deno được mô tả đầy đủ ở đây.

Quản lý dependency

Việc Deno quản lý dependency có thể nói là vô cùng đặc biệt, nhất là khi so sánh với Node.js.

Node.js sử dụng package manager như npm hay yarn để cài đặt (download) các package từ npm registry về máy của người dùng (tại thư mục node_modules). Những công cụ này sử dụng file package.json để quản lý dependency cho một project.

Deno hoàn toàn không sử dụng cơ chế như vậy, mà nó được xây dựng theo kiểu trình duyệt web hơn. Việc sử dụng các package có thể thông qua URL.

Dưới đây là một ví dụ sử dụng package Oak – một server web đơn giản:

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

const app = new Application();

app.use((ctx) => {
  ctx.response.body = 'Hello Deno!';
});

app.addEventListener('listen', ({ hostname, port, secure }) => {
  console.log(`Listening on: http://localhost:${port}`);
});

await app.listen({ port: 8000 });

Deno dùng ES module, tương tự như trình duyệt sử dụng. Một module có thể dễ dàng được import bằng cả đường dẫn tuyệt đối và tương đối, miễn là module đó export những đối tượng tương ứng. Lưu ý rằng, với Deno, tên file phải được viết đầy đủ (cả phần mở rộng).

Một module cũng có thể được import từ URL. Rất nhiều thư viện bên thứ ba được phát triển cho Deno được lưu trữ tại deno.land/x.

Ví dụ với script ở phần trước (sử dụng module từ URL https://deno.land/x/oak/mod.ts), Deno sẽ download module này và tất cả dependency của nó, lưu trữ chúng trên máy của người dùng (thư mục được xác định bằng biến môi trường DENO_DIR, mặc định sẽ là $HOME/.cache/deno). Sau này, khi chương trình được thực thi, vì mọi thứ đã được lưu trữ từ trước, Deno sẽ không download lại một lần nữa. Cách làm này tương tự như module của ngôn ngữ Go.

$ deno run --allow-net index.ts
Warning Implicitly using latest version (v11.1.0) for https://deno.land/x/oak/mod.ts
Listening on: http://localhost:8000

Với những ứng dụng cần sự ổn định hơn, để phòng tránh trường hợp các thư viện có thể bị xóa bỏ vì một lý do nào đó, Deno recommend việc đưa code của những thư viện này vào quản lý chung với code của dự án. Cách làm cụ thể như sau: thay đổi biến môi trường DENO_DIR để trỏ vào thư mục lưu trữ code của thư viện (ví dụ vendor), và chạy lệnh sau:

$ DENO_DIR=$PWD/vendor deno cache index.ts

Đương nhiên là khi chạy deno run thì cũng cần trỏ DENO_DIR giống như trên. Tuy nhiên, trong trường hợp này, ý kiến cá nhân của tôi là không nên tiếp tục dùng những thư viện đó nữa. Một thư viện đã bị xóa bỏ, thì bản thân nó có thể có vấn đề gì đó. Tiếp tục sử dụng thư viện đó thường hại nhiều hơn lợi.

Một điểm nữa cần phải nhắc tới, đó là Deno cũng hỗ trợ việc chỉ định version của dependency, để đảm bảo sự thống nhất khi làm việc trên các môi trường khác nhau. Như trong phần trước, tôi ví dụ sử dụng thư viện Oak (https://deno.land/x/oak/mod.ts). Khi thực thi code, phiên bản mới nhất của thư viện sẽ được download và sử dụng. Điều này có thể gây ra một số vấn đề khi thư viện này được update sau này. Đó là lý do Deno đưa ra cảnh báo khi lần đầu tải thư viện này về:

Warning Implicitly using latest version (v11.1.0) for https://deno.land/x/oak/mod.ts

Best practice ở đây là chỉ định chính xác phiên bản thư viện cần sử dụng như dưới đây:

import { Application } from 'https://deno.land/x/oak@v11.1.0/mod.ts';

Trong trường hợp một module này được import nhiều nơi, sẽ có nguy cơ về việc các phiên bản được sử dụng không thống nhất. Để giải quyết vấn đề này, một thủ thuật nhỏ như sau sẽ cực kỳ hữu ích:

export { Application, Router } from 'https://deno.land/x/oak@v11.1.0/mod.ts';

Cách làm ở đây là import toàn bộ module vào trong một file (ví dụ deps.ts) sau đó export những thành phần của module đó. Sau này cần sử dụng thì import từ deps.ts chứ không import trực tiếp từ URL của module nữa. Bằng cách này, việc quản lý và cập nhật phiên bản của module được thực hiện ở một nơi duy nhất, sẽ không phải lo việc cập nhật thiếu thay thừa ở đâu:

import { Application, Router } from './deps.ts';

Thư viện chuẩn (standard library) của Deno

Deno cung cấp một thư viện chuẩn – standard libary (stdlib) (tương tự như ngôn ngữ lập trình Go – Deno học hỏi rất nhiều từ ngôn ngữ này). Những module trong bộ thư viện chuẩn này được kiểm tra bởi Deno team và sẽ được cập nhật cùng với Deno. Mục đích của việc này là cung cấp cho người dùng bộ thư viện hữu ích để có thể phát triển các ứng dụng ngay lập tức mà không cần phải dùng đến thư viện của bên thứ ba (khá là trái ngược với Node.js).

Một số module nổi bật trong bộ thư viện chuẩn của Deno có thể kể đến là:

  • HTTP: HTTP server & client
  • Fmt: formatted output
  • Testing: Test & benchmark
  • FS: làm việc với filesystem
  • Encoding: làm việc với các loại file và encode khác nhau: XML, CSV, base64, YAML, binary, v.v…
  • Node: Sử dụng thư viện của Node.js

Dưới đây là một ví dụ, lấy từ docs của Deno, mô tả việc sử dụng HTTP module trong bộ thư viện chuẩn để tạo một server web đơn giản:

// Start listening on port 8080 of localhost.
const server = Deno.listen({ port: 8080 });
console.log(`HTTP webserver running.  Access it at:  http://localhost:8080/`);

// Connections to the server will be yielded up as an async iterable.
for await (const conn of server) {
  // In order to not be blocking, we need to handle each connection individually
  // without awaiting the function
  serveHttp(conn);
}

async function serveHttp(conn: Deno.Conn) {
  // This "upgrades" a network connection into an HTTP connection.
  const httpConn = Deno.serveHttp(conn);
  // Each request sent over the HTTP connection will be yielded as an async
  // iterator from the HTTP connection.
  for await (const requestEvent of httpConn) {
    // The native HTTP server uses the web standard `Request` and `Response`
    // objects.
    const body = `Your user-agent is:\n${
      requestEvent.request.headers.get("user-agent") ?? "Unknown"
    }\n`;
    // The requestEvent's `.respondWith()` method is how we send the response
    // back to the client.
    requestEvent.respondWith(
      new Response(body, {
        status: 200,
      }),
    );
  }
}

Chạy thử server này bằng lệnh sau:

$ deno run --allow-net content/index.ts
HTTP webserver running.  Access it at:  http://localhost:8080/

Thử truy cập vào server để kiểm tra hoạt động:

$ curl http://localhost:8080
Your user-agent is:
curl/7.85.0

Sử dụng NPM packages trong Deno

Một trong những ưu điểm nổi trội của Node.js so với Deno và các ngôn ngữ lập trình khác đó là nó có một cộng đồng sử dụng lớn với rất nhiều thư viện khác nhau, giúp cho việc phát triển ứng dụng tiện lợi hơn rất nhiều. Nếu chuyển từ Node.js sang Deno, hầu hết mọi người đều mong muốn tiếp tục sử dụng những thư viện này.

Hiện tại, các lập trình viên có 2 cách để giải quyết bài toán này:

Vào thời điểm hiện tại thì sử dụng npm specifiers và node specifiers sẽ tốt hơn. Mặc dù vậy, những tính năng này vẫn chưa hoàn thiện (xem tiến độ ở đây). Trong trường hợp gặp vấn đề với cách làm này, sử dụng CDN sẽ là biện pháp thay thế.

Deno tool

Deno CLI được cài đặt kèm với rất nhiều công cụ khác nhau cho phép lập trình viên làm việc với Deno dễ dàng hơn. Tương tự Node.js, Deno cũng có một trình REPL (Read Evaluate Print Loop):

$ deno repl
Deno 1.30.0
exit using ctrl+d, ctrl+c, or close()
> 2 + 2
4
>

Deno cũng có sẵn công cụ file watcher có thể được sử dụng trong rất nhiều trường hợp. Ví dụ, khi muốn Deno tự động chạy lại chương trình khi thay đổi code, chỉ cần thêm tùy chọn --watch vào lệnh là được. Với Node.js, việc này phức tạp hơn, thường phải sử dụng thư viện bên thứ bao như nodemon.

$ deno run --allow-net --watch index.ts
HTTP webserver running. Access it at: http://localhost:8080/
Watcher File change detected! Restarting!
HTTP webserver running. Access it at: http://localhost:8080/

Từ phiên bản Deno 1.6, Deno cho phép biên dịch mã nguồn thành tập tin binary chứa mã máy, máy tính có thể thực thi trực tiếp mà không cần cài đặt Deno (với Node.js thì cần phải sử dụng thêm thư viện, ví dụ như pkg). Việc biên dịch cho những nền tảng khác nhau cũng có thể được thực hiện dễ dàng với Deno (bằng việc thêm --target vào lệnh).

Một lưu ý nữa là khi biên dịch, Deno vẫn yêu cầu cấp quyền giống như khi thực thi code để đảm bảo yếu tố bảo mật:

$ deno compile --allow-net --output server index.ts
Check file:///path/index.ts
Compile file:///path/index.ts
Emit server
$ ./server
HTTP webserver running. Access it at: http://localhost:8080/

Lưu ý rằng việc biên dịch này thường sẽ tạo ra tập tin khối lượng khá lớn (do phải đóng gói môi trường thực thi vào). Tôi đã test thử một chương trình Hello, world đơn giản (chỉ duy nhất một lệnh console.log('Hello, world!');) cũng mất tới hơn 90MB. Vì vậy cách này chưa nên dùng vào thời điểm này (Deno team đang làm việc để tối ưu quá trình này, hy vọng kết quả của nó sẽ sớm đạt được trong tương lai).

Một cách khác để build và thực thi các chương trình Deno là đóng gói toàn bộ mã nguồn vào một file JavaScript. File này có thể được chạy bởi Deno mà không cần cài đặt các thư viện cần thiết.

$ deno bundle index.ts index.bundle.js
Bundle file:///path/index.ts
Emit "index.bundle.js" (657B)
$ deno run --allow-net index.bundle.js
HTTP webserver running. Access it at: http://localhost:8080/

Một điểm nổi bật nữa không thể không nhắc tới là Deno đã tích hợp sẵn những công cụ là linter (deno lint) và formatter (deno fmt). Với Node.js, những công cụ này không được tích hợp sẵn, mà lập trình viên phải sử dụng những công cụ bên thứ ba như ESLint hay Prettier.

Vì vậy với Deno, lập trình viên không cần cài đặt hay cấu hình bất cứ một công cụ nào để kiểm tra và sửa lỗi code. Đặc biệt, không chỉ JavaScript hay TypeScript, các ngôn ngữ khác cũng đã được hỗ trợ.

Unit test

Deno tích hợp sẵn tính năng unit test cho cả JavaScript và TypeScript. Khi chạy test, Deno sẽ tự động kiểm tra các file kiểu *_test.ts hoặc *.test.ts và chạy những lệnh test được định nghĩa trong đó.

Dưới đây là một ví dụ unit test đơn giản, lưu trong file index.test.ts:

import { assertEquals } from 'https://deno.land/std/testing/asserts.ts';

Deno.test('Multiply two numbers', () => {
  const ans = 2 * 2;
  assertEquals(ans, 4);
});

Phương thức Deno.test là phương thức Deno cung cấp sẵn để định nghĩa unit test. Tham số đầu tiên là tên test, tham số thứ hai là hàm thực hiện test. Ngoài ra, phương thức này cũng có thể truyền vào một object thay vì hai tham số như dưới đây (với cách này, có thể định nghĩa việc test phức tạp hơn, ví dụ như điều kiện sẽ chạy test):

Deno.test({
  name: 'Multiply two numbers',
  fn() {
    const ans = 2 * 2;
    assertEquals(ans, 4);
  },
});

Phương thức assertEquals() lấy từ thư viện chuẩn của Deno, cung cấp phương thức để kiểm tra kết quả. Sau khi định nghĩa, có thể thực thi test như sau:

$ deno test
Warning Implicitly using latest version (0.175.0) for https://deno.land/std/testing/asserts.ts
Check file:///path/index.test.ts
running 1 test from ./index.test.ts
Multiply two numbers ... ok (19ms)

ok | 1 passed | 0 failed (66ms)

Deno language server

Để lập trình một ngôn ngữ hiệu quả, việc tích hợp nó với các editor hoặc IDE là rất cần thiết. Deno cũng không ngoại lệ, mặc dù nó không phải một ngôn ngữ lập trình, nhưng dù là cùng ngôn ngữ TypeScript, thì lập trình trên Deno rất khác với TypeScript thông thường (vốn được thiết kế để hoạt động tốt cùng với Node.js). Chuyện này cũng tương tự như JavaScript trên trình duyệt rất khác với JavaScript trên Node.js vậy.

Deno đã tích hợp language server sẵn thông qua lệnh deno lsp. Language server sẽ cung cấp một số tính năng như:

  • autocompletion
  • go-to-definition
  • lint & format

Và còn nhiều tính năng thông minh khác để hoạt động với editor hỗ trợ Language Server Protocol (LSP) (ví dụ tôi dùng VSCode là một editor như thế). Để tích hợp Deno với các editor và IDE khác nhau, tài liệu của Deno đã mô tả rất chi tiết ở đây.

Nên chọn Deno hay Node.js?

Trong bài viết này, tôi đã giới thiệu sơ lược một số điểm quan trọng của Deno và so sánh với Node.js. Có thể còn nhiều điều phải nói nữa về Deno, nhưng tôi mới làm quen với nó và cũng chưa hiểu được hết về toàn bộ hệ sinh thái của Deno.

Một câu hỏi đặt ra lúc này: giữa Node.js và Deno, nên chọn công cụ nào? Ý tôi ở đây là chọn công cụ nào để phát triển ứng dụng web (cụ thể hơn là phần backend). Còn công cụ nào để học hỏi và tìm hiểu thì đơn giản là chọn cả hai, vì đâu có chuyện có cái này thì không có cái kia. Về mặt hiệu năng hay tốc độ, sẽ không có quá nhiều khác biệt giữa Node.js và Deno, vì cả hai đều sử dụng V8 JavaScript Engine.

Thời điểm hiện tại, nếu phải chọn một công cụ để phát triển ứng dụng tôi vẫn chọn Node.js. Sự phong phú về các thư viện có sẵn cũng như cộng đồng người dùng rất mạnh là điểm cộng quá lớn của Node.js. Cũng dễ hiểu thôi, Node.js có lịch sử lâu đời hơn, được dùng nhiều hơn trong khi Deno mới xuất hiện mấy năm gần đây (Node.js được phát triển từ năm 2009 trong khi Deno 1.0 mới được được công bố tháng 5 năm 2020).

Ngoài ra, cũng cần nhắc lại rằng, nhiều tính năng quan trọng trên Deno sau này có thể sẽ xuất hiện trên Node.js (có thể được tích hợp sẵn, có thể thông qua thư viện của bên thứ ba). Vì vậy với những ứng dụng cần sự ổn định, phát triển nhanh thì Node.js tốt hơn. Còn Deno là công cụ của tương lai, tìm hiểu nó từ bây giờ không bao giờ là thừa, nhưng để ứng dụng nó vào thực tế thì cần thêm chút thời gian.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.