Skip to content

Commit 70f7164

Browse files
authored
Add trpc template as DAB. (#581)
1 parent 7e3c8cf commit 70f7164

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+16211
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"properties": {
3+
"project_name": {
4+
"type": "string",
5+
"default": "my_trpc_app",
6+
"description": "Project Name",
7+
"order": 1,
8+
"pattern": "^[A-Za-z0-9_-]+$",
9+
"pattern_match_failure_message": "Name must consist of letters, numbers, underscores, or hyphens."
10+
},
11+
"sql_warehouse_id": {
12+
"type": "string",
13+
"description": "SQL Warehouse ID (Optional)",
14+
"order": 2
15+
}
16+
},
17+
"success_message": "\nYour new project has been created in the '{{.project_name}}' directory!"
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
node_modules
2+
dist
3+
.git
4+
*.log
5+
.env*
6+
.DS_Store
7+
npm-debug.log*
8+
yarn-debug.log*
9+
yarn-error.log*
10+
coverage
11+
.vscode
12+
.idea
13+
*.swp
14+
*.swo
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Dependencies
2+
node_modules/
3+
/.pnp
4+
.pnp.js
5+
6+
# Testing
7+
/coverage
8+
9+
# Build outputs
10+
/build
11+
/dist
12+
/.next/
13+
/out/
14+
server/public
15+
16+
# Environment variables
17+
.env
18+
.env.local
19+
.env.development.local
20+
.env.test.local
21+
.env.production.local
22+
23+
# Logs
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# Editor directories
29+
.idea/
30+
.vscode/
31+
*.swp
32+
*.swo
33+
34+
# OS files
35+
.DS_Store
36+
Thumbs.db
37+
\n# Databricks\n.databricks/
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
TypeScript full-stack template with tRPC for type-safe API communication between React frontend and Node.js backend. Use this when building type-safe TypeScript applications with the following structure:
2+
- server/: Node.js backend with tRPC API
3+
- client/: React frontend with tRPC client
4+
5+
## Testing Guidelines:
6+
7+
**CRITICAL**: Use Node.js native test runner only. Do NOT import vitest, jest, or supertest.
8+
Put tests next to the code (e.g. src/*.test.ts)
9+
10+
```typescript
11+
import { test } from "node:test";
12+
import { strict as assert } from "node:assert";
13+
```
14+
15+
## Databricks Type Handling:
16+
17+
- **executeQuery REQUIRES Zod schema**: Pass the Zod schema object as second parameter, NOT a TypeScript type annotation
18+
```typescript
19+
// ❌ WRONG - Do NOT use generic type parameter
20+
const result = await client.executeQuery<MyType>(sql);
21+
22+
// ✅ CORRECT - Pass Zod schema as parameter
23+
const mySchema = z.object({ id: z.number(), name: z.string() });
24+
const result = await client.executeQuery(sql, mySchema);
25+
```
26+
- **QueryResult access**: `executeQuery()` returns `{rows: T[], rowCount: number}`. Always use `.rows` property: `const {rows} = await client.executeQuery(...)` or `result.rows.map(...)`
27+
- **Type imports**: Use `import type { T }` (not `import { T }`) when `verbatimModuleSyntax` is enabled
28+
- **Column access**: Use bracket notation `row['column_name']` (TypeScript strict mode requirement)
29+
- **DATE/TIMESTAMP columns**: Databricks returns Date objects. Use `z.coerce.date()` in schemas (never `z.string()` for dates)
30+
- **Dynamic properties**: Cast explicitly `row['order_id'] as number`
31+
32+
### Helper Utilities:
33+
34+
**mapRows<T>(rows, schema)** - Validates and maps raw SQL rows using Zod schema:
35+
```typescript
36+
import { mapRows } from './databricks';
37+
38+
// When you have raw rows and need manual mapping
39+
const rawRows = [{id: 1, name: "Alice"}, {id: 2, name: "Bob"}];
40+
const userSchema = z.object({ id: z.number(), name: z.string() });
41+
const users = mapRows(rawRows, userSchema);
42+
// users is now typed as { id: number; name: string }[]
43+
```
44+
45+
Use this when:
46+
- Processing nested query results
47+
- Manually mapping row data before returning from tRPC
48+
- Need to validate data from non-Databricks sources
49+
50+
## Frontend Styling Guidelines:
51+
52+
### Component Structure Pattern:
53+
- Use container with proper spacing: `<div className="container mx-auto p-4">`
54+
- Page titles: `<h1 className="text-2xl font-bold mb-4">Title</h1>`
55+
- Forms: Use `space-y-4` for vertical spacing between inputs
56+
- Cards: Use shadcn Card components or `border p-4 rounded-md` for item display
57+
- Grids: Use `grid gap-4` for list layouts
58+
59+
### Example App Structure:
60+
```tsx
61+
<div className="container mx-auto p-4">
62+
<h1 className="text-2xl font-bold mb-4">Page Title</h1>
63+
<form className="space-y-4 mb-8">{/* form inputs */}</form>
64+
<div className="grid gap-4">{/* list items */}</div>
65+
</div>
66+
```
67+
68+
### Tailwind Usage:
69+
- Use Tailwind classes directly in JSX
70+
- Avoid @apply unless creating reusable component styles
71+
- When using @apply, only in @layer components (never @layer base)
72+
- Template has CSS variables defined - use via Tailwind (bg-background, text-foreground, etc.)
73+
74+
### Typography & Spacing:
75+
- Headings: text-2xl font-bold with mb-4
76+
- Secondary text: text-foreground/70
77+
- Card titles: text-xl font-semibold
78+
- Form spacing: space-y-4 between inputs, mb-8 after forms
79+
- Grid/list spacing: gap-4 for consistent item spacing
80+
81+
### Component Organization:
82+
Create separate components when:
83+
- Logic exceeds ~100 lines
84+
- Component is reused in multiple places
85+
- Component has distinct responsibility (e.g., ProductForm, ProductList)
86+
File structure:
87+
- Shared UI: client/src/components/ui/
88+
- Feature components: client/src/components/FeatureName.tsx
89+
90+
### Visual Design:
91+
- Adjust visual mood to match user prompt, prefer clean and modern visually appealing aesthetics, but avoid overly flashy designs - keep it professional and user-friendly;
92+
- Use shadcn/radix components (Button, Input, Card, etc.) for consistent UI
93+
- Forms should have loading states: `disabled={isLoading}`
94+
- Show empty states with helpful text when no data exists
95+
96+
### Best Practices:
97+
- Always fetch real data from tRPC (never use mock/hardcoded data)
98+
- Handle nullable fields: `value={field || ''}` for inputs
99+
- Type all callbacks explicitly: `onChange={(e: React.ChangeEvent<HTMLInputElement>) => ...}`
100+
- Use proper relative imports for server types: `import type { Product } from '../../server/src/schema'`
101+
102+
## Data Visualization with Recharts
103+
104+
The template includes Recharts for data visualization. Use Databricks brand colors for chart elements: `['#40d1f5', '#4462c9', '#EB1600', '#0B2026', '#4A4A4A', '#353a4a']` (apply via `stroke` or `fill` props).
105+
106+
### Basic Chart Pattern:
107+
```tsx
108+
import { useState, useEffect } from 'react';
109+
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
110+
import { Card, CardContent, CardHeader, CardTitle } from './components/ui/card';
111+
import { trpc } from './utils/trpc';
112+
113+
function MyDashboard() {
114+
const [data, setData] = useState<{ name: string; value: number }[]>([]);
115+
const [error, setError] = useState<string | null>(null);
116+
117+
useEffect(() => {
118+
// fetch from Databricks via tRPC
119+
trpc.getMetrics.query()
120+
.then(setData)
121+
.catch((err) => setError(err.message));
122+
}, []);
123+
124+
return (
125+
<Card>
126+
<CardHeader>
127+
<CardTitle>My Metrics</CardTitle>
128+
</CardHeader>
129+
<CardContent>
130+
<ResponsiveContainer width="100%" height={300}>
131+
<LineChart data={data}>
132+
<CartesianGrid strokeDasharray="3 3" />
133+
<XAxis dataKey="name" />
134+
<YAxis />
135+
<Tooltip />
136+
<Line type="monotone" dataKey="value" stroke="hsl(var(--primary))" />
137+
</LineChart>
138+
</ResponsiveContainer>
139+
</CardContent>
140+
</Card>
141+
);
142+
}
143+
```
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Multi-stage Dockerfile for tRPC template
2+
FROM node:20-alpine3.22 AS builder
3+
4+
# install build tools for native Node.js modules (e.g., better-sqlite3, bcrypt)
5+
# python3: required by node-gyp, make/g++: build toolchain
6+
RUN apk add --no-cache python3 make g++
7+
8+
WORKDIR /app
9+
10+
# Copy all package files (may not have lock files if agent modified deps)
11+
COPY client/package*.json ./client/
12+
COPY server/package*.json ./server/
13+
14+
# Install all dependencies with BuildKit cache mount
15+
# Note: Using npm install (not npm ci) in build stage because:
16+
# - Agent may add new dependencies to package.json during development
17+
# - package-lock.json may be missing or out of sync
18+
# - npm install will generate/update the lock file
19+
# The generated lock file is then copied to production stage for reproducible builds
20+
RUN --mount=type=cache,target=/root/.npm \
21+
cd client && npm install
22+
23+
RUN --mount=type=cache,target=/root/.npm \
24+
cd server && npm install
25+
26+
# Copy server source (needed for client build due to tRPC types)
27+
COPY server/ ./server/
28+
29+
# Copy client source and build
30+
COPY client/ ./client/
31+
RUN cd client && npm run build
32+
33+
# Stage 2: Production image
34+
FROM node:20-alpine3.22
35+
36+
# Install curl for healthcheck and build tools for native modules
37+
RUN apk add --no-cache curl python3 make g++
38+
39+
WORKDIR /app
40+
41+
# Copy server source first (excluding node_modules via .dockerignore)
42+
COPY server/ ./server/
43+
44+
# Copy package files including the lock file generated in build stage
45+
# This ensures reproducible production builds with exact dependency versions
46+
COPY --from=builder /app/server/package*.json ./server/
47+
48+
# Install dependencies - this will compile native modules for Alpine
49+
RUN --mount=type=cache,target=/root/.npm \
50+
cd server && npm ci --omit=dev
51+
52+
# Copy built client from builder stage
53+
RUN mkdir -p server/public
54+
COPY --from=builder /app/client/dist/ ./server/public/
55+
56+
# Set working directory to server
57+
WORKDIR /app/server
58+
59+
# Expose port
60+
EXPOSE 8000
61+
62+
# Start the application
63+
CMD ["npm", "start"]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
command: ["npm", "run", "start"]
2+
env:
3+
- name: "DATABRICKS_AUTH_MODE"
4+
value: "app"
5+
- name: DATABRICKS_WAREHOUSE_ID
6+
valueFrom: "sql-warehouse"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "src/index.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

0 commit comments

Comments
 (0)