TypeScript 中的資料建模
Deno KV 和相關的雲端原語 API,例如佇列和 cron,目前為實驗性質,且可能會變更。儘管我們盡力確保資料耐用性,但資料遺失仍有可能發生,特別是在 Deno 更新時。
使用 KV 的 Deno 程式在啟動程式時需要 --unstable
旗標,如下所示
deno run -A --unstable my_kv_code.ts
在 TypeScript 應用程式中,通常需要建立強類型、文件編寫良好的物件來包含應用程式運作的資料。使用 介面 或 類別,您可以描述程式中物件的形狀和行為。
不過,如果您使用 Deno KV,則需要多做一些工作才能保留和擷取強類型物件。在本指南中,我們將介紹處理強類型物件進出 Deno KV 的策略。
使用介面和類型斷言
在 Deno KV 中儲存和擷取應用程式資料時,您可能想要先使用 TypeScript 介面來描述資料的形狀。以下是描述部落格系統中一些主要元件的物件模型
export interface Author {
username: string;
fullName: string;
}
export interface Post {
slug: string;
title: string;
body: string;
author: Author;
createdAt: Date;
updatedAt: Date;
}
此物件模型描述部落格文章和相關作者。
使用 Deno KV,您可以使用這些 TypeScript 介面,例如 資料傳輸物件 (DTO),這是強類型包裝器,可用於傳送至或從 Deno KV 接收的未類型化物件。
無需任何額外工作,您便可以輕鬆地將其中一個 DTO 的內容儲存在 Deno KV 中。
import { Author } from "./model.ts";
const kv = await Deno.openKv();
const a: Author = {
username: "acdoyle",
fullName: "Arthur Conan Doyle",
};
await kv.set(["authors", a.username], a);
然而,從 Deno KV 擷取這個相同的物件時,預設不會有與其關聯的型別資訊。不過,如果你知道為金鑰儲存的物件形狀,你可以使用 型別斷言 來告知 TypeScript 編譯器關於物件的形狀。
import { Author } from "./model.ts";
const kv = await Deno.openKv();
const r = await kv.get(["authors", "acdoyle"]);
const ac = r.value as Author;
console.log(ac.fullName);
你也可以為 get
指定一個選用的 型別參數。
import { Author } from "./model.ts";
const kv = await Deno.openKv();
const r = await kv.get<Author>(["authors", "acdoyle"]);
console.log(r.value.fullName);
對於較簡單的資料結構,這個技巧可能就夠用了。但通常,你會想要或需要在建立或存取你的網域物件時套用一些商業邏輯。當有這個需求時,你可以開發一組純粹函式,它們可以在你的 DTO 上運作。
使用服務層封裝商業邏輯
當你的應用程式的持久性需求變得更複雜時,例如當你需要建立 次要索引 來透過不同的金鑰查詢你的資料,或維護物件之間的關聯時,你會想要建立一組函式,放在你的 DTO 上方,以確保傳遞的資料是有效的(而不仅仅是正確的型別)。
從我們上面的商業物件,Post
物件夠複雜,很可能需要一小段程式碼來儲存和擷取物件的執行個體。以下是兩個函式的範例,它們包裝了底層的 Deno KV API,並為 Post
介面傳回強型別的物件執行個體。
值得注意的是,我們需要儲存 Author
物件的識別碼,以便我們稍後可以從 KV 擷取作者資訊。
import { Author, Post } from "./model.ts";
const kv = await Deno.openKv();
interface RawPost extends Post {
authorUsername: string;
}
export async function savePost(p: Post): Promise<Post> {
const postData: RawPost = Object.assign({}, p, {
authorUsername: p.author.username,
});
await kv.set(["posts", p.slug], postData);
return p;
}
export async function getPost(slug: string): Promise<Post> {
const postResponse = await kv.get(["posts", slug]);
const rawPost = postResponse.value as RawPost;
const authorResponse = await kv.get(["authors", rawPost.authorUsername]);
const author = authorResponse.value as Author;
const post = Object.assign({}, postResponse.value, {
author,
}) as Post;
return post;
}
這個薄層使用 RawPost
介面,它延伸實際的 Post
介面,以包含一些額外的資料,用於參照另一個索引中的資料(關聯的 Author
物件)。
savePost
和 getPost
函式取代直接的 Deno KV get
或 set
操作,以便它們可以適當地序列化和「補充」模型物件,並為我們提供適當的類型和關聯。