16 KiB
Looking - Backend API
Express.js REST API with MongoDB for the Looking art project
This is the backend REST API that powers the "Looking" application. Built with Express 4 and MongoDB 4.4, it provides endpoints for profile management, user authentication, messaging, and geolocation features.
📋 Table of Contents
- Overview
- Tech Stack
- Project Structure
- Getting Started
- Data Flow
- Environment Variables
- Database Seeding
- Development Workflow
- Troubleshooting
- Related Documentation
🎯 Overview
The backend API provides:
- Authentication: JWT-based user sessions with PBKDF2 password hashing
- Profile Management: CRUD operations with approval workflow
- Messaging: Conversation threads with image support
- Image Processing: Automatic thumbnail and detail image generation
- Geolocation: Google Maps API integration for location features
- Email: Password reset and notification emails via Nodemailer
Server Port: 3069 (configurable via PORT environment variable)
Database: MongoDB 4.4 on port 27017 (database name: urge)
🛠 Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| Express | 4.14.0 | Web framework |
| Node.js | 14.x | Runtime |
| MongoDB | 4.4 | Database |
| Mongoose | 4.7.4 | MongoDB ODM |
| jsonwebtoken | 7.3.0 | JWT authentication |
| multer | 1.2.0 | File upload handling |
| @google/maps | 0.4.5 | Google Maps API client |
| nodemailer | 4.0.1 | Email sending |
| winston | 2.4.0 | Application logging |
| morgan | 1.7.0 | HTTP request logging |
| moment | 2.17.1 | Date/time manipulation |
| shortid | 2.2.8 | Short ID generation |
| vcard-js | 1.2.2 | VCard generation |
Dev Tools:
- gulp 3.9.1 - Task runner
- nodemon 1.11.0 - Auto-restart on file changes
- mocha 3.0.1 - Test framework
- chai 3.5.0 - Assertion library
📁 Project Structure
backend/
├── src/
│ ├── app.js # Express app configuration
│ │
│ ├── bin/
│ │ └── www # Server startup script (port 3069)
│ │
│ ├── routes/ # API route handlers
│ │ ├── auth.js # Authentication (login, logout, password reset)
│ │ ├── profiles.js # Profile CRUD + approval workflow
│ │ ├── users.js # User management
│ │ └── geocache.js # Geolocation/geocoding
│ │
│ ├── models/ # Mongoose schemas
│ │ ├── user.js # User schema (auth, permissions)
│ │ ├── profile.js # Profile schema (details, messages)
│ │ ├── message.js # Message schema (text, image, timestamp)
│ │ ├── reset.js # Password reset token schema
│ │ └── geocache.js # Location cache schema
│ │
│ ├── modules/ # Utility modules
│ │ ├── authentication.js # PBKDF2 password hashing
│ │ ├── token.js # JWT creation/verification
│ │ ├── images.js # Image upload/processing (multer)
│ │ ├── mailer.js # Email sending (nodemailer)
│ │ ├── geocoder.js # Google Maps geocoding
│ │ └── logger.js # Winston logging configuration
│ │
│ └── images/ # Uploaded image storage
│ ├── profile/ # Profile photos (thumb + detail)
│ ├── message/ # Message attachments
│ └── cruise/ # Additional images
│
├── data/
│ └── profiles.json # Seed data (source of truth)
│
├── package.json # Dependencies and scripts
├── gulpfile.js # Gulp tasks (dev, test)
└── Dockerfile # Docker build configuration
🚀 Getting Started
Prerequisites
- Node.js 14.x (included in DevContainer)
- MongoDB 4.4 (included in DevContainer)
- Environment variables configured (see Environment Variables)
Installation
# From project root
npm run install:all
# Or from backend directory
cd backend
npm install
Start Development Server
# From project root (recommended)
npm run dev:backend
# Or from backend directory
cd backend
npm run dev
# Or using gulp directly
gulp
Server starts on port 3069 with auto-restart via nodemon.
Verify Server is Running
curl http://localhost:3069/profiles
Expected: JSON array of profiles or 404 if no data seeded.
🔄 Data Flow
Request Lifecycle
graph TD
A[Client HTTP Request] --> B[Express App app.js]
B --> C[Morgan Logger logs request]
C --> D[Body Parser parses JSON]
D --> E[CORS Headers added]
E --> F{Route Match?}
F -->|Yes| G[Route Handler routes/*]
F -->|No| H[404 Error Handler]
G --> I{Auth Required?}
I -->|Yes| J[Token.verifyThen]
I -->|No| K[Execute Controller Logic]
J --> L{Valid Token?}
L -->|Yes| M{Has Permission?}
L -->|No| N[403 Forbidden]
M -->|Yes| K
M -->|No| N
K --> O[Mongoose Model models/*]
O --> P[MongoDB Query]
P --> Q[Pre-Save Hooks?]
Q -->|Image Processing| R[modules/images]
R --> S[Save to Filesystem]
S --> T[Update Document Path]
Q -->|No Hooks| U[Save to DB]
T --> U
U --> V[Return Response]
V --> W[Winston Logger logs result]
W --> X[Send JSON to Client]
Example: Message Creation with Image
graph TD
A[POST /profiles/:id/message] --> B[Token Verification]
B --> C[Extract message data from request body]
C --> D[Message.save pre-hook triggered]
D --> E{message.image is object?}
E -->|Yes Base64 data| F[Images.saveMessageImage]
E -->|No string path| G[Skip processing]
F --> H[Decode Base64]
H --> I[Generate unique filename]
I --> J[Write to src/images/message/]
J --> K[Return filename path]
K --> L[Update message.image = filename]
L --> M[Save to MongoDB]
G --> M
M --> N[Emit event to route handler]
N --> O[Return 200 with message data]
🔐 Environment Variables
Required environment variables must be configured in .env.example.
Create Environment File
# Copy example file
cp .env.example .env
# Edit with your values
nano .env
Required Variables
| Variable | Description | Example |
|---|---|---|
| PORT | Server port | 3069 |
| MONGODB_URI | MongoDB connection string | mongodb://mongo:27017/urge |
| JWT_SECRET | Secret key for JWT signing | your-super-secret-key-min-32-chars |
| GOOGLE_MAPS_API_KEY | Google Maps API key for geocoding | AIzaSy... |
| MAIL_HOST | SMTP server hostname | smtp.gmail.com |
| MAIL_PORT | SMTP server port | 587 |
| MAIL_USER | SMTP username | support@example.com |
| MAIL_PASS | SMTP password | your-password |
Security Notes
- JWT_SECRET: Use a random 32+ character string. Generate with:
openssl rand -base64 32 - MAIL_PASS: Use app-specific passwords for Gmail/G Suite
- GOOGLE_MAPS_API_KEY: Restrict API key to your backend IP/domain
- Never commit
.envfiles to version control
📄 See .env.example for complete configuration with descriptions
💾 Database Seeding
⚠️ Important: Data Entry Workflow
This application does NOT support interactive data entry through the UI. All profile and message data must be added to the seed file and database reseeded.
Seed Data Location
Source of Truth: data/profiles.json
How to Add/Edit Profiles
-
Edit
data/profiles.json:[ { "order": 1, "details": { "name": "John", "age": 28, "location": "San Francisco, CA", "about": "User's story...", "pic": { "thumb": "profile/john_thumbnail.png", "detail": "profile/john_detail.png" }, "position": ["Top", "Versatile"], "looking": ["Dates", "Friends"], "tribes": ["Geek", "Jock"], "ethnos": ["White", "Latino"] }, "messages": [ { "text": "What brought you to dating apps?", "isUser": false, "timestamp": "2024-01-15T10:30:00Z" }, { "text": "I moved to a new city and wanted to meet people...", "isUser": true, "timestamp": "2024-01-15T10:32:00Z" } ], "submitted": true, "approved": true } ] -
Add images to
src/images/profile/andsrc/images/message/ -
Run seed script:
# From project root npm run seed # Or from backend directory cd backend npm run seed -
Verify data loaded:
curl http://localhost:3069/profiles
Seed Script Behavior
- Drops existing database (all data wiped)
- Creates fresh collections from schema
- Loads data from
data/profiles.json - Processes images via pre-save hooks
- No backup created - commit changes to Git first
Production Deployment
⚠️ Database is wiped and reseeded on each deployment. All content changes must be committed to data/profiles.json before deploying.
If the application becomes interactive with user-generated content, implement proper backup strategies (see DEPLOYMENT.md).
💻 Development Workflow
Gulp Tasks
The project uses Gulp for development automation.
Default task (auto-restart on changes):
gulp
This runs:
- nodemon - Restarts server on
.jsfile changes - mocha - Runs tests on changes
- Watches
src/**/*.jsfor changes
Test task:
gulp test
Runs Mocha tests with "nyan" reporter (cat animation 🐱).
Manual Development
Without Gulp:
# Start with nodemon
nodemon src/bin/www
# Or plain Node.js
node src/bin/www
Testing
# Run tests
npm test
# Or with Gulp
gulp test
Note: Test coverage is limited. Most tests are in backend, none in frontend.
Logging
Winston logger outputs to:
- Console: Colorized logs during development
- Files:
logs/error.log,logs/combined.log(if configured)
Morgan HTTP logger logs all requests:
GET /profiles 200 45ms - 2.5kb
POST /auth/login 401 12ms - 87b
Code Linting
# From project root
npm run lint:backend
# Or from backend directory
cd backend
npm run lint
Note: No ESLint config in backend currently. Consider adding:
npm install -D eslint
npx eslint --init
🔧 Troubleshooting
MongoDB Connection Failed
Error:
MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017
Solutions:
-
Verify MongoDB is running:
docker ps | grep mongo -
Check connection string:
echo $MONGODB_URI # Should be: mongodb://mongo:27017/urge (DevContainer) # Or: mongodb://localhost:27017/urge (local) -
Test connection:
docker exec -it <mongo-container> mongo --eval "db.adminCommand('ping')" -
Restart MongoDB:
docker-compose -f .devcontainer/docker-compose.yml restart mongo
JWT Token Invalid
Error:
JsonWebTokenError: invalid signature
Solutions:
-
Verify JWT_SECRET is set:
echo $JWT_SECRET -
Check .env file exists:
ls -la .env -
Restart backend after changing environment variables:
npm run dev:backend -
Generate new token by logging in again
Image Upload Fails
Error:
ENOENT: no such file or directory, open 'src/images/profile/...'
Solutions:
-
Create image directories:
mkdir -p src/images/profile src/images/message src/images/cruise -
Check permissions:
chmod -R 755 src/images -
Verify volume mount in DevContainer:
ls -la src/images -
Check multer configuration in
modules/images.js
Port 3069 Already in Use
Error:
Error: listen EADDRINUSE: address already in use :::3069
Solutions:
# Find process using port 3069
lsof -i :3069
# Kill the process
kill -9 <PID>
# Or use different port
export PORT=3070
npm run dev
Seed Script Fails
Error:
Error: Cannot find module 'data/profiles.json'
Solutions:
-
Verify file exists:
ls -la data/profiles.json -
Check JSON syntax:
node -e "JSON.parse(require('fs').readFileSync('data/profiles.json'))" -
Run from correct directory:
cd backend npm run seed
Google Maps API Errors
Error:
REQUEST_DENIED: The provided API key is invalid
Solutions:
-
Set API key in environment:
export GOOGLE_MAPS_API_KEY="your-key-here" -
Enable APIs in Google Cloud Console:
- Geocoding API
- Places API (if used)
-
Check API restrictions (IP/domain whitelisting)
-
Verify billing enabled (Google requires it even for free tier)
Email Sending Fails
Error:
Invalid login: 535-5.7.8 Username and Password not accepted
Solutions:
-
Use app-specific password (Gmail):
- Go to Google Account → Security → 2-Step Verification → App passwords
- Generate password for "Mail"
- Use that password in MAIL_PASS
-
Verify SMTP settings:
echo $MAIL_HOST $MAIL_PORT $MAIL_USER -
Test SMTP connection:
telnet $MAIL_HOST $MAIL_PORT -
Enable "Less secure app access" (not recommended, use app passwords instead)
📚 Related Documentation
- API Reference - Complete REST API endpoint documentation
- Database Schema - MongoDB collection schemas and relationships
- Environment Variables - Configuration template
- Root README - Project overview and quick start
- Frontend README - Ionic app documentation
- DevContainer Guide - Development environment
- Deployment Guide - Production deployment
Need Help? Check the root troubleshooting section or API documentation for endpoint-specific issues.