React Native X AWS 的 APP 初體驗(下)

web 仔想踏入雲端領域瞧瞧的第一步

Last Modified by YuKai on Friday, June 21, 2024

前言

這一篇要來講後端的部分,採用 node.ts + express,結合 AWS 資源的 RDS 和 S3,如果要看前端的部分,請見上篇。

Backend Build

路由劃分

後端的部分先在地端開發測試完後,才部屬到 EC2 上面,而由於這份專案,使用到了兩個身分,所以路由、Database 也拆分成兩部分來調用,如下:

// routes/admin.ts
import express from "express";
export const router = express.Router();
router.get('/get_seller', async (req, res) => {
    const seller = await adminDB.query_seller();
    res.json(seller);
  }
);
// server.ts
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import { router as admin_router } from "./routes/admin";

const app = express();

app.use(cors());
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use("/admin", admin_router);

這樣當我要調用這個路由底下的子路由,可以這麼做 http://server_ip/admin/get_seller,這樣就能發送到 routes 資料夾底下 admin.ts 中註冊的 /get_seller,這樣我就可以劃分更詳細的路由階層,而不會使得路由名過長

AWS RDS

連接 AWS RDS 其實跟地端的方法差不多,只要提供資料庫的資訊就可以了,這邊透過創建連接池來控制連接的數量,和提供資料庫資訊

const pool: any = mysql.createPool({
  connectionLimit: 5,
  host: process.env.RDS_HOSTNAME,
  user: process.env.RDS_USERNAME,
  password: process.env.RDS_PASSWORD,
  port: parseInt(process.env.RDS_PORT as string),
  database: process.env.RDS_DATABASE,
});

而我使用連結池的意義在於,一開始我不太確定資料庫的計費方式,所以以為是當連接資料庫時才開始計費,這變向來說,只要我一直連著,但卻沒有對資料庫有任何操作,豈不是很浪費,所以才使用連接池來控制連接數,只要指令結束就取消連接,但後來才知道其實是根據 RDS 的配置來計費的,所以就算一直連著也不會比較貴,反而沒有去連接才是最浪費的,因此現在連接池對我來說的意義變成是為了可以更好的去控管每次的連接,限制同時連接的量而做的進階配置。

AWS S3

關於 S3 的配置就比較複雜,首先需要下載 aws-sdk,並於本機安裝 aws-cli。

npm i aws-sdk

關於 AWS CLI 的安裝方式請參考這網址

接著使用 aws cli 的指令,進行設定,來生成 .aws 資料

aws configure

好了之後,在 .aws 底下有兩個檔案 config 和 crendentials,前者是 aws 設定,後者就是儲存可使用身分。

記得要將使用的身分加入 crendentiasl,才能讓 aws cli 去核對身分,不然會出現 aws record not found 錯誤。

最後在安裝 multer-s3 用於上傳檔案到 s3,這只是因為我比較習慣 multer 對檔案的處理方式,所以才安裝這個插件,如果要單純靠 client-s3 用指令上傳也是可以的

npm i multer-s3

都安裝好後,就可以開始寫程式。

  1. 創建 S3 Client S3 Client 用來創建一個身分來訪問 S3,可以看到下面 credentials 中有三個屬性,而這可以從 IAM 獲取,但由於我是 Learner Lab 的使用者,沒有足夠權限創建 IAM Role,只能拿 Learner Lab 提供為期四小時的臨時身分,所以每過四小時,我都需要重新配置有關身分的環境變數,另外這個臨時身分也要記得加進 .aws/crendentials 內,不然會找不到

    const s3Config = new S3Client({
        region: process.env.AWS_REGION as string,
        credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
            sessionToken: process.env.AWS_SESSION_TOKEN as string
        }
    })
    
  2. 使用 multer-s3 上傳檔案 注意這邊的 key 是指上傳到 s3 的檔案名稱,這邊是用 uuidv4 生成

    const uploadToS3 = multer({
        storage: multerS3({
            s3: s3Config,
            bucket: BUCKET_NAME,
            metadata: (req, file, cb) => {
                cb(null, {fieldName: file.fieldname});
            },
            key: (req, file, cb) => {
                cb(null, uuidv4().toString());
            }
        })
    })
    
  3. 從 S3 獲取圖片 由於直接從 S3 點擊儲放的圖片連結,會因為沒有身分導致沒有權限訪問,所以我們需要將身分帶入連結參數,我使用 s3-request-presigner 協助生成一個帶有身分的臨時連結,這個連結就能有效地訪問到圖片

import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const getImageFromS3 = async (params: {key: string}) => {
    const getObjectParams = {
        Bucket: BUCKET_NAME,
        Key: params.key
    }
    const command = new GetObjectCommand(getObjectParams);
    const url = await getSignedUrl(s3Config, command, { expiresIn: 3600 });
    return url;
}

以上基本上就是後端需要寫的程式,接著講講 AWS 上的配置

AWS Setting

基本上沒有特別需求就不用調整太多設定,只是要記得把公有請求打開,或是把本機 ip 加入安全群組內,主要講一下 S3 對於身分權限的配置

S3 Bucket 創好之後,還需要額外新增 policy 和開放 CORS,前者是對於身分權限的控制、後者是允許跨網域請求。

image

上圖為 policy 的簡易設置,重點在於 Statement 內的屬性

  • Sid:任意取即可,用來辨別這個 policy 負責什麼
  • Effect:身分權限的控制
  • Principal:允許什麼樣的身分,圖上配置為不限制
  • Action:身分能做的事
  • Resource:身分可以訪問的 bucket
{
    "Version": "",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3::[bucketname]"
        }
    ]
}

image

[
    {
        "AllowedHeaders":[""],
        "AllowedMethods":["HEAD", "GET"],
        "AllowedOrigins":[""],
        "ExposeHeaders":[]
    }
]

總結

這一次的開發,讓我對 React 生態圈有更多的認識,也發現雲端資源的有趣之處,希望在這之後就時大雲端時代,全部地端跑完之後,搬上雲端實現網路自由,所以很感謝雲端課的 Kate 老師很慷慨地給我這麼多的額度,讓我可以不用顧忌金錢,持續挖掘成為更有能力的開發者。


comments powered by Disqus