| 1 | import { createFileRoute } from "@tanstack/react-router"; |
| 2 | import { createServerFn } from "@tanstack/react-start"; |
| 3 | import { getLatestPosts } from "../prisma/queries"; |
| 4 | |
| 5 | const getRuntime = createServerFn({ method: "GET" }).handler(async () => { |
| 6 | const posts = await getLatestPosts(10); |
| 7 | return { |
| 8 | databaseReady: Boolean(process.env.DATABASE_URL), |
| 9 | configurationReady: process.env.TANSTACK_SHARED_FLAG === "project-level", |
| 10 | message: process.env.TANSTACK_MESSAGE ?? "missing message", |
| 11 | posts: posts.map((post) => ({ |
| 12 | title: post.title, |
| 13 | excerpt: post.excerpt, |
| 14 | })), |
| 15 | }; |
| 16 | }); |
| 17 | |
| 18 | export const Route = createFileRoute("/")({ |
| 19 | loader: () => getRuntime(), |
| 20 | component: Home, |
| 21 | }); |
| 22 | |
| 23 | function Home() { |
| 24 | const runtime = Route.useLoaderData(); |
| 25 | |
| 26 | return ( |
| 27 | <main |
| 28 | style={{ |
| 29 | minHeight: "100vh", |
| 30 | padding: "48px 20px", |
| 31 | fontFamily: |
| 32 | 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', |
| 33 | background: "#f7f8fb", |
| 34 | color: "#171923", |
| 35 | }} |
| 36 | > |
| 37 | <section |
| 38 | style={{ |
| 39 | width: "min(680px, 100%)", |
| 40 | margin: "0 auto", |
| 41 | display: "grid", |
| 42 | gap: 20, |
| 43 | }} |
| 44 | > |
| 45 | <div> |
| 46 | <p |
| 47 | style={{ |
| 48 | margin: "0 0 8px", |
| 49 | color: "#4c6fff", |
| 50 | fontSize: 13, |
| 51 | fontWeight: 700, |
| 52 | textTransform: "uppercase", |
| 53 | }} |
| 54 | > |
| 55 | Alchemy + Prisma |
| 56 | </p> |
| 57 | <h1 style={{ margin: 0, fontSize: 40, lineHeight: 1.05 }}> |
| 58 | TanStack Start with Prisma Postgres |
| 59 | </h1> |
| 60 | </div> |
| 61 | |
| 62 | <dl |
| 63 | style={{ |
| 64 | margin: 0, |
| 65 | display: "grid", |
| 66 | gap: 10, |
| 67 | padding: 20, |
| 68 | border: "1px solid #d9deea", |
| 69 | borderRadius: 8, |
| 70 | background: "#ffffff", |
| 71 | }} |
| 72 | > |
| 73 | <RuntimeRow label="Message" value={runtime.message} /> |
| 74 | <RuntimeRow |
| 75 | label="Database" |
| 76 | value={runtime.databaseReady ? "ready" : "unavailable"} |
| 77 | /> |
| 78 | <RuntimeRow |
| 79 | label="Configuration" |
| 80 | value={runtime.configurationReady ? "ready" : "unavailable"} |
| 81 | /> |
| 82 | <RuntimeRow label="Posts" value={String(runtime.posts.length)} /> |
| 83 | <RuntimeRow label="Health" value="/api/health" /> |
| 84 | </dl> |
| 85 | |
| 86 | <section |
| 87 | style={{ |
| 88 | display: "grid", |
| 89 | gap: 12, |
| 90 | }} |
| 91 | > |
| 92 | <h2 style={{ margin: "12px 0 0", fontSize: 18 }}>Latest Posts</h2> |
| 93 | <div |
| 94 | style={{ |
| 95 | display: "grid", |
| 96 | gap: 10, |
| 97 | }} |
| 98 | > |
| 99 | {runtime.posts.map((post) => ( |
| 100 | <article |
| 101 | key={post.title} |
| 102 | style={{ |
| 103 | padding: 16, |
| 104 | border: "1px solid #d9deea", |
| 105 | borderRadius: 8, |
| 106 | background: "#ffffff", |
| 107 | }} |
| 108 | > |
| 109 | <h3 style={{ margin: "0 0 6px", fontSize: 15 }}> |
| 110 | {post.title} |
| 111 | </h3> |
| 112 | <p style={{ margin: 0, color: "#4a5568", fontSize: 13 }}> |
| 113 | {post.excerpt} |
| 114 | </p> |
| 115 | </article> |
| 116 | ))} |
| 117 | </div> |
| 118 | </section> |
| 119 | </section> |
| 120 | </main> |
| 121 | ); |
| 122 | } |
| 123 | |
| 124 | function RuntimeRow(props: Readonly<{ label: string; value: string }>) { |
| 125 | return ( |
| 126 | <div |
| 127 | style={{ |
| 128 | display: "grid", |
| 129 | gridTemplateColumns: "120px minmax(0, 1fr)", |
| 130 | gap: 12, |
| 131 | alignItems: "baseline", |
| 132 | }} |
| 133 | > |
| 134 | <dt style={{ color: "#667085", fontSize: 13 }}>{props.label}</dt> |
| 135 | <dd |
| 136 | style={{ |
| 137 | margin: 0, |
| 138 | minWidth: 0, |
| 139 | overflowWrap: "anywhere", |
| 140 | fontFamily: |
| 141 | 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace', |
| 142 | fontSize: 13, |
| 143 | }} |
| 144 | > |
| 145 | {props.value} |
| 146 | </dd> |
| 147 | </div> |
| 148 | ); |
| 149 | } |