Vercel serverless stack for low cost
Vercel 最近大力推動無伺服器。它在Vercel Ship虛擬活動中宣布了一整套服務,其中最大的重磅炸彈是存儲——Postgres、KV 和Blob——所有這些都是“從前端開發人員的角度重新想像的”,而且最重要的是,所有服務都是無伺服器的。
TL;DR 的意思如下:
-
Vercel Postgres – 由Neon支援的 PostgreSQL 資料庫,易於設置,易於與任何 Postgres 用戶端整合(在 Vercel 儀表板中自動生成環境變數),您可以為其編寫 SQL(作為字串),並且可以使用它只是HTTP 。不需要 TCP 連接,而且它是可管理的、可擴展的(甚至在不使用時為零)並且容錯啟動。
-
Vercel KV – 與 Redis 相容的鍵值存儲,由Upstash提供支援,可在 Vercel 邊緣站點使用,並可與任何 Redis 用戶端一起使用。
-
Vercel Blob – 可存取、簡化的 Amazon S3 相容解決方案,用於上傳、儲存和提供二進位文件,由Cloudflare R2提供支持,但無需配置儲存桶、權限或擺弄 SDK 的複雜性。有一個 REST API。
這並不完全令人震驚——Vercel 在嚴格無伺服器上下了很大的賭注,而在無伺服器/邊緣工作的儲存(或者,至少有狀態)解決方案仍然有點像狂野的西部場景。Vercel 在這裡所做的就是用易於使用的API 包裝現有的服務提供者(NeonDB、Upstash 和Cloudflare R2),為前端開發人員提供DX 方面的「最佳實踐即服務」解決方案,就像他們所做的那樣與 React 的 NextJS 一起使用。
一方面,我明白了。我感受到了前端開發人員無操作、DX 增強的工作流程的吸引力,以及將部署網站/Web 應用程式(包括儲存和安全性)的過程簡化為幾次點擊的好處。
另一方面……這些標記相當糟糕,而且考慮到這三個服務一開始整合起來有多容易,添加的 DX 並不是那麼重要。
那麼還有什麼選擇呢?因為 AWS Amplify 確實不是這樣。
讓我們來討論一下,看看一些替代方案,也許還討論如何讓 DX 變得更容易,同時免費或非常接近免費。
磁帶的故事
首先,讓我們看看您支付的保費到底有多糟糕。
這個問題在數字上已經很明顯了,但以下是您需要了解的一些關鍵事項。
-
對於 Vercel Postgres,您將按計算時間付費,而不是按發出的請求付費(與許多其他提供者一樣)。他們還按月收取實際數據傳輸費用!(這將是 SQL 查詢數據,所以不是一個瘋狂的數量,但它就在那裡。)
-
對於 Vercel KV,您再次需要為實際資料傳輸付費,但幸運的是不需要計算時間。
-
Vercel Blob 定價目前處於測試階段,但就目前情況而言,您將需要支付 0.15 美元/GB 的出口費用(在您獲得免費的 1 GB 後),而 R2 不收取任何費用。此外,Vercel 上的儲存成本(每 GB)增加了一倍。
我理解您不想自行設置包括 CDN 和 CI/CD 管道在內的所有基礎設施……但這三個服務都是 SaaS 提供商,它們的設計初衷是讓您可以輕鬆地自行集成,無論您是否使用Next .js/React 與否。
那為什麼要支付保費呢?讓我們看看如何使用這些相同的服務(無伺服器),並以類型安全、DX 增強的方式,使用 WunderGraph 來實現它們:
- 無供應商鎖定
- 無加價成本
- 類型安全、增強型 DX
- 輕鬆的本地開發,使用任何選擇的框架。
WunderGraph – 快速入門
WunderGraph是一款免費開源(Apache 2.0 授權)全端開發工具,可讓您以聲明方式將資料依賴項(微服務、資料庫和第 3 方 API)轉變為透過JSON-RPC公開的安全、統一且可擴充的API 。
但資料並不是您的專案必須處理的唯一依賴項,這就是為什麼 WunderGraph 還可以整合用於文件上傳的 S3 相容儲存、OIDC/非 OIDC 相容身份驗證提供者等。
然後,它將所有這些聚合並抽象化為命名空間虛擬圖,然後您可以使用GraphQL 或TypeScript 定義對它們的操作,並透過類型安全資料取得掛鉤(如果使用基於React 的框架)從前端存取它們,或者僅透過呼叫 JSON-RPC 端點。
最後,WunderGraph 可以作為獨立伺服器運行,也可以與您選擇的框架(Next.js、Remix、Nuxt、SvelteKit 等)一起運行。WunderGraph 的主伺服器(WunderNode)需要部署到支援 Docker 化映像的平台(例如 Fly.io),或僅採用其完全託管選項 – WunderGraph Cloud。
使用 WunderGraph 作為 Vercel 儲存的替代方案
讓我們使用 WunderGraph 來製作我們自己的 Vercel Storage 替代方案,使用完全相同的提供者 – 用於 PostgreSQL 的 Neon、用於 Redis 的 Upstash 以及用於 blob 儲存的 Cloudflare R2。
先決條件
首先,我們將使用其 Next.js 快速入門範本設定 WunderGraph:
npx create-wundergraph-app my-project -E nextjs
然後,cd 進入my-project
(或您使用的任何名稱),然後:
npm i && npm start
如果您看到http://localhost:3000在瀏覽器上打開,並顯示範例查詢的結果,那麼您就已準備就緒!
1.Postgres 與 Neon
首先,建立一個 Neon 資料庫:
- 登入Neon,如果您還沒有帳戶,請註冊。
- 從控制台建立一個專案。
- 建立一個資料庫,並使用種子 SQL 填入它。或者,使用預設的 neondb 資料庫(我們將使用後者,特別是其 Customer 表)。
與任何 Postgres 提供者一樣,您需要一個 TCP 連接字串,從「連接詳細資訊」小部件複製該字串(直接連接,但如果從 Neon 儀表板上訪問,建議使用池連接。
Neon 透過角色提供存取權限。建立一個,並記下密碼。現在,您的連接字串將類似於postgres://ROLE:[[email protected]](/cdn-cgi/l/email-protection)/neondb.
👉如果您使用的是 Wundergraph Cloud,請遵循此便捷指南,以便更輕鬆地將 Neon 與 WunderGraph 集成,自動生成帶有密碼的角色。
現在,我們必須將其作為依賴項新增至 WunderGraph 配置中(不要忘記命名空間!),並將其包含在apis
依賴項數組中。
./.wundergraph/操作/wundergraph.config.ts
const neondb = introspect.postgresql({
apiNamespace: "neondb",
databaseURL: "postgres://ROLE:[email protected]/neondb"
})
configureWunderGraphApplication({
apis: [neondb],
...
})
現在,當我們儲存時,WunderGraph 的伺服器 (WunderNode) 將內省此資料來源,並產生我們可以在其上定義的所有資料操作,包括所需的所有類型定義。為了取得我們想要的數據,我們必須為此編寫一個簡單的 GraphQL 操作。
./.wundergraph/作業/AllCustomers.graphql
query AllCustomers {
customers: neondb_findManycustomer{
id
first_name
last_name
email
phone
ip_address
username
}
}
您會注意到我們已經對這裡的所有欄位進行了自動填充,現在 WunderGraph 已經對其進行了內省。DX 完勝!
一旦您點擊“儲存”,WunderGraph 將再次開始工作,產生我們可以在前端使用的類型安全資料以獲取掛鉤。
索引.tsx
import { useQuery, withWunderGraph, } from "../components/generated/nextjs"; const Home: NextPage = () => { // postgres from Neon const { data: customerData, isLoading } = useQuery({ operationName: "AllCustomers", }); return ( ... {!isLoading &&
{JSON.stringify(customerData, null, 2)}}
...
)export default withWunderGraph(Home);
由於比較懶,只是在此處列印 JSON 回應,但您可以按照自己喜歡的方式視覺化此資料。如果需要的話,將其放入中!
但這還不是全部。
Vercel 的 Postgres 用戶端有這個漂亮的功能,它實際上允許您將 SQL 查詢編寫為字串。
import { sql } from "@vercel/postgres"; import { redirect } from "next/navigation"; async function create(formData: FormData) { "use server"; const { rows } = await sql` INSERT INTO products (name) VALUES (${formData.get("name")}) `; redirect(
/product/${rows[0].slug}
); }想要同樣的開發體驗嗎?一起讓它成為現實!
Neon 支援對資料庫進行 SQL 查詢,我們可以像 Vercel 一樣使用它。WunderGraph 對我們資料庫的內省表明,我們可以為此使用 queryRawJSON 查詢(您可以在 wundergraph.schema.graphql 檔案中查看生成的虛擬圖的架構)。
為此,首先建立一個 GraphQL 操作來執行原始 SQL 查詢。
./.wundergraph/操作/AllCustomersSQL.graphql
query AllCustomers ($query: String!) { customers: neondb_queryRawJSON(query: $query) }
非常簡單 – 接收 SQL 字串,以 JSON 格式輸出資料。當然,我們不能真正信任它在客戶端運行,因此請將其帶到伺服器。
輸入TypeScript 操作!使用這些,您可以編寫自訂 TypeScript 函數(本質上是非同步解析器)來執行資料操作。它們完全在伺服器上運行,並且永遠不會暴露在客戶端,因此您的應用程式保持安全。另外,您甚至可以在其中使用現有的 GraphQL 操作——這正是我們要做的。
這些 TypeScript 操作是什麼樣的呢?它們只是操作目錄中的命名空間 .ts 文件,與 GraphQL 操作相同。在這裡,我在“neon”目錄中創建一個 .ts 文件,但命名由您決定。只要確保你堅持下去即可。
./.wundergraph/操作/neon/get.ts
](#./.wundergraph/operations/neon/get.ts)
import { createOperation } from "../../generated/wundergraph.factory"; export default createOperation.query({ handler: async ({ operations }) => { const customers = await operations.query({ operationName: "AllCustomersSQL", input: { query: "SELECT * FROM CUSTOMER", }, }); return { customers: customers?.data?.customers, }; }, });
請注意,我們
AllCustomersSQL.graphql
在這裡引用該文件。💡 如果您根本不想編寫 GraphQL,您可以在此處匯入並使用任何 postgres 用戶端,等待 SQL 操作(就像在 API 處理程序或伺服器函數中一樣),然後根據需要傳回資料。
然後,我們可以像以前一樣透過 GraphQL 操作在客戶端透過鉤子使用它。
索引.tsx
... const { data: customerData, isLoading: pgIsLoading } = useQuery({ operationName: "neon/get", }); ...
2. Redis 與 Upstash
將 Upstash 與 WunderGraph 或多或少相同,只是我們將完全依賴伺服器端 TypeScript 操作,使用 @upstash/redis 函式庫。
設定相當簡單——很像 Redis 本身。使用您的 Amazon、Github 或 Google 帳戶登入/註冊…或只是常規電子郵件/密碼組合。
然後,建立一個資料庫,為其輸入名稱,然後選擇資料庫將運行的區域。為了獲得最佳效能,請選擇您的應用程式更接近的區域 – 儘管您也可以轉到全域並選擇主要寫入 + 主要讀取區域的組合。
點擊「建立」按鈕後,您的叢集應該會啟動並運行,並且您可以使用任何 Redis 用戶端連接到資料庫。如前所述,為了簡單起見,我們將使用 Upstash 自己的@upstash/redis函式庫。
npm i @upstash/redis
為了可重複使用性,我們建立並匯出一個 Redis 客戶端實例。
./lib/redis.ts
](#./lib/redis.ts)
import { redis } from "../../../lib/redis"; import { createOperation } from "../../generated/wundergraph.factory"; export default createOperation.query({ handler: async () => { const data = await redis.get("A_REDIS_KEY_HERE"); return { data, }; }, });
像往常一樣,使用鉤子來存取客戶端的資料。
./pages/index.tsx
](#./pages/index.tsx)
import { useQuery, withWunderGraph, } from "../components/generated/nextjs"; const Home: NextPage = () => { // redis from Upstash const { data: redisData, isLoading } = useQuery({ operationName: "redis/get", }); return ( ... {!isLoading &&
{JSON.stringify(redisData, null, 2)}...
)
export default withWunderGraph(Home);3. 使用 Cloudflare R2 進行 Blob 存儲
最後,對於二進位存儲,使用 WunderGraph,您可以將檔案上傳到任何 S3 相容的儲存供應商,例如 AWS S3、Minio、DigitalOcean Spaces 或我們在這裡使用的Cloudflare R2。
首先,登入/註冊 Cloudflare,在您的帳戶上設定 R2 計劃,然後建立儲存桶。複製您的存取金鑰 ID 和您的秘密,然後返回 WunderGraph 配置,您需要透過配置即程式碼定義提供者。
./.wundergraph//wundergraph.config.ts
const cloudflareR2 = { name: "cloudflareR2", endpoint: "YOUR_CLOUDFLARE_USER_ID.r2.cloudflarestorage.com", accessKeyID: "YOUR_ACCESS_KEY_ID", secretAccessKey: "YOUR_SECRET_ACCESS_KEY", bucketLocation: "", // not all S3 providers require this bucketName: "YOUR_BUCKET_NAME", useSSL: true, // you'll always want SSL enabled for cloud storage uploadProfiles: { // profile for a user's 'avatar' picture avatar: { maxAllowedUploadSizeBytes: 1024 * 1024 * 10, // 10 MB, optional, defaults to 25 MB maxAllowedFiles: 1, // limit the number of files to 1, leave undefined for unlimited files allowedMimeTypes: ["image/png", "image/jpeg"], // wildcard is supported, e.g. 'image/ *', leave empty/undefined to allow all allowedFileExtensions: ["png", "jpg"], // leave empty/undefined to allow all}z requireAuthentication: false, // WunderGraph only lets authenticated users upload files but for this demonstration, use this to override it }, }, }; configureWunderGraphApplication({ ... s3UploadProvider: [cloudflareR2], ... })
為上傳創建S3 配置文件(例如專門用於上傳頭像圖像的配置文件)在技術上是可選的,但我無論如何都將其包含在此處,因為它對於定義可以上傳和不能上傳的規則(和限制)非常有用。
在客戶端,我們可以使用
useFileUpload
鉤子來處理向我們定義的提供者的上傳。在下面的程式碼中,我們將附加文件清單儲存在 中files
,並在提交時建立一個FormData
包含所有files
已提交文件的對象,並將它們上傳到我們配置的S3 提供者(和儲存桶),並使用給定的參數設定檔名稱avatar
,確保使用特定的設定檔。./pages/index.tsx
import { useState } from "react"; import { useFileUpload, withWunderGraph, } from "../components/generated/nextjs"; const Home: NextPage = () => { // s3 compatible blob via cloudflare r2 const [files, setFiles] = useState
(); // list of files that were attached const [uploadData, setUploadData] = useState ([]); // an array to keep track of uploaded file names const { upload } = useFileUpload(); // WunderGraph autogenerated hook for file uploads // handle files being added via input on form const onFileChange = (e: React.ChangeEvent ) => { if (e.target.files) setFiles(e.target.files); }; // handle form submission const onSubmit = async (e: React.FormEvent ) => { e.preventDefault(); // if no files were attached. if (!files) { alert("No files!"); return; } // otherwise, handle upload try { const result = await upload({ provider: "cloudflareR2", // specify name of S3 provider profile: "avatar", // specify which S3 profile we want files, // files attached }); result && setUploadData(result); // set data with array of uploaded filenames if successful } catch (e) { // if unsuccessful, show error messages const msg = e instanceof Error ? e.message : "Upload failed!"; alert(msg); console.error("Couldn't upload files : ", msg); } }; return ( ... Files uploaded : {JSON.stringify(uploadData, null, 2)}...
)export default withWunderGraph(Home);
夥計們,這就是全部!
結論
關於現代網頁開發,沒有人警告過你什麼?你必須戴很多很多帽子。專案管理、UI/UX 設計、前端 + 後端邏輯、基礎架構、測試、安全性,以及設定和管理持久性儲存。
對於除企業開發人員之外的任何人來說,身兼所有這些角色並試圖靠專案謀生將很難平衡。DX 可以像 Vercel 的產品一樣簡化全端開發,這是天賜之物,我很高興它的存在 – 我只是希望它不必付出如此高昂的代價。
他們會很快修改這個定價嗎?這似乎就是業界的情緒。
但同時,如果您可以使用 WunderGraph 作為 API 網關/BFF 伺服器來獲得出色的 devex,那麼:
-
以輕鬆、聲明式、類型安全的方式為您整合這些服務,並使用自動生成的掛鉤來查詢/更改來自前端的數據,
-
提供所有 DX 優勢,同時處理基本 Neon/Upstash/Cloudflare R2 定價,無加價,
-
不僅相容於 Next.js/React,
-
並且全部使用開源技術,沒有供應商鎖定,
那為什麼要繳 Vercel 稅呢?
如何開始使用 WunderGraph
WunderGraph 的入門既簡單又免費,因為它是在 Apache 2.0 許可證下作為開源工具提供的。立即開始您的 WunderGraph 之旅,只需按照我們的1 分鐘快速入門指南操作即可