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