import "dotenv/config"; import Fastify from "fastify"; import fastifyCors from "@fastify/cors"; {{#if (eq api "trpc")}} import { fastifyTRPCPlugin, type FastifyTRPCPluginOptions } from "@trpc/server/adapters/fastify"; import { createContext } from "./lib/context"; import { appRouter, type AppRouter } from "./routers/index"; {{/if}} {{#if (eq api "orpc")}} import { RPCHandler } from "@orpc/server/node"; import { CORSPlugin } from "@orpc/server/plugins"; import { appRouter } from "./routers/index"; import { createServer } from "node:http"; {{#if auth}} import { createContext } from "./lib/context"; {{/if}} {{/if}} {{#if (includes examples "ai")}} import { streamText, type UIMessage, convertToModelMessages } from "ai"; import { google } from "@ai-sdk/google"; {{/if}} {{#if auth}} import { auth } from "./lib/auth"; {{/if}} const baseCorsConfig = { origin: process.env.CORS_ORIGIN || "", methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowedHeaders: [ "Content-Type", "Authorization", "X-Requested-With" ], credentials: true, maxAge: 86400, }; {{#if (eq api "orpc")}} const handler = new RPCHandler(appRouter, { plugins: [ new CORSPlugin({ origin: process.env.CORS_ORIGIN, credentials: true, allowHeaders: ["Content-Type", "Authorization"], }), ], }); const fastify = Fastify({ logger: true, serverFactory: (fastifyHandler) => { const server = createServer(async (req, res) => { const { matched } = await handler.handle(req, res, { context: await createContext(req.headers), prefix: "/rpc", }); if (matched) { return; } fastifyHandler(req, res); }); return server; }, }); {{else}} const fastify = Fastify({ logger: true, }); {{/if}} fastify.register(fastifyCors, baseCorsConfig); {{#if auth}} fastify.route({ method: ["GET", "POST"], url: "/api/auth/*", async handler(request, reply) { try { const url = new URL(request.url, `http://${request.headers.host}`); const headers = new Headers(); Object.entries(request.headers).forEach(([key, value]) => { if (value) headers.append(key, value.toString()); }); const req = new Request(url.toString(), { method: request.method, headers, body: request.body ? JSON.stringify(request.body) : undefined, }); const response = await auth.handler(req); reply.status(response.status); response.headers.forEach((value, key) => reply.header(key, value)); reply.send(response.body ? await response.text() : null); } catch (error) { fastify.log.error({ err: error }, "Authentication Error:"); reply.status(500).send({ error: "Internal authentication error", code: "AUTH_FAILURE" }); } } }); {{/if}} {{#if (eq api "trpc")}} fastify.register(fastifyTRPCPlugin, { prefix: "/trpc", trpcOptions: { router: appRouter, createContext, onError({ path, error }) { console.error(`Error in tRPC handler on path '${path}':`, error); }, } satisfies FastifyTRPCPluginOptions["trpcOptions"], }); {{/if}} {{#if (includes examples "ai")}} interface AiRequestBody { id?: string; messages: UIMessage[]; } fastify.post('/ai', async function (request, reply) { // there are some issues with the ai sdk and fastify, docs: https://ai-sdk.dev/cookbook/api-servers/fastify const { messages } = request.body as AiRequestBody; const result = streamText({ model: google('gemini-1.5-flash'), messages: convertToModelMessages(messages), }); return result.pipeUIMessageStreamToResponse(reply.raw); }); {{/if}} fastify.get('/', async () => { return 'OK'; }); fastify.listen({ port: 3000 }, (err) => { if (err) { fastify.log.error(err); process.exit(1); } console.log("Server running on port 3000"); });