A modern web application for browsing and discovering 3D models, built with Next.js, TypeScript, and Drizzle ORM.
- Browse 3D Models: View a curated collection of 3D models across various categories
- Category Filtering: Filter models by category (3D Printer, Art, Education, Fashion, etc.)
- Responsive Design: Optimized for desktop, tablet, and mobile devices
- Type-Safe Database: Full TypeScript support with Drizzle ORM
- Performance Optimized: Caching for frequently accessed data
- Modern Stack: Built with Next.js 15, TypeScript, and Tailwind CSS
- Feature-Based Architecture: Well-organized codebase with clear separation of concerns
- Framework: Next.js 15 with App Router and PPR (Partial Prerendering)
- Language: TypeScript
- Styling: Tailwind CSS v4
- Database: Neon (PostgreSQL) with Drizzle ORM
- Authentication: NextAuth.js v5 with Google OAuth
- Search Params: nuqs for type-safe URL state management
- Linting & Formatting: Biome
- Type Checking: tsgo
- Package Manager: Bun
- Build Tool: Turbopack
src/
├── app/ # Next.js App Router
│ ├── _navigation/ # Private navigation components
│ │ ├── AuthButtons.tsx
│ │ ├── Navbar.tsx
│ │ └── NavLink.tsx
│ ├── 3d-models/ # 3D models routes
│ │ ├── @categories/ # Parallel route for categories nav
│ │ ├── @results/ # Parallel route for search results
│ │ ├── [id]/ # Individual model page
│ │ ├── categories/ # Category-specific pages
│ │ ├── layout.tsx # Models layout
│ │ └── page.tsx # Models listing page
│ ├── about/ # About page
│ ├── auth/ # Authentication routes
│ ├── api/ # API routes
│ ├── globals.css # Global styles
│ ├── layout.tsx # Root layout
│ └── page.tsx # Home page
├── features/
│ ├── models/ # Models feature
│ │ ├── actions/ # Server actions
│ │ │ ├── likes.ts
│ │ │ └── search-actions.ts
│ │ ├── components/ # Model-specific components
│ │ │ ├── AdvancedSearchForm.tsx
│ │ │ ├── EnhancedSearchInput.tsx
│ │ │ ├── HeartButton.tsx
│ │ │ ├── LikeStatus.tsx
│ │ │ ├── ModelCard.tsx
│ │ │ ├── ModelsGrid.tsx
│ │ │ └── SearchInput.tsx
│ │ ├── queries/ # Model data queries (split files)
│ │ │ ├── get-all-models.ts
│ │ │ ├── get-model-by-id.ts
│ │ │ ├── get-model-with-like-status.ts
│ │ │ ├── get-models-by-category.ts
│ │ │ └── search-models.ts
│ │ ├── schemas/ # Validation schemas
│ │ │ └── search-schemas.ts
│ │ ├── utils/ # Model utilities
│ │ │ └── cache-invalidation.ts
│ │ └── search-params.ts # Type-safe search params
│ └── categories/ # Categories feature
│ ├── components/ # Category-specific components
│ │ ├── CategoriesNav.tsx
│ │ └── CategoriesNavClient.tsx
│ └── queries/ # Category data queries (split files)
│ ├── get-all-categories.ts
│ ├── get-category-by-slug.ts
│ └── get-display-name-from-slug.ts
├── components/ # Shared/generic components
│ └── Pill.tsx # Reusable pill component
├── db/ # Database configuration
│ ├── schema/ # Database schema definitions
│ │ ├── auth.ts # Authentication tables
│ │ ├── likes.ts # Likes table
│ │ ├── models.ts # Models and categories tables
│ │ ├── relations.ts # Table relations
│ │ └── index.ts # Schema exports
│ ├── schema.ts # Schema exports
│ ├── seed.ts # Database seeding script
│ └── index.ts # Database connection
├── lib/ # Utility functions
│ ├── auth.ts # NextAuth configuration
│ └── date.ts # Date utilities
├── types/ # Type definitions
│ └── index.ts # Shared types
└── middleware.ts # Next.js middleware
The project follows a feature-based architecture where related functionality is co-located:
features/models/
: All model-related components, actions, queries, and search paramsfeatures/categories/
: All category-related components and data queriesapp/_navigation/
: Private navigation components (not part of routing)
_
prefix: Private folders that are not part of Next.js routingfeatures/
: Feature-based modules with their own components and queriescomponents/
: Shared/generic components used across featuresdb/seed-data/
: Explicitly named seed data files
- Node.js 18+ or Bun
- Neon database account (or any PostgreSQL database)
-
Clone the repository
git clone <repository-url> cd 3dmodels
-
Install dependencies
bun install
-
Environment Setup Create a
.env
file in the root directory:DATABASE_URL="your-neon-database-connection-string" AUTH_GOOGLE_ID="your-google-oauth-client-id" AUTH_GOOGLE_SECRET="your-google-oauth-client-secret"
-
Database Setup
# Generate and run migrations bun run db:generate bun run db:push # Seed the database with initial data bun run db:seed
-
Start the development server
bun run dev
Open http://localhost:3000 to view the application.
id
: Primary key (auto-increment)displayName
: Human-readable category nameslug
: URL-friendly identifier (unique)
id
: Primary key (auto-increment)name
: Model namedescription
: Model descriptionlikes
: Number of likesimage
: Image URLcategorySlug
: Foreign key to categories.slugdateAdded
: Timestamp when model was added
bun run db:generate
- Generate new migration filesbun run db:migrate
- Run pending migrationsbun run db:push
- Push schema changes directly to databasebun run db:studio
- Open Drizzle Studio for database managementbun run db:seed
- Seed database with initial data
The application uses Next.js cache with granular cache tags for efficient invalidation:
- Models: Cached with
models
,model-{id}
, andmodels-category-{slug}
tags - Categories: Cached with
categories
tag - Cache Life: 1 hour for most queries, weeks for static categories
- Invalidation: Centralized utilities in
features/models/utils/cache-invalidation.ts
- Colors: Custom color palette with orange accent
- Typography: Consistent font hierarchy
- Spacing: Systematic spacing using Tailwind utilities
- Responsive: Mobile-first responsive design
features/models/components/ModelCard
- Individual model displayfeatures/models/components/ModelsGrid
- Grid layout for model cardsfeatures/models/components/HeartButton
- Like/unlike functionalityfeatures/models/components/SearchInput
- Model search functionalityfeatures/categories/components/CategoriesNav
- Category filtering sidebar
app/_navigation/Navbar
- Main navigationapp/_navigation/NavLink
- Navigation link with active state
components/Pill
- Small label component
lib/auth
- NextAuth configuration with Google OAuth
- Biome: Linting and formatting
- tsgo: TypeScript type checking
- TypeScript: Static type checking
bun run dev
- Start development serverbun run build
- Build for productionbun run start
- Start production serverbun run lint
- Run Biome linterbun run lint:fix
- Fix linting issuesbun run format
- Format code with Biomebun run type
- Run TypeScript type checking
The project follows a consistent coding style with:
- ES modules (import/export syntax)
- TypeScript for type safety
- Tailwind CSS for styling
- Feature-based organization
- Component-specific type definitions
- Proper error handling and logging
- Connect your repository to Vercel
- Set environment variables in Vercel dashboard
- Deploy automatically on push to main branch
Ensure these are set in your deployment environment:
DATABASE_URL
: Your Neon database connection stringAUTH_GOOGLE_ID
: Your Google OAuth client IDAUTH_GOOGLE_SECRET
: Your Google OAuth client secret
- Update
src/db/seed-data/models.ts
with new model data - Run
bun run db:seed
to update the database
- Update
src/db/seed-data/categories.ts
with new category data - Run
bun run db:seed
to update the database
- Use centralized cache invalidation utilities in
features/models/utils/cache-invalidation.ts
- Functions:
invalidateAllModels()
,invalidateModel(id)
,invalidateCategory(slug)
- Cache tags provide granular control over what gets invalidated
- Automatic cache invalidation on data mutations
- Fork the repository
- Create a feature branch
- Make your changes following the feature-based architecture
- Run tests and linting
- Submit a pull request
This project is licensed under the MIT License.
For support and questions:
- Check the documentation
- Review existing issues
- Create a new issue with detailed information