A full-stack newsletter application built with React Router SSR, TypeScript, Express, and PostgreSQL.
- React Router v7 with Server-Side Rendering (SSR)
- TypeScript for type safety
- TipTap rich text editor for post creation
- TanStack Query for infinity scroll
- CSS Modules for component-scoped styling
- Express.js REST API
- Prisma ORM for database operations and migrations
- Rensed for email notifications (idempotency key)
- TypeScript for type safety
- PostgreSQL database with connection pooling
- PgBoss job queue system for background processing
newsletter/
βββ client/ # React Router SSR client
β βββ app/
β β βββ components/ # Reusable UI components
β β βββ config/ # App configuration
β β βββ hooks/ # Custom React hooks
β β βββ lib/ # Utilities and API client
β β βββ routes/ # Route components
β β β βββ index.tsx # Home page with infinite scroll
β β β βββ new-post.tsx # Post creation form
β β β βββ post.$slug.tsx # Individual post view
β β β βββ subscribe.tsx # Newsletter subscription
β β β βββ unsubscribe.tsx # Newsletter subscription
β β βββ utils/ # Helper functions
β β βββ root.tsx # Root layout component
β β βββ root.css # Global styles
β β βββ routes.ts # Route configuration
β βββ public/ # Static assets (favicons, images)
β βββ react-router.config.ts # React Router configuration
β βββ package.json
βββ server/ # Express.js API server
β βββ src/
β β βββ config/ # Server configuration
β β βββ providers/ # External service providers
β β βββ routes/ # API route handlers
β β β βββ posts.ts # Post CRUD operations
β β β βββ subscribers.ts # Subscriber management
β β βββ workers/ # Background job workers
β β β βββ newsletter.ts # Newsletter publishing and email workers
β β β βββ queue.ts # Queue management utilities
β β β βββ consts.ts # Worker configuration constants
β β β βββ index.ts # Worker registration
β β βββ lib/
β β β βββ jobs/ # PgBoss job queue setup
β β βββ prisma.ts # Prisma client setup
β β βββ index.ts # Server entry point with SSR
β βββ prisma/
β β βββ schema.prisma # Database schema
β β βββ seed.ts # Database seeding
β β βββ migrations/ # Database migrations
β βββ package.json
βββ Dockerfile # Multi-stage Docker build
βββ docker-compose.yml # Docker Compose for local development
βββ ecosystem.config.js # PM2 configuration for development
βββ render.yaml # Render deployment configuration (for the preDeployCommand, but it's only in paid tiers)
βββ pnpm-workspace.yaml # PNPM workspace configuration
βββ package.json # Root package.json with workspace scripts- Node.js (v20.19.0 or higher)
- pnpm (v8.15.0 or higher)
- PostgreSQL (v12 or higher)
- PM2 (installed globally, v5.3.0+)
-
Clone the repository
git clone [email protected]:aemartos/newsletter.git cd newsletter
-
Install dependencies
pnpm install
-
Set up environment variables
# Copy the example environment files cp server/.env.example server/.env cp client/.env.example client/.env -
Set up the database
The application includes a
docker-compose.ymlfile to start a PostgreSQL database:# Start PostgreSQL database docker-compose up -d newsletter-dbcd server # Generate Prisma client pnpm db:generate # Push the schema to your database pnpm db:push # Seed the database with sample data pnpm db:seed
pnpm devor
pm2 start# Start only the client
pnpm dev:client
# Start only the server
pnpm dev:serveror
# Start only the client
pm2 client
# Start only the server
pm2 servercd server
# Generate Prisma client
pnpm db:generate
# Push schema changes
pnpm db:push
# Create and run migrations
pnpm db:migrate
# Seed the database
pnpm db:seedThe application uses the following main entities:
- Subscribers: newsletter subscribers
- Posts: posts and content
- EmailDeliveries: history of emails sent
GET /api/health- Application health status and database connectivity
POST /api/subscribers- Create a new subscriber (subscribe)POST /api/subscribers/unsubscribe- Unsubscribe a subscriber (unsubscribe)
GET /api/posts- Get posts by filter (paginated)- Query parameters:
limit,cursor,status,sortBy,sortOrder
- Query parameters:
GET /api/posts/:slug- Get post by slugPOST /api/posts- Create post with optional scheduling- Supports
schedulefield for future publication - Automatically triggers background job for scheduled posts
- Required fields:
title,slug,content - Optional fields:
excerpt,schedule
- Supports
The application uses PgBoss for reliable background job processing, leveraging PostgreSQL as the job queue backend.
-
Newsletter Publishing (
newsletter.publish-post)- Publishes a scheduled post
- Updates post status to
PUBLISHED - Creates email delivery records for all subscribers
- Triggers individual email sending jobs
-
Email Sending (
newsletter.send-email)- Sends individual emails to subscribers
- Uses Resend for email delivery with idempotency keys
- Updates delivery status in database
- Handles retry logic for failed deliveries
- Publish Worker: 1 worker, processes posts sequentially
- Email Workers: 2 concurrent workers (respects Resend's 2 req/sec rate limit)
- Retry Settings: Up to 10 retries with exponential backoff
- Singleton Keys: Prevents duplicate email sends per subscriber
# Lint all code
pnpm lint
# Fix linting issues
pnpm lint:fix# Check types across the project
pnpm type-checkThis project is licensed under the MIT License - see the LICENSE file for details.
- React Router for the SSR framework
- Prisma for the database ORM
- Express.js for the backend framework
- PgBoss for reliable job queue processing
- Resend for email delivery services with idempotency keys
- Server-side rendering for better SEO and initial load performance
- Built-in data loading with loaders and actions
- Reliability: Jobs are persisted in PostgreSQL and won't be lost
- Scalability: Multiple workers can process jobs concurrently
- Retry Logic: Automatic retry with exponential backoff for failed jobs
- Singleton Jobs: Prevents duplicate job execution
- Simple Setup: Uses existing PostgreSQL database, no additional infrastructure
- Type Safety: Generated TypeScript types for database operations
- SQL Injection Protection: Built-in query sanitization
- Migration Management: Version-controlled database schema changes
- Reliability: Enterprise-grade email delivery
- Analytics: Built-in delivery tracking and analytics
- Scalability: Handles high-volume email sending
- Compliance: Built-in spam and compliance features
- Idempotency key: to avoid sending the send email twice
- Trade-off: Using PostgreSQL for job storage instead of Redis
- Why acceptable:
- No additional infrastructure needed
- Jobs are persisted and won't be lost on restart
- Simpler deployment and monitoring
- Limitation: Database load monitoring needed for optimal performance
- Trade-off: Couldn't use Prisma Accelerate due to PgBoss requiring direct database URL (I'll need to research more bout this)
- Why acceptable:
- Direct connection provides better performance for job processing
- Simpler configuration and debugging
- No additional service dependencies
- Security Concern: Using
dangerouslySetInnerHTMLfor post content - Risk: Potential XSS attacks from malicious scripts
- Mitigation: Content should be sanitized before storage or use a safe HTML parser
- Trade-off: Using SSR framework instead of client-side only React
- Why acceptable:
- Better SEO and initial load performance
- Built-in data loading and form handling
- Limitation: More complex deployment and server-side rendering considerations
- Trade-off: Limited time to build a complete newsletter platform
- Why acceptable:
- Focus on core functionality (posts, subscribers, email delivery)
- Prioritize working features over perfect architecture
- Can iterate and improve over time
- Specific Limitations:
- Express.js Performance: Using Express instead of faster frameworks
- No Authentication: Anyone can create posts, no user management
- Basic Error Handling: Generic error messages, no detailed error tracking
- Limited Validation: Basic form validation, no comprehensive input sanitization
- No Rate Limiting: API endpoints not protected against abuse
- No Caching: No Redis or CDN integration for performance
- No Monitoring: No application monitoring or alerting system
- Content Sanitization: Implement DOMPurify or similar for HTML content
- XSS Protection: Replace
dangerouslySetInnerHTMLwith safe HTML rendering - Rate Limiting: Add API rate limiting for public endpoints
- Infinite Scroll Optimization: Hide previous posts to avoid performance issues
- Batching: Optimize jobs with batching strategies
- Caching Layer: Add Redis for caching frequently accessed posts
- Alerting System: Implement comprehensive alerting for failures and performance issues
- Application Monitoring: Integrate monitoring tools like DataDog for application performance monitoring
- Metrics Collection: Custom metrics for business logic (email delivery rates, post engagement, etc.)
- Testing: Unit, e2e, integration, stress...
- Expand model: adapt the model to new features/needs
- Transaction Optimization: Improve database transactions in API endpoints
- Data Archiving: Archive old posts and email delivery records
- Read Replicas: Implement read replicas for better read performance
- User System: Implement user authentication and authorization
- Author Posts: Associate posts with specific authors
- Roles & Permissions: Role-based access control system
- OAuth Integration: Social login with Google, GitHub, etc.
- Posts Management: Prevent modification of scheduled posts
- Search Functionality: Implement search bar for posts
- Post Creation Improvements:
- Image upload and management
- Rich text editor with more formatting options
- Post templates and reusable content blocks
- Draft Management: View unpublished posts and schedules
- Autosave: Implement automatic saving of post drafts
- Daily Post Merging: Merge multiple scheduled posts into single daily newsletter
- Email Templates: Rich HTML email templates with branding
- Content Import/Export: Import/export posts in various formats
- Comment System: Allow readers to comment on posts
- Social Sharing: Social media sharing integration
- Design Overhaul: Complete UI/UX redesign for better user experience
- Responsive Design: Enhanced mobile and tablet experience
- Accessibility: WCAG compliance and screen reader support
- Internationalization: Multi-language support
- SEO
With Render (or similar):
- Simple Setup: Uses your existing Dockerfile with zero configuration
- Managed Database: Built-in PostgreSQL with automatic backups
- Automatic Deployments: Deploys on every git push or webhook trigger
- SSL Included: Automatic HTTPS certificates
- Cost-Effective: Cheap for starter plans
- No Infrastructure Management: Focus on code, not servers
The application uses a multi-stage Docker build optimized for production:
- Base Stage: Installs pnpm and dependencies
- Builder Stage: Generates Prisma client and builds both client and server
- Production Stage: Creates a minimal production image with only necessary files
Architecture:
- Development: Two separate servers (Express API + React Router dev server)
- Production: Single Express server that serves everything:
- API routes (
/api/*) - Static assets (from built client)
- React Router SSR (imports built server bundle)
- API routes (
-
Connect Repository
- Go to render.com
- Connect your GitHub repository
-
Create Web Service
- Choose "Web Service"
- Select your repository
- Render automatically detects your Dockerfile
-
Add Database
- Create a new PostgreSQL database
- Render provides the connection string
-
Set Environment Variables
Git Push Deployment:
- Manual: Use git tags for production releases
- Rollback: Use Render dashboard to deploy any previous deployments
Deployment Commands:
git tag v1.0.0
git push origin v1.0.0Rollback Process:
- Go to Render dashboard
- Click "Manual deploy" on any previous deployment
- Health Checks:
/api/healthendpoint monitoring - Database Monitoring: Connection pool, query performance
- Job Queue Monitoring: PgBoss job status and failures
- Application Metrics: Response times, error rates
- Database connection failures
- High error rates (>5%)
- Job queue backlog (>100 pending jobs)
- Email delivery failures (>10% failure rate)
- Application downtime
- Application: Stateless design allows easy horizontal scaling
- Auto-scaling: Scale down during low traffic periods
- Database: Read replicas for read-heavy workloads
- Job Processing: Scale PgBoss workers independently (implement batching)
- CDN: Global content delivery for static assets