# 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](#overview) - [Tech Stack](#tech-stack) - [Project Structure](#project-structure) - [Getting Started](#getting-started) - [Data Flow](#data-flow) - [Environment Variables](#environment-variables) - [Database Seeding](#database-seeding) - [Development Workflow](#development-workflow) - [Troubleshooting](#troubleshooting) - [Related Documentation](#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](#environment-variables)) ### Installation ```bash # From project root npm run install:all # Or from backend directory cd backend npm install ``` ### Start Development Server ```bash # 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 ```bash curl http://localhost:3069/profiles ``` Expected: JSON array of profiles or 404 if no data seeded. --- ## 🔄 Data Flow ### Request Lifecycle ```mermaid 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 ```mermaid 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`](.env.example). ### Create Environment File ```bash # 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: ```bash 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** `.env` files to version control 📄 **See [.env.example](.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`](data/profiles.json) ### How to Add/Edit Profiles 1. **Edit `data/profiles.json`**: ```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 } ] ``` 2. **Add images** to `src/images/profile/` and `src/images/message/` 3. **Run seed script**: ```bash # From project root npm run seed # Or from backend directory cd backend npm run seed ``` 4. **Verify data loaded**: ```bash 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](../DEPLOYMENT.md)). --- ## 💻 Development Workflow ### Gulp Tasks The project uses **Gulp** for development automation. **Default task** (auto-restart on changes): ```bash gulp ``` This runs: - **nodemon** - Restarts server on `.js` file changes - **mocha** - Runs tests on changes - Watches `src/**/*.js` for changes **Test task**: ```bash gulp test ``` Runs Mocha tests with "nyan" reporter (cat animation 🐱). ### Manual Development Without Gulp: ```bash # Start with nodemon nodemon src/bin/www # Or plain Node.js node src/bin/www ``` ### Testing ```bash # 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 ```bash # 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: ```bash npm install -D eslint npx eslint --init ``` --- ## 🔧 Troubleshooting ### MongoDB Connection Failed **Error:** ``` MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017 ``` **Solutions:** 1. **Verify MongoDB is running**: ```bash docker ps | grep mongo ``` 2. **Check connection string**: ```bash echo $MONGODB_URI # Should be: mongodb://mongo:27017/urge (DevContainer) # Or: mongodb://localhost:27017/urge (local) ``` 3. **Test connection**: ```bash docker exec -it mongo --eval "db.adminCommand('ping')" ``` 4. **Restart MongoDB**: ```bash docker-compose -f .devcontainer/docker-compose.yml restart mongo ``` --- ### JWT Token Invalid **Error:** ``` JsonWebTokenError: invalid signature ``` **Solutions:** 1. **Verify JWT_SECRET is set**: ```bash echo $JWT_SECRET ``` 2. **Check .env file exists**: ```bash ls -la .env ``` 3. **Restart backend** after changing environment variables: ```bash npm run dev:backend ``` 4. **Generate new token** by logging in again --- ### Image Upload Fails **Error:** ``` ENOENT: no such file or directory, open 'src/images/profile/...' ``` **Solutions:** 1. **Create image directories**: ```bash mkdir -p src/images/profile src/images/message src/images/cruise ``` 2. **Check permissions**: ```bash chmod -R 755 src/images ``` 3. **Verify volume mount** in DevContainer: ```bash ls -la src/images ``` 4. **Check multer configuration** in `modules/images.js` --- ### Port 3069 Already in Use **Error:** ``` Error: listen EADDRINUSE: address already in use :::3069 ``` **Solutions:** ```bash # Find process using port 3069 lsof -i :3069 # Kill the process kill -9 # Or use different port export PORT=3070 npm run dev ``` --- ### Seed Script Fails **Error:** ``` Error: Cannot find module 'data/profiles.json' ``` **Solutions:** 1. **Verify file exists**: ```bash ls -la data/profiles.json ``` 2. **Check JSON syntax**: ```bash node -e "JSON.parse(require('fs').readFileSync('data/profiles.json'))" ``` 3. **Run from correct directory**: ```bash cd backend npm run seed ``` --- ### Google Maps API Errors **Error:** ``` REQUEST_DENIED: The provided API key is invalid ``` **Solutions:** 1. **Set API key in environment**: ```bash export GOOGLE_MAPS_API_KEY="your-key-here" ``` 2. **Enable APIs** in Google Cloud Console: - Geocoding API - Places API (if used) 3. **Check API restrictions** (IP/domain whitelisting) 4. **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:** 1. **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 2. **Verify SMTP settings**: ```bash echo $MAIL_HOST $MAIL_PORT $MAIL_USER ``` 3. **Test SMTP connection**: ```bash telnet $MAIL_HOST $MAIL_PORT ``` 4. **Enable "Less secure app access"** (not recommended, use app passwords instead) --- ## 📚 Related Documentation - **[API Reference](API.md)** - Complete REST API endpoint documentation - **[Database Schema](SCHEMA.md)** - MongoDB collection schemas and relationships - **[Environment Variables](.env.example)** - Configuration template - **[Root README](../README.md)** - Project overview and quick start - **[Frontend README](../app/README.md)** - Ionic app documentation - **[DevContainer Guide](../.devcontainer/README.md)** - Development environment - **[Deployment Guide](../DEPLOYMENT.md)** - Production deployment --- **Need Help?** Check the [root troubleshooting section](../README.md#troubleshooting) or API documentation for endpoint-specific issues.