diff --git a/apps/web/package.json b/apps/web/package.json index 553150b..826ef18 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,14 +17,17 @@ "@tanstack/react-form": "^1.23.5", "@tanstack/react-query": "^5.80.6", "@tanstack/react-router": "^1.141.1", + "@tanstack/react-router-ssr-query": "^1.162.6", "@tanstack/react-router-with-query": "^1.130.17", "@tanstack/react-start": "^1.141.1", "@tanstack/router-plugin": "^1.141.1", "@zendegi/auth": "workspace:*", + "@zendegi/db": "workspace:*", "@zendegi/env": "workspace:*", "better-auth": "catalog:", "clsx": "^2.1.1", "dotenv": "catalog:", + "drizzle-orm": "^0.45.1", "lucide-react": "^0.525.0", "next-themes": "^0.4.6", "react": "19.2.3", diff --git a/apps/web/src/functions/get-timelines.ts b/apps/web/src/functions/get-timelines.ts new file mode 100644 index 0000000..33fe313 --- /dev/null +++ b/apps/web/src/functions/get-timelines.ts @@ -0,0 +1,20 @@ +import { eq } from "drizzle-orm"; +import { db } from "@zendegi/db"; +import { createServerFn } from "@tanstack/react-start"; +import { timeline } from "@zendegi/db/schema/timeline"; +import { queryOptions } from "@tanstack/react-query"; + +export const getTimelines = createServerFn({ method: "GET" }).handler( + async () => { + return db.query.timeline.findMany({ + where: eq(timeline.visibility, "public"), + orderBy: (t, { desc }) => [desc(t.createdAt)], + }); + } +); + +export const timelinesQueryOptions = () => + queryOptions({ + queryKey: ["timelines"], + queryFn: () => getTimelines(), + }); diff --git a/apps/web/src/functions/get-user.ts b/apps/web/src/functions/get-user.ts index e0489d6..f62b74e 100644 --- a/apps/web/src/functions/get-user.ts +++ b/apps/web/src/functions/get-user.ts @@ -1,5 +1,4 @@ import { createServerFn } from "@tanstack/react-start"; - import { authMiddleware } from "@/middleware/auth"; export const getUser = createServerFn({ method: "GET" }) diff --git a/apps/web/src/router.tsx b/apps/web/src/router.tsx index 28968b8..90d2103 100644 --- a/apps/web/src/router.tsx +++ b/apps/web/src/router.tsx @@ -1,19 +1,29 @@ import { createRouter as createTanStackRouter } from "@tanstack/react-router"; +import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query"; +import { QueryClient } from "@tanstack/react-query"; import Loader from "./components/loader"; import "./index.css"; import { routeTree } from "./routeTree.gen"; export const getRouter = () => { + const queryClient = new QueryClient(); + const router = createTanStackRouter({ routeTree, + context: { queryClient }, scrollRestoration: true, defaultPreloadStaleTime: 0, - context: {}, defaultPendingComponent: () => , defaultNotFoundComponent: () =>
Not Found
, Wrap: ({ children }) => <>{children}, }); + + setupRouterSsrQueryIntegration({ + router, + queryClient, + }); + return router; }; diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index e3065b5..407d5a1 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -8,9 +8,12 @@ import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import Header from "../components/header"; import appCss from "../index.css?url"; +import type { QueryClient } from "@tanstack/react-query"; import { Toaster } from "@/components/ui/sonner"; -export interface RouterAppContext {} +export interface RouterAppContext { + queryClient: QueryClient; +} export const Route = createRootRouteWithContext()({ head: () => ({ diff --git a/apps/web/src/routes/timelines.tsx b/apps/web/src/routes/timelines.tsx index 6af7fba..7346d99 100644 --- a/apps/web/src/routes/timelines.tsx +++ b/apps/web/src/routes/timelines.tsx @@ -1,9 +1,24 @@ import { createFileRoute } from "@tanstack/react-router"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { timelinesQueryOptions } from "@/functions/get-timelines"; export const Route = createFileRoute("/timelines")({ + loader: async ({ context }) => { + await context.queryClient.ensureQueryData(timelinesQueryOptions()); + }, component: RouteComponent, }); function RouteComponent() { - return
List of timelines
; + const timelinesQuery = useSuspenseQuery(timelinesQueryOptions()); + return ( +
+

List of timelines

+
+ {timelinesQuery.data.map((t) => ( +
{t.title}
+ ))} +
+
+ ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69d82e2..a888590 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: '@tanstack/react-router': specifier: ^1.141.1 version: 1.159.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-router-ssr-query': + specifier: ^1.162.6 + version: 1.162.6(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.3))(@tanstack/react-router@1.159.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.159.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-router-with-query': specifier: ^1.130.17 version: 1.130.17(@tanstack/react-query@5.90.20(react@19.2.3))(@tanstack/react-router@1.159.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.159.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -90,6 +93,9 @@ importers: '@zendegi/auth': specifier: workspace:* version: link:../../packages/auth + '@zendegi/db': + specifier: workspace:* + version: link:../../packages/db '@zendegi/env': specifier: workspace:* version: link:../../packages/env @@ -102,6 +108,9 @@ importers: dotenv: specifier: 'catalog:' version: 17.2.4 + drizzle-orm: + specifier: ^0.45.1 + version: 0.45.1(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0) lucide-react: specifier: ^0.525.0 version: 0.525.0(react@19.2.3) @@ -1538,6 +1547,16 @@ packages: '@tanstack/router-core': optional: true + '@tanstack/react-router-ssr-query@1.162.6': + resolution: {integrity: sha512-njTCANKdS5WeQ4mTGugiacTQ5QSqD/GP/El4rYGCIQpiuLuJDV41CAAdhkXmXqzREYjnbBqBOv7W7loijjTpww==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/query-core': '>=5.90.0' + '@tanstack/react-query': '>=5.90.0' + '@tanstack/react-router': '>=1.127.0' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + '@tanstack/react-router-with-query@1.130.17': resolution: {integrity: sha512-TNaSocW20KuPwUojEm130DLWTr9M5hsSzxiu4QqS2jNCnrGLuDrwMHyP+6fq13lG3YuU4u9O1qajxfJIGomZCg==} engines: {node: '>=12'} @@ -1622,6 +1641,13 @@ packages: webpack: optional: true + '@tanstack/router-ssr-query-core@1.162.6': + resolution: {integrity: sha512-kE1uENrVTubRCJybYUJrHR90Tzxh/tqKX8e6sVnccc694sNB4yNgAMsyxgqhWGUgOux7BZUj5dhoREjaU9XflA==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/query-core': '>=5.90.0' + '@tanstack/router-core': '>=1.127.0' + '@tanstack/router-utils@1.158.0': resolution: {integrity: sha512-qZ76eaLKU6Ae9iI/mc5zizBX149DXXZkBVVO3/QRIll79uKLJZHQlMKR++2ba7JsciBWz1pgpIBcCJPE9S0LVg==} engines: {node: '>=12'} @@ -5574,6 +5600,17 @@ snapshots: transitivePeerDependencies: - csstype + '@tanstack/react-router-ssr-query@1.162.6(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.2.3))(@tanstack/react-router@1.159.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.159.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/query-core': 5.90.20 + '@tanstack/react-query': 5.90.20(react@19.2.3) + '@tanstack/react-router': 1.159.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-ssr-query-core': 1.162.6(@tanstack/query-core@5.90.20)(@tanstack/router-core@1.159.4) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - '@tanstack/router-core' + '@tanstack/react-router-with-query@1.130.17(@tanstack/react-query@5.90.20(react@19.2.3))(@tanstack/react-router@1.159.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.159.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@tanstack/react-query': 5.90.20(react@19.2.3) @@ -5695,6 +5732,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@tanstack/router-ssr-query-core@1.162.6(@tanstack/query-core@5.90.20)(@tanstack/router-core@1.159.4)': + dependencies: + '@tanstack/query-core': 5.90.20 + '@tanstack/router-core': 1.159.4 + '@tanstack/router-utils@1.158.0': dependencies: '@babel/core': 7.29.0