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

feat(react-query): next-app-router example with prefetch helpers #5451

Open
wants to merge 102 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
70a0c71
next-app-router example with prefetch helpers
juliusmarminge Feb 6, 2024
fd05259
chore: apply lint and formatting fixes
autofix-ci[bot] Feb 6, 2024
e8e3c8d
<Posts/>
juliusmarminge Feb 6, 2024
04bb3b9
commity env
juliusmarminge Feb 6, 2024
18df440
readme
juliusmarminge Feb 6, 2024
1375467
why did autofixer do thsi?
juliusmarminge Feb 6, 2024
5b5f71e
fix manypkg things...
juliusmarminge Feb 6, 2024
82d4d85
Merge branch 'next' into app-router-example
juliusmarminge Feb 7, 2024
9d7762a
chore: apply lint and formatting fixes
autofix-ci[bot] Feb 7, 2024
e9fe669
Merge branch 'next' into app-router-example
juliusmarminge Feb 8, 2024
efa894c
wtf linter???
juliusmarminge Feb 8, 2024
40ea132
no infer
juliusmarminge Feb 8, 2024
ad941c8
Merge branch 'next' into app-router-example
juliusmarminge Feb 8, 2024
1b2ea2d
skip this autolinter thing
juliusmarminge Feb 8, 2024
bbeaea4
Merge branch 'next' into app-router-example
juliusmarminge Mar 4, 2024
42d96f5
Merge branch 'next' into app-router-example
juliusmarminge Mar 7, 2024
45a9eb5
up
juliusmarminge Mar 7, 2024
b729b2b
wait query with nested suspense
juliusmarminge Mar 7, 2024
97c049d
docs: update batching toggle docs (#5553)
KATT Mar 8, 2024
9e26b44
fix(server + client): skip referencing `NodeJS.*` (#5562)
KATT Mar 11, 2024
0175098
fix: Export required types for TypeScript workspaces (#5500)
jeffberry Mar 11, 2024
dde5e8b
fix(example): custom websocket server not upgrading due to next 13.4.…
dougkulak Mar 12, 2024
5eadbc7
chore: make dependabot understand bumps in `next`-tag (#5578)
KATT Mar 22, 2024
3be4c5a
chore: bump pnpm (#5579)
KATT Mar 22, 2024
cf27db6
feat(server): add `experimental_caller()` (#5584)
KATT Mar 26, 2024
c36d09f
feat(server): add an `onError`-option to router caller (#5585)
KATT Mar 26, 2024
bba3f97
feat(next app dir): rethrow built-in Next errors (#5587)
KATT Mar 26, 2024
bf5ce50
feat(server): add next.js `experimental_caller()`-adapter (#5589)
KATT Mar 27, 2024
b9b3610
return redirect (#5593)
juliusmarminge Mar 27, 2024
db93230
patch(server): bump `@fastify/websocket` to 10.0.1 (#5592)
vafanassieff Mar 27, 2024
3067e25
chore(docs): Update setup.mdx (#5594)
KATT Mar 27, 2024
143bfaa
feat: release v11 release candidate (#5601)
KATT Mar 30, 2024
5c009f9
patch(server): fix `dev --turbo` (#5604)
KATT Apr 2, 2024
50959e1
fix(deps): update dependency eslint-plugin-unicorn to v52 (#5608)
renovate[bot] Apr 3, 2024
375abc3
fix(react-query): use `optsRef` in `useSubscription` (#5610)
KATT Apr 4, 2024
683e592
fix(deps): update dependency @testing-library/dom to v10 (#5617)
renovate[bot] Apr 8, 2024
86dd51d
fix(server): remove extra export for router (#5621)
ax-at Apr 9, 2024
af1a832
docs: Add Ethereum DApp example (#5615)
tr1sm0s1n Apr 9, 2024
7fd3e2e
fix(deps): update dependency @testing-library/react to v15 (#5624)
renovate[bot] Apr 10, 2024
550563e
chore(deps): Remove unused dependency aws-lambda (#5628)
astuyve Apr 13, 2024
c61de82
chore: make sure test job runs on pushes (#5630)
KATT Apr 13, 2024
c48eb0a
chore: fix codecov (#5631)
KATT Apr 13, 2024
fbf388a
patch(client): createInnerProxy exclude apply bind and call (#5629)
KATT Apr 13, 2024
4e9ca0a
chore: bump prisma (#5632)
KATT Apr 13, 2024
aa0d06d
feat: export procedure types (#5640)
infodusha Apr 19, 2024
7bb86bb
chore: remove `name:` from jobs in ci (#5641)
KATT Apr 19, 2024
f418809
fix(deps): update dependency devalue to v5 (#5646)
renovate[bot] Apr 19, 2024
854353d
feat: Support Content Types other than JSON (FormData, File, Blob, Ui…
Nick-Lucas Apr 20, 2024
235787b
fix: dump complex formdata stuff in favor of "using the platform" (#5…
juliusmarminge Apr 20, 2024
1769441
chore: better error for mismatched content-type (#5654)
juliusmarminge Apr 21, 2024
2a81b34
feat(react-query): expose `setQueriesData` in `useUtils` (#5605)
nabeelvalley Apr 21, 2024
1b66b3f
Update migrate-from-v10-to-v11.mdx (#5655)
KATT Apr 21, 2024
be7d3f0
Update migrate-from-v10-to-v11.mdx (#5656)
KATT Apr 21, 2024
e36bbae
fix: rollback formdata/octet type stream support (#5661)
KATT Apr 21, 2024
f5b8246
feat: add trpc-rabbitmq & trpc-mqtt to the awesome-trpc list (#5664)
edorgeville Apr 24, 2024
74cb83d
fix(deps): update dependency cssnano to v7 (#5669)
renovate[bot] Apr 25, 2024
c060eb2
feat(server): export `TRPCBuilder` class from `@trpc/server` package …
opiation Apr 25, 2024
8a63dd1
chore: bump react version (#5680)
KATT Apr 27, 2024
935cc8b
chore: add secret to codecov (#5633)
KATT Apr 28, 2024
4ff00b3
chore: fix codecov badge (#5689)
KATT Apr 28, 2024
0b9f580
chore: fix codecov for forks (#5688)
KATT Apr 28, 2024
634775a
feat(react-query): add support for global onSuccess for mutation (#5668)
tomer-yechiel Apr 28, 2024
2b3f2c3
docs: fix link to the react query integration page (#5693)
fpetrakov Apr 30, 2024
5fe9779
feat: add formdata and octet stream support (#5665)
Nick-Lucas Apr 30, 2024
ab18070
chore(server): rewrite `resolveHTTPResponse` with Fetch (#5684)
KATT Apr 30, 2024
5bd12e0
chore: tweak some comments (#5694)
KATT Apr 30, 2024
c67c598
chore: trigger subtree after release (#5695)
KATT Apr 30, 2024
f3e31b2
fix(deps): update dependency @headlessui/react to v2 (#5706)
renovate[bot] May 6, 2024
f9af5c3
chore(server): use `Readable.fromWeb()` to pipe responses (#5701)
KATT May 7, 2024
bfd5f2e
fix(deps): update dependency eslint-plugin-unicorn to v53 (#5712)
renovate[bot] May 10, 2024
05af9e4
chore: bump react (#5720)
KATT May 13, 2024
b35c918
feat(client): add `'none'` color option to default logger, add `withC…
jonluca May 14, 2024
d66a1d7
feat(server): websocket keep alive (#5715)
buraktt May 14, 2024
a43a348
Fixes a single typo in docs (#5716)
babakfp May 14, 2024
0784b4b
docs: add used by google (#5722)
KATT May 15, 2024
3a3bc2e
fix(server): handle large POST bodies in node-http (#5724)
jcarrus May 16, 2024
9bd66a4
feat(server + client): streaming mutations and queries over HTTP (#5700)
KATT May 19, 2024
1c0fc79
Add trpc-cli to awesome-trpc (#5733)
mmkal May 23, 2024
e01b7d5
docs: update aws-lambda.md (#5740)
KATT May 25, 2024
81d5810
Merge branch 'next' into app-router-example
juliusmarminge May 25, 2024
f8455e7
rev
juliusmarminge May 25, 2024
97340c3
updates
juliusmarminge May 25, 2024
1c4916e
rm dep
juliusmarminge May 25, 2024
3e31afb
try getting stream provider working
juliusmarminge May 26, 2024
0198a3e
Merge branch 'next' into app-router-example
juliusmarminge May 26, 2024
b515336
chore: apply lint and formatting fixes
autofix-ci[bot] May 26, 2024
ffa4e27
this'd be neat
juliusmarminge May 27, 2024
88aae3e
okayyy
juliusmarminge May 27, 2024
d0fd6d5
nicee
juliusmarminge May 27, 2024
857c383
Merge branch 'next' into app-router-example
juliusmarminge May 27, 2024
c11abdf
rm prefetchquery hoc
juliusmarminge May 27, 2024
37762c3
rethrow
juliusmarminge May 27, 2024
86545ce
reset lock
juliusmarminge May 27, 2024
1ee8428
revert autofixed stuff...
juliusmarminge May 27, 2024
f10c6f7
chore: apply lint and formatting fixes
autofix-ci[bot] May 27, 2024
1fc24fe
disable autofixer
juliusmarminge May 27, 2024
4d068ac
release tmp
juliusmarminge May 27, 2024
895b718
Merge branch 'next' into app-router-example
juliusmarminge Jun 2, 2024
59cdcc8
clarify comments
juliusmarminge Jun 2, 2024
bc87f07
fix bad import paths
juliusmarminge Jun 2, 2024
d4ee6d9
merge
juliusmarminge Jun 2, 2024
840e186
hmm
juliusmarminge Jun 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ permissions:
contents: read
on:
pull_request:
branches:
- '!app-router-example'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ dist/
.eslintcache
*.tsbuildinfo

db.sqlite

node_modules/
package-lock.json
yarn.lock
Expand Down
4 changes: 4 additions & 0 deletions examples/next-app-router/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AUTH_GITHUB_ID=''
AUTH_GITHUB_SECRET=''
AUTH_URL='http://localhost:3000'
AUTH_SECRET='s1cIAYmD4Rb1KbeAYebbgUauyoT+/KrRV7d97c3DfqI='
21 changes: 21 additions & 0 deletions examples/next-app-router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Next.js App Router + tRPC

This example shows you how to use tRPC with the Next.js App Router. Spiced up with some Tailwind and some shadcn/ui components.

## Setup

```bash
npx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-app-router trpc-next-app-router
cd next-app-router
pnpm i
pnpm db:push
pnpm dev
```

## Development

### Start project

```bash
pnpm dev
```
5 changes: 5 additions & 0 deletions examples/next-app-router/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
6 changes: 6 additions & 0 deletions examples/next-app-router/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: { ignoreDuringBuilds: true },
};

export default nextConfig;
57 changes: 57 additions & 0 deletions examples/next-app-router/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@examples/next-app-router",
"version": "0.1.0",
"type": "module",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"db:push": "drizzle-kit push:sqlite --config src/db/config.ts",
"db:studio": "drizzle-kit studio --config src/db/config.ts"
},
"dependencies": {
"@auth/drizzle-adapter": "^0.6.2",
"@hookform/resolvers": "npm:@hookform/resolvers@3.3.4",
"@juliusmarminge/next-themes": "^0.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
"@tanstack/react-query": "^5.0.0",
"@trpc/client": "npm:@trpc/client@next",
"@trpc/next": "npm:@trpc/next@next",
"@trpc/react-query": "npm:@trpc/react-query@next",
"@trpc/server": "npm:@trpc/server@next",
"better-sqlite3": "^9.4.0",
"class-variance-authority": "^0.7.0",
"drizzle-orm": "^0.29.3",
"geist": "^1.2.2",
"next": "^14.0.1",
"next-auth": "npm:next-auth@5.0.0-beta.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.3",
"server-only": "^0.0.1",
"sonner": "^1.4.0",
"tailwind-merge": "^2.0.0",
"tupleson": "0.23.1",
"zod": "^3.0.0"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^5.0.0",
"@types/node": "^20.10.0",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"drizzle-kit": "^0.20.14",
"eslint": "^8.40.0",
"eslint-config-next": "^14.0.1",
"postcss": "^8.4.14",
"tailwindcss": "^3.3.0",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.3.3"
},
"eslintConfig": {
"root": true,
"extends": "next/core-web-vitals"
}
}
1 change: 1 addition & 0 deletions examples/next-app-router/postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { plugins: { tailwindcss: {} } };
117 changes: 117 additions & 0 deletions examples/next-app-router/src/app/_components/posts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use client';

import { CreatePostSchema } from '~/lib/validators';
import { trpc } from '~/trpc/react';
import { Button } from '~/ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
useForm,
} from '~/ui/form';
import { Input } from '~/ui/input';
import { toast } from 'sonner';

export function Posts() {
// Won't be fetched initially, but instead pulled from query cache.
const [data] = trpc.post.list.useSuspenseQuery();

const utils = trpc.useUtils();
const deletePost = trpc.post.delete.useMutation({
onSuccess: async () => {
await utils.post.invalidate();
},
onError: (err) => {
toast.error(
err?.data?.code === 'UNAUTHORIZED'
? 'You can only delete your own posts'
: 'Failed to delete post',
);
},
});

return (
<div className="flex flex-col gap-4">
<CreatePost />
{data.length === 0 && <p>No posts</p>}

{data.map(({ post, user }) => (
<div key={post.id} className="bg-background-muted relative rounded p-4">
<h2 className="text-lg font-semibold">{post.title}</h2>
<p className="text-sm">{post.content}</p>
<p className="text-xs text-gray-500">
By {user?.name ?? 'unknown'} at {post.createdAt.toISOString()}
</p>
<Button
onClick={() => deletePost.mutate(post.id)}
className="absolute right-2 top-2"
>
Delete
</Button>
</div>
))}
</div>
);
}

function CreatePost() {
const form = useForm({
schema: CreatePostSchema,
defaultValues: {
content: '',
title: '',
},
});

const utils = trpc.useUtils();
const createPost = trpc.post.create.useMutation({
onSuccess: async () => {
form.reset();
await utils.post.invalidate();
},
onError: (err) => {
toast.error(
err?.data?.code === 'UNAUTHORIZED'
? 'You must be logged in to post'
: 'Failed to create post',
);
},
});

return (
<Form {...form}>
<form
className="flex w-full flex-col gap-2"
onSubmit={form.handleSubmit((data) => createPost.mutate(data))}
>
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormControl>
<Input {...field} placeholder="Title" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="content"
render={({ field }) => (
<FormItem>
<FormControl>
<Input {...field} placeholder="Content" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button>Create</Button>
</form>
</Form>
);
}
33 changes: 33 additions & 0 deletions examples/next-app-router/src/app/_components/user-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { auth, signIn, signOut } from '~/lib/auth';
import { Button } from '~/ui/button';

export async function UserButton() {
const session = await auth();
if (session) {
return (
<form
action={async () => {
'use server';
await signOut();
}}
>
<Button type="submit" className="px-10">
Sign Out
</Button>
</form>
);
}

return (
<form
action={async () => {
'use server';
await signIn('github', { redirectTo: '/' });
}}
>
<Button type="submit" className="px-10">
Sign in with GitHub
</Button>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GET, POST } from '~/lib/auth';
19 changes: 19 additions & 0 deletions examples/next-app-router/src/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { auth } from '~/lib/auth';
import { createTRPCContext } from '~/trpc/init';
import { appRouter } from '~/trpc/routers/_app';

const handler = auth(async (req) =>
fetchRequestHandler({
endpoint: '/api/trpc',
router: appRouter,
req,
createContext: () =>
createTRPCContext({
session: req.auth,
headers: req.headers,
}),
}),
);

export { handler as GET, handler as POST };
51 changes: 51 additions & 0 deletions examples/next-app-router/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--background-muted: 240 4.8% 95.9%;
--foreground: 240 10% 3.9%;
--foreground-muted: 240 3.8% 46.1%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 72.22% 50.59%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5% 64.9%;

--radius: 0.5rem;
}

.dark {
--background: 240 10% 3.9%;
--background-muted: 240 3.7% 15.9%;
--foreground: 0 0% 98%;
--foreground-muted: 240 5% 64.9%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
}
38 changes: 38 additions & 0 deletions examples/next-app-router/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import './globals.css';
import { ThemeProvider } from '@juliusmarminge/next-themes';
import { cn } from '~/lib/cn';
import { TRPCReactProvider } from '~/trpc/react';
import { Toaster } from '~/ui/sonner';
import { GeistSans } from 'geist/font/sans';
import type { Metadata, Viewport } from 'next';

export const metadata = {
title: 'Next.js App Router x tRPC',
description: 'Next.js App Router x tRPC',
icons: [{ rel: 'icon', url: '/favicon.ico' }],
} satisfies Metadata;

export const viewport = {
themeColor: [
{ media: '(prefers-color-scheme: light)', color: 'white' },
{ media: '(prefers-color-scheme: dark)', color: 'black' },
],
} satisfies Viewport;

export default function RootLayout(props: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body
className={cn(
'bg-background text-foreground min-h-screen font-sans antialiased',
GeistSans.variable,
)}
>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<TRPCReactProvider>{props.children}</TRPCReactProvider>
<Toaster />
</ThemeProvider>
</body>
</html>
);
}