• [Kites.js] #3 DI System: Injectable, Inject, Providers và Services

    Kites.js là một Framework có khả năng tự thân lắp ráp các phần mở rộng extensions để tạo thành một ứng dụng cụ thể.

    Với những dự án lớn, code nhiều, DI là thứ rất cần thiết để đảm bảo code dễ bảo trì, dễ nâng cấp. Do đó, Kites.js trang bị một DI System để làm cho việc sử dụng và khởi tạo các sự phụ thuộc một cách đơn giản.

    Bài này cố gắng giải thích DI System là gì và được sử dụng trong Kites.js như thế nào.

    Full mã nguồn ví dụ, bạn có thể tải về tại đây:

    Dependency Injection

    Cho những bạn mới chưa biết, thì DI là viết tắt của Dependency Injection.

    Theo Wikipedia, và mình dịch lại đại ý như thế này:

    Dependency injection là một kỹ thuật cho phép một đối tượng có thể cung cấp các phụ thuộc của một đối tượng khác. Một phụ thuộc là một đối tượng có giá trị sử dụng (ví dụ: nó có thể là một hằng số hoặc một Service nào đó, chẳng hạn TodoService).

    Sự phụ thuộc là một quan hệ phụ thuộc nhau, như hình dưới đây thì A đang phụ thuộc vào B, lý do A cần sử dụng một vài phương thức của B.

    84c174e5-2c19-4fb8-b14b-333a47d5a065-image.png

    Như vậy, phụ thuộc có nghĩa trông cậy vào một sự hỗ trợ, trợ giúp nào đó. Việc chuyển nhiệm vụ khởi tạo các phụ thuộc cho đơn vị khác và trực tiếp sử dụng biến phụ thuộc đó được gọi là dependency injection.

    Ở bài số #2, trong đoạn mã nguồn định nghĩa TodoController, thì controller này đang phụ thuộc vào TodoService. Bạn có thể thấy TodoController không quan tâm đến TodoService được khởi tạo như thế nào cả, đúng ko? Nó chỉ quan tâm đến việc đón nhận các Requests và sử dụng trực tiếp các methods của svTodo.

    import { Controller, Get, Put, RequestParam, Delete, Post, RequestBody } from '@kites/rest';
    import { TodoService } from './todo.service';
    
    @Controller('/todo')
    export class TodoController {
    
      constructor(public svTodo: TodoService) { }
    
      @Get('/')
      list() {
        return this.svTodo.getAll();
      }
    
      @Get('/:id')
      details(@RequestParam('id') task) {
        return this.svTodo.get(task);
      }
    
      @Post('/')
      create(@RequestBody() body) {
        return this.svTodo.create(body);
      }
      // ....
    }
    

    Kites Container, Kỹ thuật Inversion of Control (IoC)

    Bên cạnh 4 tính chất của lập trình hướng đối tượng OOP thì IoC là mẫu thiết kế tuân thủ nguyên lý số 5 Dependency Inversion trong nguyên lý thiết kế phần mềm hướng đối tượng S.O.L.I.D. Tức là, các Module cấp thấp sẽ được inject (truyền vào) vào Module cấp cao thông qua Constructor hoặc thông qua Properties.

    Như ví dụ ở trên thì svTodo được truyền vào Constructor, taskbody được truyền vào dưới dạng tham số của methods.

    Như vậy, nếu bạn để ý thì TodoController đã ủy nhiệm cho một đơn vị chịu trách nhiệm khởi tạo TodoService, đó là DI Container hay còn gọi là IoC Container.

    Kites.js cài đặt một DI Container trong core engine, có nhiệm vụ là nạp các phụ thuộc trong quá trình khởi động, quản lý và khởi tạo các sự phụ thuộc khi có yêu cầu.

    Tuy nhiên, trong phiên bản hiện tại v1.1.2, thì chúng ta phải config auto-mát-tay các Service phụ thuộc trong file ./app.ts, bằng cách định danh các providers như trong đoạn mã dưới đây:

    import { TodoService } from './todo/todo.service';
    
    async function bootstrap() {
      const app = await KitesFactory
        .create({
          providers: [
            TodoService,
          ],
        })
        .use(Express)
        .use(Rest)
        .init();
    
      app.logger.info(`Server started! Let's browse http://localhost:3000/api/todo`);
    }
    

    Sau khi KitesInstance được tạo, bạn có thể lấy được instance của TodoService, bằng cách gọi lệnh:

    const svTodo = app.container.inject(TodoService);
    const tasks = svTodo.getAll();
    console.log(tasks);
    

    @Injectable() và @Inject()

    TODO: update post ... chờ cập nhật thêm nhé bạn!

    posted in Tutorials
  • [Kites.js] #2 Hướng dẫn tạo Restful Api Server

    Chào bạn,

    Trong bài này mình hướng dẫn các bạn sử dụng Kites.js để xây dựng dự án Restful Api. Phục vụ cho các bài toán thêm, sửa, xóa dữ liệu và có thể kết hợp/tích hợp với một công nghệ phía Frontend như Angular, React, Vue, ...

    Nếu bạn chưa biết kites.js là gì, hãy đọc bài viết này nhé

    Giới thiệu

    Theo codehub.vn thì Restful Api được định nghĩa như thế này:

    RESTful API là một tiêu chuẩn dùng trong việc thết kế các thiết kế API cho các ứng dụng web để quản lý các resource. RESTful là một trong những kiểu thiết kế API được sử dụng phổ biến nhất ngày nay.

    Với một trang blog, để quản lý các bài viết chúng ta có các API URL đi kèm với HTTP method. Ví dụ:

    Bạn xem thêm các link trích dẫn ở mục Tham khảo cuối bài viết nhé!

    Phần sau đây là các bước cơ bản để các bạn có thể khởi tạo và phát triển dự án Restful API riêng của mình.

    Cụ thể, mình sẽ hướng dẫn các bạn xây dựng khung một dự án tạo Restful API Server, một ứng dụng Todo App.

    1. Chuẩn bị và khởi tạo dự án

    Ngay trong đầu thì bạn có thể nghĩ tới các công cụ, các tài liệu thiết kế để hỗ trợ bạn khởi tạo dự án. Để đơn giản hóa, ở đây mình chỉ cần các chương trình sau:

    • NPM - chương trình này khi bạn cài Nodejs mặc định có và có thể sử dụng trên Terminal, hoặc Command Prompt (cmd).
    • Visual Studio Code - IDE dành riêng cho code dự án Nodejs, rất nhẹ và hiệu quả, nếu các bạn thích Notepad++ thì vẫn được
    • ??? - Tạm thế đã nhé, thiếu sẽ bổ sung sau :)

    1 - Tại thư mục dự án D:\projects\my-api-project, khởi tạo dự án từ đầu bằng lệnh sau:

    npm init -y
    

    Sau lệnh trên, thư mục dự án đã có một file package.json được sinh ra, như hình:

    434404db-b6bb-47db-8d78-4dadbf41b50a-image.png

    Không tệ lắm ... trên là bước đầu tiên của tất cả dự án mà ai code nodejs đều từng trải qua!

    Rồi, đến đây tạm dừng một chút! Mình chợt nhớ ra là cần cài thêm một số công cụ hỗ trợ phát triển, như:

    • TypeScript - Phiên bản nâng cao của javascript, có kiểu và cú pháp hướng đối tượng.
    • ts-node-dev - Công cụ khởi động ứng dụng và tự động restart khi có thay đổi mã nguồn

    Các dự án thông thường mình sẽ sử dụng thêm tslint, trong series khởi đầu này, mình sẽ bỏ qua cho nó gọn gàng và dễ tiếp cận hơn một chút. Các bạn có kinh nghiệm rồi thì cứ setup để có được quy tắc viết mã gọn gàng, sạch sẽ nhé!

    2 - Cài đặt các gói thư viện hỗ trợ phát triển

    # Cài đặt gói thư viện hỗ trợ phát triển
    npm install typescript ts-node-dev @types/node --save-dev
    
    # Cài đặt thư viện phụ thuộc kites.js
    npm install @kites/core @kites/common @kites/express @kites/rest reflect-metadata
    
    # Lệnh này sẽ tạo ra file `tsconfig.json` quy định biên dịch mã
    npx tsc --init
    

    Sau khi cài đặt và khởi tạo tsc, bạn hãy thay toàn bộ nội dung sinh ra bằng nội dung sau để mình hiểu được những gì trình dịch tsc thực sự làm nhé:

    {
      "compilerOptions": {
        "module": "commonjs",
        "declaration": true,
        "noImplicitAny": false,
        "removeComments": true,
        "noLib": false,
        "allowSyntheticDefaultImports": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es6",
        "sourceMap": true,
        "outDir": "./dist",
        "baseUrl": "./"
      },
      "exclude": [
        "node_modules"
      ]
    }
    

    Mở file package.json, thêm vào thẻ scripts như sau:

      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "ts-node-dev --respawn --transpileOnly ./app.ts",
        "start:prod": "npm run build && node dist/app.js",
        "build": "tsc"
      },
    

    Coi như đến đây đã xong phần khởi tạo dự án, chúng ta sẽ bước sang phần 2 để xem cách thức viết mã nguồn cho dự án nhé.

    2. Viết mã nguồn

    Trong bài viết #1 mình đã giới thiệu tới các bạn viết một ứng dụng sử dụng Kites.js framework bằng JavaScript thuần. Và bây giờ, chúng ta sẽ viết lại nó bằng cú pháp TypeScript nhé.

    Bạn hãy tạo file app.ts với nội dung sau đây:

    import { engine } from "@kites/core";
    
    async function bootstrap() {
        const app = await engine().init();
        app.logger.info('Hello World!');
    }
    
    bootstrap();
    

    Khi chạy bằng lệnh: npx ts-node-dev app.ts chúng ta có được kết quả tương tự như khi viết bằng mã JavaScript:

    bca3ee06-d8f5-4a39-b127-0ca463645755-image.png

    Sound good? Có 2 việc bạn hãy thử tiếp theo như này:

    1. Thay vì dùng lệnh npx, bạn hãy thử gõ lệnh: npm start
    2. Sau đó, sửa file app.ts, dòng số 5 thành: app.logger.info('Hello Kites.js!');
    3. Nhấn Ctrl+S để lưu lại, bạn quan sát thấy gì trên Terminal?

    API Service

    Ngay phần đầu, chúng ta đã đặt ra đầu bài là viết ứng dụng TODO App. Một ứng dụng Restful API thường chia api ra làm 2 thành phần: Service và Controller.

    Service là phần dịch vụ, cung cấp các giao tiếp với các điểm đầu cuối, có thể đảm nhiệm thêm phần quản lý các nghiệp vụ lặt vặt khác nữa.

    Sau đây, mình sẽ tạo ra một TodoService, có nhiệm vụ quản lý toàn bộ thông tin và trạng thái về các đầu việc (Task) cần phải làm.

    1 - Tại thư mục gốc của dự án, bạn tạo ra file: ./todo/todo.service.ts có nội dung sau:

    import { Injectable } from '@kites/common';
    
    @Injectable()
    export class TodoService {
      public getAll(): string {
        return 'Get all todos!!!';
      }
    
      public create(task: any) {
        console.log('Create task: ', task);
        return { _id: Date.now(), ...task };
      }
    
      public get(task: string) {
        return `Get details: ${task}`;
      }
    
      public begin(task: string) {
        return `Start: ${task}`;
      }
    
      public trash(task: string) {
        return `Move task "${task}" to trash!`;
      }
    }
    

    API Controller

    Sau khi có được Service, chúng ta tạo ra một Controller tuân thủ các yêu cầu đặc tả của một API Restful. Như vậy, chúng ta sẽ có và định nghĩa các method GET, POST, PUT, DELETE như đã đề cập ở trên.

    2 - Bạn hãy tạo thêm một file: ./todo/todo.controller.ts có nội dung:

    import { Controller, Get, Put, RequestParam, Delete, Post, RequestBody } from '@kites/rest';
    import { TodoService } from './todo.service';
    
    @Controller('/todo')
    export class TodoController {
    
      constructor(public svTodo: TodoService) { }
    
      @Get('/')
      list() {
        return this.svTodo.getAll();
      }
    
      @Get('/:id')
      details(@RequestParam('id') task) {
        return this.svTodo.get(task);
      }
    
      @Post('/')
      create(@RequestBody() body) {
        return this.svTodo.create(body);
      }
    
      @Put('/:id')
      begin(@RequestParam('id') task) {
        return this.svTodo.begin(task);
      }
    
      @Delete('/:id')
      remove(@RequestParam('id') task) {
        return this.svTodo.trash(task);
      }
    }
    

    Ghép nối

    Đến đây thì chúng ta đã có được một API ToDo, bao gồm 1 Controller và 1 Service. Trong file ./app.ts chúng ta sẽ cập nhật với nội dung sau đây, để chuẩn bị khởi chạy server:

    import { KitesFactory } from '@kites/core';
    import Express from '@kites/express';
    import Rest from '@kites/rest';
    import { TodoService } from './todo/todo.service';
    
    import './todo/todo.controller';
    
    async function bootstrap() {
      const app = await KitesFactory
        .create({
          providers: [
            TodoService,
          ],
        })
        .use(Express)
        .use(Rest)
        .init();
    
      app.logger.info(`Server started! Let's browse http://localhost:3000/api/todo`);
    }
    
    bootstrap();
    

    Phù!!! Vậy là đã xong rồi! Bài có vẻ hơi dài một chút, xong những bài sau mình có thể làm ngắn gọn hơn được và đi giải thích cụ thể từng kỹ thuật, mã nguồn đã sử dụng trong bài này.

    Ví dụ như Provider, Injectable là gì? Hay cơ chế nạp Extension như nào, muốn tạo một Middleware trên mỗi controller, route thì làm thế nào? Cơ chế logs như nào? Còn nhiều kỹ thuật tiếp theo, hãy đón chờ nhé!

    3. Kiểm thử

    Bạn hoàn toàn có thể viết Unit Test, hay Intergration Test!

    Trong phạm vi bài viết này, mình recommend bạn dùng Postman để test các api vừa được tạo ra.

    Ví dụ mình test với api POST, tạo một todo task:

    062b403f-08b6-4c6a-95be-8b5e1eab5389-image.png

    Server Logs:

    a7be0501-60e3-4e7e-b80a-c54180cdf965-image.png

    4. Triển khai

    • Bạn có thể triển khai dự án lên Docker, VPS, Heroku hoặc bất kỳ một Hosting nào.
    • Bạn có thể copy toàn bộ mã nguồn và chạy lệnh npm run start:prod
    • Hoặc build ra thư mục dist, copy thư mục này lên production server, bằng lệnh: npm run build

    Mã nguồn cho bài viết này bạn có thể tải xuống tại đây:

    Tham khảo

    posted in Tutorials
  • [Kitesjs] #1 Giới thiệu kites.js

    Xin chào,

    Bài đầu tiên mình xin giới thiệu một framework đơn giản và nhỏ gọn, nhưng có khả năng tạo ra các ứng dụng mạnh mẽ như: Restful Api Server, CMS, CRM, Video Call, ...

    Giới thiệu

    Framework mình muốn giới thiệu tới các bạn có tên Kites.js, nó có nghĩa là những cánh diều, rất nhẹ và có thể bay cao!

    Để đáp ứng được tiêu chí như tên gọi của nó, mình đã cố gắng thiết kế core engine của nó thật đơn giản nhất, có nhiệm vụ duy nhất là có khả năng nạp tự động các Extensions được cắm vào project, tính năng Plug and Play.

    • Một ví dụ cụ thể các bạn có thể hình dung như thế này:
    const kites = require('@kites/core');
     
    kites.engine().init().then((app) => {
      app.logger.info('Hello World!');
    });
    
    • Để chạy được chương trình trên, bạn cần install các phụ thuộc cho nó như lệnh dưới đây:
    npm install @kites/core @kites/common
    
    • Kết quả khi chạy chương trình:

    ed76616a-d262-42b3-a4cf-6d2ebdbb1733-image.png

    Trên là một ứng dụng nhỏ nhất có thể viết với Kitesjs. Như bạn thấy, nó đơn giản khởi tạo một ứng dụng kites và in ra một dòng thông điệp: Hello World!

    Tuy nhiên, điều mà kites.js muốn hướng đến xa hơn, đó là có thể sử dụng để lập trình các ứng dụng lớn phổ biến như: Blog, CMS, CRM, Restful API, Websockets, Video Call, ...

    Các bạn muốn sử dụng TypeScript để lập trình, bạn có thể clone project starter tại đây:

    Tính năng

    Về tính năng cốt lõi của Framework, tập trung giải quyết các vấn đề sau:

    • Extension as a feature
    • Autodiscover extensions
    • Micro frontends development
    • Storage mutiple providers
    • Rich decorators system
    • Event-driven programming
    • Reactive programming

    Với các tính năng trên với thì Developer có thể là Backend, Frontend hoặc Full-stack developers đều có thể sử dụng một cách đơn giản, tùy vào mỗi mục tiêu của dự án.

    Extension as a feature

    Extension có nghĩa là phần mở rộng, trong kitesjs thì mỗi extension được xem như một tính năng của hệ thống. Và nó đảm nhiệm một vai trò nhất định, được phát triển độc lập mà không ảnh hướng đến hoạt động của cả hệ thống. Và đôi khi cũng có extension này là mở rộng của extension khác để đáp ứng được nghiệp vụ có yêu cầu cao hơn hoặc chi tiết hơn.

    Các packages tạo nên kites cũng là các extensions độc lập, ví dụ như:

    • @kites/express, Extension tích hợp ExpressJS để tạo ra web server nhanh chóng
    • @kites/rest, Extension cho phép sử dụng decorators để tạo ra các Web API
    • @kites/user, Extension phép phép quản lý tài khoản người dùng và mật khẩu đăng nhập hệ thống (TODO)
    • Và nhiều extension khác nữa đang được xây dựng ...

    Autodiscover extensions

    Đây là tính năng tìm kiếm, phát hiện, tự động nạp các extensions nằm trong thư mục của ứng dụng, ví dụ trong thư mục node_modules.

    Tính năng này sẽ dựa vào một file cấu hình có tên kites.config.js mô tả thông tin về extension đó và các options thiết lập mặc định. Tuy nhiên các options này vẫn có thể được thiết lập lại (override) trong file cấu hình ứng dụng kites.config.json, dev.config.json hoặc prod.config.json trong thư mục gốc của dự án.

    Micro frontends development

    Định nghĩa micro frontend có thể nói như sau:

    The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in. A team is cross functional and develops its features end-to-end, from database to user interface. (micro-frontends.org)

    Ý là về giao diện của mỗi tính năng hoàn toàn có thể phát triển một cách độc lập và tự do. Các team làm việc độc lập và không phải phụ thuộc vào nhau.

    Trong các ứng dụng Single Page Application, phần render, logic của web app được nằm hoàn toàn ở phía client-side, không còn là những file HTML đơn giản nữa, thay vào đó là những phần logic phức tạp không kém so với ở backend side.

    TODO: Đây là tính năng mà Framework đang hướng tới, hiện chưa ready trong phiên bản 1.x, rất mong có nhiều người nhiệt huyết tham gia đóng góp ý tưởng để feature này sớm available.

    Tham gia

    Toàn bộ mã nguồn của framework bạn có thể tìm thấy ở đây:

    Các bạn có thể tham gia bằng một trong các cách như:

    • Đóng góp ý kiến, phản biện một tính năng, hoặc có thể là tính thực tiễn của dự án
    • Sử dụng framework này để làm bài tập lớn, hoặc dự án của bạn :smile:
    • Tham gia sửa lỗi, viết mã phát triển framework này đáp ứng các tiêu chí về mục tiêu tính năng.

    Mọi ý kiến đóng góp xin gửi về địa chỉ [email protected]

    posted in Tutorials
  • RE: Cần tìm SENIOR/EXPERT mentor người hỗ trợ tôi làm dự án

    @Nguyen-Nguyen clone FB trên web cụ thể là như nào bạn? Tại sao lại là trên Web, clone gồm những thông tin gì?

    posted in Discuss Node.JS
  • RE: Hỏi làm Ajax với Node.JS quản lý thông tin người dùng, sử dụng Wizard Form????

    @PHAT-MINH said in Hỏi làm Ajax với Node.JS quản lý thông tin người dùng, sử dụng Wizard Form????:

    Ý tưởng của bạn mình chưa rõ lắm. Nếu chỉ là quản lý người dùng thì cứ áp dụng mô hình MVC, viết code nghiệp vụ thôi.

    Không hiểu ý Wizard Form của bạn có ý nghĩa như nào?

    posted in Discuss Node.JS
  • RE: Tìm sự phụ nodejs

    @Trâ-n-Ba-o said in Tìm sự phụ nodejs:

    Em muốn làm về nodejs, em cũng tự học sơ sơ rồi. Ai train cho em với được không ạ . Em cảm ơn

    Tìm công ty thực tập thôi em :+1:

    posted in Discuss Node.JS
  • RE: Tool convert image to base64

    @Nguyen-Jason Những tool online như này khá hữu dụng cho dev! :smile:

    posted in Showcase - Open Source
  • RE: Hỏi về website Live Stream , mọi người giúp e với ạ!

    @nguyen-phuong WebRTC chỉ là công nghệ truyền dẫn media, với yêu cầu nhiều người xem bạn phải tìm hiểu về thêm kiến trúc triển khai. Bạn có thể tìm hiểu 2 kiến trúc điển hình SFU hoặc MCU với WebRTC nhé. Tức là có sự tham gia điều khiển của Media Server.

    Để đơn giản về cách thức triển khai cũng là xu hướng hiện tại người ta sẽ chọn SFU. Hiện có khá nhiều open source, bạn có thể google là có.

    posted in General Discussion
  • RE: Lỗi tải app lên heroku, mong ace giúp đỡ ạ !!!

    @Viet-Duc-Nguyen bạn có cấu hình ưu tiên nhận PORT từ env không? bạn tham khảo dòng lệnh này nhé https://stackoverflow.com/a/52806264/1896897

    posted in General Discussion