AWS Serverless RESTful APIs
近幾年在雲架構上討論熱度較高的 2 大主題分別是 容器化(Containerize)與 無伺服器(Serverless)架構。本篇文章會帶你瞭解什麼是 AWS 無伺服器架構,以及如何使用 Serverless Framework 這個框架,快速開發 RESTful APIs。
近幾年在雲架構上討論熱度較高的 2 大主題分別是 容器化(Containerize)與 無伺服器(Serverless)架構。本篇文章會帶你瞭解什麼是 AWS 無伺服器架構,以及如何使用 Serverless Framework 這個框架,快速開發 RESTful APIs。
- 什麼是 XaaS?
- AWS 無伺服器架構
- Serverless Framework
- CRUDable Service
- 基礎設施即程式碼
馬雲說過一句話:「過去的一百年,我們把人變成了機器,未來的一百年,我們將會把機器變成人。」

如果要用一句話來定義 XaaS(X as a Service)的話,那就是「萬物皆服務」。
舉凡 IaaS(基礎設施及服務)、PaaS(平台即服務)或 SaaS(軟體即服務)等⋯⋯,用一張圖來概括各個 XaaS 的關係:

身為程式設計師,我們就是那個負責寫服務來取代人類的存在。
所以能夠以最快速度開發出可驗證的服務原型,就成了開發者彼此之間的競爭條件之一,也是本篇文章的目的。
AWS 是目前雲技術的領頭羊,如果想要用 AWS 來開發 Serverless 的 RESTful APIs,主要會由 3 個核心服務所構成:
- AWS Lambda(Functions as a Service)
- Amazon API Gateway(REST API Endpoint as a Service)
- Amazon DynamoDB(NoSQL DB as a Service)

這裡不會贅述如何操作這些 AWS 服務,如果你已經熟悉這些服務,可以直接前往下一章的 Serverless Framework;如果還不知道它們是怎麼運作的,強烈建議先跑過一遍官方的幾篇教學與實作,理解這 3 個傢伙為什麼這麼酷,也才能理解為什麼接下來要介紹的 Serverless Framework 比它們更酷!
接下來進入本篇文章的重點,如果每次建立 REST API 都需要手動操作 Lambda、API Gateway 和 DynamoDB 這些服務的話就太麻煩了,所以這裡介紹一個第三方社群維護的框架:Serverless Framework。

Serverless Framework 提供了一整套專為「無伺服器架構」所設計的開發&部署工具,除了 AWS 之外還支援了 Google Cloud Platform 和 Microsoft Azure 等雲服務平台,讓開發者只需專注在 Web、Mobile 和 IoT 應用的核心功能,而無需煩惱基礎設施的維護。
先來跑一遍官方的快速入門暖暖身,首先安裝 Serverless 的 CLI:
$ npm install serverless --global注意:因為 Serverless CLI 會透過 AWS SDK 間接操作你的 AWS 帳戶,所以在開始之前,請先確認本機端已經配置完 AWS 的 credential,詳細設定方法可以參考官方文件的《Credentials》。
新增一個 Node.js 環境的專案 hello-service:
module.exports.hello = (event, context, callback) => { const response = { statusCode: 200, body: JSON.stringify({ message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }), };
callback(null, response);};部署 Lambda Function 至 AWS:
$ serverless deploy --verbose*--verbose** 顯示部署過程的細節。*
如果每次都需要重新部署所有 Lambda Function 會很沒效率,Serverless 也可以只部署單個 Lambda Function:
$ serverless deploy function --function hello*--function** 指定 Lambda Function 的名稱。*
部署完成之後,使用 invoke 執行 Lambda Function:
$ serverless invoke --function hello --log*--log** 顯示執行回傳的結果。*
{ "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}"}CLI 也可以使用 logs 和 --tail 來持續監控 Lambda Function 的執行結果:
$ serverless logs --function hello --tail瞭解整個 Serverless 的運作方式之後,CLI 還有提供 remove 指令,讓你可以將整個專案從 AWS 帳戶移除,畢竟佔用這些資源是要計費的:
$ serverless remove瞭解 Serverless Framework 如何輕易地部署 Lambda Function 之後,我們來看看 Serverless Framework 如何開發一套完整、可以 CRUD 資料庫的 REST API 服務。
下載 Serverless 社群寫好的 To-do CRUD 範例:
$ serverless install --url https://github.com/pmuens/serverless-crud注意:因為這個範例有用到其它第三方的 npm packages,所以部署前記得先 *npm install* 安裝。
部署 To-do service 至 AWS:
$ serverless deploy --verbose部署完成之後,CLI 會顯示你的 API Gateway endpoints:
...
endpoints: POST - https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos GET - https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos GET - https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos/{id} PUT - https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos/{id} DELETE - https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
...使用 cURL 或 HTTPie 之類的 HTTP client 來測試結果,首先是取得所有 to-dos:
$ http GET https://18055egsjj.execute-api.us-east-1.amazonaws.com/dev/todos沒意外的話,會回傳空的 To-do 陣列:
HTTP 200 OK
[]打開程式碼,Read all to-dos 的 Lambda Function 很簡單,透過 AWS SDK 的 DynamoDB scan API 來取得所有 todos table 的 items:
const AWS = require('aws-sdk');const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports = (event, callback) => { const params = { TableName: 'todos', };
return dynamoDb.scan(params, (error, data) => { if (error) { callback(error); } callback(error, data.Items); });};接著我們試著新增一筆 to-do:
$ http POST https://<省略>/todos body="Hodor"新增成功並回傳一筆包含 id、body 和 updateAt 欄位的 to-do item:
HTTP 200 OK
{ "body": "Hodor", "id": "1b691310-a0e7-11e7-b1c4-1d790783726f", "updatedAt": 1506230011585}Create 的 Lambda Function 也很好懂,透過 UUID 以及 DynamoDB 的 put API 新增一筆 to-do item:
...const uuid = require('uuid');
module.exports = (event, callback) => { const data = JSON.parse(event.body);
data.id = uuid.v1(); data.updatedAt = new Date().getTime();
const params = { TableName: 'todos', Item: data };
return dynamoDb.put(params, (error, data) => { if (error) { callback(error); } callback(error, params.Item); });};接著我們試著讀取剛才新增的 id 為 1b691310-a0e7-11e7-b1c4-1d790783726f 的 to-do:
$ http GET https://<省略>/todos/1b691310-a0e7-11e7-b1c4-1d790783726f
HTTP 200 OK{ "body": "Hodor", "id": "1b691310-a0e7-11e7-b1c4-1d790783726f", "updatedAt": 1506230011585}這裡要注意,DynamoDB 讀取所有 items 所使用 API 是 scan,讀取單筆 item 的 API 是 get:
...module.exports = (event, callback) => { const params = { TableName: 'todos', Key: { // 透過 event.pathParameters 來取得 URL 的 todo item id id: event.pathParameters.id } }; return dynamoDb.get(params, (error, data) => { if (error) { callback(error); } callback(error, data.Item); });};更新已經存在的單筆 to-do:
$ http PUT https://<省略>/todos/1b691310-a0e7-11e7-b1c4-1d790783726f body="Hold The Door"
HTTP 200 OK
{ "body": "Hold The Door", "id": "1b691310-a0e7-11e7-b1c4-1d790783726f", "updatedAt": 1506230311609}更新 DynamoDB item 的程式碼基本上與新增的方式差不多:
...module.exports = (event, callback) => { const data = JSON.parse(event.body); data.id = event.pathParameters.id; data.updatedAt = new Date().getTime(); const params = { TableName : 'todos', Item: data }; return dynamoDb.put(params, (error, data) => { if (error) { callback(error); } callback(error, params.Item); });};最後剩下刪除已存在的單筆 to-do:
$ http DELETE https://<省略>/todos/1b691310-a0e7-11e7-b1c4-1d790783726f
HTTP 200 OK
{ "id": "1b691310-a0e7-11e7-b1c4-1d790783726f"}刪除的 Lambda Function 程式碼:
module.exports = (event, callback) => { const params = { TableName : 'todos', Key: { id: event.pathParameters.id } }; return dynamoDb.delete(params, (error, data) => { if (error) { callback(error); } callback(error, params.Key); });};跑完上面的 to-do services 之後有沒有感到很神奇呢?竟然一個 serverless deploy 指令,就可以把原本需要手動配置的所有 AWS 基礎服務都自動建立好了,這是因為 Serverless Framework 用到了 AWS CloudFormation 這個服務。

我們先打開 serverless.yml 這個檔案來看看 Lambda Function 和 API Gateway 的配置:
functions: create: ... readAll: ... readOne: handler: handler.readOne events: - http: path: todos/{id} method: get ... update: ... delete: ...DynamoDB 的設定:
resources: Resources: TodosDynamoDbTable: ... Properties: AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id ... TableName: 'todos'這就是 Infrastructure as a Code(基礎設施即程式碼)的威力,它的好處:
- 隨地配置:直接使用程式碼控制你的基礎設施資源,忘記那些繁瑣的操作流程
- 易於維護:因為程式碼可以上版本控制,所以可以多人共同維護一份基礎設施
- 持續交付:因為可以進版本控制,所以更容易整合進 CI/CD 的自動化流程
- XaaS(萬物皆服務)是未來的趨勢,而服務的本質就是 API,誰能夠越快地開發出原型、越有機會成功
- 如果要用 AWS 的無伺服器架構來開發 REST API,核心服務分別是 Lambda、API Gateway 和 DynamoDB
- Serverless Framework 利用「基礎設施即程式碼」的概念,讓開發者可以更迅速地在 AWS 上建立一套可維護的 CRUD RESTful API Services