前言
在前端與後端分離的時代,前端與後端之間的連接都需透過 API。除了 REST、GraphQL,也出現了 tRPC 這種 Type-safe 的方式。但如果考慮外部服務或 Mobile App,必須以串接文件為前提,選擇就變得有限。若還需要 Type-safe,選擇就更少(GraphQL:有人需要我嗎?)。
研究了一陣子後發現,除了 NestJS 與 GraphQL 提供方便的 Code-First 寫法,其他多為 Schema-First,維護起來並不方便。
ElysiaJs Eden
原本以為 Eden Treaty 與 tRPC 一樣僅利用了 TypeScript 的特性,等到撰寫文件時才發現並非如此。
Eden Treaty 保留了 REST 的特性,只要在伺服器端將整個 Elysia server type 匯出,程式碼如下:
Server side
// server.ts
import { Elysia, t } from 'elysia'
const app = new Elysia()
.get('/hi', () => 'Hi Elysia')
.get('/id/:id', ({ params: { id } }) => id)
.post('/mirror', ({ body }) => body, {
body: t.Object({
id: t.Number(),
name: t.String()
})
})
.listen(3000)
export type App = typeof app
Client side
然後在客戶端引入從伺服器端匯出的 App 類型:
import { env } from "$env/dynamic/public";
import { treaty } from "@elysiajs/eden";
import type { App } from "../../../crypto-backend/src/app";
const client = treaty<App>(env.PUBLIC_API_URL);
此時需設定後端 URL(env.PUBLIC_API_URL),並確保服務已啟動,接著 client 即可實現令人驚喜的功能。
Path
client 即可將後端 API 的所有路徑引入,包括多層路徑,原本使用 /,現在改為使用 .。原本為 /users/profile。
Query, Body, Params
取得得列表的參數 (query)
新增資料的 Body
甚至回傳的 response
都可以得到,完全可以省去查看文件的時間。
結論
但在撰寫時也遇到了一些問題,特別是列表(/)與動態路徑(/:id)的寫法。因為通常會拆分為不同的控制器,寫法如下:
export const transcation = new Elysia({
tags: ['Transcation'],
prefix: '/transcations',
})
.decorate('user', {} as JwtPayload)
.decorate('env', {} as Env)
.get(
'',
async ({ user, env, query }) => {.....})
.get(
'/:id',
async ({ user, env, params }) => {......})
如果是列表 /transcations,因為 prefix 已經設定,所以路徑應為 ''(空字串),不可寫成 /,否則會對應不到。
同樣,若是取單一資源 /transcations/<id></id>,路徑應寫為 /:id,否則看到文件中的範例會感到困惑。
目前我在前端使用 SvelteKit 進行全端開發,以串接後端 API。SvelteKit 的設計比 Next.js 簡單許多,寫起來也相當有趣。如果厭倦了 React 或 Vue,不妨試試 SvelteKit。
文字工作者,寫作時間常常在人類與電腦之間拉鋸,相信閱讀,相信文字與思想所構築的美麗境界