Skip to content

ericjypark/bookmarket

Repository files navigation

Bookmarket

(Don't) Manage your bookmarks (with Chrome)

screenshot-with-background (2)

TypeScript Next.js NestJS PostgreSQL Docker

πŸ“– Table of Contents

🌟 Overview

Bookmarket is a modern, full-stack bookmark management application designed to replace traditional browser bookmark systems. Built with a focus on user experience, performance, and self-hosting capabilities, it provides a comprehensive solution for organizing, sharing, and discovering web content.

The application features a monorepo architecture using Turborepo and pnpm, consisting of a Next.js frontend, NestJS backend, and PostgreSQL database, all orchestrated with Docker for seamless development and deployment.

πŸš€ Features

Core Functionality

  • πŸ” Multi-Provider Authentication: JWT-based auth with Google and GitHub OAuth integration
  • πŸ“š Smart Bookmark Management: URL input with automatic metadata and favicon fetching
  • 🏷️ Category Organization: Drag-and-drop interface for organizing bookmarks into categories
  • 🌐 Public Sharing: Share bookmark collections via clean, public URLs (/s/username)
  • ⌨️ Command Menu: Global search and command interface using cmdk for power users
  • πŸ“± Progressive Web App: Full PWA support with offline capabilities and native app-like experience
  • 🎫 User Slot Management: First-come-first-serve user registration with configurable limits and real-time slot tracking

Advanced Features

  • πŸ” Self-Hosted Metadata: Server-side metadata extraction without external API dependencies
  • πŸ“Š Error Monitoring: Comprehensive Sentry integration for both frontend and backend
  • 🎨 Dark/Light Mode: System-aware theme switching with Tailwind CSS
  • πŸ”„ Real-time Updates: Optimistic UI updates with TanStack Query
  • 🎯 Type Safety: End-to-end TypeScript for robust development experience
  • πŸ”’ Security Headers: Comprehensive security headers and CSP policies
  • πŸ“ Responsive Design: Mobile-first design that works across all devices
  • ⚑ Real-time Slot Updates: Live slot availability counter with short polling for immediate feedback
  • πŸ›‘οΈ Race Condition Protection: Atomic slot reservation prevents concurrent signup issues

πŸ— Architecture

System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Frontend (Next.js)                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  App Router     β”‚  β”‚  Server Actions  β”‚  β”‚  Client State   β”‚ β”‚
β”‚  β”‚  (Pages)        β”‚  β”‚  (API Calls)     β”‚  β”‚  (Zustand)      β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  UI Components  β”‚  β”‚  TanStack Query  β”‚  β”‚  PWA Service    β”‚ β”‚
β”‚  β”‚  (Radix UI)     β”‚  β”‚  (Server State)  β”‚  β”‚  Worker         β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β”‚ HTTP/REST API
                                   β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Backend (NestJS)                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Controllers    β”‚  β”‚  Services        β”‚  β”‚  Guards &       β”‚ β”‚
β”‚  β”‚  (REST API)     β”‚  β”‚  (Business Logic)β”‚  β”‚  Middleware     β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  JWT Auth       β”‚  β”‚  Metadata        β”‚  β”‚  TypeORM        β”‚ β”‚
β”‚  β”‚  (OAuth + Local)β”‚  β”‚  Scraping        β”‚  β”‚  (Database)     β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β”‚ SQL Queries
                                   β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Database (PostgreSQL)                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Users          β”‚  β”‚  Bookmarks       β”‚  β”‚  Categories     β”‚ β”‚
β”‚  β”‚  (Auth Data)    β”‚  β”‚  (URLs + Meta)   β”‚  β”‚  (Organization) β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow

  1. Authentication Flow: OAuth providers β†’ JWT tokens β†’ Cookie storage β†’ Request headers
  2. Bookmark Creation: URL input β†’ Metadata fetching β†’ Database storage β†’ Real-time UI updates
  3. Category Management: Drag-and-drop β†’ Optimistic updates β†’ API calls β†’ State synchronization
  4. Public Sharing: Username-based URLs β†’ Public API endpoints β†’ Cached responses

πŸ› οΈ Tech Stack

Frontend Stack

  • πŸš€ Next.js 15 - React framework with App Router for server-side rendering and static generation
  • βš›οΈ React 19 - Latest React with concurrent features and improved performance
  • πŸ“˜ TypeScript 5.7 - Static type checking for robust development
  • 🎨 Tailwind CSS 3.4 - Utility-first CSS framework with custom design system
  • 🧩 Radix UI - Accessible, unstyled component primitives
  • ✨ Framer Motion 11 - Production-ready motion library for React
  • πŸ”„ TanStack Query 5 - Powerful data synchronization for server state
  • 🐻 Zustand 5 - Small, fast, and scalable state management
  • πŸ“Š Sentry - Real-time error tracking and performance monitoring
  • πŸ“± Next PWA - Progressive Web App features with Workbox
  • ⌨️ cmdk - Fast, composable command menu interface
  • πŸŒ™ next-themes - Perfect dark mode implementation
  • πŸ” nuqs - Type-safe search params state management

Backend Stack

  • πŸ—οΈ NestJS 10 - Progressive Node.js framework for scalable server-side applications
  • πŸ“˜ TypeScript 5 - Full-stack type safety
  • 🐘 PostgreSQL - Advanced open-source relational database
  • πŸ”§ TypeORM 0.3 - TypeScript ORM with migrations and relationships
  • πŸ”‘ JWT Authentication - Secure token-based authentication with cookie storage
  • πŸ” OAuth Integration - Google and GitHub OAuth providers
  • πŸ”’ bcrypt - Secure password hashing
  • πŸ•·οΈ Cheerio - Server-side HTML parsing for metadata extraction
  • πŸ“Š Sentry - Backend error tracking and performance monitoring
  • πŸͺ cookie-parser - HTTP cookie parsing middleware
  • 🎯 class-validator - Decorator-based validation for DTOs

Development & DevOps

  • πŸ“š Turborepo 2.4 - High-performance build system for JavaScript monorepos
  • πŸ“¦ pnpm 8.9.2 - Fast, disk space efficient package manager
  • πŸ” ESLint 8 - Static code analysis with custom rules and plugins
  • ✨ Prettier 3 - Opinionated code formatter with Tailwind plugin
  • 🐳 Docker Compose - Multi-container development environment
  • πŸ§ͺ Jest - Comprehensive testing framework for backend
  • πŸ”— GitHub Actions - CI/CD pipeline for automated testing and deployment

Additional Tools & Libraries

  • 🌐 Geist Font - Modern, clean typography
  • 🎭 Lucide React - Beautiful, customizable SVG icons
  • πŸ“‹ React Hook Form - Performant, flexible forms with easy validation
  • πŸ”” Sonner - Elegant toast notifications
  • πŸŽͺ Vaul - Accessible drawer component for mobile interfaces
  • πŸ“ class-variance-authority - TypeScript-first variant API for component styling

πŸ“ Project Structure

bookmarket/
β”œβ”€β”€ πŸ“ apps/
β”‚   β”œβ”€β”€ πŸ“ web/                    # Next.js Frontend Application
β”‚   β”‚   β”œβ”€β”€ πŸ“ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ app/           # Next.js App Router
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ (pages)/   # Route Groups
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ (auth)/        # Authentication pages
β”‚   β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ login/
β”‚   β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ signup/
β”‚   β”‚   β”‚   β”‚   β”‚   β”‚   └── πŸ“ oauth/      # OAuth callbacks
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ (home)/        # Main application
β”‚   β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ home/       # Dashboard & bookmarks
β”‚   β”‚   β”‚   β”‚   β”‚   β”‚   └── πŸ“ _components/ # Home-specific components
β”‚   β”‚   β”‚   β”‚   β”‚   └── πŸ“ (shared)/      # Public bookmark sharing
β”‚   β”‚   β”‚   β”‚   β”‚       └── πŸ“ s/[username]/ # Public user pages
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ _common/           # Shared application code
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ actions/       # Server actions
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ components/    # Reusable components
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ hooks/         # Custom React hooks
β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ providers/     # React context providers
β”‚   β”‚   β”‚   β”‚   β”‚   └── πŸ“ state/         # State management
β”‚   β”‚   β”‚   β”‚   └── πŸ“ _core/             # Core utilities
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ πŸ“ components/    # Base UI components
β”‚   β”‚   β”‚   β”‚       └── πŸ“ utils/         # Utility functions
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ styles/                # Global styles
β”‚   β”‚   β”‚   └── πŸ“„ middleware.ts          # Next.js middleware
β”‚   β”‚   β”œβ”€β”€ πŸ“ public/                    # Static assets
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ images/                # Application images
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ logos/                 # Brand assets
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“„ manifest.json          # PWA manifest
β”‚   β”‚   β”‚   └── πŸ“„ sw.js                  # Service worker
β”‚   β”‚   β”œβ”€β”€ πŸ“„ next.config.mjs            # Next.js configuration
β”‚   β”‚   β”œβ”€β”€ πŸ“„ tailwind.config.ts         # Tailwind CSS configuration
β”‚   β”‚   └── πŸ“„ package.json
β”‚   β”‚
β”‚   └── πŸ“ server/                 # NestJS Backend Application
β”‚       β”œβ”€β”€ πŸ“ src/
β”‚       β”‚   β”œβ”€β”€ πŸ“ iam/            # Identity & Access Management
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ authentication/    # Auth controllers & services
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ guards/            # Security guards
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ decorators/        # Custom decorators
β”‚       β”‚   β”‚   └── πŸ“ social/            # OAuth providers
β”‚       β”‚   β”œβ”€β”€ πŸ“ bookmarks/      # Bookmark management
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ dto/               # Data transfer objects
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ entities/          # Database entities
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ services/          # Business logic
β”‚       β”‚   β”‚   └── πŸ“„ bookmarks.controller.ts
β”‚       β”‚   β”œβ”€β”€ πŸ“ categories/     # Category management
β”‚       β”‚   β”œβ”€β”€ πŸ“ users/          # User management
β”‚       β”‚   β”œβ”€β”€ πŸ“ common/         # Shared utilities
β”‚       β”‚   β”‚   β”œβ”€β”€ πŸ“ entities/          # Base entities
β”‚       β”‚   β”‚   └── πŸ“ constants/         # Application constants
β”‚       β”‚   β”œβ”€β”€ πŸ“ migrations/     # Database migrations
β”‚       β”‚   β”œβ”€β”€ πŸ“„ main.ts         # Application entry point
β”‚       β”‚   └── πŸ“„ data-source.ts  # Database configuration
β”‚       β”œβ”€β”€ πŸ“„ Dockerfile          # Container configuration
β”‚       └── πŸ“„ package.json
β”‚
β”œβ”€β”€ πŸ“ packages/                   # Shared Configurations
β”‚   β”œβ”€β”€ πŸ“ eslint-config/          # Shared ESLint configuration
β”‚   β”œβ”€β”€ πŸ“ prettier-config/        # Shared Prettier configuration
β”‚   └── πŸ“ typescript-config/      # Shared TypeScript configuration
β”‚
β”œβ”€β”€ πŸ“„ docker-compose.yml          # Development environment
β”œβ”€β”€ πŸ“„ turbo.json                  # Turborepo configuration
β”œβ”€β”€ πŸ“„ pnpm-workspace.yaml         # pnpm workspace configuration
β”œβ”€β”€ πŸ“„ CLAUDE.md                   # AI assistant instructions
└── πŸ“„ README.md                   # This file

βš™οΈ Installation

Prerequisites

  • Node.js 18 or higher (Download)
  • pnpm 8.9.2 or higher (npm install -g pnpm@8.9.2)
  • Docker & Docker Compose (Download)
  • Git for version control

Quick Start

  1. Clone the repository

    git clone https://github.com/yourusername/bookmarket.git
    cd bookmarket
  2. Install dependencies

    pnpm install
  3. Environment Configuration

    # Copy environment examples
    cp apps/web/.env.example apps/web/.env
    cp apps/server/.env.example apps/server/.env
  4. Configure Environment Variables

    Frontend (apps/web/.env):

    # Base Configuration
    NEXT_PUBLIC_BASE_URL=http://localhost:3000
    NEXT_PUBLIC_API_URL=http://localhost:8000
    
    # OAuth Configuration
    NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id
    NEXT_PUBLIC_GITHUB_CLIENT_ID=your_github_client_id
    
    # Optional: Error Monitoring
    NEXT_PUBLIC_SENTRY_DSN=your_sentry_dsn

    Backend (apps/server/.env):

    # Database Configuration
    DATABASE_URL=
    POSTGRES_USER=
    POSTGRES_PASSWORD=
    POSTGRES_DB=
    
    # JWT Configuration
    JWT_SECRET=
    JWT_TOKEN_AUDIENCE=
    JWT_TOKEN_ISSUER=
    JWT_ACCESS_TOKEN_TTL=
    JWT_REFRESH_TOKEN_TTL=
    
    # OAuth Secrets
    GOOGLE_CLIENT_SECRET=
    GITHUB_CLIENT_SECRET=
    
    # Optional: Error Monitoring
    SENTRY_DSN=
  5. Start Development Environment

    # Starts PostgreSQL + Frontend + Backend
    pnpm dev
  6. Setup Database (First time only)

    cd apps/server
    pnpm run migration:run
  7. Access the Application

πŸ’» Development

Development Commands

# Start full development environment (DB + Web + Server)
pnpm dev

# Start individual services
pnpm --filter bookmarket-client dev    # Frontend only
pnpm --filter bookmarket-server dev    # Backend only

# Database only (useful for external API testing)
docker compose up db -d

Development Workflow

  1. Feature Development: Create feature branches from main
  2. Code Quality: Run linting and type checking before commits
  3. Database Changes: Generate migrations for schema changes
  4. Testing: Run backend tests before pushing
  5. Build Verification: Ensure production build works

Hot Reloading & Live Updates

  • Frontend: Automatic hot reloading with Next.js Fast Refresh
  • Backend: Automatic restart with NestJS watch mode
  • Database: Persistent data with Docker volumes
  • Types: Shared TypeScript types with automatic regeneration

πŸ“– API Documentation

Authentication Flow

The API uses JWT-based authentication with HTTP-only cookies for security:

  1. Sign Up/Sign In: Returns access & refresh tokens
  2. Cookie Storage: Tokens stored in secure HTTP-only cookies
  3. Automatic Refresh: Seamless token refresh on expiration
  4. OAuth Integration: Google & GitHub OAuth providers

Base URLs

  • Development: http://localhost:8000
  • Production: Your deployed backend URL

Authentication Endpoints

POST /authentication/signup

Creates a new user account with email/password.

Request Body:

{
  "email": "user@example.com",
  "password": "strongPassword123",
  "picture": "https://example.com/avatar.jpg" // optional
}

Response:

{
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "username": null,
    "firstName": null,
    "lastName": null,
    "picture": "https://example.com/avatar.jpg",
    "isPublic": false,
    "auth_provider": "email"
  },
  "accessToken": "jwt_token",
  "refreshToken": "refresh_token"
}

POST /authentication/signin

Authenticates existing user with email/password.

POST /authentication/refresh-token

Refreshes expired access tokens.

POST /authentication/google

Authenticates user via Google OAuth.

POST /authentication/github

Authenticates user via GitHub OAuth.

User Management Endpoints

GET /users/me

Returns current authenticated user's profile.

Response:

{
  "id": "uuid",
  "email": "user@example.com",
  "username": "johndoe",
  "firstName": "John",
  "lastName": "Doe",
  "picture": "https://example.com/avatar.jpg",
  "isPublic": true
}

PATCH /users

Updates current user's profile.

Request Body:

{
  "username": "newusername", // max 12 characters
  "firstName": "John", // max 50 characters
  "lastName": "Doe" // max 50 characters
}

GET /users/check-username?username=johndoe

Checks username availability for current user.

Bookmark Management Endpoints

POST /bookmarks

Creates a new bookmark with automatic metadata fetching.

Request Body:

{
  "url": "https://example.com",
  "title": "Custom Title", // optional, auto-fetched if not provided
  "description": "Description", // optional, auto-fetched if not provided
  "faviconUrl": "https://...", // optional, auto-fetched if not provided
  "category": "Work" // optional, creates category if not exists
}

Response:

{
  "id": "uuid",
  "url": "https://example.com",
  "title": "Example Site",
  "description": "An example website",
  "faviconUrl": "https://example.com/favicon.ico",
  "createdAt": "2024-01-01T00:00:00.000Z",
  "updatedAt": "2024-01-01T00:00:00.000Z",
  "category": {
    "id": "uuid",
    "name": "Work"
  }
}

GET /bookmarks

Retrieves all bookmarks for authenticated user.

Query Parameters:

  • category (optional): Filter by category name

GET /bookmarks/metadata?url=https://example.com

Fetches metadata for a given URL without creating bookmark.

Response:

{
  "title": "Example Site",
  "description": "An example website",
  "faviconUrl": "https://example.com/favicon.ico"
}

PATCH /bookmarks/:id

Updates existing bookmark.

PATCH /bookmarks/:id/category

Assigns bookmark to a specific category.

DELETE /bookmarks/:id

Deletes bookmark.

Category Management Endpoints

POST /categories

Creates a new category.

Request Body:

{
  "name": "Work Projects"
}

GET /categories

Retrieves all categories for authenticated user.

PATCH /categories/:id

Updates category name.

DELETE /categories/:id

Deletes category (bookmarks become uncategorized).

Public Sharing Endpoints

GET /bookmarks/s/:username

Retrieves public bookmarks for a user (no authentication required).

Query Parameters:

  • category (optional): Filter by category name

GET /categories/s/:username

Retrieves public categories for a user (no authentication required).

Slot Management Endpoints

GET /slots/status

Returns current slot availability information (no authentication required).

Response:

{
  "remaining": 85,
  "total": 100,
  "canSignUp": true
}

Description:

  • remaining: Number of available signup slots
  • total: Maximum number of users allowed
  • canSignUp: Boolean indicating if new signups are permitted

This endpoint is used by the frontend to display real-time slot availability and enable/disable signup functionality.

Error Responses

All endpoints return consistent error responses:

{
  "statusCode": 400,
  "message": "Validation failed",
  "error": "Bad Request"
}

Common HTTP status codes:

  • 200 - Success
  • 201 - Created
  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (invalid/missing token)
  • 403 - Forbidden (insufficient permissions or slots full)
  • 404 - Not Found
  • 500 - Internal Server Error

Slot-specific Error Responses:

When signup slots are full, authentication endpoints return:

{
  "statusCode": 403,
  "message": "No more signup slots available. Maximum of 100 users reached.",
  "error": "Forbidden"
}

πŸ—„οΈ Database

Database Schema

Users Table

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR UNIQUE NOT NULL,
  username VARCHAR(12) UNIQUE,
  first_name VARCHAR(50),
  last_name VARCHAR(50),
  picture VARCHAR,
  password_hash VARCHAR, -- For email auth
  google_id VARCHAR,     -- For Google OAuth
  github_id VARCHAR,     -- For GitHub OAuth
  auth_provider VARCHAR NOT NULL, -- 'email' | 'google' | 'github'
  is_public BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Categories Table

CREATE TABLE categories (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR NOT NULL,
  user_id UUID REFERENCES users(id) ON DELETE CASCADE,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(name, user_id) -- Unique category names per user
);

Bookmarks Table

CREATE TABLE bookmarks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  url VARCHAR NOT NULL,
  title VARCHAR NOT NULL,
  description TEXT,
  favicon_url VARCHAR,
  user_id UUID REFERENCES users(id) ON DELETE CASCADE,
  category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Database Operations

Migration Management

cd apps/server

# Generate new migration after entity changes
pnpm run migration:generate MigrationName

# Run pending migrations
pnpm run migration:run

# Revert last migration
pnpm run migration:revert

# Check migration status
pnpm run typeorm migration:show

Database Development

# Start database only
docker compose up db -d

# Connect to database
docker exec -it bookmarket-db-1 psql -U bokdol -d bookmarket

# View logs
docker compose logs db -f

# Reset database (⚠️ Destructive)
docker compose down -v
docker compose up db -d
cd apps/server && pnpm run migration:run

Entity Relationships

Users (1) ────── (N) Categories
  β”‚
  └── (1) ────── (N) Bookmarks ────── (N) Categories
                                  └── (1)
  • One user can have many categories
  • One user can have many bookmarks
  • One category can have many bookmarks
  • One bookmark belongs to one category (optional)

Slot Management System

Configuration

The user slot system is configured in apps/server/src/slots/slots.service.ts:

private readonly MAX_NEW_USERS = 100; // Configurable limit

Features

  • Real-time Tracking: Live slot availability displayed on landing page
  • Race Condition Protection: Atomic database transactions prevent concurrent signup issues
  • Universal Enforcement: Applies to all signup methods (email, Google OAuth, GitHub OAuth)
  • Frontend Integration: Real-time updates via 10-second polling
  • Error Handling: Graceful error messages when slots are full

Frontend Components

  • Slots Counter: Visual indicator showing remaining slots with color coding
  • Dynamic UI: Signup buttons and forms automatically disable when slots are full
  • Real-time Updates: Short polling keeps slot status current across all users

Security Considerations

  • Atomic Operations: Uses database transactions with pessimistic locking
  • Race Prevention: Slot reservation happens atomically with user creation
  • Consistent Enforcement: All authentication paths check slots before user creation

πŸš€ Deployment

Docker Production Deployment

  1. Prepare Environment Files

    # Production environment variables
    cp apps/web/.env.example apps/web/.env.production
    cp apps/server/.env.example apps/server/.env.production
    
    # Update with production values
    vim apps/web/.env.production
    vim apps/server/.env.production
  2. Build and Deploy

    # Build production containers
    docker compose -f docker-compose.prod.yml build
    
    # Start production services
    docker compose -f docker-compose.prod.yml up -d
    
    # Run database migrations
    docker compose -f docker-compose.prod.yml exec server pnpm run migration:run

Vercel + Railway Deployment

Frontend (Vercel):

  1. Connect GitHub repository to Vercel
  2. Set build command: cd apps/web && pnpm build
  3. Set environment variables in Vercel dashboard
  4. Deploy automatically on push to main

Backend (Render.io):

  1. Connect GitHub repository to Render.io
  2. Set start command: cd apps/server && pnpm run start:prod
  3. Set environment variables in Render dashboard

Environment Variables for Production

Critical Production Variables:

  • JWT_SECRET - Strong, unique secret key
  • DATABASE_URL - Production database connection
  • NEXT_PUBLIC_BASE_URL - Your frontend domain
  • OAuth client secrets
  • Sentry DSN for error tracking

Health Checks & Monitoring

The application includes:

  • Database health checks in Docker Compose
  • Sentry error monitoring for both frontend and backend
  • PostgreSQL connection monitoring
  • JWT token validation

πŸ”’ Security

Security Features

Frontend Security

  • Content Security Policy (CSP) configured in next.config.mjs
  • Security Headers: XSS Protection, Frame Options, Content Type Options
  • HTTP-only Cookies for JWT token storage
  • HTTPS enforcement in production
  • Input sanitization for all user inputs
  • Image security with allowlist for external images

Backend Security

  • JWT Authentication with refresh token rotation
  • Password hashing with bcrypt (12 rounds)
  • CORS configuration for cross-origin requests
  • Rate limiting on authentication endpoints
  • SQL injection prevention with TypeORM parameterized queries
  • Input validation with class-validator decorators
  • Helmet.js for additional security headers

Database Security

  • Connection encryption with SSL in production
  • User access controls with PostgreSQL roles
  • Data encryption at rest (depends on hosting provider)
  • Regular backup strategies recommended

Security Best Practices

  1. Never commit secrets to version control
  2. Use environment variables for all configuration
  3. Rotate JWT secrets periodically
  4. Monitor authentication logs for suspicious activity
  5. Keep dependencies updated with security patches
  6. Use HTTPS in production with valid SSL certificates

πŸ“± PWA Features

Progressive Web App Capabilities

The application is a full-featured PWA with:

Installation

  • Add to Home Screen on mobile devices
  • Desktop installation via browser
  • Custom app icons and splash screens
  • Standalone display mode (no browser UI)

Offline Support

  • Service Worker caches static assets
  • Offline fallback pages when network unavailable
  • Background sync for bookmark creation when back online
  • Cache-first strategy for optimal performance

Native-like Experience

  • Push notifications (ready for implementation)
  • Background app refresh
  • Native app-like navigation
  • Device integration (camera, sharing, etc.)

PWA Configuration

Manifest (public/manifest.json):

{
  "name": "Bookmarket",
  "short_name": "Bookmarket",
  "description": "Modern bookmark management",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "/logos/logo-base-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    }
  ]
}

Service Worker Features:

  • Workbox-powered service worker
  • Automatic asset caching
  • Runtime caching strategies
  • Background sync capabilities

πŸ§ͺ Testing

Backend Testing

cd apps/server

# Run all tests
pnpm test

# Run tests in watch mode
pnpm test:watch

# Run tests with coverage report
pnpm test:cov

# Run end-to-end tests
pnpm test:e2e

# Run specific test file
pnpm test -- bookmarks.service.spec.ts

Test Structure

apps/server/src/
β”œβ”€β”€ bookmarks/
β”‚   β”œβ”€β”€ __tests__/
β”‚   β”‚   β”œβ”€β”€ bookmarks.controller.spec.ts
β”‚   β”‚   └── bookmarks.service.spec.ts
β”‚   └── ...
β”œβ”€β”€ categories/
β”‚   β”œβ”€β”€ __tests__/
β”‚   └── ...
└── test/                 # E2E tests
    β”œβ”€β”€ app.e2e-spec.ts
    └── auth.e2e-spec.ts

Testing Guidelines

  1. Unit Tests: Test individual functions and methods
  2. Integration Tests: Test API endpoints and database interactions
  3. E2E Tests: Test complete user workflows
  4. Mocking: Mock external dependencies (database, HTTP requests)
  5. Coverage: Aim for >80% code coverage on critical paths

Frontend Testing (Future Enhancement)

Recommended setup for frontend testing:

  • Vitest for unit testing
  • React Testing Library for component testing
  • Playwright for E2E testing
  • MSW for API mocking

🧹 Code Quality

Linting & Formatting

# Run linting across all packages
pnpm lint

# Fix auto-fixable linting issues
pnpm lint --fix

# Format code with Prettier
pnpm format

# Type checking across all packages
pnpm check-types

Code Quality Tools

ESLint Configuration

  • TypeScript ESLint rules
  • React/Next.js specific rules
  • Import/export organization
  • Unused imports detection
  • Custom rules for project conventions

Prettier Configuration

  • Consistent formatting across all files
  • Tailwind CSS class sorting
  • Import sorting with custom order
  • Line length limits and spacing rules

TypeScript Configuration

  • Strict mode enabled
  • Path mapping for clean imports
  • Shared configurations across packages
  • Build-time type checking

Pre-commit Hooks (Recommended)

# Install husky for git hooks
pnpm add -D husky lint-staged

# Setup pre-commit hook
npx husky add .husky/pre-commit "pnpm lint-staged"

πŸ”§ Troubleshooting

Common Issues & Solutions

Database Connection Issues

# Check if PostgreSQL is running
docker compose ps

# View database logs
docker compose logs db

# Reset database connection
docker compose restart db

# Check database connectivity
docker compose exec db pg_isready -U bokdol -d bookmarket

Port Already in Use

# Find process using port 3000/8000
lsof -ti:3000
lsof -ti:8000

# Kill process using port
kill -9 $(lsof -ti:3000)

# Or use different ports
NEXT_PUBLIC_PORT=3001 pnpm dev

Node.js Version Issues

# Check Node.js version (needs 18+)
node --version

# Use Node Version Manager
nvm install 18
nvm use 18

# Or use Volta
volta install node@18

pnpm Installation Issues

# Install pnpm globally
npm install -g pnpm@8.9.2

# Clear pnpm cache
pnpm store prune

# Reinstall dependencies
rm -rf node_modules pnpm-lock.yaml
pnpm install

Migration Issues

# Check migration status
cd apps/server
pnpm run typeorm migration:show

# Revert problematic migration
pnpm run migration:revert

# Generate new migration
pnpm run migration:generate FixIssue

OAuth Configuration Issues

  1. Google OAuth: Ensure redirect URIs match in Google Console
  2. GitHub OAuth: Check OAuth app settings in GitHub
  3. Environment Variables: Verify all OAuth secrets are set correctly
  4. CORS Issues: Ensure frontend URL is allowed in backend CORS settings

Build Issues

# Clear Next.js cache
rm -rf apps/web/.next

# Clear TypeScript cache
rm -rf apps/web/.tsbuildinfo

# Rebuild everything
pnpm clean
pnpm install
pnpm build

Development Tips

  1. Hot Reload Not Working: Restart development server
  2. Database Schema Changes: Always generate migrations
  3. Environment Variables: Restart server after changes
  4. TypeScript Errors: Check import paths and type definitions
  5. Docker Issues: Try docker system prune to clean up

Getting Help

  • GitHub Issues: Report bugs and feature requests
  • Documentation: Check CLAUDE.md for AI assistant guidance
  • Community: Join discussions in GitHub Discussions
  • Logs: Always check browser console and server logs for errors

🀝 Contributing

We welcome contributions to Bookmarket! Here's how to get started:

Development Setup

  1. Fork & Clone

    git clone https://github.com/yourusername/bookmarket.git
    cd bookmarket
  2. Install Dependencies

    pnpm install
  3. Setup Development Environment

    cp apps/web/.env.example apps/web/.env
    cp apps/server/.env.example apps/server/.env
    # Update environment variables
  4. Start Development

    pnpm dev

Contribution Guidelines

Code Standards

  • TypeScript: All code must be properly typed
  • Linting: Pass ESLint checks (pnpm lint)
  • Formatting: Use Prettier (pnpm format)
  • Testing: Add tests for new features
  • Documentation: Update README for significant changes

Git Workflow

  1. Create Feature Branch: git checkout -b feature/amazing-feature
  2. Make Changes: Follow code standards and best practices
  3. Test Changes: Ensure all tests pass
  4. Commit Changes: Use conventional commit messages
  5. Push Branch: git push origin feature/amazing-feature
  6. Open Pull Request: Describe changes and link related issues

Commit Message Format

type(scope): description

feat(bookmarks): add bulk bookmark import
fix(auth): resolve OAuth callback redirect issue
docs(readme): update installation instructions

Types: feat, fix, docs, style, refactor, test, chore

Areas for Contribution

High Priority

  • Testing: Add frontend tests with Vitest
  • Performance: Optimize bundle size and loading times
  • Accessibility: Improve keyboard navigation and screen reader support
  • Mobile: Enhance mobile user experience

Medium Priority

  • Features: Bookmark import/export, search functionality
  • Documentation: API documentation, deployment guides
  • Monitoring: Enhanced error tracking and analytics
  • Internationalization: Multi-language support

Nice to Have

  • Browser Extension: Chrome/Firefox bookmark sync
  • Themes: Additional color schemes and themes
  • Integrations: Pocket, Instapaper, other bookmark services
  • Analytics: User dashboard with bookmark statistics

Review Process

  1. Automated Checks: CI/CD runs tests and linting
  2. Code Review: Maintainers review for quality and standards
  3. Testing: Manual testing for UI/UX changes
  4. Documentation: Ensure documentation is updated
  5. Merge: Approved PRs are merged to main branch

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

MIT License Summary

  • βœ… Commercial use allowed
  • βœ… Modification allowed
  • βœ… Distribution allowed
  • βœ… Private use allowed
  • ❌ Liability - No warranty provided
  • ❌ Warranty - Use at your own risk

πŸ“§ Support & Contact

Getting Help

Maintainers

Acknowledgments

Special thanks to the amazing open-source projects that make Bookmarket possible:

  • Next.js team for the incredible React framework
  • NestJS community for the robust backend framework
  • Vercel for deployment and hosting solutions
  • All contributors and users who help improve Bookmarket

About

(Don't) Manage your bookmarks (with Chrome)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors