教學課程:使用 TypeScript 和 MongoDB 建置無伺服器 API
在本教學課程中,您將瞭解如何使用 Azure Functions 和 TypeScript 建立無伺服器 API,以將數據儲存在 MongoDB 中。 我們將引導您將應用程式部署至 Azure,使其可透過公用 HTTP 端點存取。
必要條件
安裝下列軟體:
- 建立免費的 Azure 訂用帳戶
- 安裝 Node.js LTS。 選取 Azure Functions 支援的版本。
- TypeScript 最新版本。
- 針對本機開發記憶體全域安裝的 Azurite
- 全域安裝的 Azure Functions Core Tools 最新版本用於本機開發。
- 安裝 Visual Studio Code ,並使用下列延伸模組:
解決方案架構
解決方案會使用 Azure Functions 應用程式來接收數據,然後從 Mongoose SDK 傳送至 Azure Cosmos DB。
開啟開發環境
在終端機或命令提示字元中,於本機系統上建立新資料夾,以作為 Azure Functions 專案的根目錄。
mkdir <YOUR-NEW_FOLDER-NAME>
變更為新的資料夾。
cd <YOUR-NEW_FOLDER-NAME>
在 Visual Studio Code 中開啟此資料夾。
code .
在 Visual Studio Code 中登入 Azure
- 開啟命令選擇區。
- 搜尋並選取
Azure: Sign in
。 完成向 Azure 驗證的步驟。
建立 Azure 資源群組
資源群組是以區域為基礎的資源集合。 藉由建立資源群組,然後在該群組中建立資源,在教學課程結束時,您可以刪除資源群組,而不需要個別刪除每個資源。
在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
在 [資源] 下尋找您的訂用帳戶,+然後選取圖示,然後選取 [建立資源群組]。
使用下表完成提示:
提示 值 輸入新資源群組的名稱。 azure-tutorial
選取新資源的位置。 選取靠近您的地理區域。
建立本機 Functions 應用程式
建立包含 HTTP 觸發程式函式的本機 Azure Functions(無伺服器)應用程式。
在 Visual Studio Code 中,開啟命令選擇區 (Ctrl + Shift + P)。
搜尋並選取 [Azure Functions:建立新專案 ]。
使用下表完成建立本機 Azure 函式專案:
提示 值 備註 選取將包含函式項目的資料夾 選取目前 (預設) 資料夾。 選取語言 TypeScript 選取 TypeScript 程式設計模型 模型 V4 為您專案的第一個函式選取範本 HTTP 觸發程序 使用 HTTP 要求叫用 API。 提供函式名稱 blogposts
API 路由為 /api/blogposts
當 Visual Studio Code 建立專案時,請在 檔案中
./src/functions/blogposts.ts
檢視您的 API 程式代碼。import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; export async function blogposts(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function processed request for url "${request.url}"`); const name = request.query.get('name') || await request.text() || 'world'; return { body: `Hello, ${name}!` }; }; app.http('blogposts', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: blogposts });
此程式代碼是新 v4 程式設計模型中的標準重複使用。 它不是用來指出使用 POST 和 GET 撰寫 API 層的唯一方法。
將先前的程式代碼取代為下列程式代碼,只允許 GET 要求傳回所有部落格文章。
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; // curl --location 'http://localhost:7071/api/blogposts' --verbose export async function getBlogPosts(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function getBlogPosts processed request for url "${request.url}"`); // Empty array for now ... will fix later const blogposts = []; return { status: 200, jsonBody: { blogposts } }; }; app.get('getBlogPosts', { route: "blogposts", authLevel: 'anonymous', handler: getBlogPosts });
此程式代碼有數個 Azure Functions Node.js v4 程式設計模型變更 ,您應該注意:
- 的函式名稱
getBlobPosts
,表示它是 GET 要求,將協助您隔離記錄中的函式。 - 屬性
route
設定為blogposts
,這是提供/api/blogposts
的預設 API 路由的一部分。 - 屬性
methods
已移除,而且不必要,因為app
物件的使用get
表示這是 GET 要求。
- 的函式名稱
啟動 Azurite 本機記憶體模擬器
在您的本機計算機上開發函式需要記憶體模擬器(免費)或 Azure 儲存體 帳戶(付費)。
在不同的終端機中 ,啟動 Azurite 本機記憶體模擬器。
azurite --silent --location ./azurite --debug ./azurite/debug.log
這是使用本機 Azure 儲存體 模擬器在本機執行 Azure Functions 的必要專案。
使用 AzureWebJobsStorage 屬性將檔案中指定的
local.settings.json
本機記憶體模擬器更新為 的值UseDevelopmentStorage=true
。{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "node", "AzureWebJobsFeatureFlags": "EnableWorkerIndexing" } }
執行本機無伺服器函式
在本機執行 Azure Functions 專案,以在部署至 Azure 之前進行測試。
在 Visual Studio Code 中,於 getBlogPosts 函式結尾的 語句上
return
設定斷點。在 Visual Studio Code 中,按 F5 啟動調試程式並附加至 Azure Functions 主機。 如果系統提示您,請啟用公用和私人端點。
您也可以使用 [偵錯>開始偵錯] 功能表命令。
輸出會出現在 終端 機面板中。
在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
在 [工作區] 區段中,尋找並展開 [本機專案 - Functions ->> getBlogPosts]。
以滑鼠右鍵按下函式名稱 getBlogPosts,然後選取 [複製函式 URL]。
在您的瀏覽器中,貼上並提交URL。
或者,您可以在終端機中使用下列 cURL 命令:
curl http://localhost:7071/api/blogposts --verbose
當調試程式在 Visual Studio Code 中停止時,您可以在 Variables-Local> 視窗中看到空白的部落格文章。 允許偵錯再次按 F5 以繼續超過該斷點。
空的部落格文章陣列回應會傳回為:
{ "blogposts": [] }
在 VS Code 中,停止調試程式 Shift + F5。
在 Visual Studio Code 中建立 Azure 函式應用程式
在本節中,您會在 Azure 訂用帳戶中建立函式應用程式雲端資源和相關資源。
在 Visual Studio Code 中,開啟命令選擇區 (Ctrl + Shift + P)。
搜尋並選取 Azure Functions:在 Azure 中建立函式應用程式 (進階) 。
提示中會提供下列資訊:
提示 選取項目 選取訂用帳戶 選取您的計費訂閱。 輸入函數應用程式的全域唯一名稱 輸入 URL 路徑中有效的名稱,例如 first-function
。 Postpend 3 個字元,讓 URL 成為全域唯一的。 您鍵入的名稱會經過驗證,確定其在 Azure Functions 中是唯一。選取主控方案 選擇 [ 取用]。 選取新資源的位置 選取您附近的地理位置。 選取執行階段堆疊 選擇最新的 LTS 版本。 選取OS 選擇 [Linux]。 選取主控方案 選擇 [ 取用]。 選取新資源的資源群組 選取您在上一個步驟中建立的資源群組。 選取儲存體帳戶 選取 [建立新的記憶體帳戶 ],並接受預設名稱。 選取應用程式的 Application Insights 資源。 選取 [建立新的 Application Insights 資源 ],並接受預設名稱。 等候通知確認應用程式已建立。
在 Visual Studio Code 中將 Azure 函式應用程式部署至 Azure
重要
部署至現有的函數應用程式一律會覆寫該應用程式在 Azure 中的內容。
- 選擇活動列中的 Azure 圖示,然後在 [資源] 區域中,以滑鼠右鍵按兩下您的函式應用程式資源,然後選取 [部署至函式應用程式]。
- 如果系統詢問您是否確定要部署,請選取 [部署]。
- 部署完成之後,通知會顯示數個選項。 選取 [ 檢視輸出 ] 以檢視結果。 如果您錯過通知,請選取右下角的鈴鐺圖示,以再次查看。
執行遠端無伺服器函式
在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
在 [資源] 區段中,展開您的 Azure 函式應用程式資源。 以滑鼠右鍵按下函式名稱,然後選取 [ 複製函式 URL]。
將 URL 貼到瀏覽器中。 當您在本機執行函式時,會傳回相同的空陣列。
{"blogposts":[]}
新增適用於 MongoDB API 整合的 Azure Cosmos DB
Azure Cosmos DB 提供 MongoDB API,以提供熟悉的整合點。
在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
在 [ 資源] 區段中,選取 + ,然後選取 [ 建立資料庫伺服器]。 使用下表完成建立新 Azure Cosmos DB 資源的提示。
提示 值 備註 選取 Azure 資料庫伺服器 適用於 MongoDB 的 Azure Cosmos DB API 提供 Azure Cosmos DB 帳戶名稱。 cosmosdb-mongodb-database
加上三個字元以建立唯一的名稱。 名稱會成為 API URL 的一部分。 定義容量模型。 Serverless
選取 [MongoDB 版本]。 選取最新版本。 選取新資源的資源群組。 選取您在上一個步驟中建立的資源群組。 選取您在上一節中建立的資源群組。 等候建立資源。 您可以在結果窗格的 [Azure] 區段中看到狀態。
安裝mongoose相依性
在 Visual Studio Code 終端機中,Ctrl + Shift + `,然後安裝 npm 套件:
npm install mongoose
新增部落格文章的mongoose程式代碼
在 Visual Studio Code 中,在 建立
./src/
名為 lib 的子目錄,建立名為./database.ts
的檔案,並將下列程式代碼複製到其中。import { Schema, Document, createConnection, ConnectOptions, model, set } from 'mongoose'; const connectionString = process.env.MONGODB_URI; console.log('connectionString', connectionString); const connection = createConnection(connectionString, { useNewUrlParser: true, useUnifiedTopology: true, autoIndex: true } as ConnectOptions); export interface IBlogPost { author: string title: string body: string } export interface IBlogPostDocument extends IBlogPost, Document { id: string created: Date } const BlogPostSchema = new Schema({ id: Schema.Types.ObjectId, author: String, title: String, body: String, created: { type: Date, default: Date.now } }); BlogPostSchema.set('toJSON', { transform: function (doc, ret, options) { ret.id = ret._id; delete ret._id; delete ret.__v; } }); export const BlogPost = model<IBlogPostDocument>('BlogPost', BlogPostSchema); connection.model('BlogPost', BlogPostSchema); export default connection;
在 Visual Studio Code 中
./src/functions/blogposts
,開啟 檔案,並以下列內容取代整個檔案的程式代碼:import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; import connection from '../lib/database'; // curl --location 'http://localhost:7071/api/blogposts' --verbose export async function getBlogPosts(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function getBlogPosts processed request for url "${request.url}"`); const blogposts = await connection.model('BlogPost').find({}); return { status: 200, jsonBody: { blogposts } }; }; app.get('getBlogPosts', { route: "blogposts", authLevel: 'anonymous', handler: getBlogPosts });
將 連接字串 新增至本機應用程式
在 Visual Studio Code 的 Azure 總管中,選取 [Azure Cosmos DB ] 區段,然後展開以滑鼠右鍵按下選取您的新資源。
選取 [複製 連接字串]。
在 Visual Studio Code 中,使用 [檔案總管] 開啟
./local.settings.json
。新增名為
MONGODB_URI
的新屬性,並貼上 連接字串的值。{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "node", "AzureWebJobsFeatureFlags": "EnableWorkerIndexing", "MONGODB_URI": "mongodb://...." } }
檔案中的
./local.settings.json
秘密:- 不會部署至 Azure,因為它包含在檔案中
./.funcignore
。 - 不會簽入原始檔控制,因為它包含在檔案中
./.gitignore
。
- 不會部署至 Azure,因為它包含在檔案中
將 連接字串 新增至遠端應用程式
- 在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
- 在 [資源] 區段中,尋找您的 Azure Cosmos DB 實例。 以滑鼠右鍵按兩下資源,然後選取 [ 複製連接字串]。
- 在相同的 [資源] 區段中,尋找您的函式應用程式並展開節點。
- 以滑鼠右鍵按兩下 [ 應用程式設定 ],然後選取 [ 新增設定]。
- 輸入應用程式設定名稱,
MONGODB_URI
然後選取 Enter。 - 貼上您複製的值,然後按 Enter 鍵。
新增用於建立、更新和刪除部落格文章的 API
在 Visual Studio Code 中,使用命令選擇區來尋找並選取 [Azure Functions:建立函式]。
選取 [HTTP 觸發程式 ] 並將其
blogpost
命名為 [單一]。將下列程式碼複製至 檔案。
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; import connection, { IBlogPost, IBlogPostDocument } from '../lib/database'; // curl -X POST --location 'http://localhost:7071/api/blogpost' --header 'Content-Type: application/json' --data '{"author":"john","title":"my first post", "body":"learn serverless node.js"}' --verbose export async function addBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function addBlogPost processed request for url "${request.url}"`); const body = await request.json() as IBlogPost; const blogPostResult = await connection.model('BlogPost').create({ author: body?.author, title: body?.title, body: body?.body }); return { status: 200, jsonBody: { blogPostResult } }; }; // curl -X PUT --location 'http://localhost:7071/api/blogpost/64568e727f7d11e09eab473c' --header 'Content-Type: application/json' --data '{"author":"john jones","title":"my first serverless post", "body":"Learn serverless Node.js with Azure Functions"}' --verbose export async function updateBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function updateBlogPost processed request for url "${request.url}"`); const body = await request.json() as IBlogPost; const id = request.params.id; const blogPostResult = await connection.model('BlogPost').updateOne({ _id: id }, { author: body?.author, title: body?.title, body: body?.body }); if(blogPostResult.matchedCount === 0) { return { status: 404, jsonBody: { message: 'Blog post not found' } }; } return { status: 200, jsonBody: { blogPostResult } }; }; // curl --location 'http://localhost:7071/api/blogpost/6456597918547e37d515bda3' --verbose export async function getBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function getBlogPosts processed request for url "${request.url}"`); console.log('request.params.id', request.params.id) const id = request.params.id; const blogPost = await connection.model('BlogPost').findOne({ _id: id }); if(!blogPost) { return { status: 404, jsonBody: { message: 'Blog post not found' } }; } return { status: 200, jsonBody: { blogPost } }; }; // curl --location 'http://localhost:7071/api/blogpost/6456597918547e37d515bda3' --request DELETE --header 'Content-Type: application/json' --verbose export async function deleteBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function deleteBlogPost processed request for url "${request.url}"`); const id = request.params.id; const blogPostResult = await connection.model('BlogPost').deleteOne({ _id: id }); if(blogPostResult.deletedCount === 0) { return { status: 404, jsonBody: { message: 'Blog post not found' } }; } return { status: 200, jsonBody: { blogPostResult } }; }; app.get('getBlogPost', { route: "blogpost/{id}", authLevel: 'anonymous', handler: getBlogPost }); app.post('postBlogPost', { route: "blogpost", authLevel: 'anonymous', handler: addBlogPost }); app.put('putBlogPost', { route: "blogpost/{id}", authLevel: 'anonymous', handler: updateBlogPost }); app.deleteRequest('deleteBlogPost', { route: "blogpost/{id}", authLevel: 'anonymous', handler: deleteBlogPost });
再次使用調試程序啟動本機函式。 下列 API 可供使用:
deleteBlogPost: [DELETE] http://localhost:7071/api/blogpost/{id} getBlogPost: [GET] http://localhost:7071/api/blogpost/{id} getBlogPosts: [GET] http://localhost:7071/api/blogposts postBlogPost: [POST] http://localhost:7071/api/blogpost putBlogPost: [PUT] http://localhost:7071/api/blogpost/{id}
使用 cURL 命令中的
blogpost
(單一) API 來新增一些部落格文章。curl -X POST --location 'http://localhost:7071/api/blogpost' --header 'Content-Type: application/json' --data '{"author":"john","title":"my first post", "body":"learn serverless node.js"}' --verbose
使用 cURL 命令中的
blogposts
(plural) API 來取得部落格文章。curl http://localhost:7071/api/blogposts --verbose
回應包含單一部落格文章的 JSON 陣列:
:[{"author":"john","title":"my first post","body":"learn serverless node.js","created":"2024-07-11T21:30:41.688Z","id":"66904f0148b2e4d8a2b9971e"}]}
。
使用適用於 Azure Cosmos DB 的 Visual Studio Code 擴充功能檢視所有數據
在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
在 [資源] 區段中,以滑鼠右鍵按兩下您的 Azure Cosmos DB 資料庫,然後選取 [重新整理]。
展開測試資料庫和部落格文章集合節點,以檢視檔。
選取其中一個專案,以檢視 Azure Cosmos DB 實例中的數據。
重新部署函式應用程式以包含資料庫程序代碼
- 在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
- 在 [資源] 區段中,以滑鼠右鍵按下您的 Azure 函式應用程式,然後選取 [部署至函式應用程式]。
- 在彈出視窗中,詢問您是否確定要部署,請選取 [ 部署]。
- 等到部署完成再繼續。
使用雲端式 Azure 函式
- 仍在 Azure 檔案總管的 [函式] 區域中,選取並展開您的函式,然後 選取 [函 式] 節點,其中會列出 API
- 以滑鼠右鍵按兩下其中一個 API,然後選取 [ 複製函式 URL]。
- 編輯先前的 cURL 命令,以使用遠端 URL,而不是本機 URL。 執行命令以測試遠端 API。
查詢您的 Azure 函式記錄
若要搜尋記錄,請使用 Azure 入口網站。
在 Visual Studio Code 中,選取 [Azure 總管],然後在 [函式] 底下,以滑鼠右鍵按兩下您的函式應用程式,然後選取 [在入口網站中開啟]。
這會開啟 Azure 函式 Azure 入口網站。
從 [設定] 中,選取 [Application Insights],然後選取 [ 檢視 Application Insights 數據]。
此連結會帶您前往使用 Visual Studio Code 建立 Azure 函式時為您建立的個別計量資源。
從 [監視] 區段中,選取 [記錄]。 選取 快顯右上角的 X 以關閉任何彈出視窗。
在 [新增查詢 1] 窗格中的 [數據表] 索引卷標上,按兩下追蹤數據表。
這會在查詢視窗中輸入 Kusto 查詢
traces
。將查詢模式從 簡單模式 變更為 KQL 模式。
編輯查詢以搜尋自訂記錄:
traces | where message startswith "***"
選取執行。
如果記錄檔未顯示任何結果,可能是因為 HTTP 要求與 Azure Function 與 Kusto 中的記錄可用性之間有幾分鐘的延遲。 請稍候幾分鐘,然後再次執行查詢。
您不需要執行任何額外動作,即可取得此記錄資訊:
- 程序代碼使用
context.log
Function 架構所提供的函式。 藉由使用context
,而不是console
,您可以將記錄篩選為特定的個別函式。 如果您的函式應用程式有許多函式,這會很有用。 - 函式應用程式已為您新增ApplicationInsights。
- Kusto 查詢工具包含在 Azure 入口網站 中。
- 您可以選取
traces
,而不必學習撰寫 Kusto 查詢 ,以從您的記錄取得最低資訊。
- 程序代碼使用
可用的原始程式碼
此 Azure 函式應用程式的完整原始碼:
清除資源
因為您使用了單一資源群組,因此您可以藉由刪除資源群組來刪除所有資源。
- 在 Visual Studio Code 中,選取主要側邊列中的 Azure 圖示或使用鍵盤快捷方式 (Shift + Alt + A) 來開啟 Azure 總管。
- 搜尋並選取 [Azure:依資源群組分組]。
- 以滑鼠右鍵按下選取您的資源群組,然後選取 [ 刪除資源群組]。
- 輸入資源組名以確認刪除。
後續步驟
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應