Hedystia 1.10 - Type Mastery
Type-Safe Route Testing, Conditional Routes, Real-Time WebSockets, and Framework-Agnostic Type Generation
Hedystia 1.10 is all about developer experience and type safety. We've introduced powerful new features that make testing routes easier, provide more control over which routes are active, support real-time connections via WebSocket, and enable type-safe APIs with any framework through standalone type generation.
🧪 Built-In Route Testing Framework
One of the most requested features was the ability to test routes without leaving your route definitions. Hedystia 1.10 introduces the test property for all HTTP methods.
See the complete guide in Route Testing.
const app = new Hedystia()
.get('/users/:id', ({ params }) => ({ id: params.id, name: 'John' }), {
params: h.object({ id: h.number().coerce() }),
response: h.object({ id: h.number(), name: h.string() }),
test: async ({ createRequest, expect }) => {
const { response, statusCode } = await createRequest({
params: { id: 123 },
})
expect(response.id).toBe(123)
expect(statusCode).toBe(200)
},
})Run all tests with await app.runTests() for a formatted report with timings and assertion counts.
The test context provides:
createRequest()— Make requests to your route with params, query, body, and headersexpect()— Create assertions with rich matchers:toBe(),toEqual(),toContain(),toBeGreaterThan(),toMatch(), and moreassert(),assertEqual(),assertThrows()— Additional assertion utilities
🎛️ Conditional Route Registration
The new .if() method allows you to conditionally register routes at runtime based on feature flags, environment variables, or any other condition.
See the complete guide in Conditional Routes.
const ENABLE_BETA = process.env.ENABLE_BETA === 'true'
const ENABLE_ADMIN = process.env.ENABLE_ADMIN === 'true'
const app = new Hedystia()
.get('/', () => 'Welcome')
.if((app) => {
if (ENABLE_BETA) {
return app
.get('/beta/features', () => ({ features: ['new feature'] }), {
response: h.object({ features: h.string().array() }),
})
}
})
.if((app) => {
if (ENABLE_ADMIN) {
return app
.get('/admin/users', () => [], {
response: h.object({ id: h.number() }).array(),
})
.post('/admin/users/:id/ban', () => ({ ok: true }), {
params: h.object({ id: h.number().coerce() }),
response: h.object({ ok: h.boolean() }),
})
}
})Type-safe conditional routes: Routes registered inside .if() are still fully type-safe. The client knows about conditionally-registered routes at compile time.
Use cases:
- Feature flags — A/B testing and gradual rollouts
- Environment-specific endpoints — Dev/staging/production differences
- Plugin-based routing — Optional extension routes
- Tier-based APIs — Premium vs free tier features
🔌 Real-Time WebSocket Support in Client
Hedystia now has first-class WebSocket support. Server-side WebSocket registration works alongside HTTP routes:
const app = new Hedystia()
.ws('/chat', {
open: (ws) => {
ws.send('Welcome to chat!')
},
message: (ws, msg) => {
ws.publish('chat', `User sent: ${msg}`)
},
})And now the client can connect to WebSockets with full type safety:
const client = createClient<typeof app>('http://localhost:3000')
const connection = client.chat.ws((ws) => {
ws.onConnect(() => {
console.log('Connected!')
ws.send('Hello server!')
})
ws.onMessage((message) => {
console.log('Received:', message)
})
ws.onError((error) => {
console.error('Error:', error)
})
ws.onDisconnect(() => {
console.log('Disconnected')
})
})
// Send messages
connection.ws.send('Hello')
// Disconnect when done
connection.disconnect()The client includes automatic reconnection with exponential backoff, ensuring reliable real-time communication.
🏗️ Framework-Agnostic Type Generation
The @hedystia/types package provides standalone type generation that works with any framework. This allows you to generate fully-typed Hedystia clients for any web framework.
See the complete guide in Types Plugin.
import { generateTypes } from '@hedystia/types'
const routes = [
{
method: 'GET',
path: '/users/:id',
params: { id: 'number' },
response: { id: 'number', name: 'string' },
},
]
await generateTypes(routes, './api.d.ts')Use your generated types with Express:
// express-server.ts
import express from 'express'
import { generateTypes } from '@hedystia/types'
const routes = [
{
method: 'GET',
path: '/users/:id',
params: { id: 'number' },
response: { id: 'number', name: 'string' },
},
]
await generateTypes(routes, './api.d.ts')
const app = express()
app.get('/users/:id', (req, res) => {
res.json({ id: 1, name: 'John' })
})
app.listen(3000)Then use the typed client:
type AppRoutes = [
{ method: 'GET'; path: '/users/:id'; response: { id: number; name: string } }
]
const client = createClient<AppRoutes>('http://localhost:3000')
const { data } = await client.users.id(1).get()
console.log(data?.name) // ✓ Fully typed!This works perfectly with:
- Express — Classic Node.js framework
- Hono — Modern lightweight framework
- Fastify — High-performance alternative
- Any REST API — If you can describe it, we can type it
📦 Other Notable Changes
Client Improvements
- WebSocket manager for connection handling
- Improved connection state management
- Better error handling and reconnection logic
Server Improvements
isBunHTMLBundle()detection for serving Bun HTML imports- Improved
determineContentType()for better content negotiation - Support for returning raw values in static routes (string, JSON, etc.)
- Better handling of async route handlers
Assertion Methods
The test framework now includes comprehensive assertion methods:
- Equality:
toBe(),toEqual(),toStrictEqual() - Truthiness:
toBeTruthy(),toBeFalsy(),toBeNull(),toBeDefined(),toBeUndefined(),toBeNaN(),toBeFinite(),toBeInfinite() - Numbers:
toBeGreaterThan(),toBeLessThan(),toBeGreaterThanOrEqual(),toBeLessThanOrEqual(),toBeCloseTo() - Containment:
toContain(),toHaveLength(),toHaveProperty(),toMatch() - Instances:
toBeInstanceOf() - Functions:
toThrow(),toHaveBeenCalled(),toHaveBeenCalledWith(),toHaveBeenCalledTimes(),toHaveReturnedWith() - Negation: Chain
.notto negate any assertion
Documentation
- New Route Testing guide with complete API reference
- Conditional Routes guide
- Types Plugin documentation
- WebSocket support in client guide
🎯 Summary
Hedystia 1.10 focuses on what developers need most:
- Easy Testing — Test routes inline without separate files
- Feature Control — Conditionally register routes for flags and environments
- Real-Time Communication — native WebSocket support with auto-reconnection
- Type Portability — Use Hedystia typing with any framework
We're excited about these additions and how they enable better developer experiences and more maintainable code.
Upgrade
bun update hedystiaBreaking Changes
None! This is a fully backward-compatible release.
Thank you to everyone in the Hedystia community for your feedback and support. We're excited to see what you build! 🚀
