SvelteKit + Hono + ORPC + OpenAPI
The most cursed combination of frameworks
Table of Contents
You probably shouldn't use this if you don't want to go insane.
What?
The most cursed way of creating a REST API with SvelteKit, ORPC, OpenAPI, and Hono.
Creates a sveltekit endpoint that has a hono instance that uses ORPS's OpenAPI reference plugin.
Gives you the following:
- / => sveltekit
- /api => openapi
- /api/test => hono
- /api/ping => orpc
Implementation
src/routes/api/[...rest]/+server.ts
import { Hono } from 'hono';
import { RPCHandler } from "@orpc/server/fetch";
import { OpenAPIHandler } from '@orpc/openapi/fetch';
import { onError } from '@orpc/server';
import { ZodSmartCoercionPlugin, ZodToJsonSchemaConverter } from '@orpc/zod';
import { OpenAPIReferencePlugin } from '@orpc/openapi/plugins';
import { os } from '@orpc/server';
import { z } from 'zod';
const appRouter = os.router({
health: {
ping: os
.route({
method: 'GET',
path: '/ping',
summary: 'Health check endpoint',
tags: ['Health'],
})
.output(z.string())
.handler(() => 'pong'),
}
});
const rpcHandler = new RPCHandler(appRouter);
const openApiHandler = new OpenAPIHandler(appRouter, {
interceptors: [
onError((error) => {
console.error(error);
}),
],
plugins: [
new ZodSmartCoercionPlugin(),
new OpenAPIReferencePlugin({
schemaConverters: [
new ZodToJsonSchemaConverter(),
],
specGenerateOptions: {
info: {
title: 'API',
version: '1.0.0',
},
security: [{ bearerAuth: [] }],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
},
},
},
},
docsConfig: {
authentication: {
securitySchemes: {
bearerAuth: {
token: 'default-token',
},
},
},
},
}),
],
});
export const GET = (event) => {
const api = new Hono();
api.get("/api/test", (c) => {
return c.text("Hello World!");
});
// Combined API endpoint - handles both RPC and OpenAPI
api.use("/api/*", async (c, next) => {
// Try RPC handler first
const { matched, response: rpcResponse } = await rpcHandler.handle(c.req.raw, {
prefix: "/api",
context: {
event
},
});
if (matched) {
return c.newResponse(rpcResponse.body, rpcResponse);
}
// If RPC didn't match, try OpenAPI handler
const { response: openApiResponse } = await openApiHandler.handle(c.req.raw, {
prefix: "/api",
context: {
event
},
});
if (openApiResponse) {
return c.newResponse(openApiResponse.body, openApiResponse);
}
await next();
});
return api.fetch(event.request);
};