Express, Koa, Meteor, Sails.js: 4 “kị sĩ khải huyền”
Những năm gần đây, cùng với nhu cầu ứng dụng web tăng cao, JavaScript đang dần chiếm lấy vị trí dẫn đầu trong ngôn ngữ lập trình. Khi lập trình cho nhiều nền tảng trình duyệt, JavaScript là lựa chọn hàng đầu cho các lập trình viên front-end. Có thể nhiều người sẽ nghĩ đến CoffeeScript, ...
Những năm gần đây, cùng với nhu cầu ứng dụng web tăng cao, JavaScript đang dần chiếm lấy vị trí dẫn đầu trong ngôn ngữ lập trình. Khi lập trình cho nhiều nền tảng trình duyệt, JavaScript là lựa chọn hàng đầu cho các lập trình viên front-end. Có thể nhiều người sẽ nghĩ đến CoffeeScript, TypeScript hay Dart nữa, tuy nhiên, sự thật là CoffeeScript chỉ được xem như cú pháp đặc biệt (syntactic sugar) cấu thành JavaScript mà thôi. Còn TypeScript cũng chỉ là superset non trẻ của JavaScript, mang một số chứng năng ngôn ngữ xoay quanh object, như nhập kiểu tĩnh (static typing), class và giao diện tùy chọn. Dart cũng là một ngôn ngữ xoay quanh object mang cấu trúc giống C, nhưng vẫn compile JavaScript cho các trình duyệt phổ biến.
Với sự xuất hiện và phát triển mạnh mẽ của Node.js, JavaScript không chỉ giới hạn trong lập trình front-end nữa, và lập trình back-end cũng không còn quá phức tạp với lập trình viên front-end. Mọi người cho rằng JavaScript là “viên đạn bạc” dùng cho mọi tình huống: front-end, web server, ứng dụng desktop, embedded system, database…. và càng ngày càng mở rộng. Trong thực tế, với phạm vi người dùng rộng khắp của JavaScript, Node.js+MongoDB+AngularJS/React đã tạo ra một lượng lớn lập trình viên full stack. Tuy nhiên, Node,js được thiết kế nhẹ nhàng và chỉ cung cấp chức năng web server nền tảng để tăng tốc độ lập trình web. Trong thực tế, một trong những framework sẵn sàng đưa vào sử dụng dưới dạng gói npm vẫn là lựa chọn tốt hơn.
Trong bài viết này, chúng ta sẽ điểm qua một vài Node.js framework nổi tiếng và ổn định. Những framework này đã giúp lập trình viên tránh rất nhiều công việc lặp đi lặp lại. Cụ thể hơn, ta sẽ tìm hiểu về Express, Koa, Meteor and Sails.js. Thay vì tìm hiểu những framework này “ăn nằm” với nhau như thế nào, ta sẽ xem thử những framework này nổi trội ở điểm nào và chúng liên quan đến các khâu nào trong một project.
Express: Minimalist Web Framework
Có thể nói Express là phần quan trọng nhất trong Node.js. Hầu như tất cả người dùng Node.js đã nghe đến và (dù có biết hay không cũng) đang sử dụng nó. Express hiện ở thế hệ thứ 4, và có kha khá Node.js framework hiện đang được xây dựng dựa trên nó.
Performance (hiệu suất)
Đa số lập trình viên thích Node.js vì tốc độ của nó, và khi nói đến việc lựa chọn framework, một người cầu toàn sẽ loại đi mọi nguy cơ ảnh hưởng đến hiệu suất. Express cung cấp một layer mỏng ngay trên Node.js, với nhiều tính năng ứng dụng web như routing cơ bản, middleware, template engine và static files serving, từ đó hiệu suất I/O mạnh mẽ của Node.js không bị ảnh hưởng.
Express là một framework nhỏ, linh hoạt. Nó không áp dụng các pattern thiết kế thịnh hành như MVC, MVP, MVVM,… Đây là lợi thế rất lớn trong mắt các “fan” của sự đơn giản. Bởi vì ta có thể xây dựng ứng dụng theo ý thích, và không cần đến “đường cong học tập” không cần thiết. Điều này sẽ rất có lợi khi ta tạo một project cá nhân mới, không gánh nặng quá khứ, nhưng nếu team của bạn mở rộng, thiếu thiết đặt chuẩn có thể gây khó khăn khi quản lý project/code, và trường hợp tệ nhất, ta mất khả năng duy trì project.
Generator
Express vẫn có generator tạo cấu trúc folder cụ thể. Sau khi cài đặt gói npm express-generator và tạo khung xương ứng dụng với lệnh generator, một folder ứng dụng với cấu trúc rõ ràng sẽ được tạo để giúp bạn quản lysimage, front-end static JavaScript, stylesheet file và HTML template files.
1 2 3 4 |
npm install express-generator -g express helloapp |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
create : helloapp create : helloapp/package.json create : helloapp/app.js create : helloapp/public create : helloapp/public/images create : helloapp/routes create : helloapp/routes/index.js create : helloapp/routes/users.js create : helloapp/public/stylesheets create : helloapp/public/stylesheets/style.css create : helloapp/views create : helloapp/views/index.jade create : helloapp/views/layout.jade create : helloapp/views/error.jade create : helloapp/bin create : helloapp/bin/www install dependencies: $ cd helloapp && npm install run the app: $ DEBUG=helloapp:* npm start create : helloapp/public/javascripts |
Middleware
Middleware về cơ bản chỉ là các hàm có truy cập toàn phần đến cả object request và response.
Đúng như tên, middleware sẽ áp đặt một số chỉ dẫn filtering trước khi giao quyền kiểm soát cho business logic thực sự hoặc cấp độ middleware tiếp theo. Một số công việc thường thấy bao gồm kiểm tra trạng thái đăng nhập của user, chứng thực ủy quyền, hoặc ngăn chặn các cuộc tấn công cross-site.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var app = express(); app.use(cookieParser()); app.use(bodyParser()); app.use(logger()); app.use(authentication()); app.get('/', function (req, res) { // ... }); app.listen(3000); |
Một ứng dụng Express, về cơ bản, là Node.js với gốc là hàm middleware. Dù cho bạn có muốn tùy chỉnh middleware riêng hay tận dụng middlewares built-in của framework, thì Express cũng sẽ giúp quá trình tự nhiên và trực quan hơn.
Template Engine
Template engine cho phép lập trình viên nhúng biến backend vào file HTML, và khi được yêu cầu, template file sẽ được trả lại format HTML thông thường với biến được nội suy từ giá trị thực tế của chúng. Theo mặc định, express-generator sử dụng Pug (ban đầu được biết đến với tên Jade) template engine, nhưng những tùy chọn khác như Mustache và EJS cũng làm việc trơn tru với Express.
Database Integration
Express, là minimal framework, sẽ không bắt buộc database integaration. Khi sử dụng một một công nghệ lưu trữ dữ liệu cụ thể, dù là MySQL, MongoDB, PostgreSQL, Redis, ElasticSearch,.. vấn đề chỉ nằm ở chỗ cài đặt gói npm làm database driver. Những database driver của bên thứ 3 này không phù hợp với cấu trúc thống nhất khi đi theo chỉ thị CRUB, dẫn đến việc thay đổi database phức tạp và tìm ẩn lỗi.
Koa: Tận dụng tính năng JavaScript thế hệ mới
Koa được phát triển bởi cùng một đội ngũ đằng sau Express, và mục tiêu của nó là tối thiểu hóa Express hết mức có thể bằng cách không gói middleware vào lõi. Ngoài việc thiếu middleware ra, Koa khá tương tự Express, nhẹ nhàng và linh hoạt. Tuy nhiên, điểm nổi bật lớn nhất của Koa là nó đã hoàn toàn loại bỏ callback bằng cách sử dụng tính năng ES6 Generator.
Control Flow tinh tế
Javascript là một ngôn ngữ lập trình thiếu đồng bộ, với tính chất của ngôn ngữ như vậy, cộng với cơ chế event-driven đơn luồng của Node.js, callback sẽ rất lộn xộn.
Async.js là một cách giúp ta quản lý callback. Async.js cung cấp kỹ thuật map, parallelize, serialize hoặc iterate nhiều hàm mà không cần phải nhúng chúng với nhau và chuyển giao luồng điều khiển (control flow) với hàm callback, một hàm xử lý callback và một hàm xử lý lỗi là đã đủ cho một loạt hàm do một Async.js method nhóm với nhau. Tuy vậy Async.js không thể hoàn toàn loại bỏ callback. Khi viết code Node.js với Async.js, độ lõm của code vẫn có xu dướng dạt qua bên phải, chỉ là không sâu bằng.
Còn một cách khác sạch sẽ then-able Promises. Một vài thư viện Promise nổi tiếng của bên thứ ba là bluebird and q. Trong phiên bản JavaScript mới nhất, ES6, Promise được tích hợp làm chuẩn. Tóm lại, Promise đảm bảo hàm được execute và trả lại theo cách tuần tự bằng cách kết nối block/hàm implementation với một loạt hàm “then” Promise. Ta sẽ phải “resolve” tại cuối mỗi block/hàm impletation để hàm “then” tiếp theo được execute, hoặc “reject” implementation tiếp theo để luồng điều khiển nhảy ngay đến xử lý lỗi. Theo cách này, bạn sẽ tập hợp được tất cả hàm xử lý lỗi lại một chỗ và hoàn toàn loại bỏ được callback.
Giờ đây ES6, với ES6 Generator đã hoàn toàn thay đổi cuộc cuộc chơi. Notion này này khá mới với JavaScript, nhưng không mới với thế giới lập trình chút nào. ES6 Generator tương tự interuption trong C, ES6 Generator gới thiệu cách thức chạy->ngưng và chạy gì đó khác -> quay lại kết thúc công việc dang dở.
Koa đang tận dụng ES6 Generator làm giải pháp tinh tế cho việc giải quyết vấn đề lập trình thiếu đồng bộ của JavaScript, vì thế bạn không hề thấy callback trong ứng dụng thuần túy Koa. Một trường hợp ứng dụng tiêu biểu của ES6 Generator trong Koa là middleware cascading, cho phép middleware tùy chỉnh được lần lượt execute mà không phải vấp phải callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var app = koa(); function* responseTimeLogger(next){ var start = new Date; yield next; var ms = new Date - start; console.log(this.method + ' ' + this.url + ': ' + ms); } app.use(responseTimeLogger); // ... app.listen(3000); |
Ta không thể nhảy đến kết luận khẳng định rằng kỹ thuật này vượt trội hơn phương pháp của như Async.js, Promise hay event emitter, nhưng một điều chắc chắn hiện nay là khái niệm mới này sẽ mất thời gian để làm quen. Với tuần tự luồng điều khiển không theo quy ước, Kỹ thuật này có thể gây thêm khó khăn cho việc debug code.
Meteor: Framework Cho Web, Mobile Và Desktop
Meteor là một JavaScript framework toàn diện. Khác với khuynh hướng đơn giản hóa của Express và Koa, Meteor hoàn toàn đi theo hướng ngược, tự khẳng định mình là full-stack framework, bao trùm cả server, mobile, destop và ứng dụng web.
Giải pháp toàn diện
Nếu bạn tìm hiểu kỹ hơn, bạn sẽ nhận thấy Meteor thực tế là Node.js+Blaze/AngularJS/React+Cordova+MongoDB. Node.js và MongoDB lần lượt đứng sau server side bussiness logic và data storage. Một trong số Blaze, AngularJS, hay React sẽ quản lý front-end UI. Và Cordova, giải pháp HTML nổi tiếng nhất cho ứng dụng lại mobile, kết đưa web page sang giao diện mobile.
Đồng bộ dữ liệu
Quy trình chủ đạo trong chia sẻ dữ liệu backend và front-end như sau:
- client yêu cầu dữ liệu hoặc giao diện HTML cụ thể
- Server nhận dữ liệu từ database, trộn dữ liệu với giao diện HTML sử dingj templating engine và gửi ngược lại front-end
- Client render và thể hiện thân thiện dữ liệu/giao diện.
Một ứng dụng web modern single page sẽ tweak quá trình trên đôi chút. Xét ví dụ AngularJS, AngularJS sẽ đặt HTML template dưới dạng file tĩnh chung với những dữ liệu khác như file JavaScript, stylesheet và hình ảnh. AngularJS kế tiếp sẽ yêu cầu dữ liệu backend với Ajax RESTful API để lấp dữ liệu vào HTML template. Với tất cả trường hợp, lập trình viên back-end sẽ hoàn toàn chịu trách nhiệm xử lý yêu cầu thay đổi dữ liệu từ front-end và lưu thay đổi trong database.
Hơn nữa, Meteor còn có tính năng đặc biệt, đó là cơ chế đồng bộ dữ liệu giữa server và ứng dụng front-end/mobile.Trong Meteor, client sẽ giữ một shadow copy của mini-database (bản sao một phần nhỏ của server database, do client yêu cầu và được server ủy quyền). Khi client muốn thay đổi data, client sẽ dùng database API cố định như server để thực hiện chỉ thị CRUD, và rồi thay đổi trên dữ liệu sẽ được gửi tự động đến server và lưu trữ trong database thực. Nếu server phát hiện bất cứ thay đổi dữ liệu nào, server sẽ đẩy dữ liệu bị thay đổi đến client đứng tên những dữ liệu đó. Quá trình đồng bộ dữ liệu hai chiều này được thực hiện hoàn toàn tự động. Để có được “phép màu” này, Meteor sử dụng WebSocket để kết nối client và server, từ đó bất cứ thay đổi ở bên này, sẽ được đồng dạng áp dụng ngay đến bên kia.
Công cụ tự động
Không chỉ dừng lại ở ứng dụng web app và server, Meteor còn đơn giản hóa quá trình build ứng dụng iOS/Android với công cụ mang tên Isobuild và Cordova (thư viện giúp bundle HTML/JavaScript/CSS với chức năng native mobile). Ứng dụng mobile được tạo là ứng dụng lai, chạy JavaScript hoặc hiển thị trang HTML trong Webview. Tuy nhiên, vẫn có nguy cơ thay đổi UX so với native mobile apps. Với nhiều người, khả năng quản lý toàn bộ dưới cùng một code base với web apps là một điểm cộng to bự, giúp ta tiếm kiệm rất nhiều chi phí phát triển.
Nhìn chung, Meteor là framework tự động hóa cao. Tự động hóa cao sẽ giúp ích lập trình viên rất nhiều, tuy vậy, đổi lại hiệu suất sẽ thiếu ổn định và khả năng mở rộng bị giới hạn. Khi user base mở rộng, kỹ thuật đồng bộ dữ liệu tự động sẽ đi đến tắc nghẽn. Để đạt quy mô và hiệu suất như những công nghệ backend bằng tay khác, Meteor thường sử dụng nhiều tài nguyên băng thông và tài nguyên phần cứng hơn. Vì vậy, Meteor là điểm khởi đầu tuyệt vời, và một toolbox hoàn hảo cho những ai muốn chạy thử project trên mọi nền tảng chính, và tái thiết kế lại cấu trúc khi bản chạy thử đi vào production và client base mở rộng.
Sails.js: MVC Framework Mạnh Mẽ Cho Node.js
Sails.js có nhiều điểm tương đồng với Express. Sails.js là project generator, và engine middleware/templating. Trong thực tế, Sails.js được xây dựng dựa trên Express, cộng với các chức năng nâng cao giúp tăng tốc lập trình.
MVC
Sails.js kế thừa design pattern từ Model-View-Controller. Vỡi những ai chuyển từ Ruby trên Rails hay Laravel, bạn sẽ thấy cấu trúc của ứng dụng Salis.js rất quen thuộc. Model thể hiện model dữ liệu phản ánh sơ đồ table/collection của database, View là HTML view với dữ liệu đã điền, Controller là nơi bạn sẽ đặt tất cả business logic và là “keo dính” giữa dữ liệu và view.
Giao tiếp thời gian thực
Không như yêu cầu HTTP, người dùng phải liên tục truy vấn server data, hoặc kết nối polling rất lâu, từ đó đưa server vào idling mode, Socket.io thiết lập giao tiếp đa chiều dựa trên sự kiện giữa client và server. Sails.js tích hợp Socket.io và kết thúc với API có mức trừu tượng cao hơn. Từ đó có thể thấy, Sails.js cực kỳ thích hợp cho ứng dụng chat hoặc trò trơi trực tuyến.
Database ORM
Giữa vận dụng backend logic và database, có một layer ORM ở giữa (gọi là Waterline). Nói cách khác, công cụ ORM cung cấp cấp trúc cố định giúp truy cập nhiều database. Lập trình viên không phải lo lắng về nhiều loại ngôn ngữ truy vấn database khác nhau, SQL hay NoSQL, schema hay schema-less,…
Sails.js có mức tự động hóa thuộc tầm trung. Sails.js tập trung vào server side logic, và cung cấp tốc độ phát triển nhanh hơn Express, mà lại không có vấn dề về hiệu suất hay mở rộng trong tương lai.
Lời kết
Bài viết này không xếp hạng những framework Node.js trên, mà chỉ đơn thuần liệt kê những điểm mạnh yếu nổi bật của từng framework, từ đó giúp lập trình viên Node.js lựa chọn ra công cụ thích hợp nhất cho project của mình.
Techtalk via toptal