This document provides detailed information for developers working on the TickPath project, including architectural decisions, coding standards, and advanced development workflows.
TickPath follows a modern full-stack architecture using TanStack Start, which provides:
- Server-Side Rendering (SSR) - Pages are rendered on the server for better SEO and initial load performance
- Client-Side Hydration - Interactive features are hydrated on the client
- Type-Safe API - End-to-end type safety from database to UI
- File-Based Routing - Intuitive routing structure using the filesystem
User Input → TanStack Query → ORPC → Drizzle ORM → MySQL
↓
React Components ← TanStack Query ← ORPC Response ← Database
- User interactions trigger API calls through TanStack Query
- ORPC handlers process requests with type safety and validation
- Drizzle ORM provides type-safe database operations
- MySQL database stores and retrieves data
- Responses flow back through the same chain with full type safety
- Reason: Better TypeScript integration, file-based routing, built-in data loading
- Benefits: Type-safe navigation, automatic code splitting, better developer experience
- Reason: Simpler setup, excellent TypeScript integration, built for React
- Benefits: Less boilerplate, automatic type inference, better error handling
- Reason: Better performance, closer to SQL, lightweight
- Benefits: More control over queries, better TypeScript support, smaller bundle size
- Reason: Modern architecture, better TypeScript support, more flexible
- Benefits: Better session management, easier customization, framework agnostic
// Component with proper TypeScript and patterns
interface ComponentProps {
data: SomeType;
onAction?: (id: string) => void;
}
export function Component({ data, onAction }: ComponentProps) {
// Hooks at the top
const [state, setState] = useState();
const query = useQuery(/* ... */);
// Event handlers
const handleClick = useCallback(() => {
onAction?.(data.id);
}, [onAction, data.id]);
// Early returns for loading/error states
if (query.isLoading) return <LoadingSpinner />;
if (query.isError) return <ErrorMessage error={query.error} />;
// Main render
return <div className="component-root">{/* ... */}</div>;
}// Custom hook for reusable logic
export function useIssueActions() {
const queryClient = useQueryClient();
const updateStatus = useMutation({
mutationFn: orpc.issues.updateStatus.mutate,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["issues"] });
},
});
return {
updateStatus,
isUpdating: updateStatus.isPending,
};
}// Always use Drizzle query builder for type safety
export const getAllIssuesWithDetails = async () => {
return await db.query.issue.findMany({
with: {
status: true,
priority: true,
assignee: {
columns: {
id: true,
name: true,
image: true,
},
},
labels: {
with: {
label: true,
},
},
},
});
};-
Always use explicit types for public APIs
// Good interface CreateIssueInput { title: string; description?: string; statusId: string; } // Avoid function createIssue(data: any) { ... }
-
Use type-safe database operations
// Good const issue = await db.query.issue.findFirst({ where: eq(issue.id, issueId), }); // Avoid const issue = await db.execute( sql`SELECT * FROM issue WHERE id = ${issueId}` );
-
Prefer union types over enums
// Good type IssueStatus = 'todo' | 'in_progress' | 'done'; // Avoid (unless really needed) enum IssueStatus { ... }
- Use functional components with hooks
- Prefer composition over inheritance
- Extract complex logic into custom hooks
- Use React.memo() for expensive components
- Always handle loading and error states
-
Use Tailwind utility classes
<div className="flex items-center gap-2 p-4 bg-background border rounded-lg">
-
Use CSS variables for theming
.custom-component { color: hsl(var(--foreground)); background: hsl(var(--background)); }
-
Follow shadcn/ui patterns for custom components
-
Create feature branch from main
git checkout -b feature/issue-filtering
-
Database changes (if needed)
# Modify schema in src/db/schema.ts pnpm run db:generate # Generate migration pnpm run db:push # Apply to dev database
-
Implement backend changes
# Add/modify ORPC handlers in src/orpc/router.ts # Ensure type safety and input validation
-
Implement frontend changes
# Create/modify components # Add/update routes if needed # Implement proper error handling
-
Test the feature
pnpm run dev # Manual testing in browser # Check responsive design # Test error states
# 1. Modify schema.ts
# 2. Generate migration
pnpm run db:generate
# 3. Review generated migration
# 4. Apply to database
pnpm run db:push
# 5. Update seed data if needed
pnpm run db:seedpnpm run db:studioOpens a web interface to browse and edit database data at http://localhost:4983
-
Define the handler
// src/orpc/router.ts const createIssue = os .input( z.object({ title: z.string().min(1), description: z.string().optional(), statusId: z.string(), }) ) .handler(async ({ input }) => { return await db.insert(issue).values(input); });
-
Add to router
export const router = { issues: { getAll: getAllIssues, create: createIssue, // Add new endpoint }, };
-
Use in components
const createIssueMutation = useMutation({ mutationFn: orpc.issues.create.mutate, });
- Feature works in both light and dark themes
- Responsive design works on mobile/tablet/desktop
- Loading states are shown appropriately
- Error states are handled gracefully
- Type safety is maintained end-to-end
- Database operations work correctly
- Authentication/authorization works as expected
- Chrome (primary development browser)
- Firefox
- Safari (if available)
- Mobile browsers (Chrome/Safari on iOS/Android)
# Check if Docker container is running
docker ps
# Restart database
docker-compose down
docker-compose up -d
# Check database connection
pnpm run db:studio# Regenerate route tree if router types are wrong
# This happens after adding new routes
pnpm run dev # Should auto-regenerate
# Check for TypeScript errors
npx tsc --noEmit# Clear node_modules and reinstall
rm -rf node_modules pnpm-lock.yaml
pnpm install
# Clear Vite cache
rm -rf .vite- TypeScript and JavaScript Language Features
- Tailwind CSS IntelliSense
- ES7+ React/Redux/React-Native snippets
- Prettier - Code formatter
- Auto Rename Tag
- React Developer Tools
- TanStack Query DevTools (built-in)
- TanStack Router DevTools (built-in)
-
Code Splitting
- TanStack Router automatically code-splits routes
- Use dynamic imports for heavy components
const HeavyComponent = lazy(() => import("./HeavyComponent"));
-
Query Optimization
- Use proper staleTime and cacheTime
- Implement optimistic updates where appropriate
const { data } = useQuery({ queryKey: ["issues"], queryFn: orpc.issues.getAll.query, staleTime: 5 * 60 * 1000, // 5 minutes });
-
Image Optimization
- Use appropriate image formats (WebP when possible)
- Implement lazy loading for images
-
Database Queries
- Use indexes on frequently queried columns
- Avoid N+1 queries using Drizzle's
withoption
const issues = await db.query.issue.findMany({ with: { assignee: true, // Join instead of separate query }, });
-
Caching
- TanStack Query handles client-side caching
- Consider server-side caching for expensive operations
-
Always validate user permissions
const updateIssue = os .input(updateIssueSchema) .handler(async ({ input, context }) => { // Check if user has permission to update this issue if (!canUserUpdateIssue(context.user, input.issueId)) { throw new Error("Unauthorized"); } // ... rest of handler });
-
Input validation with Zod
const schema = z.object({ title: z.string().min(1).max(100), email: z.string().email(), });
-
Environment variables
- Never commit secrets to version control
- Use environment-specific configurations
- Validate environment variables with
@t3-oss/env-core
- Sanitize user inputs
- Use HTTPS in production
- Implement proper session management
- Follow OWASP guidelines
- TanStack Query DevTools - Monitor API calls and cache
- TanStack Router DevTools - Monitor routing and navigation
- Browser Performance Tab - Monitor rendering performance
- Console warnings/errors - Fix all console output
- Error Boundaries - Implement proper error handling
- Logging - Add structured logging for API endpoints
- Performance Monitoring - Consider adding performance monitoring
- User Analytics - Track user interactions (if needed)
- All TypeScript errors resolved
- Build completes successfully
- Database migrations applied
- Environment variables configured
- Dependencies updated and secure
- Performance testing completed
- Development - Local with Docker database
- Staging - Production-like environment for testing
- Production - Live application
Each environment should have its own:
- Database instance
- Environment variables
- Authentication configuration
This development guide should be updated as the project evolves and new patterns emerge. All developers should contribute to keeping this documentation current and comprehensive.