Add backend framework selection between hono, elysiajs

This commit is contained in:
Aman Varshney
2025-03-26 11:41:41 +05:30
parent b6b113766e
commit 91fe9f861f
40 changed files with 451 additions and 345 deletions

View File

@@ -8,10 +8,8 @@
"compile": "bun build --compile --minify --sourcemap --bytecode ./src/index.ts --outfile server"
},
"dependencies": {
"@hono/trpc-server": "^0.3.4",
"@trpc/server": "^11.0.0",
"dotenv": "^16.4.7",
"hono": "^4.7.5",
"zod": "^3.24.2"
},
"devDependencies": {

View File

@@ -8,7 +8,7 @@
"skipLibCheck": true,
"baseUrl": "./",
"outDir": "./dist",
"types": ["node"],
"types": ["node", "bun"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
},

View File

@@ -1,13 +1,13 @@
import type { Context as HonoContext } from "hono";
import type { Context as ElysiaContext } from "elysia";
import { auth } from "./auth";
export type CreateContextOptions = {
hono: HonoContext;
context: ElysiaContext;
};
export async function createContext({ hono }: CreateContextOptions) {
export async function createContext({ context }: CreateContextOptions) {
const session = await auth.api.getSession({
headers: hono.req.raw.headers,
headers: context.request.headers,
});
return {

View File

@@ -2,12 +2,12 @@ import type { Context as HonoContext } from "hono";
import { auth } from "./auth";
export type CreateContextOptions = {
hono: HonoContext;
context: HonoContext;
};
export async function createContext({ hono }: CreateContextOptions) {
export async function createContext({ context }: CreateContextOptions) {
const session = await auth.api.getSession({
headers: hono.req.raw.headers,
headers: context.req.raw.headers,
});
return {

View File

@@ -1,7 +1,7 @@
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import * as schema from "../db/schema/auth";
import { db } from "../db";
import * as schema from "../db/schema/auth";
export const auth = betterAuth({
database: drizzleAdapter(db, {

View File

@@ -0,0 +1,38 @@
import "dotenv/config";
import { Elysia } from "elysia";
import { cors } from "@elysiajs/cors";
import { auth } from "./lib/auth";
import { createContext } from "./lib/context";
import { appRouter } from "./routers/index";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
const app = new Elysia()
.use(
cors({
origin: process.env.CORS_ORIGIN || "",
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
}),
)
.all("/api/auth/*", async (context) => {
const { request } = context;
if (["POST", "GET"].includes(request.method)) {
return auth.handler(request);
} else {
context.error(405);
}
})
.all("/trpc/*", async (context) => {
const res = await fetchRequestHandler({
endpoint: "/trpc",
router: appRouter,
req: context.request,
createContext: () => createContext({ context }),
});
return res;
})
.get("/", () => "OK")
.listen(3000, () => {
console.log(`Server is running on http://localhost:3000`);
});

View File

@@ -1,3 +1,4 @@
import { trpcServer } from "@hono/trpc-server";
import "dotenv/config";
import { Hono } from "hono";
@@ -27,8 +28,8 @@ app.use(
"/trpc/*",
trpcServer({
router: appRouter,
createContext: (_opts, hono) => {
return createContext({ hono });
createContext: (_opts, context) => {
return createContext({ context });
},
}),
);

View File

@@ -1,44 +0,0 @@
import { z } from "zod";
import { router, publicProcedure } from "../lib/trpc";
import { todo } from "../db/schema";
import { eq } from "drizzle-orm";
import { db } from "../db";
export const todoRouter = router({
getAll: publicProcedure.query(async () => {
return await db.select().from(todo).all();
}),
create: publicProcedure
.input(z.object({ text: z.string().min(1) }))
.mutation(async ({ input }) => {
return await db
.insert(todo)
.values({
text: input.text,
})
.returning()
.get();
}),
toggle: publicProcedure
.input(z.object({ id: z.number(), completed: z.boolean() }))
.mutation(async ({ input }) => {
return await db
.update(todo)
.set({ completed: input.completed })
.where(eq(todo.id, input.id))
.returning()
.get();
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db
.delete(todo)
.where(eq(todo.id, input.id))
.returning()
.get();
}),
});

View File

@@ -1,18 +0,0 @@
import type { Context as HonoContext } from "hono";
import { auth } from "./auth";
export type CreateContextOptions = {
hono: HonoContext;
};
export async function createContext({ hono }: CreateContextOptions) {
const session = await auth.api.getSession({
headers: hono.req.raw.headers,
});
return {
session,
};
}
export type Context = Awaited<ReturnType<typeof createContext>>;

View File

@@ -1,24 +0,0 @@
import { initTRPC, TRPCError } from "@trpc/server";
import type { Context } from "./context";
export const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Authentication required",
cause: "No session",
});
}
return next({
ctx: {
...ctx,
session: ctx.session,
},
});
});

View File

@@ -1,24 +0,0 @@
import { initTRPC, TRPCError } from "@trpc/server";
import type { Context } from "./context";
export const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Authentication required",
cause: "No session",
});
}
return next({
ctx: {
...ctx,
session: ctx.session,
},
});
});

View File

@@ -0,0 +1,27 @@
import "dotenv/config";
import { Elysia } from "elysia";
import { cors } from "@elysiajs/cors";
import { createContext } from "./lib/context";
import { appRouter } from "./routers/index";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
const app = new Elysia()
.use(
cors({
origin: process.env.CORS_ORIGIN || "",
methods: ["GET", "POST", "OPTIONS"],
}),
)
.all("/trpc/*", async (context) => {
const res = await fetchRequestHandler({
endpoint: "/trpc",
router: appRouter,
req: context.request,
createContext: () => createContext({ context }),
});
return res;
})
.get("/", () => "OK")
.listen(3000, () => {
console.log(`Server is running on http://localhost:3000`);
});

View File

@@ -0,0 +1,13 @@
import type { Context as ElysiaContext } from "elysia";
export type CreateContextOptions = {
context: ElysiaContext;
};
export async function createContext({ context }: CreateContextOptions) {
return {
session: null,
};
}
export type Context = Awaited<ReturnType<typeof createContext>>;

View File

@@ -22,8 +22,8 @@ app.use(
"/trpc/*",
trpcServer({
router: appRouter,
createContext: (_opts, hono) => {
return createContext({ hono });
createContext: (_opts, context) => {
return createContext({ context });
},
}),
);

View File

@@ -1,10 +1,10 @@
import type { Context as HonoContext } from "hono";
export type CreateContextOptions = {
hono: HonoContext;
context: HonoContext;
};
export async function createContext({ hono }: CreateContextOptions) {
export async function createContext({ context }: CreateContextOptions) {
return {
session: null,
};

View File

@@ -1,18 +0,0 @@
import type { Context as HonoContext } from "hono";
import { auth } from "./auth";
export type CreateContextOptions = {
hono: HonoContext;
};
export async function createContext({ hono }: CreateContextOptions) {
const session = await auth.api.getSession({
headers: hono.req.raw.headers,
});
return {
session,
};
}
export type Context = Awaited<ReturnType<typeof createContext>>;

View File

@@ -1,24 +0,0 @@
import { initTRPC, TRPCError } from "@trpc/server";
import type { Context } from "./context";
export const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Authentication required",
cause: "No session",
});
}
return next({
ctx: {
...ctx,
session: ctx.session,
},
});
});

View File

@@ -1,24 +0,0 @@
import { initTRPC, TRPCError } from "@trpc/server";
import type { Context } from "./context";
export const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Authentication required",
cause: "No session",
});
}
return next({
ctx: {
...ctx,
session: ctx.session,
},
});
});