A modern, full-stack personal portfolio website with a FastAPI backend and dynamic frontend built with Alpine.js. Features a complete admin panel for content management, RESTful API, and database integration with Supabase.
- Full-Stack Architecture: FastAPI backend with PostgreSQL database (Supabase)
- Admin Panel: Secure content management system with JWT authentication
- Dynamic Content: All portfolio sections load from database via REST API
- Responsive Design: Mobile-first approach with Tailwind CSS
- Modern UI/UX: Smooth animations, interactive elements, and clean design
- Blog Section: Markdown-based blog with categories and tags
- Bookshelf: Track reading progress with statistics
- Performance Optimized: Lazy loading, efficient queries, and serverless deployment
portfolio-website/
├── index.html # Main portfolio page
├── blog/ # Blog section
│ └── index.html # Blog listing page
├── bookshelf/ # Bookshelf section
│ └── index.html # Book collection page
├── assets/ # All static assets
│ ├── css/ # Modular stylesheets
│ │ ├── base.css # Base styles and resets
│ │ ├── components.css # Reusable component styles
│ │ ├── animations.css # Animation keyframes
│ │ ├── skills_terminal.css # Skills section styling
│ │ ├── utilities.css # Utility classes
│ │ └── main.css # Main stylesheet (imports all)
│ ├── js/ # JavaScript modules
│ │ ├── api.js # API service layer (backend communication)
│ │ ├── config.js # Tailwind configuration
│ │ ├── portfolio.js # Alpine.js main component
│ │ └── app.js # Navigation and routing logic
│ └── images/ # Image assets
├── backend/ # FastAPI backend application
│ ├── app/ # Application code
│ │ ├── main.py # FastAPI app initialization & routes
│ │ ├── config.py # Configuration (DB, JWT, environment)
│ │ ├── database.py # Database connection & session management
│ │ ├── models/ # SQLAlchemy models
│ │ │ ├── __init__.py
│ │ │ ├── profile.py
│ │ │ ├── skills.py
│ │ │ ├── experience.py
│ │ │ ├── projects.py
│ │ │ ├── education.py
│ │ │ ├── certifications.py
│ │ │ ├── blog.py
│ │ │ ├── books.py
│ │ │ └── admin.py
│ │ ├── routes/ # API route handlers
│ │ │ ├── __init__.py
│ │ │ ├── admin.py # Admin authentication
│ │ │ ├── content.py # Portfolio content endpoints
│ │ │ ├── blog.py # Blog endpoints
│ │ │ └── books.py # Bookshelf endpoints
│ │ └── schemas/ # Pydantic schemas for validation
│ │ └── ...
│ ├── admin/ # Admin panel frontend
│ │ ├── index.html # Admin dashboard
│ │ ├── login.html # Admin login page
│ │ └── assets/ # Admin panel assets
│ │ ├── css/
│ │ └── js/
│ ├── requirements.txt # Python dependencies
│ ├── vercel.json # Vercel deployment config
│ └── migration_insert_data.sql # Database population script
└── README.md # This file
Frontend:
- A modern web browser (Chrome, Firefox, Safari, Edge)
Backend:
- Python 3.9+
- Supabase account (for database and storage)
- Vercel account (for production deployment)
-
Clone the repository:
git clone <repository-url> cd portfolio-website
-
Set up Supabase:
- Create a project at supabase.com
- Run the migration SQL files in Supabase SQL Editor (in order):
- Database schema setup
- Storage setup for images
- Insert initial data (optional)
- Get your Supabase URL and keys from Project Settings → API
-
Configure Backend: Create a
.envfile in thebackend/directory:SUPABASE_URL=your-supabase-url SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_KEY=your-service-role-key JWT_SECRET_KEY=your-secret-key-here ADMIN_USERNAME=admin ADMIN_PASSWORD=your-secure-password
-
Install Backend Dependencies:
cd backend python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt
-
Run Backend Locally:
uvicorn app.main:app --reload
Backend will be available at
http://localhost:8000 -
Open Frontend: Simply open
index.htmlin your browser. The frontend automatically detects:- Local Development: Uses
http://localhost:8000when opened from localhost - Production: Uses Vercel URL when deployed
No configuration needed! The API URL switches automatically based on environment.
- Local Development: Uses
Backend Deployment to Vercel:
-
Install Vercel CLI:
npm install -g vercel
-
Deploy from backend directory:
cd backend vercel --prod -
Set environment variables in Vercel dashboard:
SUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_KEYJWT_SECRET_KEYADMIN_USERNAMEADMIN_PASSWORD
Frontend Deployment:
- Deploy to any static hosting (Netlify, Vercel, GitHub Pages)
- No API URL configuration needed! The frontend automatically detects the environment:
- When accessed via
localhost, it useshttp://localhost:8000 - When accessed via production domain, it uses the production Vercel URL
- When accessed via
- To change the production URL, edit
assets/js/app-config.js
Base URL:
- Local Development:
http://localhost:8000(automatic) - Production:
https://portfolio-website-nine-red-56.vercel.app(automatic)
The frontend automatically detects and uses the correct URL based on your environment.
GET /api/blog/posts- Get all blog posts- Query params:
published_only=true,page=1,page_size=10,category - Returns:
{ posts: [], total: 0, page: 1, page_size: 10 }
- Query params:
GET /api/blog/posts/{post_id}- Get single blog post by IDGET /api/blog/posts/slug/{slug}- Get single blog post by slug
GET /api/books- Get all books- Query params:
status_filter(read/reading/to-read),include_hidden=false - Returns: Array of books
- Query params:
GET /api/books/stats- Get reading statistics- Returns:
{ total_read: 0, currently_reading: 0, to_read: 0 }
- Returns:
GET /api/books/{book_id}- Get single book by ID
Authentication:
POST /api/auth/login- Login with username/password- Body:
{ username: "admin", password: "your-password" } - Returns:
{ access_token: "jwt-token", token_type: "bearer" }
- Body:
- All admin endpoints require
Authorization: Bearer <token>header
POST /api/blog/posts- Create new blog postPUT /api/blog/posts/{post_id}- Update blog postDELETE /api/blog/posts/{post_id}- Delete blog post
POST /api/books- Add new bookPUT /api/books/{book_id}- Update bookDELETE /api/books/{book_id}- Delete book
POST /api/storage/upload/blog- Upload blog imagePOST /api/storage/upload/book-cover- Upload book cover imageDELETE /api/storage/delete/{path}- Delete image from storage
The frontend uses the centralized API service (window.api) which automatically handles environment detection:
// Using the API service (recommended)
const blogPosts = await window.api.getBlogPosts({
published_only: true,
page: 1,
page_size: 10
});
const books = await window.api.getBooks();
const stats = await window.api.getBookStats();
// Direct fetch (if needed)
const response = await fetch(`${window.AppConfig.getApiBaseUrl()}/api/blog/posts`);
const data = await response.json();
// Admin login
const loginResponse = await fetch(`${window.AppConfig.getApiBaseUrl()}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'your-password' })
});
const { access_token } = await loginResponse.json();The CSS is organized into modular files in assets/css/:
- base.css: Modify foundational styles (typography, resets, base elements)
- components.css: Update component-specific styles (cards, buttons, navigation)
- animations.css: Add or modify animations and keyframes
- utilities.css: Add utility classes and responsive breakpoints
All CSS is imported via main.css, which is loaded in the HTML.
JavaScript is organized into modules in assets/js/:
- app-config.js: Environment detection and API URL configuration
- Automatically detects localhost vs production
- Provides
window.AppConfig.getApiBaseUrl()for environment-aware API calls
- api.js: Centralized API service for all backend communication
- Uses app-config.js to automatically switch between local and production URLs
- Provides methods:
getBlogPosts(),getBooks(),getBookStats()
- portfolio.js: Alpine.js component with reactive state and data loading
- Handles blog and bookshelf data fetching
- Manages loading states and error handling
- app.js: Navigation, routing, and tab management
- config.js: Tailwind CSS configuration
The application automatically detects whether it's running locally or in production:
How it works:
app-config.jscheckswindow.location.hostname- If hostname is
localhostor127.0.0.1, useshttp://localhost:8000 - Otherwise, uses the production Vercel URL
Changing Production URL:
Edit assets/js/app-config.js and update the production URL:
getApiBaseUrl() {
if (this.isDevelopment()) {
return 'http://localhost:8000';
} else {
return 'https://your-production-url.vercel.app'; // Update this
}
}Benefits:
- No manual configuration when switching between development and production
- No need to modify code before commits or deployments
- Works seamlessly when opening
index.htmldirectly or via a local server
Skills are managed via a simple JSON file for easy updates when your resume changes:
To update your skills:
- Edit
assets/data/skills.json - Add, remove, or modify skills in any category:
{ "languages": ["Python", "JavaScript", "Your New Language"], "ai": ["LLMs", "RAG Architecture", "New AI Tool"], "testing": ["Selenium", "Playwright"], "cloud": ["AWS", "Docker", "Kubernetes"], "data": ["PostgreSQL", "MongoDB"], "backend": ["FastAPI", "Django"] } - Save the file
- Refresh your website - changes appear immediately!
No code changes needed - just edit the JSON file and your skills section updates automatically.
Categories:
languages: Programming languagesai: AI/ML tools and frameworkstesting: Testing frameworks and toolscloud: Cloud platforms and DevOps toolsdata: Databases and data toolsbackend: Backend frameworks
All content can be managed through the admin panel:
- Start your backend locally or access your deployed backend
- Navigate to
/admin/index.html(or/admin/login.htmlif not logged in) - Login with your admin credentials (configured in
.env) - Use the dashboard to add/edit/delete:
- Blog posts: Create, update, publish/unpublish posts
- Books: Add books, update reading status, upload cover images
Admin Features:
- JWT-based authentication
- Image upload for blog posts and book covers (stored in Supabase Storage)
- Rich text content editing
- Published/draft status management for blog posts
- Reading status tracking for books (read, reading, to-read)
Use the API endpoints with authentication:
// Example: Add a new blog post
const token = 'your-jwt-token';
const newPost = {
title: 'My First Blog Post',
slug: 'my-first-blog-post',
content: 'This is the content of my blog post...',
excerpt: 'A short summary',
cover_image: 'https://example.com/image.jpg',
category: 'Technology',
published: true
};
await fetch(`${window.AppConfig.getApiBaseUrl()}/api/blog/posts`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(newPost)
});The portfolio is a single-page application with multiple sections:
Static Sections (content embedded in HTML):
- Summary: Professional overview and introduction
- Skills: Technical skills organized by categories (Languages, AI/ML, Testing, Cloud, Data, Backend)
- Terminal/code editor aesthetic with proficiency indicators
- Experience: Professional work history with date ranges
- Education: Academic background
- Projects: Notable projects with descriptions and links
- Certifications: Professional certifications
Dynamic Sections (loaded from Supabase via API):
- Blog: Latest blog posts with excerpts
- Loads published posts from API
- Shows post title, excerpt, read time, and published date
- Responsive card layout
- Bookshelf: Reading list with book covers
- Displays books with status (read, reading, to-read)
- Shows reading statistics
- Book covers loaded from Supabase Storage
Update colors in assets/js/config.js:
tailwind.config = {
theme: {
extend: {
colors: {
primary: { /* your colors */ },
secondary: { /* your colors */ }
}
}
}
};Update profile via the admin panel or directly through the API:
- Personal information
- Bio and summary
- Social media links
- Contact information
To add new sections to the main page:
- Create database model in
backend/app/models/ - Add API routes in
backend/app/routes/ - Update admin panel with CRUD interface
- Add tab button in navigation (
index.html) - Create section template with Alpine.js directives
- Update
portfolio.jsto fetch data from API
Frontend:
- Lazy Loading: Images use lazy loading via Intersection Observer
- Modular CSS: Only load what you need
- CDN Resources: External libraries loaded from CDNs
- Parallel Data Fetching: All API calls made concurrently with Promise.all()
- Loading States: Smooth loading overlays for better UX
Backend:
- Serverless Deployment: Vercel serverless functions for auto-scaling
- Database Connection Pooling: SQLAlchemy connection pooling
- Efficient Queries: Optimized database queries with proper indexing
- CORS Optimization: Proper CORS headers for cross-origin requests
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome Mobile)
Frontend:
- HTML5: Semantic markup
- CSS3: Modern styling with Flexbox and Grid
- Tailwind CSS: Utility-first CSS framework
- Alpine.js: Lightweight reactive JavaScript framework
- Font Awesome: Icon library
- Google Fonts: Inter font family
Backend:
- FastAPI: Modern Python web framework
- SQLAlchemy: SQL toolkit and ORM
- PostgreSQL: Relational database (via Supabase)
- Pydantic: Data validation and settings management
- JWT: JSON Web Tokens for authentication
- Uvicorn: ASGI server
- Vercel: Serverless deployment platform
The database includes the following tables:
- profile: Personal information and bio
- skills: Technical skills with categories and proficiency levels
- experience: Work history with date ranges
- projects: Portfolio projects with descriptions and links
- education: Academic background
- certifications: Professional certifications
- blog_posts: Blog articles with markdown content
- blog_categories: Blog post categories
- blog_tags: Blog post tags
- books: Reading list with ratings and reviews
- admin_users: Admin authentication
Completed:
- ✅ Full backend API with FastAPI
- ✅ Database integration with Supabase
- ✅ Admin panel with JWT authentication
- ✅ Skills section dynamic rendering
- ✅ API service layer for frontend
- ✅ Loading states and error handling
- ✅ CORS configuration for deployment
- ✅ Dark/light theme toggle
- ✅ Responsive design
In Progress:
- 🔄 Dynamic rendering for Experience section
- 🔄 Dynamic rendering for Projects section
- 🔄 Dynamic rendering for Education section
- 🔄 Dynamic rendering for Certifications section
- 🔄 Blog content population
- 🔄 Bookshelf content population
Future Enhancements:
- Full-text search for blog posts
- RSS feed for blog
- Markdown editor in admin panel
- Image upload functionality
- Analytics dashboard
- Contact form with email integration
- Resume download functionality
- Social media integration
© 2025 Shyam Patadia. All rights reserved.
- Email: [email protected]
- LinkedIn: linkedin.com/in/shyampatadia
- GitHub: github.com/shyampatadia
Note: This is a static website. For dynamic features like blog comments or contact forms, you'll need to integrate backend services or third-party APIs.