Overview
The Resolid Framework provides HTTP server utilities built on Hono, a fast and lightweight web framework. The server integrates with React Router for SSR and provides platform-specific implementations.
Function: createHonoNodeServer()
Creates a Hono-based HTTP server for Node.js.
async function createHonoNodeServer(
options?: HonoNodeServerOptions
): Promise<HonoNodeServer>
Server configuration options
options.defaultLogger
boolean
default:"true in development"
Enable default request logging
options.configure
(app: Hono) => Promise<void> | void
Function to configure the Hono app before routes are added
options.getLoadContext
(c: Context) => RouterContextProvider | Promise<RouterContextProvider>
Function to create the React Router load context
options.onShutdown
() => Promise<void> | void
Callback to execute on server shutdown
options.listeningListener
(info: AddressInfo) => void
Callback when server starts listening
The configured Hono server instance
Basic Usage
import { createHonoNodeServer } from '@resolid/dev/http.server';
const server = await createHonoNodeServer({
port: 3000,
configure: async (app) => {
// Add custom middleware or routes
app.use('*', async (c, next) => {
console.log(`Request: ${c.req.method} ${c.req.url}`);
await next();
});
}
});
Server Configuration
Custom Port
const server = await createHonoNodeServer({
port: parseInt(process.env.PORT || '3000')
});
Disable Default Logger
const server = await createHonoNodeServer({
defaultLogger: false
});
Custom Middleware
import { cors } from 'hono/cors';
import { compress } from 'hono/compress';
const server = await createHonoNodeServer({
configure: async (app) => {
// CORS
app.use('*', cors({
origin: 'https://example.com',
credentials: true
}));
// Compression
app.use('*', compress());
// Custom routes
app.get('/api/health', (c) => c.json({ status: 'ok' }));
}
});
Load Context
Provide context data to React Router loaders and actions:
import { DatabaseService } from '@resolid/app-db';
import { LogService } from '@resolid/app-log';
const server = await createHonoNodeServer({
getLoadContext: (c) => {
const context = new RouterContextProvider();
// Inject services into context
context.set('db', app.get(DatabaseService));
context.set('logger', app.get(LogService));
context.set('user', c.get('user'));
return context;
}
});
Shutdown Handler
const server = await createHonoNodeServer({
onShutdown: async () => {
console.log('Server shutting down...');
await cleanupResources();
}
});
Built-in Middleware
The framework provides several built-in middleware:
Request ID
import { requestId } from '@resolid/dev/http.server';
const server = await createHonoNodeServer({
configure: async (app) => {
app.use('*', requestId());
}
});
Client IP
import { clientIp, nodeClientIpGetter } from '@resolid/dev/http.server';
const server = await createHonoNodeServer({
configure: async (app) => {
app.use('*', clientIp(nodeClientIpGetter()));
}
});
Request Origin
import { requestOrigin, nodeRequestOriginGetter } from '@resolid/dev/http.server';
const server = await createHonoNodeServer({
configure: async (app) => {
app.use('*', requestOrigin(nodeRequestOriginGetter()));
}
});
Cache Control
The server automatically configures cache control for static assets:
import { cacheControl } from '@resolid/dev/http.server';
const server = await createHonoNodeServer({
configure: async (app) => {
// Cache static assets for 1 year (immutable)
app.use('/assets/*', cacheControl(60 * 60 * 24 * 365, true));
// Cache other files for 1 hour
app.use('*', cacheControl(60 * 60));
}
});
Function: cacheControl()
Creates a cache control middleware.
function cacheControl(seconds: number, immutable?: boolean): MiddlewareHandler
Cache duration in seconds
Whether the resource is immutable
Hono middleware that sets Cache-Control headers
Node.js Client IP Getter
function nodeClientIpGetter(options?: GetClientIpOptions): ClientIpGetter
Options for extracting client IP
Example:
import { clientIp, nodeClientIpGetter } from '@resolid/dev/http.server';
app.use('*', clientIp(nodeClientIpGetter({
headers: ['x-forwarded-for', 'x-real-ip']
})));
Node.js Request Origin Getter
function nodeRequestOriginGetter(proxy?: boolean): RequestOriginGetter
Whether the server is behind a proxy
Example:
import { requestOrigin, nodeRequestOriginGetter } from '@resolid/dev/http.server';
app.use('*', requestOrigin(nodeRequestOriginGetter(true)));
Type Definitions
HonoNodeServerOptions
type HonoNodeServerOptions = HonoServerOptions<NodeEnv> & {
port?: number;
defaultLogger?: boolean;
listeningListener?: (info: AddressInfo) => void;
}
HonoServerOptions
interface HonoServerOptions<E extends Env = BlankEnv> {
configure?: (app: Hono<E>) => Promise<void> | void;
honoOptions?: HonoOptions<E>;
getLoadContext?: (
c: Context<E>,
options: { build: ServerBuild; mode?: string }
) => Promise<RouterContextProvider> | RouterContextProvider;
onShutdown?: () => Promise<void> | void;
}
NodeEnv
type NodeEnv = {
Bindings: HttpBindings | Http2Bindings;
}
Complete Example
import { createApp } from '@resolid/core';
import { createHonoNodeServer, requestId, clientIp, nodeClientIpGetter } from '@resolid/dev/http.server';
import { cors } from 'hono/cors';
import { compress } from 'hono/compress';
import { DatabaseService } from '@resolid/app-db';
import { LogService } from '@resolid/app-log';
// Create the Resolid app
const app = await createApp({
// ... providers and extensions
});
await app.run();
// Create the HTTP server
const server = await createHonoNodeServer({
port: parseInt(process.env.PORT || '3000'),
defaultLogger: process.env.NODE_ENV !== 'production',
configure: async (hono) => {
// Add middleware
hono.use('*', requestId());
hono.use('*', clientIp(nodeClientIpGetter()));
hono.use('*', cors({ origin: '*' }));
hono.use('*', compress());
// Add custom routes
hono.get('/api/health', (c) => c.json({ status: 'ok' }));
hono.get('/api/status', (c) => {
const db = app.get(DatabaseService);
return c.json({
database: db ? 'connected' : 'disconnected',
uptime: process.uptime()
});
});
},
getLoadContext: (c) => {
const context = new RouterContextProvider();
context.set('app', app);
context.set('db', app.get(DatabaseService));
context.set('logger', app.get(LogService));
return context;
},
onShutdown: async () => {
console.log('Shutting down server...');
await app.dispose();
},
listeningListener: (info) => {
console.log(`Server listening on http://localhost:${info.port}`);
}
});
Source Code
Locations:
packages/dev/src/http/index.ts
packages/dev/src/http/platforms/node.ts
packages/dev/src/http/utils/server.ts