Skip to main content

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· 4 min read

Debug cũng quan trọng giống như lúc bạn code vậy. Với những bạn làm quen với Lambda thì không phải ai cũng biết làm sao để có thể debug được. Đa phần các bạn sẽ chọn cách in dữ liệu ra màn hình để debug. Hôm nay tôi sẽ hướng dẫn các bạn cách debug ứng dụng viết bằng Lamba nhé.

Remote Debugging

Như các bạn đều biết thì để có thể debug được ứng dụng Java thì bạn cần phải Remote tới cổng Debug của trình thực thi Java. Quá trình này được gọi là Remote Debugging. Với ứng dụng Java bình thường các bạn có thể dễ dàng sử dụng các IDE có hỗ trợ Remote Debugging một cách dễ dàng. Với các ứng dụng Lambda bằng Java thì sao?

Khởi động ứng dụng Lambda với chế độ Remote Debugging

Trong bài viết Phát triển ứng dụng Lambda bằng Java, tôi đã hướng dẫn các bạn cách sử dụng SAM để chạy các ứng dụng Lambda viết bằng ngôn ngữ Java. Các bạn theo dõi bài viết trên sẽ thấy ứng dụng được chạy trên một máy ảo Java trông một docker container. Để khời động chế để Remote Debugging thì các bạn gõ lệnh sau(các bạn nhớ khởi động Docker trước khi khởi động SAM nhé):

hieunv@HieuNV sam-app % sam local start-api -d 5858
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-03-30 20:10:33 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

Các bạn sẽ thấy SAM được khởi động và lắng nghe ở cổng 3000. Còn cổng 5858 thì sao? Tại thời điểm này nó chưa được khởi động. Khi bạn access vào http://127.0.0.1:3000/hello thì cổng Remote Debugging 5858 mới được khởi động.

Invoking helloworld.App::handleRequest (java11)

Fetching lambci/lambda:java11 Docker container image......
Mounting /Users/hieunv/Projects/hieunv/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
Picked up _JAVA_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=*:5858 -XX:MaxHeapSize=2834432k -XX:MaxMetaspaceSize=163840k -XX:ReservedCodeCacheSize=81920k -XX:+UseSerialGC -XX:-TieredCompilation -Djava.net.preferIPv4Stack=true

Cấu hình Remote Debug trong Visual Studio Code

Các bạn quay lại Visual Studio Code, vào Tab Debug sau đó chọn create a launch.json file. Tại mục chọn kiểu Debug bạn chon Add Configuration và chọn

add configuration

Sau đó các bạn chon Attach To Remote Program

Attach To Remote Program

Tiếp đó các bạn sửa lại cấu hình hostName thành localhostport thành 5858(đấy là cổng Remote Debug của trình thực thi Java)

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Debug (Attach) - Remote",
"request": "attach",
"hostName": "localhost",
"port": 5858
}
]
}

Đặt break point

Các bạn quay lại Visual Studio Code và mở mã nguồn muốn debug sau đó đặt break point

break point

Khởi động Visual Studio Code Debug bằng các click vào nut Start

Xem output log bạn sẽ thấy thông báo sau:

START RequestId: 4f69214b-9a3a-19ef-0137-5081d7caccea Version: $LATEST
END RequestId: 4f69214b-9a3a-19ef-0137-5081d7caccea
REPORT RequestId: 4f69214b-9a3a-19ef-0137-5081d7caccea Init Duration: 58932.30 ms Duration: 10421.29 ms Billed Duration: 10500 ms Memory Size: 512 MB Max Memory Used: 73 MB
2020-03-30 20:39:18 127.0.0.1 - - [30/Mar/2020 20:39:18] "GET /hello HTTP/1.1" 500 -
``

Sau đó bạn access http://127.0.0.1:3000/hello bằng Postman và quay lại Visual Studio Code

debug mode

Như vậy là chúng ta đã debug thành công vào hàm Lambda rồi.

Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết sẽ giúp ích với dự án của các bạn. Chúc các bạn thành công.

· 5 min read

Như các bạn đã biết hiện nay môi trường thực thi sử dụng trong Lambda phần lớn đang sử dụng Node hay Python. Tuy nhiên trên thực tế đôi khi bạn cần sử dụng một môi trường thực thi khác như Java chẳng hạn. Trên thực tế thì AWS cũng đang hỗ trợ khá nhiều môi trường thực thi khác nhau. Có nhiều lý do dẫn tới việc chúng ta phải sử dụng một môi trường thực thi nào đó tuỳ vào tình hình dự án. Trong bài viết này tôi sẽ hướng dẫn các bạn xây dựng ứng dụng Lamba sử dụng môi trường thực thi là Java.

Các công cụ cần thiết

Docker

Chúng ta cần Docker bởi vì công cụ thực thi SAM CLI sẽ sử dụng docker container để thực thi ứng dụng. Bạn thao khảo đường dẫn sau để cài đặt Docker

SAM

Chúng ta sẽ sử dụng SAM vì chúng ta cần một môi trường thực thi có thể chạy trên môi trường cục bộ và có thể debug được. Để cài SAM bạn làm theo hướng dẫn sau:

brew tap aws/tap
brew install aws-sam-cli

Chúng ta sử dụng brew để cài SAM nên bạn cần cài brew trước. Nếu chưa cài brew thì bạn có thể thao khảo cách cài brew như sau:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
hieunv@HieuNV ~ % brew --version
Homebrew 2.2.10
Homebrew/homebrew-core (git revision f0179; last commit 2020-03-22)
Homebrew/homebrew-cask (git revision 0a88ae; last commit 2020-03-22)

Để kiểm tra xem bạn đã cài đặt thành công chưa, bạn sử dụng lệnh sau:

hieunv@HieuNV ~ % sam --version
SAM CLI, version 0.45.0

Trên Windows thì bạn thao khảo đường dẫn này

Oracle JDK

Chúng ta sẽ sử dụng môi trường thực thi Java nên việc cài đặt Oracle JDK là đương nhiên đúng không. Các bạn tham khảo cách cài đặt Oracle JDK tại đây nhé.

Maven

SAM sẽ sử dụng maven để build nên chúng ta cần cài đặt thêm maven. Để cài đặt Maven các bạn sử dụng lệnh sau:

brew install --ignore-dependencies maven

Các bạn chú ý, chúng ta cần sử dụng --ignore-dependencies để bỏ qua việc cài đặt Open JDK nhé. Mặc định maven sẽ sử dụng Open JDK. Tuy nhiên chúng ta đã cài đặt Oracle JDK rồi nên không cần cài Open JDK nữa.

Tài liệu tham khảo:

Tạo project bằng SAM

  • Tạo một project mới
hieunv@HieuNV hieunv % sam init -r java11
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1

Which dependency manager would you like to use?
1 - maven
2 - gradle
Dependency manager: 1

Project name [sam-app]:

Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git

AWS quick start application templates:
1 - Hello World Example: Maven
2 - EventBridge Hello World: Maven
3 - EventBridge App from scratch (100+ Event Schemas): Maven
Template selection: 1

-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: java11
Dependency Manager: maven
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./sam-app/README.md
  • Trước khi thực thi bạn cần build project trước
hieunv@HieuNV hieunv % cd sam-app
hieunv@HieuNV sam-app % sam build
Building resource 'HelloWorldFunction'
/usr/local/bin/mvn is using a JVM with major version 13 which is newer than 11 that is supported by AWS Lambda. The compiled function code may not run in AWS Lambda unless the project has been configured to be compatible with Java 11 using 'maven.compiler.target' in Maven.
Running JavaMavenWorkflow:CopySource
Running JavaMavenWorkflow:MavenBuild
Running JavaMavenWorkflow:MavenCopyDependency
Running JavaMavenWorkflow:MavenCopyArtifacts

Build Succeeded

Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
  • Khởi động ứng dụng (trước khi khởi động bạn cần đảm bảo rằng Docker đang hoạt động)
hieunv@HieuNV sam-app % sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-03-22 22:07:45 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

Chúng ta thử truy cập vào http://127.0.0.1:3000/hello bằng Postman. Nếu các bạn chưa chạy lần nào thì sẽ phải chờ hơi lâu một chút để Docker tải image cần thiết.

start-api

Trong bài viết này tôi đã hướng dẫn các bạn cách viết một API bằng Lambda sử dụng môi trường thực thi Java. Hy vọng bài viết sẽ giúp ích cho dự án của các bạn.

· 3 min read

Mình đã viết khá nhiều bài sử dụng Serverless, tại sao mình lại viết bài này. Thực ra mình cũng mới bắt đầu làm AWS Lambda được một thời gian ngắn. Dự án đầu tiên mình làm Lambda thì đã các bạn đi được đã chọn Serverless để phát triển. Dự án thứ hai mình làm với AWS Lambda thì khách hàng đưa cho mình bộ mã nguồn đã được cấu hình sử dụng Serverless. Mọi thứ đều có vẻ ổn cho đến một ngày mình quyết định thử debug Lambda bằng Visual Studio Code. Mọi thứ trở nên phức tạp và mình tìm thấy SAM, dường như nó đã giải quyết vấn đề của mình nên mình quyết định viết bài này để cho các bạn nếu mới đến với thế giới AWS thì có thể dễ dàng lựa chọn thứ mình cần.

Chọn Serverless hay SAM?

Dự án đầu tiên mình dùng Serverless và viết bằng JavaScript, mọi thứ đều ổn vì mình chỉ dùng Serverless có kết hợp với Serverless Offline để chạy các hàm Lambda API trên máy tính cá nhân được. Việc debug cũng không gặp trở ngại gì do Serverless Offline có hỗ trợ debug. Thế nhưng đến dự án tiếp theo, ngôn ngữ được chọn làm môi trường thực thi là Python và mình thực sự gặp khó khăn. Mình vẫn có thể chạy được các hàm Lambda trên máy tính cá nhân nhưng không thể debug đươc. Và thế là mình bắt đầu tìm hiểu để giải quyết vấn đề này. Rồi mình tìm thấy SAM và mọi thứ dường như được giải quyết.

Ngôn ngữ nào được hỗ trợ?

  • Serverless Offline hỗ trợ những ngôn ngữ sau:
    • Python
    • Ruby
    • Node
  • SAM hỗ trợ nhưng ngôn ngữ sau:
    • Python
    • Ruby
    • Node
    • Java
    • .NET Core
    • ...

Được hỗ trợ như thế nào?

  • Serverless Offline là plugin được cá nhân phát triển. Nó không phải gói được hỗ trợ chính thức từ AWS.

  • SAM được hỗ trợ chính thức từ AWS.

Hỗ trợ debug như thế nào?

  • Serverless Offline chỉ hỗ trợ debug với Node.

  • SAM thì hiện tại theo mình tìm hiểu tài liệu chính thức thì đang hỗ trợ debug cho:

    • Python
    • Ruby
    • Node

Môi trường thực thi

  • Serverless chạy trực triếp trên máy host.
  • SAM thì sử dụng container trong docker để thực thi.

Các bạn có thể tham khảo hướng dẫn sử dụng Serverless ở các tài liệu sau nhé:

Kết luận

SAM dường như có những lợi thế hơn hẳn so với Serverless. Nếu bạn quyết định phát triển bằng Node thì bạn sẽ không gặp nhiều khó khăn khi dùng Serverless hay SAM. Nếu bạn chọn một môi trường thực thi khác như Python hay Ruby hay bất kỳ môi trường nào khác thì lựa chọn SAM sẽ là quyết định sáng suốt hơn đấy. Mình sẽ hướng dẫn các bạn sử dụng SAM trong loạt bài viết về SAM sau nhé.

· 3 min read

introduction

Như các bạn đã biết, Static Type Checking giúp chúng ta kiểm soát kiểu dữ liệu tốt hơn khi viết mã nguồn. Các bạn có thể sử dụng Flow hoặc TypeScript đều được. Tuy nhiên khi dữ liệu được liên kết với API chẳng hạn, khi đó bạn không thể kiểm soát được giá trị được truyền vào cho biến nào đó. Do đó chúng ta vẫn cần thêm một bước nữa để hạn chế các lỗi tiềm ẩn bằng Runtime Type Checking. Trong bài viết này tôi sẽ hướng dẫn các bạn sử dụng Runtime Type Checking như thế nào cho hiệu quả.

Runtime Type Checking là gì?

Runtime Type Checking là quá trình xác minh lại kiểu dữ liệu của giá trị truyền vào cho biến có đúng với kiểu dữ liệu mong muốn hay không. Trong ứng dụng React khi bạn khai báo propTypes cho component nào đó chính là lúc bạn đang định nghĩa kiểu dữ liệu mong muốn. Tại thời điểm thực thi các định nghĩa này sẽ được kiểm tra giúp bạn kiểm soát việc binding dữ liệu từ API vào component có tương thích với nhau hay không.

Định nghĩa propTypes

React cung cấp gói prop-types giúp bạn định nghĩa các thuộc tính cần cho Runtime Type Checking. Trước đây thì gói này nằm trong React, hiện tại thì nó đã tách ra thành một gói riêng rồi. Các bạn khai báo propTypes như sau:

import React from "react";
import PropTypes from "prop-types";

export function Octocat(props) {
const { login, avatar_url, name } = props;
return (
<ul>
<li>{login}</li>
<li>{name}</li>
<li>
<img src={avatar_url} alt={login} />
</li>
</ul>
);
}

Octocat.propTypes = {
login: PropTypes.string,
avatar_url: PropTypes.string,
name: PropTypes.string,
};

Trong quá trình thực thi nếu dữ liệu được truyền vào component không đúng với định nghĩa propTypes bạn sẽ nhận được một thông báo tương tự như sau:

prop-types

Kết hợp với Spread Attributes

Bạn có để ý rằng khi chúng ta khai báo giá trị truyền vào trong props thì khi sử dụng component này bạn phải khai báo như sau:

<Octocat login="abc" avatar_url="def" name="xyz" />

Nếu số lượng thuộc tính trong component này tăng lên thì việc viết mã nguồn sẽ khá là vất vả. Rất may là trong JSX cung cấp cho bạn tính năng Spead Attributes và bạn có thể viết code như sau:

const data = {login: "abc" avatar_url: "def" name: "xyz"};
<Octocat {...data} />

Toán tử ... được gọi là toán tử spead.

Kết hợp với Destructuring Props

Các bạn có thấy rằng khi khai báo Octocat component tôi đã truyền props như làm tham số của một hàm và sau đó mới gán lại vào các biến. Với Destructuring Props bạn có thể viết mã nguồn đơn giản hơn như sau:

import React from "react";
import PropTypes from "prop-types";

export function Octocat({ login, avatar_url, name }) {
return (
<ul>
<li>{login}</li>
<li>{name}</li>
<li>
<img src={avatar_url} alt={login} />
</li>
</ul>
);
}

Octocat.propTypes = {
login: PropTypes.string,
avatar_url: PropTypes.string,
name: PropTypes.string,
};

Tài liệu tham khảo:

Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết sẽ giúp các bạn viết mã nguồn tốt hơn.

· 3 min read

Một trọng những vấn đề quan trọng trong các dự án đó là điều khiển quyền truy cập. Với các ứng dụng xây dựng trên nền tảng AWS việc điều khiển truy cập cũng phức tạp hơn. Trong bài viết này tôi sẽ hướng dẫn các bạn cách tôi đã làm để điểu khiển truy cập với các API sử dụng API Gateway Lambda Authorizers.

Luồng xác thực Lambda Authorizer

Luồng xác thực của Lambda Authorizer được minh hoạ trong hình sau:

auhorizer

Các bược xác thực như sau:

  1. Máy khách gửi yêu cầu lên API Gateway API có kèm theo Bearer Token.
  2. API Gateway kiểm tra cấu hình authorizer đã được cấu hình tương ứng với hàm Lambda. Nếu nó tồn tại thì Lambda Authoirizer sẽ được gọi.
  3. Lambda Authorizer sẽ thực hiện xác thực bằng Bearer Token đã được gửi lên.
  4. Nếu việc gọi Lambda Authrorizer thực hiện thành công, hàm Lambda sẽ trả về thông tin chứa chính sách IAM và thông tin người dùng.
  5. API Gateway sử dụng thông tin trả về từ Lambda Authorizer để kiểm tra quyền truy cập:
  • Trường hợp nhận được thông tin từ chối truy cập thì API Gateway sẽ trả về mã 403 và từ chối truy cập tới API từ máy khách.
  • Trường hợp nhận được thôn tin cho phép truy cập thì phương thức sẽ được thực thi.

Định nghĩa Lambda Authorizer

  • Khai báo authorizer trong serverless.yml:
functions:
authorizer:
handler: src.api.authorizer.lambda_handler
cors: true
  • Định nghĩa hàm Lambda Authorizer:
import jwt


def lambda_handler(event, context):
try:
token = event.get("authorizationToken").split(" ")[1] # lấy thông tin token trong Authorization header
claims = jwt.decode(token, "secret", algorithms=["HS256"]) # decode xem token có hợp lệ không
return {
"principalId": claims["uid"], # lấy thông tin user đề gán vào IAM
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow", # cho phép nếu token hợp lệ
"Resource": event["methodArn"],
}
],
},
}
except:
return {
"principalId": "denied",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny", # từ chối nếu token không hợp lệ
"Resource": event["methodArn"],
}
],
},
}

Định nghĩa hàm Lambda cần điều khiển quyề truy cập

  • Khai báo hàm Lambda trong serverless.yml
functions:
test:
handler: src.api.test.lambda_handler
events:
- http:
method: get
path: api/test
cors: true
authorizer: authorizer # khai báo Lambda Authorizer
  • Định nghĩa hàm Lambda:
import json


def lambda_handler(event, context):
headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
body = {"status": "success"}
return {
"statusCode": 200,
"headers": headers,
"body": json.dumps(body),
}

Test thử Lambda với Authorizer

  • Trường hợp không truyền token trên Authorizer Header, API Gateway sẽ trả về 403

No Auth

  • Trường hợp có truyền token trên Authorization Header, API Gateway sẽ cho phép phương thức được thực thi

Token được tạo như sau

(zpn) hieunv@HieuNV lambda % python
Python 3.7.7 (default, Mar 10 2020, 15:43:33)
[Clang 11.0.0 (clang-1100.0.33.17)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import jwt
>>> jwt.encode({'uid': 'hieunv'}, "secret", algorithm='HS256')
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJoaWV1bnYifQ.xuZSlS_3lw6NvGvw_fQ2qXGBWiv2HpXTFtYtO85lQac'

Truyền token lên Authorizer Header

Bearer Auth

Tài liệu tham khảo:

Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết có thể giúp các bạn cài đặt việc điều khiển truy cập dễ dàng hơn với các ứng dụng trên nền tảng AWS.

· 5 min read

react

Static Type Checking và Runtime Type Checking là hai thuật ngữ ban đâu cảm giác có vẻ không quen thuộc cho nhưng nó lại lai thứ có lẽ các bạn đang dùng hàng ngày. Như các bạn biết thì JavaScript là ngôn ngữ định kiểu động, nghĩa là kiểu dữ liệu chỉ được xác định tại thời điểm thực thi. Do đó khi lập trình chúng ta không cần xác định kiểu dữ liệu cho biến. Điều này vô hình chung làm cho mã nguồn của chúng ta trở nên khó đọc hơn và khó debug hơn nữa. Chính vì vậy việc xác định kiểu dữ liệu giúp cho chúng ta kiểu soát và debug dễ dàng hơn. Trong bài viết này tôi sẽ chia sẻ với các bạn cách công đồng đang làm để hỗ trợ hai kiểu kiểm tra kiểu dữ liệu này.

Static Type Checking là gì?

Hiện nay có hai xu hướng để kiểm tra kiểu dữ liệu tĩnh là FlowTypeScript. Static Type Checking sẽ phân tích mã nguồn tĩnh trước khi biên dịch mã nguồn sang mã JavsScript(ES5) là mã nguồn mà trình duyệt có thể đọc được. Vậy thì chúng khác nhau ở điểm nào?

Flow

Flow là một phần mở rộng của Babel cung cấp cho bạn việc kiểm tra kiểu dữ liệu tĩnh bằng cách sử dụng các chỉ thị bằng comment mà bạn thêm vào mã nguồn.

Ví dụ cách sử dụng flow như bên dưới:

// @flow
function square(n: number): number {
return n * n;
}

square("2"); // Error!

TypeScript

TypeScript là ngôn ngữ dựa trên JavaScript được Microsoft phát triển. TypeScript khác với Flow là nó không phân tích mã nguồn dự trên các chỉ thị bằng comment. Kiểu dữ liệu được hỗ trợ từ trong ngôn ngữ luôn. Dữ liệu được kiểm tra tại thời điểm phân tích cú pháp của chương trình dịch. Ngoài ra thì TypeScript hỗ trợ rất mạnh OOP nên nó rất phù hợp với các bạn quen thuộc với các ngôn ngữ định kiểu như Java và C#.

const add = (x: number, y: number) => {
return x + y;
};

Sử dụng Static Type Checking có lợi ích gì?

  • Khi sử dụng Static Type Checking bạn được hỗ trợ việc kiểm soát dữ liệu tại thời điểm viết mã nguồn, tất cả việc gán hoặc gọi hàm với kiểu dữ liệu không phù hợp sẽ được phát hiện tại thời điểm này.

  • IDE hỗ trợ việc kiểm soát kiểu dữ liệu giúp bạn viết code nhanh hơn và sai sốt ít hơn.

  • Mã nguồn dễ đọc hơn cho người mới.

Bạn mất gì khi sử dụng Static Type Checking?

Static Type Checking mang những lợi ích nhất định trong việc kiểm soát kiểu dữ liệu, tuy nhiên nó cũng có những nhiểm điểm:

  • Bạn phải viết code nhiều hơn thì mới kiểm soát được kiểu dữ liệu, việc này đương nhiên sẽ tốn công sức. Tuy nhiên so với việc mất thời gian vào debug thì có lẽ nó vẫn tốt hơn.

  • Bạn vẫn không kiểm soát được kiểu dữ liệu tại thời điểm chạy chương trình, lý do là nếu chương trình của bạn có liên kết với API thì không có gì đảm bảo kiểu dữ liệu của bạn chạy đúng với backend cả. Đương nhiên cái này là vấn đề không thể giải quyết được rồi. Tôi sẽ hướng dẫn các bạn giải quyết vấn đề này trong bài viết Runtime Type Checking sau nhé.

Kết luận

Đây chỉ là kết luận mang tính cá nhân của mình :). Các bạn tự suy ngẫm để đưa ra lựa chọn phù hợp nhé.

  • Việc viết theo Flow có cảm giác không tự nhiên lắm, thường xuyên phải thêm comment khiến bạn khá mất thời gian. Thêm một vấn đề nữa là lúc viết code sử dụng Flow khá tốn tài nguyên của máy. Về vấn đề này thì mình thấy TypeScript có vẻ ổn hơn.

  • TypeScript không đơn giản là một sự mở rộng của JavaScript, Microsoft đã thêm vào nhiều thứ hơn thế và nó cũng không dựa trên các tiêu chuẩn của ES6, ES7, ... (có support ES6, ES7, ... nhưng có những thêm vào nhiều thứ khác nữa). Về điểm này thì Flow có vẻ tốt hơn.

  • Sau sự ra đời của React Hooks thì mọi thế mạnh của TypeScript gần như không còn nữa so với Flow, bản thân mình cũng không dùng TypeScript nữa.

  • Một điểm quan trong nữa là cả TypeScript và Flow đều không thể xác mình kiểu dữ liệu lúc thực thi nên bạn vẫn cần Runtime Type Checking.

Các bạn có dùng Static Type Checking không? Mình thì đã không dùng nữa và chuyển sang xác mình toàn bộ bằng Runtime Type Checking rồi. Thời gian tốn cho debug cũng không phát sinh nhiều so với việc phải viết mã nguồn xác định kiểu dữ liệu.

Cảm ơn các bạn đã theo dõi bài viết. Các bạn cùng chờ bài viết mình hướng dẫn về Runtime Type Checking nhé.

· 3 min read

react

Chắc hẳn ai cũng biết tới nguyên lý đầu tiên của SOLID đó là trách nhiệm đơn nhất. Nghĩa là mỗi một đối tượng chỉ làm một nhiệm vụ duy nhất. Đơn giản vì với tính đơn nhất chúng ta sẽ dễ dàng kiểm soát mã nguồn hơn (cho cả người code lần người review). Chưa kể tới việc với tính đơn nhất thì mã nguồn của bạn cũng trở nên trong sáng hơn. Với anh em lập trình React cũng vậy, bài toán khá đau đầu là làm sao tách logic ra khỏi phần mã nguồn kết xuất HTML. Cách làm như vậy giúp chúng ta thay vì viết một React Component để là cả hai việc thì bây giờ chúng ta sẽ viết hai React Component để làm hai nhiệm vụ khác nhau. Trong bài viết này tôi sẽ hướng dẫn các bạn sử dụng mấu container để làm việc đó.

Container component là gì?

Chúng ta không có một định nghĩa chính xác nào về container component cả, container component đơn giản là một React Component làm nhiệm vụ goi API để lấy dữ liệu và sau đó truyền nó xuống một React Component để kết xuất mã HTML.

Định nghĩa container

Để các bạn dễ hình dung tôi sẽ viết thử một container, container này sẽ thực hiện lấy thông tin của octocat

src/containers/OctocatContainer.jsx

import React, { useState, useEffect } from "react";
import { Octocat } from "../components/Octocat";
export function OctocatContainer() {
const [octocat, setOctocat] = useState(null);

useEffect(() => {
async function getOctocat() {
// gọi hàm fetch với phướng thực mặc định là GET
const response = await fetch("https://api.github.com/users/octocat");
const body = await response.json();
// sử dụng React Hook để thông tin octocat vào state
setOctocat(body);
}

getOctocat();
}, []); // tham số mảng rỗng thể hiện hàm effect không phụ thuộc vào đối tượng nào, nó tương đương với hàm componentDidMount

return <Octocat {...octocat} />;
}

Trong ví dụ trên tôi có sử dụng thêm JSX spread attributes để tránh việc phải khai báo props cho Octocat component. Mẫu này đặc biệt có ý nghĩa đối với các component mà bạn cần khai báo nhiều props.

Định nghĩa component để kết xuất mã HTML

Sau khi có dữ liệu rồi thì chúng ta sẽ kết xuất nó ra HTML như sau:

src/components/Octocat/Octocat.jsx

import React from "react";

export function Octocat(props) {
const { login, avatar_url, name } = props;
return (
<ul>
<li>{login}</li>
<li>{name}</li>
<li>
<img src={avatar_url} alt={login} />
</li>
</ul>
);
}

Như các bạn thấy, component trên chỉ làm nhiệm vụ bind dữ liệu lên màn hình mà thôi.

Cùng xem kết quả nhé :)

octocat

Cám ơn các bạn đã theo dõi bài viết. Đây là bài đầu tiên trong loạt bài React Patterns mình sẽ chia sẻ với các bạn. Các bạn cùng chờ các bài tiếp theo nhé.

· 4 min read

dynamodb streams

Với các ứng dụng hiện nay, việc giao tiếp giữa client và server phổ biến đang sử dụng Rest API. Trong một số trường hợp việc phải để client đợi xử lý là điều không thể chấp nhận được. Do đó để giải quyết vấn đề này thì phần lớn cách giải quyết là sử dụng batch process, nghĩa là tại thời điểm đó chúng ta sẽ trả lại dữ liệu cho client ở trạng thái đang xử lý(tránh xảy ra tình trạng timeout). Tuy nhiên ngay tại thời điểm đó một batch process sẽ được khởi động để thực thi tiếp các công việc còn lại. Trong bài viết này tôi sẽ hướng dẫn các bạn viết batch process bằng AWS Lambda bằng cách sử dụng DynamoDB Streams

DynamoDB Streams

DynamoDB Streams là một tính năng trong DynamoDB cho phép bạn lắng nghe thay đổi trên một bảng dữ liệu nào đó và thực hiện các tác vụ đáp ứng yêu cầu nghiệp vụ trong ứng dụng của bạn. Mỗi khi có sự thay đổi DynamoDB sẽ ghi các bản ghi gần như ngay lập tức là dòng dữ liệu mà các ứng dụng đang lắng nghe.

Với DynamoDB Streams để giải quyết vấn đề timeout của API chúng ta chỉ gần ghi dữ liệu vào bảng trong DynamoDB sau đó dữ liệu được ghi lên dòng dữ liệu mà batch process của chúng ta đang lắng nghe rồi tiếp tục thực hiện nhiệm vụ còn lại.

Các bạn tham khảo link sau để cài đặt DynamoDB ở local nhé.

Định nghĩa bảng trong DynamoDB

Các bạn có thẻ sử dụng NoSQL Workbench for Amazon DynamoDB để tạo bảng hoặc viết code để chia sẻ với các member khác như sau:

# -*- coding: utf-8 -*-
import os
from datetime import datetime
import boto3

dynamodb = boto3.client(
"dynamodb",
endpoint_url="http://localhost:8000",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
)


def create_orders():
try:
dynamodb.delete_table(TableName="dev_orders")
except Exception as exp:
print(exp)

response = dynamodb.create_table(
TableName="dev_orders",
AttributeDefinitions=[
{"AttributeName": "id", "AttributeType": "S"},
{"AttributeName": "status", "AttributeType": "S"},
],
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 1},
GlobalSecondaryIndexes=[
{
"IndexName": "statusGSIndex",
"KeySchema": [{"AttributeName": "status", "KeyType": "HASH"}],
"Projection": {"ProjectionType": "ALL"},
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1,
},
},
],
# bắt buộc phải có khai báo này để sử dụng DynamoDB Streams cho bảng này
StreamSpecification={
"StreamEnabled": True,
"StreamViewType": "NEW_AND_OLD_IMAGES",
},
)
print(response)


create_orders()

Kiểm tra bảng được tạo bằng NoSQL Workbench for Amazon DynamoDB

dev_orders

Các bạn chú ý giá trị bôi vàng nhé. Đây là dòng dữ liệu sẽ được DynamoDB ghi lên đó. Khi ứng dụng của bạn lắng nghe dòng dữ liệu này thì bất kỳ hành động nào xảy ra trên bảng sẽ được ghi lên dòng dữ liệu và ứng dụng của chúng ta sẽ phát hiện được điều đó.

Định nghĩa Lambda lắng nghe dòng dữ liệu từ DynamoDB

Để lắng nghe dòng dữ liệu từ DynamoDB Streams bạn cần thêm serverless-offline-dynamodb-streams và cấu hìn serverless.yml như sau:

custom:
# ...
serverless-offline-dynamodb-streams:
endpoint: http://dynamodb:8000
accessKeyId: root
secretAccessKey: root
# ...
plugins:
- serverless-offline
- serverless-python-requirements
- serverless-offline-dynamodb-streams

Các bạn tham khảo bài viết Mô phỏng AWS Lambda & API Gateway bằng Serverless Offline để biết các viết API bằng Lambda nhé.

Trong bài viết này, dể thực hiện lắng nghe dòng dữ liệu, bạn định nghĩa hàm Lambda trong Serverless như sau:

jobs_order:
handler: src.jobs.order.lambda_handler
events:
- stream:
enabled: true
type: dynamodb
# đây là giá trị màu vàng tôi có đề cập ở trên
arn: arn:aws:dynamodb:ddblocal:000000000000:table/dev_orders/stream/2020-03-15T07:59:46.532
batchSize: 1

Thử viết Rest API ghi dữ liệu vào bảng và kiểm tra DynamoDB Streams

Các bạn định nghĩa một API như sau:

functions:
post_orders:
handler: src.api.post_orders.lambda_handler
events:
- http:
method: post
path: api/orders
cors: true

src/api/post_orders.py

import json
import logging
from datetime import datetime
from uuid import uuid4
import boto3

LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)


def lambda_handler(event, context):
headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
body = json.loads(event["body"])
dynamodb = boto3.client(
"dynamodb",
endpoint_url="http://localhost:8000",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
)
now = int(datetime.utcnow().timestamp())
body = dynamodb.put_item(
TableName="dev_orders",
Item={
"id": {"S": str(uuid4())},
"name": {"S": body["name"]},
"status": {"S": " "},
"created_at": {"N": str(now)},
"updated_at": {"N": str(now)},
},
)
return {
"statusCode": 200,
"headers": headers,
"body": json.dumps(body),
}

Các bạn thử post dữ liệu bằng Postman nhé post_orders

Các bạn để ý Terminal sau khi post dữ liệu nhé

terminal

Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết đã giúp các bạn có thể sử dùng Lambda và DynamoDB tốt hơn.