Skip to content

Commit

Permalink
add example
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusmarminge committed Dec 5, 2022
1 parent 2d25187 commit d06701a
Show file tree
Hide file tree
Showing 15 changed files with 481 additions and 0 deletions.
13 changes: 13 additions & 0 deletions examples/tanstack-router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# A minimal React tRPC example using @tanstack/react-router

Requires node 18 (for global fetch).

## Playing around

```bash
npm i
npm run dev
```

Try editing the ts files to see the type checking in action :)

13 changes: 13 additions & 0 deletions examples/tanstack-router/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com"></script>
<title>tRPC + @tanstack/router</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
25 changes: 25 additions & 0 deletions examples/tanstack-router/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@examples/tanstack-router-client",
"private": true,
"version": "10.4.3",
"type": "module",
"scripts": {
"dev": "vite"
},
"dependencies": {
"@tanstack/react-query": "^4.3.8",
"@tanstack/react-router": "0.0.1-beta.28",
"@trpc/client": "^10.4.3",
"@trpc/react-query": "^10.4.3",
"@trpc/server": "^10.4.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.5",
"@vitejs/plugin-react": "^2.1.0",
"typescript": "^4.8.3",
"vite": "^3.1.3"
}
}
13 changes: 13 additions & 0 deletions examples/tanstack-router/client/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { RouterProvider } from '@tanstack/react-router';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { router } from './utils/router';
import { TRPCProvider } from './utils/trpc';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<TRPCProvider>
<RouterProvider router={router} />
</TRPCProvider>
</React.StrictMode>,
);
145 changes: 145 additions & 0 deletions examples/tanstack-router/client/src/utils/router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* eslint-disable react-hooks/rules-of-hooks */
import {
Link,
Outlet,
createReactRouter,
createRouteConfig,
useMatch,
} from '@tanstack/react-router';
import { getPostById, getPosts } from '../../../server/fetchers';
import { queryClient, trpc } from './trpc';

const rootRoute = createRouteConfig({
component: () => {
return (
<>
<div className="p-2 flex gap-2 text-lg">
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>{' '}
<Link
to="/posts"
activeProps={{
className: 'font-bold',
}}
>
Posts
</Link>
</div>
<hr />
<Outlet /> {/* Start rendering router matches */}
</>
);
},
});

const indexRoute = rootRoute.createRoute({
path: '/',
component: () => {
const hello = trpc.hello.useQuery();
if (!hello.data) return <p>Loading...</p>;
return <div>{hello.data}</div>;
},
});

const postsRoute = rootRoute.createRoute({
path: 'posts',
loaderMaxAge: 0,
errorComponent: () => 'Oh crap!',
loader: async () => {
const postKey = trpc.post.all.getQueryKey();

queryClient.getQueryData(postKey) ??
(await queryClient.prefetchQuery(postKey, getPosts));
return {};
},

component: () => {
const postsQuery = trpc.post.all.useQuery();

return (
<div className="p-2 flex gap-2">
<ul className="list-disc pl-4">
{postsQuery.data?.map((post) => {
return (
<li key={post.id} className="whitespace-nowrap">
<Link
to={postRoute.id}
params={{
postId: post.id,
}}
className="block py-1 text-blue-800 hover:text-blue-600"
activeProps={{ className: 'text-black font-bold' }}
>
<div>{post.title.substring(0, 20)}</div>
</Link>
</li>
);
})}
</ul>
<hr />
<Outlet />
</div>
);
},
});

const postsIndexRoute = postsRoute.createRoute({
path: '/',

component: () => {
return (
<>
<div>Select a post.</div>
</>
);
},
});

const postRoute = postsRoute.createRoute({
path: '$postId',
loader: async ({ params }) => {
const postKey = trpc.post.byId.getQueryKey({ id: params.postId });

queryClient.getQueryData(postKey) ??
(await queryClient.prefetchQuery(postKey, () =>
getPostById(params.postId),
));

return {};
},

component: () => {
const { params } = useMatch(postRoute.id);
const postQuery = trpc.post.byId.useQuery({ id: params.postId });

return (
<div className="space-y-2">
<h4 className="text-xl font-bold underline">{postQuery.data?.title}</h4>
</div>
);
},
});

const routeConfig = rootRoute.addChildren([
indexRoute,
postsRoute.addChildren([postsIndexRoute, postRoute]),
]);

// Set up a ReactRouter instance
export const router = createReactRouter({
routeConfig,
defaultPreload: 'intent',
});

declare module '@tanstack/react-router' {
interface RegisterRouter {
router: typeof router;
}
}
40 changes: 40 additions & 0 deletions examples/tanstack-router/client/src/utils/trpc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { createTRPCReact } from '@trpc/react-query';
import React from 'react';
import type { AppRouter } from '../../../server';

/**
* Typesafe hooks
*/
export const trpc = createTRPCReact<AppRouter>();

/**
* Create a QueryClient outside of the app,
* so we can use it for route loaders
*/
export const queryClient = new QueryClient();

/**
* A wrapper for your app that provides the TRPC context.
* Use only in _app.tsx
*/
export const TRPCProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [trpcClient] = React.useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: 'http://localhost:2023',
}),
],
}),
);

return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
21 changes: 21 additions & 0 deletions examples/tanstack-router/client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"types": ["vite/client"]
},
"include": ["src", "vite.config.ts"]
}
13 changes: 13 additions & 0 deletions examples/tanstack-router/client/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

// https://vitejs.dev/config/
export default defineConfig({
server: {
port: 3000,
},
preview: {
port: 3000,
},
plugins: [react()],
});
17 changes: 17 additions & 0 deletions examples/tanstack-router/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@examples/tanstack-router",
"private": true,
"version": "10.4.3",
"workspaces": [
"client",
"server"
],
"scripts": {
"dev:client": "npm run dev -w client",
"dev:server": "npm run dev -w server",
"dev": "run-p dev:*"
},
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
18 changes: 18 additions & 0 deletions examples/tanstack-router/server/fetchers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const db = {
posts: [
{ id: '1', title: 'hello world' },
{ id: '2', title: 'foo bar' },
],
};

export const getPosts = async () => {
// simulate slow db
await new Promise((res) => setTimeout(res, 1000));
return db.posts;
};

export const getPostById = async (id: string) => {
// simulate slow db
await new Promise((res) => setTimeout(res, 1000));
return db.posts.find((post) => post.id === id);
};
52 changes: 52 additions & 0 deletions examples/tanstack-router/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* This is the API-handler of your app that contains all your API routes.
* On a bigger app, you will probably want to split this file up into multiple files.
*/
import { initTRPC } from '@trpc/server';
import { createHTTPHandler } from '@trpc/server/adapters/standalone';
import http from 'http';
import { z } from 'zod';
import { getPostById, getPosts } from './fetchers';

const t = initTRPC.create();

const publicProcedure = t.procedure;
const router = t.router;

const appRouter = router({
hello: publicProcedure.query(() => 'Hello world!'),
post: router({
all: publicProcedure.query(async () => {
return await getPosts();
}),
byId: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return await getPostById(input.id);
}),
}),
});

// export only the type definition of the API
// None of the actual implementation is exposed to the client
export type AppRouter = typeof appRouter;

// create handler
const handler = createHTTPHandler({
router: appRouter,
createContext: () => ({}),
});

const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Request-Method', '*');
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res.setHeader('Access-Control-Allow-Headers', '*');
if (req.method === 'OPTIONS') {
res.writeHead(200);
return res.end();
}
handler(req, res);
});

server.listen(2023);
17 changes: 17 additions & 0 deletions examples/tanstack-router/server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@examples/tanstack-router-server",
"version": "10.4.3",
"private": true,
"scripts": {
"dev": "tsx watch index.ts"
},
"dependencies": {
"@trpc/server": "^10.4.3",
"zod": "^3.0.0"
},
"devDependencies": {
"@types/node": "^18.7.20",
"tsx": "^3.9.0",
"typescript": "^4.8.3"
}
}
Loading

0 comments on commit d06701a

Please sign in to comment.