Skip to content
Our Sponsors
Open in Anthropic

title: 从 Fastify 迁移 - ElysiaJS prev: text: '快速开始' link: '/quick-start' next: text: '教程' link: '/tutorial' head: - - meta - property: 'og:title' content: 从 Fastify 迁移 - ElysiaJS

- - meta
  - name: 'description'
    content: 本指南面向 Fastify 用户,展示 Fastify 与 Elysia 的差异(包括语法),并通过示例演示如何将应用程序从 Fastify 迁移到 Elysia。

- - meta
  - property: 'og:description'
    content: 本指南面向 Fastify 用户,展示 Fastify 与 Elysia 的差异(包括语法),并通过示例演示如何将应用程序从 Fastify 迁移到 Elysia。

从 Fastify 到 Elysia

本指南面向 Fastify 用户,展示 Fastify 与 Elysia 的差异(包括语法),并通过示例演示如何将应用程序从 Fastify 迁移到 Elysia。

Fastify 是一个为 Node.js 设计的快速、低开销 Web 框架,旨在简单易用。它基于 HTTP 模块构建,提供了一组功能,使构建 Web 应用程序变得简单。

Elysia 是一个为 Bun、Node.js 和支持 Web 标准 API 的运行时设计的人体工程学 Web 框架。它注重健全的类型安全和性能,设计为人体工程学且对开发者友好。

性能

得益于原生 Bun 实现和静态代码分析,Elysia 相比 Fastify 有显著的性能提升。

  1. Elysia
    2,454,631 reqs/s
  2. Fastify

    415,600

以请求/秒为单位测量。结果来自 TechEmpower 基准测试 第 22 轮 (2023-10-17) 的 PlainText 测试

路由

Fastify 和 Elysia 有类似的路由语法,使用 app.get()app.post() 方法定义路由,路径参数语法也相似。

ts
import fastify from 'fastify'

const app = fastify()

app.get('/', (request, reply) => {
    res.send('Hello World')
})

app.post('/id/:id', (request, reply) => {
    reply.status(201).send(req.params.id)
})

app.listen({ port: 3000 })

Fastify 使用 requestreply 作为请求和响应对象

ts
import { Elysia } from 'elysia'

const app = new Elysia()
    .get('/', 'Hello World')
    .post(
    	'/id/:id',
     	({ status, params: { id } }) => {
      		return status(201, id)
      	}
    )
    .listen(3000)

Elysia 使用单一的 context 并直接返回响应

在风格指南上有一些细微差异,Elysia 推荐使用方法链和解构赋值。

如果您不需要使用上下文,Elysia 还支持为响应使用内联值。

处理器

两者都有类似的属性来访问输入参数,如 headersqueryparamsbody,并自动将请求体解析为 JSON、URL 编码数据和表单数据。

ts
import fastify from 'fastify'

const app = fastify()

app.post('/user', (request, reply) => {
    const limit = request.query.limit
    const name = request.body.name
    const auth = request.headers.authorization

    reply.send({ limit, name, auth })
})

Fastify 解析数据并将其放入 request 对象

ts
import { Elysia } from 'elysia'

const app = new Elysia()
	.post('/user', (ctx) => {
	    const limit = ctx.query.limit
	    const name = ctx.body.name
	    const auth = ctx.headers.authorization

	    return { limit, name, auth }
	})

Elysia 解析数据并将其放入 context 对象

子路由

Fastify 使用函数回调来定义子路由,而 Elysia 将每个实例视为可以即插即用的组件。

ts
import fastify, { FastifyPluginCallback } from 'fastify'

const subRouter: FastifyPluginCallback = (app, opts, done) => {
	app.get('/user', (request, reply) => {
		reply.send('Hello User')
	})
}

const app = fastify()

app.register(subRouter, {
	prefix: '/api'
})

Fastify 使用函数回调来声明子路由

ts
import { Elysia } from 'elysia'

const subRouter = new Elysia({ prefix: '/api' })
	.get('/user', 'Hello User')

const app = new Elysia()
	.use(subRouter)

Elysia 将每个实例视为组件

Elysia 在构造函数中设置前缀,而 Fastify 要求您在选项中设置前缀。

验证

Elysia 内置了对请求验证的支持,使用 TypeBox 提供开箱即用的健全类型安全,而 Fastify 使用 JSON Schema 声明模式,并使用 ajv 进行验证。

但是,Fastify 不会自动推断类型,您需要使用像 @fastify/type-provider-json-schema-to-ts 这样的类型提供器来推断类型。

ts
import fastify from 'fastify'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'

const app = fastify().withTypeProvider<JsonSchemaToTsProvider>()

app.patch(
	'/user/:id',
	{
		schema: {
			params: {
				type: 'object',
				properties: {
					id: {
						type: 'string',
						pattern: '^[0-9]+$'
					}
				},
				required: ['id']
			},
			body: {
				type: 'object',
				properties: {
					name: { type: 'string' }
				},
				required: ['name']
			},
		}
	},
	(request, reply) => {
		// 将字符串映射为数字
		request.params.id = +request.params.id

		reply.send({
			params: request.params,
			body: request.body
		})
	}
)

Fastify 使用 JSON Schema 进行验证

ts
import { 
Elysia
,
t
} from 'elysia'
const
app
= new
Elysia
()
.
patch
('/user/:id', ({
params
,
body
}) => ({
params
,
body
}), {
params
:
t
.
Object
({
id
:
t
.
Number
()
}),
body
:
t
.
Object
({
name
:
t
.
String
()
}) })
ts
import { 
Elysia
} from 'elysia'
import {
z
} from 'zod'
const
app
= new
Elysia
()
.
patch
('/user/:id', ({
params
,
body
}) => ({
params
,
body
}), {
params
:
z
.
object
({
id
:
z
.
number
()
}),
body
:
z
.
object
({
name
:
z
.
string
()
}) })
ts
import { 
Elysia
} from 'elysia'
import * as
v
from 'zod'
const
app
= new
Elysia
()
.
patch
('/user/:id', ({
params
,
body
}) => ({
params
,
body
}), {
params
:
v
.
object
({
id
:
v
.
number
()
}),
body
:
v
.
object
({
name
:
v
.
string
()
}) })

Elysia 使用 TypeBox 进行验证,并自动强制转换类型。同时支持各种验证库,如 Zod、Valibot,语法相同。

或者,Fastify 也可以使用 TypeBoxZod 进行验证,使用 @fastify/type-provider-typebox 自动推断类型。

虽然 Elysia 偏好 TypeBox 进行验证,但 Elysia 也支持标准模式,允许您开箱即用地使用 Zod、Valibot、ArkType、Effect Schema 等库。

文件上传

Fastify 使用 fastify-multipart 处理文件上传,底层使用 Busboy,而 Elysia 使用 Web 标准 API 处理表单数据,使用声明式 API 进行 MIME 类型验证。

但是,Fastify 没有提供直接的文件验证方式,例如文件大小和 MIME 类型,需要一些变通方法来验证文件。

ts
import fastify from 'fastify'
import multipart from '@fastify/multipart'

import { fileTypeFromBuffer } from 'file-type'

const app = fastify()
app.register(multipart, {
	attachFieldsToBody: 'keyValues'
})

app.post(
	'/upload',
	{
		schema: {
			body: {
				type: 'object',
				properties: {
					file: { type: 'object' }
				},
				required: ['file']
			}
		}
	},
	async (req, res) => {
		const file = req.body.file
		if (!file) return res.status(422).send('No file uploaded')

		const type = await fileTypeFromBuffer(file)
		if (!type || !type.mime.startsWith('image/'))
			return res.status(422).send('File is not a valid image')

		res.header('Content-Type', type.mime)
		res.send(file)
	}
)

Fastift 使用 fastify-multipart 处理文件上传,并伪造 type: object 以允许 Buffer

ts
import { Elysia, t } from 'elysia'

const app = new Elysia()
	.post('/upload', ({ body }) => body.file, {
		body: t.Object({
			file: t.File({
				type: 'image'
			})
		})
	})

Elysia 使用 t.File 处理文件和 MIME 类型验证

由于 multer 不验证 MIME 类型,您需要使用 file-type 或类似库手动验证 MIME 类型。

而 Elysia 验证文件上传,并自动使用 file-type 验证 MIME 类型。

生命周期事件

Fastify 和 Elysia 都有类似的生命周期事件,使用基于事件的方法。

Elysia 生命周期

Elysia 的生命周期事件可以如下图所示。 Elysia Life Cycle Graph

点击图片放大

Fastify 生命周期

Fastify 生命周期事件也使用与 Elysia 类似的基于事件的方法。

两者都有类似的语法来拦截请求和响应生命周期事件,但是 Elysia 不需要您调用 done 来继续生命周期事件。

ts
import fastify from 'fastify'

const app = fastify()

// 全局中间件
app.addHook('onRequest', (request, reply, done) => {
	console.log(`${request.method} ${request.url}`)

	done()
})

app.get(
	'/protected',
	{
		// 路由特定的中间件
		preHandler(request, reply, done) {
			const token = request.headers.authorization

			if (!token) reply.status(401).send('Unauthorized')

			done()
		}
	},
	(request, reply) => {
		reply.send('Protected route')
	}
)

Fastify 使用 addHook 注册中间件,并需要您调用 done 来继续生命周期事件

ts
import { Elysia } from 'elysia'

const app = new Elysia()
	// 全局中间件
	.onRequest(({ method, path }) => {
		console.log(`${method} ${path}`)
	})
	// 路由特定的中间件
	.get('/protected', () => 'protected', {
		beforeHandle({ status, headers }) {
  			if (!headers.authorizaton)
     			return status(401)
		}
	})

Elysia 自动检测生命周期事件,不需要您调用 done 来继续生命周期事件

健全的类型安全

Elysia 被设计为具有健全的类型安全。

例如,您可以使用 deriveresolve类型安全的方式自定义上下文,而 Fastify 不支持。

ts
import 
fastify
from 'fastify'
const
app
=
fastify
()
app
.
decorateRequest
('version', 2)
app
.
get
('/version', (
req
,
res
) => {
res
.
send
(
req
.
version
)
Property 'version' does not exist on type 'FastifyRequest<RouteGenericInterface, Server<typeof IncomingMessage, typeof ServerResponse>, IncomingMessage, ... 4 more ..., ResolveFastifyRequestType<...>>'.
})
app
.
get
(
'/token', {
preHandler
(
req
,
res
,
done
) {
const
token
=
req
.
headers
.
authorization
if (!
token
) return
res
.
status
(401).
send
('Unauthorized')
// @ts-ignore
req
.token =
token
.
split
(' ')[1]
done
()
} }, (
req
,
res
) => {
req
.
version
Property 'version' does not exist on type 'FastifyRequest<RouteGenericInterface, Server<typeof IncomingMessage, typeof ServerResponse>, IncomingMessage, ... 4 more ..., ResolveFastifyRequestType<...>>'.
res
.
send
(
req
.
token
)
Property 'token' does not exist on type 'FastifyRequest<RouteGenericInterface, Server<typeof IncomingMessage, typeof ServerResponse>, IncomingMessage, ... 4 more ..., ResolveFastifyRequestType<...>>'.
} )
app
.
listen
({
port
: 3000
})

Fastify 使用 decorateRequest 但不提供健全的类型安全

ts
import { 
Elysia
} from 'elysia'
const
app
= new
Elysia
()
.
decorate
('version', 2)
.
get
('/version', ({
version
}) =>
version
)
.
resolve
(({
status
,
headers
: {
authorization
} }) => {
if(!
authorization
?.
startsWith
('Bearer '))
return
status
(401)
return {
token
:
authorization
.
split
(' ')[1]
} }) .
get
('/token', ({
token
,
version
}) => {
version
return
token
})

Elysia 使用 decorate 扩展上下文,使用 resolve 向上下文添加自定义属性

虽然 Fastify 可以使用 declare module 扩展 FastifyRequest 接口,但它是全局可用的,没有健全的类型安全,并且不保证该属性在所有请求处理器中都可用。

ts
declare module 'fastify' {
  	interface FastifyRequest {
    	version: number
  		token: string
  	}
}

这是上述 Fastify 示例工作所必需的,它不提供健全的类型安全

中间件参数

Fastify 使用函数返回 Fastify 插件来定义命名中间件,而 Elysia 使用 定义自定义钩子。

ts
import 
fastify
from 'fastify'
import type {
FastifyRequest
,
FastifyReply
} from 'fastify'
const
app
=
fastify
()
const
role
=
(
role
: 'user' | 'admin') =>
(
request
:
FastifyRequest
,
reply
:
FastifyReply
,
next
: Function) => {
const
user
=
findUser
(
request
.
headers
.
authorization
)
if (
user
.
role
!==
role
) return
reply
.
status
(401).
send
('Unauthorized')
// @ts-ignore
request
.user =
user
next
()
}
app
.
get
(
'/token', {
preHandler
:
role
('admin')
}, (
request
,
reply
) => {
reply
.
send
(
request
.user)
Property 'user' does not exist on type 'FastifyRequest<RouteGenericInterface, Server<typeof IncomingMessage, typeof ServerResponse>, IncomingMessage, ... 4 more ..., ResolveFastifyRequestType<...>>'.
} )

Fastify 使用函数回调接受中间件的自定义参数

ts
import { 
Elysia
} from 'elysia'
const
app
= new
Elysia
()
.
macro
({
role
: (
role
: 'user' | 'admin') => ({
resolve
({
status
,
headers
: {
authorization
} }) {
const
user
=
findUser
(
authorization
)
if(
user
.
role
!==
role
)
return
status
(401)
return {
user
} } }) }) .
get
('/token', ({
user
}) =>
user
, {
role
: 'admin'
})

Elysia 使用宏向自定义中间件传递自定义参数

虽然 Fastify 使用函数回调,但它需要返回一个函数来放置在事件处理器中,或者返回一个表示钩子的对象,当需要多个自定义函数时可能难以处理,因为您需要将它们协调到单个对象中。

错误处理

Fastify 和 Elysia 都提供了生命周期事件来处理错误。

ts
import fastify from 'fastify'

const app = fastify()

class CustomError extends Error {
	constructor(message: string) {
		super(message)
		this.name = 'CustomError'
	}
}

// 全局错误处理器
app.setErrorHandler((error, request, reply) => {
	if (error instanceof CustomError)
		reply.status(500).send({
			message: 'Something went wrong!',
			error
		})
})

app.get(
	'/error',
	{
		// 路由特定的错误处理器
		errorHandler(error, request, reply) {
			reply.send({
				message: 'Only for this route!',
				error
			})
		}
	},
	(request, reply) => {
		throw new CustomError('oh uh')
	}
)

Fastify 使用 setErrorHandler 作为全局错误处理器,使用 errorHandler 作为路由特定的错误处理器

ts
import { 
Elysia
} from 'elysia'
class
CustomError
extends
Error
{
// 可选:自定义 HTTP 状态码
status
= 500
constructor(
message
: string) {
super(
message
)
this.
name
= 'CustomError'
} // 可选:应该发送给客户端的内容
toResponse
() {
return {
message
: "If you're seeing this, our dev forgot to handle this error",
error
: this
} } } const
app
= new
Elysia
()
// 可选:注册自定义错误类 .
error
({
CUSTOM
:
CustomError
,
}) // 全局错误处理器 .
onError
(({
error
,
code
}) => {
if(
code
=== 'CUSTOM')
return {
message
: 'Something went wrong!',
error
} }) .
get
('/error', () => {
throw new
CustomError
('oh uh')
}, { // 可选:路由特定的错误处理器
error
({
error
}) {
return {
message
: 'Only for this route!',
error
} } })

Elysia 提供自定义错误代码、状态码的简写形式以及用于将错误映射到响应的 toResponse

虽然两者都提供使用生命周期事件的错误处理,但 Elysia 还提供:

  1. 自定义错误代码
  2. 映射 HTTP 状态码的简写形式和用于将错误映射到响应的 toResponse

错误代码对于日志记录和调试很有用,在区分扩展同一类的不同错误类型时很重要。

Elysia 提供所有这些功能并具有类型安全,而 Fastify 不支持。

封装

Fastify 封装插件的副作用,而 Elysia 通过显式作用域机制和代码顺序让您控制插件的副作用。

ts
import fastify from 'fastify'
import type { FastifyPluginCallback } from 'fastify'

const subRouter: FastifyPluginCallback = (app, opts, done) => {
	app.addHook('preHandler', (request, reply) => {
		if (!request.headers.authorization?.startsWith('Bearer '))
			reply.code(401).send({ error: 'Unauthorized' })
	})

	done()
}

const app = fastify()
	.get('/', (request, reply) => {
		reply.send('Hello World')
	})
	.register(subRouter)
	// 没有来自 subRouter 的副作用
	.get('/side-effect', () => 'hi')

Fastify 封装插件的副作用

ts
import { Elysia } from 'elysia'

const subRouter = new Elysia()
	.onBeforeHandle(({ status, headers: { authorization } }) => {
		if(!authorization?.startsWith('Bearer '))
			return status(401)
   	})

const app = new Elysia()
    .get('/', 'Hello World')
    .use(subRouter)
    // 没有来自 subRouter 的副作用
    .get('/side-effect', () => 'hi')

Elysia 封装插件的副作用,除非明确声明

两者都有插件的封装机制来防止副作用。

但是,Elysia 可以通过声明作用域来明确说明哪些插件应该有副作用,而 Fastify 总是封装它。

ts
import { Elysia } from 'elysia'

const subRouter = new Elysia()
	.onBeforeHandle(({ status, headers: { authorization } }) => {
		if(!authorization?.startsWith('Bearer '))
			return status(401)
   	})
	// 作用域到父实例但不超过
	.as('scoped') 

const app = new Elysia()
    .get('/', 'Hello World')
    .use(subRouter)
    // 现在有来自 subRouter 的副作用
    .get('/side-effect', () => 'hi')

Elysia 提供 3 种作用域机制:

  1. local - 仅应用于当前实例,无副作用(默认)
  2. scoped - 副作用作用域到父实例但不超过
  3. global - 影响所有实例

由于 Fastify 不提供作用域机制,我们需要:

  1. 为每个钩子创建函数并手动附加它们
  2. 使用高阶函数,并将其应用于需要效果的实例

但是,如果不小心处理,这可能会导致重复的副作用。

ts
import fastify from 'fastify'
import type {
	FastifyRequest,
	FastifyReply,
	FastifyPluginCallback
} from 'fastify'

const log = (request: FastifyRequest, reply: FastifyReply, done: Function) => {
	console.log('Middleware executed')

	done()
}

const app = fastify()

app.addHook('onRequest', log)
app.get('/main', (request, reply) => {
	reply.send('Hello from main!')
})

const subRouter: FastifyPluginCallback = (app, opts, done) => {
	app.addHook('onRequest', log)

	// 这将记录两次
	app.get('/sub', (request, reply) => {
		return reply.send('Hello from sub router!')
	})

	done()
}

app.register(subRouter, {
	prefix: '/sub'
})

app.listen({
	port: 3000
})

在这种情况下,Elysia 提供了插件去重机制来防止重复的副作用。

ts
import { Elysia } from 'elysia'

const subRouter = new Elysia({ name: 'subRouter' }) 
	.onBeforeHandle(({ status, headers: { authorization } }) => {
		if(!authorization?.startsWith('Bearer '))
			return status(401)
   	})
	.as('scoped')

const app = new Elysia()
	.get('/', 'Hello World')
	.use(subRouter)
	.use(subRouter) 
	.use(subRouter) 
	.use(subRouter) 
	// 副作用只调用一次
	.get('/side-effect', () => 'hi')

通过使用唯一的 name,Elysia 将只应用插件一次,不会导致重复的副作用。

Fastify 使用 @fastify/cookie 解析 cookie,而 Elysia 内置支持 cookie,并使用基于信号的方法处理 cookie。

ts
import fastify from 'fastify'
import cookie from '@fastify/cookie'

const app = fastify()

app.use(cookie, {
	secret: 'secret',
	hook: 'onRequest'
})

app.get('/', function (request, reply) {
	request.unsignCookie(request.cookies.name)

	reply.setCookie('name', 'value', {
      	path: '/',
      	signed: true
    })
})

Fastify 使用 unsignCookie 验证 cookie 签名,使用 setCookie 设置 cookie

ts
import { Elysia } from 'elysia'

const app = new Elysia({
	cookie: {
		secret: 'secret'
	}
})
	.get('/', ({ cookie: { name } }) => {
		// 签名验证自动处理
		name.value

		// cookie 签名自动签名
		name.value = 'value'
		name.maxAge = 1000 * 60 * 60 * 24
	})

Elysia 使用基于信号的方法处理 cookie,签名验证自动处理

OpenAPI

两者都提供使用 Swagger 的 OpenAPI 文档,但是 Elysia 默认使用 Scalar UI,这是 OpenAPI 文档的更现代、用户友好的界面。

ts
import fastify from 'fastify'
import swagger from '@fastify/swagger'

const app = fastify()
app.register(swagger, {
	openapi: '3.0.0',
	info: {
		title: 'My API',
		version: '1.0.0'
	}
})

app.addSchema({
	$id: 'user',
	type: 'object',
	properties: {
		name: {
			type: 'string',
			description: 'First name only'
		},
		age: { type: 'integer' }
	},
	required: ['name', 'age']
})

app.post(
	'/users',
	{
		schema: {
			summary: 'Create user',
			body: {
				$ref: 'user#'
			},
			response: {
				'201': {
					$ref: 'user#'
				}
			}
		}
	},
	(req, res) => {
		res.status(201).send(req.body)
	}
)

await fastify.ready()
fastify.swagger()

Fastify 使用 @fastify/swagger 进行使用 Swagger 的 OpenAPI 文档

ts
import { 
Elysia
,
t
} from 'elysia'
import {
openapi
} from '@elysiajs/openapi'
const
app
= new
Elysia
()
.
use
(
openapi
())
.
model
({
user
:
t
.
Array
(
t
.
Object
({
name
:
t
.
String
(),
age
:
t
.
Number
()
}) ) }) .
post
('/users', ({
body
}) =>
body
, {
body
: 'user',
response
: {
201: 'user' },
detail
: {
summary
: 'Create user'
} })

Elysia 使用 @elysiajs/swagger 进行默认使用 Scalar 的 OpenAPI 文档,或可选使用 Swagger

两者都提供使用 $ref 进行 OpenAPI 文档的模型引用,但是 Fastify 不提供类型安全,也不提供指定模型名称的自动完成,而 Elysia 支持。

测试

Fastify 内置支持使用 fastify.inject() 模拟网络请求进行测试,而 Elysia 使用 Web 标准 API 进行实际请求。

ts
import fastify from 'fastify'
import request from 'supertest'
import { describe, it, expect } from 'vitest'

function build(opts = {}) {
  	const app = fastify(opts)

  	app.get('/', async function (request, reply) {
	    reply.send({ hello: 'world' })
	})

  	return app
}

describe('GET /', () => {
	it('should return Hello World', async () => {
  		const app = build()

		const response = await app.inject({
		    url: '/',
		    method: 'GET',
	  })

		expect(res.status).toBe(200)
		expect(res.text).toBe('Hello World')
	})
})

Fastify 使用 fastify.inject() 模拟网络请求

ts
import { Elysia } from 'elysia'
import { describe, it, expect } from 'vitest'

const app = new Elysia()
	.get('/', 'Hello World')

describe('GET /', () => {
	it('should return Hello World', async () => {
		const res = await app.handle(
			new Request('http://localhost')
		)

		expect(res.status).toBe(200)
		expect(await res.text()).toBe('Hello World')
	})
})

Elysia 使用 Web 标准 API 处理实际请求

或者,Elysia 还提供了一个名为 Eden 的辅助库,用于端到端类型安全,允许我们使用自动完成和完全类型安全进行测试。

ts
import { 
Elysia
} from 'elysia'
import {
treaty
} from '@elysiajs/eden'
import {
describe
,
expect
,
it
} from 'bun:test'
const
app
= new
Elysia
().
get
('/hello', 'Hello World')
const
api
=
treaty
(
app
)
describe
('GET /', () => {
it
('should return Hello World', async () => {
const {
data
,
error
,
status
} = await
api
.
hello
.
get
()
expect
(
status
).
toBe
(200)
expect
(
data
).
toBe
('Hello World')
}) })

端到端类型安全

Elysia 使用 Eden 内置支持端到端类型安全,无需代码生成,而 Fastify 不提供。

ts
import { 
Elysia
,
t
} from 'elysia'
import {
treaty
} from '@elysiajs/eden'
const
app
= new
Elysia
()
.
post
('/mirror', ({
body
}) =>
body
, {
body
:
t
.
Object
({
message
:
t
.
String
()
}) }) const
api
=
treaty
(
app
)
const {
data
,
error
} = await
api
.
mirror
.
post
({
message
: 'Hello World'
}) if(
error
)
throw
error
console
.
log
(
data
)

如果端到端类型安全对您很重要,那么 Elysia 是正确的选择。


Elysia 提供了更符合人体工程学和开发者友好的体验,专注于性能、类型安全和简洁性,而 Fastify 是 Node.js 的成熟框架之一,但不具备下一代框架提供的健全的类型安全端到端类型安全

如果您正在寻找一个易于使用、具有出色开发者体验并基于 Web 标准 API 构建的框架,Elysia 是您的正确选择。

或者,如果您来自不同的框架,可以查看: