主题
Eden 条约遗留问题
¥Eden Treaty Legacy
注意
这是 Eden Treaty 1 或 (edenTreaty) 的文档
¥This is a documentation for Eden Treaty 1 or (edenTreaty)
对于新项目,我们建议从 Eden Treaty 2(条约)开始。
¥For a new project, we recommended starting with Eden Treaty 2 (treaty) instead.
Eden Treaty 是一个 Elysia 服务器的对象表示。
¥Eden Treaty is an object-like representation of an Elysia server.
提供像普通对象一样的访问器,直接从服务器获取类型,帮助我们加快速度,并确保不会出现任何问题
¥Providing accessor like a normal object with type directly from the server, helping us to move faster, and make sure that nothing break
要使用 Eden Treaty,首先导出你现有的 Elysia 服务器类型:
¥To use Eden Treaty, first export your existing Elysia server type:
typescript
// server.ts
import { Elysia, t } from 'elysia'
const app = new Elysia()
.get('/', () => '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
然后导入服务器类型,并在客户端使用 Elysia API:
¥Then import the server type, and consume the Elysia API on client:
typescript
// client.ts
import { edenTreaty } from '@elysiajs/eden'
import type { App } from './server'
const app = edenTreaty<App>('http://localhost:')
// response type: 'Hi Elysia'
const { data: pong, error } = app.get()
// response type: 1895
const { data: id, error } = app.id['1895'].get()
// response type: { id: 1895, name: 'Skadi' }
const { data: nendoroid, error } = app.mirror.post({
id: 1895,
name: 'Skadi'
})
提示
Eden Treaty 完全类型安全,并支持自动补齐功能。
¥Eden Treaty is fully type-safe with auto-completion support.
结构
¥Anatomy
Eden Treaty 会将所有现有路径转换为类似对象的表示形式,具体如下:
¥Eden Treaty will transform all existing paths to object-like representation, that can be described as:
typescript
EdenTreaty.<1>.<2>.<n>.<method>({
...body,
$query?: {},
$fetch?: RequestInit
})
路径
¥Path
Eden 会将 /
转换为 .
,后者可以通过已注册的 method
进行调用,例如:
¥Eden will transform /
into .
which can be called with a registered method
, for example:
/path -> .path
/nested/path -> .nested.path
路径参数
¥Path parameters
路径参数将根据其在 URL 中的名称自动映射。
¥Path parameters will be mapped automatically by their name in the URL.
/id/:id -> .id.
<anyThing>
例如: .id.hi
例如:.id['123']
提示
如果路径不支持路径参数,TypeScript 将显示错误。
¥If a path doesn't support path parameters, TypeScript will show an error.
查询
¥Query
你可以使用 $query
将查询附加到路径:
¥You can append queries to path with $query
:
typescript
app.get({
$query: {
name: 'Eden',
code: 'Gold'
}
})
获取
¥Fetch
Eden Treaty 是一个获取封装器,你可以通过将任何有效的 获取 参数传递给 $fetch
来向 Eden 添加它:
¥Eden Treaty is a fetch wrapper, you can add any valid Fetch parameters to Eden by passing it to $fetch
:
typescript
app.post({
$fetch: {
headers: {
'x-organization': 'MANTIS'
}
}
})
错误处理
¥Error Handling
Eden Treaty 将返回 data
和 error
的值,且均为完整类型。
¥Eden Treaty will return a value of data
and error
as a result, both fully typed.
typescript
// response type: { id: 1895, name: 'Skadi' }
const { data: nendoroid, error } = app.mirror.post({
id: 1895,
name: 'Skadi'
})
if(error) {
switch(error.status) {
case 400:
case 401:
warnUser(error.value)
break
case 500:
case 502:
emergencyCallDev(error.value)
break
default:
reportError(error.value)
break
}
throw error
}
const { id, name } = nendoroid
数据和错误都将被类型化为可空,直到你使用类型保护确认它们的状态。
¥Both data, and error will be typed as nullable until you can confirm their statuses with a type guard.
简单来说,如果获取成功,数据将具有值,错误将为空,反之亦然。
¥To put it simply, if fetch is successful, data will have a value and error will be null, and vice-versa.
提示
错误信息被 Error
包裹,服务器返回的错误值可以通过 Error.value
检索。
¥Error is wrapped with an Error
with its value return from the server can be retrieve from Error.value
基于状态的错误类型
¥Error type based on status
如果你在 Elysia 服务器中明确指定了错误类型,Eden Treaty 和 Eden Fetch 都可以根据状态码缩小错误类型的范围。
¥Both Eden Treaty and Eden Fetch can narrow down an error type based on status code if you explicitly provided an error type in the Elysia server.
typescript
// server.ts
import { Elysia, t } from 'elysia'
const app = new Elysia()
.model({
nendoroid: t.Object({
id: t.Number(),
name: t.String()
}),
error: t.Object({
message: t.String()
})
})
.get('/', () => 'Hi Elysia')
.get('/id/:id', ({ params: { id } }) => id)
.post('/mirror', ({ body }) => body, {
body: 'nendoroid',
response: {
200: 'nendoroid',
400: 'error',
401: 'error'
}
})
.listen(3000)
export type App = typeof app
客户端:
¥An on the client side:
typescript
const { data: nendoroid, error } = app.mirror.post({
id: 1895,
name: 'Skadi'
})
if(error) {
switch(error.status) {
case 400:
case 401:
// narrow down to type 'error' described in the server
warnUser(error.value)
break
default:
// typed as unknown
reportError(error.value)
break
}
throw error
}
WebSocket
Eden 使用与普通路由相同的 API 支持 WebSocket。
¥Eden supports WebSocket using the same API as a normal route.
typescript
// Server
import { Elysia, t } from 'elysia'
const app = new Elysia()
.ws('/chat', {
message(ws, message) {
ws.send(message)
},
body: t.String(),
response: t.String()
})
.listen(3000)
type App = typeof app
要开始监听实时数据,请调用 .subscribe
方法:
¥To start listening to real-time data, call the .subscribe
method:
typescript
// Client
import { edenTreaty } from '@elysiajs/eden'
const app = edenTreaty<App>('http://localhost:')
const chat = app.chat.subscribe()
chat.subscribe((message) => {
console.log('got', message)
})
chat.send('hello from client')
我们可以使用 schema 在 WebSocket 上强制执行类型安全,就像普通路由一样。
¥We can use schema to enforce type-safety on WebSockets, just like a normal route.
Eden.subscribe 返回 EdenWebSocket,它扩展了 WebSocket 类,并实现了类型安全。语法与 WebSocket 相同
¥Eden.subscribe returns EdenWebSocket which extends the WebSocket class with type-safety. The syntax is identical with the WebSocket
如果需要更多控制,可以访问 EdenWebSocket.raw 来与原生 WebSocket API 交互。
¥If more control is need, EdenWebSocket.raw can be accessed to interact with the native WebSocket API.
文件上传
¥File Upload
你可以将以下任一参数传递给要附加文件的字段:
¥You may either pass one of the following to the field to attach file:
文件
FileList
Blob
附加文件会导致 content-type 变为 multipart/form-data。
¥Attaching a file will results content-type to be multipart/form-data
假设我们的服务器如下:
¥Suppose we have the server as the following:
typescript
// server.ts
import { Elysia } from 'elysia'
const app = new Elysia()
.post('/image', ({ body: { image, title } }) => title, {
body: t.Object({
title: t.String(),
image: t.Files(),
})
})
.listen(3000)
export type App = typeof app
我们可以使用客户端,如下所示:
¥We may use the client as follows:
typescript
// client.ts
import { edenTreaty } from '@elysia/eden'
import type { Server } from './server'
export const client = edenTreaty<Server>('http://localhost:3000')
const id = <T extends HTMLElement = HTMLElement>(id: string) =>
document.getElementById(id)! as T
const { data } = await client.image.post({
title: "Misono Mika",
image: id<HTMLInputElement>('picture').files!,
})