mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
add orpc
This commit is contained in:
105
apps/cli/templates/api/orpc/server/base/src/lib/context.ts.hbs
Normal file
105
apps/cli/templates/api/orpc/server/base/src/lib/context.ts.hbs
Normal file
@@ -0,0 +1,105 @@
|
||||
{{#if (eq backend 'next')}}
|
||||
import type { NextRequest } from "next/server";
|
||||
{{#if auth}}
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export async function createContext(req: NextRequest) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: req.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
return {}
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else if (eq backend 'hono')}}
|
||||
import type { Context as HonoContext } from "hono";
|
||||
{{#if auth}}
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: context.req.raw.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else if (eq backend 'elysia')}}
|
||||
import type { Context as ElysiaContext } from "elysia";
|
||||
{{#if auth}}
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: ElysiaContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: context.request.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else if (eq backend 'express')}}
|
||||
import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
|
||||
{{#if auth}}
|
||||
import { fromNodeHeaders } from "better-auth/node";
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export async function createContext(opts: CreateExpressContextOptions) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: fromNodeHeaders(opts.req.headers),
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else}}
|
||||
// Default or fallback context if backend is not recognized or none
|
||||
// This might need adjustment based on your default behavior
|
||||
export async function createContext() {
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
{{/if}}
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
17
apps/cli/templates/api/orpc/server/base/src/lib/orpc.ts.hbs
Normal file
17
apps/cli/templates/api/orpc/server/base/src/lib/orpc.ts.hbs
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ORPCError, os } from "@orpc/server";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export const o = os.$context<Context>();
|
||||
|
||||
export const publicProcedure = o;
|
||||
|
||||
{{#if auth}}
|
||||
const requireAuth = o.middleware(async ({ context, next }) => {
|
||||
if (!context.session?.user) {
|
||||
throw new ORPCError("UNAUTHORIZED");
|
||||
}
|
||||
return next({ context });
|
||||
});
|
||||
|
||||
export const protectedProcedure = publicProcedure.use(requireAuth);
|
||||
{{/if}}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{#if auth}}
|
||||
import { createContext } from '@/lib/context'
|
||||
{{/if}}
|
||||
import { appRouter } from '@/routers'
|
||||
import { RPCHandler } from '@orpc/server/fetch'
|
||||
import { NextRequest } from 'next/server'
|
||||
|
||||
const handler = new RPCHandler(appRouter)
|
||||
|
||||
async function handleRequest(req: NextRequest) {
|
||||
const { response } = await handler.handle(req, {
|
||||
prefix: '/rpc',
|
||||
context: {{#if auth}}await createContext(req){{else}}{}{{/if}},
|
||||
})
|
||||
|
||||
return response ?? new Response('Not found', { status: 404 })
|
||||
}
|
||||
|
||||
export const GET = handleRequest
|
||||
export const POST = handleRequest
|
||||
export const PUT = handleRequest
|
||||
export const PATCH = handleRequest
|
||||
export const DELETE = handleRequest
|
||||
57
apps/cli/templates/api/orpc/web/base/src/utils/orpc.ts.hbs
Normal file
57
apps/cli/templates/api/orpc/web/base/src/utils/orpc.ts.hbs
Normal file
@@ -0,0 +1,57 @@
|
||||
import { createORPCClient } from "@orpc/client";
|
||||
import { RPCLink } from "@orpc/client/fetch";
|
||||
import { createORPCReactQueryUtils } from "@orpc/react-query";
|
||||
import { QueryCache, QueryClient } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import type { appRouter } from "../../../server/src/routers/index";
|
||||
import type { RouterClient } from "@orpc/server";
|
||||
import { createContext, use } from 'react'
|
||||
import type { RouterUtils } from '@orpc/react-query'
|
||||
|
||||
type ORPCReactUtils = RouterUtils<RouterClient<typeof appRouter>>
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
queryCache: new QueryCache({
|
||||
onError: (error) => {
|
||||
toast.error(`Error: ${error.message}`, {
|
||||
action: {
|
||||
label: "retry",
|
||||
onClick: () => {
|
||||
queryClient.invalidateQueries();
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export const link = new RPCLink({
|
||||
{{#if (includes frontend "next")}}
|
||||
url: `${process.env.NEXT_PUBLIC_SERVER_URL}/rpc`,
|
||||
{{else}}
|
||||
url: `${import.meta.env.VITE_SERVER_URL}/rpc`,
|
||||
{{/if}}
|
||||
{{#if auth}}
|
||||
fetch(url, options) {
|
||||
return fetch(url, {
|
||||
...options,
|
||||
credentials: "include",
|
||||
});
|
||||
},
|
||||
{{/if}}
|
||||
});
|
||||
|
||||
export const client: RouterClient<typeof appRouter> = createORPCClient(link)
|
||||
|
||||
export const orpc = createORPCReactQueryUtils(client)
|
||||
|
||||
|
||||
export const ORPCContext = createContext<ORPCReactUtils | undefined>(undefined)
|
||||
|
||||
export function useORPC(): ORPCReactUtils {
|
||||
const orpc = use(ORPCContext)
|
||||
if (!orpc) {
|
||||
throw new Error('ORPCContext is not set up properly')
|
||||
}
|
||||
return orpc
|
||||
}
|
||||
108
apps/cli/templates/api/trpc/server/base/src/lib/context.ts.hbs
Normal file
108
apps/cli/templates/api/trpc/server/base/src/lib/context.ts.hbs
Normal file
@@ -0,0 +1,108 @@
|
||||
{{#if (eq backend 'next')}}
|
||||
import type { NextRequest } from "next/server";
|
||||
{{#if auth}}
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export async function createContext(req: NextRequest) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: req.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else if (eq backend 'hono')}}
|
||||
import type { Context as HonoContext } from "hono";
|
||||
{{#if auth}}
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: context.req.raw.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else if (eq backend 'elysia')}}
|
||||
import type { Context as ElysiaContext } from "elysia";
|
||||
{{#if auth}}
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: ElysiaContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: context.request.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else if (eq backend 'express')}}
|
||||
import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
|
||||
{{#if auth}}
|
||||
import { fromNodeHeaders } from "better-auth/node";
|
||||
import { auth } from "./auth";
|
||||
{{/if}}
|
||||
|
||||
export async function createContext(opts: CreateExpressContextOptions) {
|
||||
{{#if auth}}
|
||||
const session = await auth.api.getSession({
|
||||
headers: fromNodeHeaders(opts.req.headers),
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
{{else}}
|
||||
// No auth configured
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
{{else}}
|
||||
// Default or fallback context if backend is not recognized or none
|
||||
// This might need adjustment based on your default behavior
|
||||
export async function createContext() {
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
{{/if}}
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
26
apps/cli/templates/api/trpc/server/base/src/lib/trpc.ts.hbs
Normal file
26
apps/cli/templates/api/trpc/server/base/src/lib/trpc.ts.hbs
Normal file
@@ -0,0 +1,26 @@
|
||||
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;
|
||||
|
||||
{{#if auth}}
|
||||
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,
|
||||
},
|
||||
});
|
||||
});
|
||||
{{/if}}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
|
||||
import { appRouter } from '@/routers';
|
||||
import { createContext } from '@/lib/context';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
function handler(req: NextRequest) {
|
||||
return fetchRequestHandler({
|
||||
endpoint: '/trpc',
|
||||
req,
|
||||
router: appRouter,
|
||||
createContext: () => createContext(req)
|
||||
});
|
||||
}
|
||||
export { handler as GET, handler as POST };
|
||||
100
apps/cli/templates/api/trpc/web/base/src/utils/trpc.ts.hbs
Normal file
100
apps/cli/templates/api/trpc/web/base/src/utils/trpc.ts.hbs
Normal file
@@ -0,0 +1,100 @@
|
||||
{{#if (includes frontend 'next')}}
|
||||
{{!-- Next.js tRPC Client Setup --}}
|
||||
import { QueryCache, QueryClient } from '@tanstack/react-query';
|
||||
import { createTRPCClient, httpBatchLink } from '@trpc/client';
|
||||
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query';
|
||||
import type { AppRouter } from '../../../server/src/routers'; {{! Adjust path if necessary }}
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
queryCache: new QueryCache({
|
||||
onError: (error) => {
|
||||
toast.error(error.message, {
|
||||
action: {
|
||||
label: "retry",
|
||||
onClick: () => {
|
||||
queryClient.invalidateQueries();
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const trpcClient = createTRPCClient<AppRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
{{#if (includes frontend 'next')}}
|
||||
url: `${process.env.NEXT_PUBLIC_SERVER_URL}/trpc`,
|
||||
{{else}}
|
||||
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
|
||||
{{/if}}
|
||||
{{#if auth}}
|
||||
fetch(url, options) {
|
||||
return fetch(url, {
|
||||
...options,
|
||||
credentials: "include",
|
||||
});
|
||||
},
|
||||
{{/if}}
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export const trpc = createTRPCOptionsProxy<AppRouter>({
|
||||
client: trpcClient,
|
||||
queryClient,
|
||||
});
|
||||
|
||||
{{else if (includes frontend 'tanstack-start')}}
|
||||
{{!-- TanStack Start tRPC Client Setup --}}
|
||||
import { createTRPCContext } from "@trpc/tanstack-react-query";
|
||||
import type { AppRouter } from "../../../server/src/routers"; {{! Adjust path if necessary }}
|
||||
|
||||
export const { TRPCProvider, useTRPC, useTRPCClient } =
|
||||
createTRPCContext<AppRouter>();
|
||||
|
||||
{{else}}
|
||||
{{!-- Default Web tRPC Client Setup (TanStack Router, React Router, etc.) --}}
|
||||
import type { AppRouter } from "../../../server/src/routers"; {{! Adjust path if necessary }}
|
||||
import { QueryCache, QueryClient } from "@tanstack/react-query";
|
||||
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
||||
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
queryCache: new QueryCache({
|
||||
onError: (error) => {
|
||||
toast.error(error.message, {
|
||||
action: {
|
||||
label: "retry",
|
||||
onClick: () => {
|
||||
queryClient.invalidateQueries();
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export const trpcClient = createTRPCClient<AppRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
|
||||
{{#if auth}}
|
||||
fetch(url, options) {
|
||||
return fetch(url, {
|
||||
...options,
|
||||
credentials: "include",
|
||||
});
|
||||
},
|
||||
{{/if}}
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const trpc = createTRPCOptionsProxy<AppRouter>({
|
||||
client: trpcClient,
|
||||
queryClient,
|
||||
});
|
||||
{{/if}}
|
||||
Reference in New Issue
Block a user