Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trpc #5

Merged
merged 2 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import './src/env.js';

/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['@orbitkit/db', '@orbitkit/auth'],
transpilePackages: ['@orbitkit/db', '@orbitkit/auth', '@orbitkit/trpc'],
};

export default nextConfig;
7 changes: 7 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,21 @@
"dependencies": {
"@orbitkit/auth": "workspace:^",
"@orbitkit/db": "workspace:^",
"@orbitkit/trpc": "workspace:^",
"@orbitkit/ui": "workspace:^",
"@t3-oss/env-nextjs": "^0.8.0",
"@tanstack/react-query": "4.36.1",
"@total-typescript/ts-reset": "^0.5.1",
"@trpc/client": "^10.45.1",
"@trpc/react-query": "^10.45.1",
"@trpc/server": "^10.45.1",
"geist": "^1.2.2",
"next": "14.1.0",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^3.22.4"
},
"devDependencies": {
Expand Down
29 changes: 29 additions & 0 deletions apps/web/src/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { type NextRequest } from 'next/server';

import { fetchRequestHandler } from '@trpc/server/adapters/fetch';

import { appRouter, createTRPCContext } from '@orbitkit/trpc';

import { env } from '@/env';

const createContext = async (req: NextRequest) => {
return createTRPCContext({
headers: req.headers,
});
};

const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => createContext(req),
onError: ({ path, error }) => {
env.NODE_ENV === 'development' &&
console.error(
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`,
);
},
});

export { handler as GET, handler as POST };
7 changes: 7 additions & 0 deletions apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import { redirect } from 'next/navigation';
import { getSession } from '@orbitkit/auth';
import { Avatar, AvatarFallback, AvatarImage } from '@orbitkit/ui/avatar';

import { ThemeSwitcher } from '@/components/ThemeSwitcher';
import { trpc } from '@/lib/trpc/server';

export default async function Home() {
const { user } = await getSession();
if (!user) {
return redirect('/login');
}

const hello = await trpc.greeting.protectedHello.query();

return (
<main className="container mx-auto py-6 px-6">
<h1>Next.js app</h1>
<h1>{hello.greeting}</h1>
<ThemeSwitcher />
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import { type FC, type PropsWithChildren } from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';

import { TRPCReactProvider } from '@/lib/trpc/client';

export const Providers: FC<PropsWithChildren> = ({ children }) => {
return (
<NextThemesProvider attribute="class" enableSystem>
{children}
<TRPCReactProvider>{children}</TRPCReactProvider>
</NextThemesProvider>
);
};
11 changes: 9 additions & 2 deletions apps/web/src/env.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { createEnv } from '@t3-oss/env-nextjs';
import { vercel } from '@t3-oss/env-nextjs/presets';
import { z } from 'zod';

import { env as authEnv } from '@orbitkit/auth/env';
import { env as dbEnv } from '@orbitkit/db/env';

export const env = createEnv({
extends: [dbEnv, authEnv, vercel],
server: {},
server: {
NODE_ENV: z.enum(['development', 'test', 'production']).optional(),
PORT: z.coerce.number().default(3000),
},
client: {},
runtimeEnv: {},
runtimeEnv: {
NODE_ENV: process.env.NODE_ENV,
PORT: process.env['PORT'],
},
emptyStringAsUndefined: true,
});
44 changes: 44 additions & 0 deletions apps/web/src/lib/trpc/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client';

import { useState } from 'react';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { loggerLink, unstable_httpBatchStreamLink } from '@trpc/client';
import { createTRPCReact } from '@trpc/react-query';
import superjson from 'superjson';

import { type AppRouter } from '@orbitkit/trpc';

import { env } from '@/env';

import { getUrl } from './shared';

export const trpc = createTRPCReact<AppRouter>();

export function TRPCReactProvider(props: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());

const [trpcClient] = useState(() =>
trpc.createClient({
transformer: superjson,
links: [
loggerLink({
enabled: (op) =>
env.NODE_ENV === 'development' ||
(op.direction === 'down' && op.result instanceof Error),
}),
unstable_httpBatchStreamLink({
url: getUrl(),
}),
],
}),
);

return (
<QueryClientProvider client={queryClient}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</trpc.Provider>
</QueryClientProvider>
);
}
59 changes: 59 additions & 0 deletions apps/web/src/lib/trpc/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'server-only';

import { cache } from 'react';
import { headers } from 'next/headers';

import type { AppRouter } from '@orbitkit/trpc';

import {
createTRPCProxyClient,
loggerLink,
TRPCClientError,
} from '@trpc/client';
import { callProcedure } from '@trpc/server';
import { observable } from '@trpc/server/observable';
import { type TRPCErrorResponse } from '@trpc/server/rpc';
import superjson from 'superjson';

import { appRouter, createTRPCContext } from '@orbitkit/trpc';

const createContext = cache(() => {
const heads = new Headers(headers());
heads.set('x-trpc-source', 'rsc');

return createTRPCContext({
headers: heads,
});
});

export const trpc = createTRPCProxyClient<AppRouter>({
transformer: superjson,
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === 'development' ||
(op.direction === 'down' && op.result instanceof Error),
}),
() =>
({ op }) =>
observable((observer) => {
createContext()
.then((ctx) => {
return callProcedure({
procedures: appRouter._def.procedures,
path: op.path,
rawInput: op.input,
ctx,
type: op.type,
});
})
.then((data) => {
observer.next({ result: { data } });
observer.complete();
})
.catch((cause: TRPCErrorResponse) => {
observer.error(TRPCClientError.from(cause));
});
}),
],
});
18 changes: 18 additions & 0 deletions apps/web/src/lib/trpc/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server';

import { type AppRouter } from '@orbitkit/trpc';

import { env } from '@/env';

function getBaseUrl() {
if (typeof window !== 'undefined') return '';
if (env.VERCEL_URL) return `https://${env.VERCEL_URL}`;
return `http://localhost:${env.PORT}`;
}

export function getUrl() {
return getBaseUrl() + '/api/trpc';
}

export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;
3 changes: 3 additions & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ words:
- packagejson
- shadcn
- stylesheet
- superjson
- tailwindcss
- tanstack
- trpc
- tsbuildinfo
- tsconfigs
- tsup
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"prettier-plugin-packagejson": "^2.4.10",
"prettier-plugin-tailwindcss": "^0.5.11",
"rimraf": "^5.0.5",
"turbo": "^1.12.2",
"turbo": "^1.12.3",
"typescript": "^5.3.3"
},
"packageManager": "[email protected]",
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"arctic": "^1.1.4",
"lucia": "^3.0.1",
"next": "14.1.0",
"oslo": "^1.0.4",
"oslo": "^1.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zod": "^3.22.4"
Expand Down
2 changes: 1 addition & 1 deletion packages/db/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"typecheck": "tsc --noEmit --tsBuildInfoFile .tsbuildinfo"
},
"dependencies": {
"@neondatabase/serverless": "^0.7.2",
"@neondatabase/serverless": "^0.8.1",
"@t3-oss/env-core": "^0.8.0",
"drizzle-orm": "^0.29.3",
"pg": "^8.11.3",
Expand Down
7 changes: 7 additions & 0 deletions packages/trpc/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('eslint').Linter.Config} */
const config = {
root: true,
extends: ['orbitkit/base'],
};

module.exports = config;
31 changes: 31 additions & 0 deletions packages/trpc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@orbitkit/trpc",
"version": "0.1.0",
"private": true,
"description": "tRPC api for Orbitkit",
"license": "MIT",
"author": "Orbitkit",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"lint": "eslint . --cache --max-warnings 0",
"typecheck": "tsc --noEmit --tsBuildInfoFile .tsbuildinfo"
},
"dependencies": {
"@orbitkit/auth": "workspace:^",
"@orbitkit/db": "workspace:^",
"@trpc/server": "^10.45.1",
"superjson": "^2.2.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@orbitkit/tsconfig": "workspace:^",
"@types/node": "^20.11.16",
"eslint-config-orbitkit": "workspace:^"
},
"volta": {
"extends": "../../package.json"
}
}
10 changes: 10 additions & 0 deletions packages/trpc/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { greetingRouter } from './routers/greeting';
import { router } from './trpc';

export const appRouter = router({
greeting: greetingRouter,
});

export type AppRouter = typeof appRouter;

export { createTRPCContext } from './trpc';
21 changes: 21 additions & 0 deletions packages/trpc/src/routers/greeting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from 'zod';

import { protectedProcedure, publicProcedure, router } from '../trpc';

export const greetingRouter = router({
hello: publicProcedure
.input(z.object({ name: z.string().optional() }).optional())
.query(({ input }) => {
return {
greeting: `Hello, ${input?.name ?? 'World'}!`,
};
}),

protectedHello: protectedProcedure
.input(z.object({ name: z.string().optional() }).optional())
.query(({ input, ctx }) => {
return {
greeting: `Hello, ${input?.name ?? 'World'}! Your user ID is ${ctx.session.user.id}`,
};
}),
});
Loading
Loading