Dating App Documentation
This document serves as the official documentation for a modern dating application built using a
Python-based backend.
1. Introduction
The application, codenamed "Amore," is designed to connect users based on shared interests and
geographical proximity. The backend is built to be robust, scalable, and secure, handling user
authentication, profile management, and a matching algorithm.
2. Technology Stack
Backend
Language: Python 3.9+
Framework: Flask
Database ORM: SQLAlchemy
Database: PostgreSQL (recommended)
Authentication: JWT (JSON Web Tokens)
Password Hashing: Bcrypt
Dependency Management: Pip
3. Installation and Setup
Follow these steps to set up and run the application locally.
Prerequisites
Python 3.9 or higher
Pip
A running PostgreSQL database instance
Steps
1. Clone the Repository:
2. git clone [https://github.com/your-username/amore-dating-app.git](https://github.com/
your-username/amore-dating-app.git)
3. cd amore-dating-app
4.
5.
6.
7. Create a Virtual Environment:
8. python -m venv venv
9.
10.
11.
12. Activate the Virtual Environment:
o macOS / Linux:
o source venv/bin/activate
o Windows:
o venv\Scripts\activate
13. Install Dependencies:
14. pip install -r requirements.txt
15.
16.
17.
18. Configure the Database: Create a .env file in the project root with your database URI.
Replace the placeholder values with your own.
19. DATABASE_URI=postgresql://username:password@localhost:5400/amore_db
20.
21.
22.
23. Initialize the Database: Run the migration script to create the necessary tables.
24. python db_init.py
25.
26.
27.
28. Run the Application:
29. flask run
30.
31.
32.
The application will now be running on http://127.0.0.1:5000.
4. API Endpoints
All API endpoints are prefixed with /api/v1/. Data is sent and received in JSON format.
Authentication Endpoints
POST /api/v1/register
Description: Creates a new user account.
Request Body:
{
"email": "user@example.com",
"password": "strong-password",
"name": "Jane Doe",
"gender": "female"
}
Success Response (201 Created):
{
"message": "User created successfully",
"user_id": 123
}
POST /api/v1/login
Description: Authenticates a user and returns a JWT.
Request Body:
{
"email": "user@example.com",
"password": "strong-password"
}
Success Response (200 OK):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Profile and Discovery Endpoints
GET /api/v1/profile
Description: Retrieves the authenticated user's profile.
Authentication: Requires a valid JWT in the Authorization header.
Success Response (200 OK):
{
"id": 123,
"name": "Jane Doe",
"age": 25,
"bio": "Loves hiking and coding.",
"interests": ["hiking", "coding", "movies"]
}
GET /api/v1/users
Description: Fetches a list of potential matches for the authenticated user.
Authentication: Requires a valid JWT.
Parameters: Can include optional query parameters like limit and offset.
Success Response (200 OK):
[
{
"id": 456,
"name": "John Smith",
"age": 28,
"bio": "Into sci-fi and gaming."
},
...
]
POST /api/v1/swipe
Description: Registers a user's "like" or "dislike" on another user.
Authentication: Requires a valid JWT.
Request Body:
{
"target_user_id": 456,
"action": "like" // or "dislike"
}
Success Response (200 OK):
o If a match occurs:
o {
o "message": "Match successful!",
o "match_id": 789
o }
o If no match:
o {
o "message": "Swipe recorded."
o }
5. Database Schema
users Table
Column Type Description
id INTEGER Primary key
email VARCHAR(255) Unique user email
password_hash VARCHAR(255) Hashed password
name VARCHAR(100) User's full name
gender VARCHAR(50) User's gender
created_at TIMESTAMP Timestamp of creation
profiles Table
Column Type Description
id INTEGER Primary key
user_id INTEGER Foreign key to users
age INTEGER User's age
bio TEXT User's biography
interests ARRAY An array of user interests
photo_url VARCHAR URL of the user's profile photo
last_active TIMESTAMP Last active timestamp
likes Table
Column Type Description
id INTEGER Primary key
liker_id INTEGER Foreign key to users (the user who likes)
liked_id INTEGER Foreign key to users (the user who is liked)
status VARCHAR 'like' or 'dislike'
created_at TIMESTAMP Timestamp of the action
matches Table
Column Type Description
id INTEGER Primary key
user1_id INTEGER Foreign key to users
user2_id INTEGER Foreign key to users
created_at TIMESTAMP Timestamp of the match
6. Future Enhancements
Real-time Chat: Implement a real-time messaging system for matched users using
WebSockets.
Push Notifications: Notify users of new matches or messages.
Advanced Filtering: Add more sophisticated search and filter options (e.g., location, age
range, specific interests).
import os
from datetime import datetime
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_jwt_extended import JWTManager
# Initialize Flask app
app = Flask(__name__)
# --- Configuration ---
# Get configuration from environment variables
# You must set these in a .env file or your environment
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URI',
'postgresql://user:password@localhost:5432/amore_db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY',
'your_super_secret_key')
# Initialize extensions
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
jwt = JWTManager(app)
# --- Database Models ---
# The models are defined here to match the database schema in the
documentation.
class User(db.Model):
"""Represents a user account in the database."""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True,
nullable=False)
password_hash = db.Column(db.String(255), nullable=False)
name = db.Column(db.String(100), nullable=False)
gender = db.Column(db.String(50), nullable=False)
created_at = db.Column(db.TIMESTAMP, default=datetime.utcnow)
# A one-to-one relationship with the Profile table.
profile = db.relationship('Profile', backref='user',
uselist=False, cascade='all, delete-orphan')
def __repr__(self):
return f'<User {self.email}>'
class Profile(db.Model):
"""Stores additional user profile information."""
__tablename__ = 'profiles'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'),
unique=True, nullable=False)
age = db.Column(db.Integer)
bio = db.Column(db.Text)
interests = db.Column(db.ARRAY(db.String))
photo_url = db.Column(db.String)
last_active = db.Column(db.TIMESTAMP,
default=datetime.utcnow)
def __repr__(self):
return f'<Profile of User {self.user_id}>'
# --- API Endpoints ---
# All endpoints are prefixed with /api/v1/
@app.route('/api/v1/register', methods=['POST'])
def register():
"""
Handles new user registration.
Expects a JSON payload with email, password, name, and
gender.
"""
# Check if the request body is valid JSON
if not request.is_json:
return jsonify({"message": "Request body must be
JSON."}), 400
data = request.get_json()
email = data.get('email')
password = data.get('password')
name = data.get('name')
gender = data.get('gender')
# Basic input validation
if not all([email, password, name, gender]):
return jsonify({"message": "Missing one or more required
fields (email, password, name, gender)."}), 400
# Check if the user already exists
if User.query.filter_by(email=email).first():
return jsonify({"message": "Email already registered."}),
400
# Hash the password for secure storage
hashed_password =
bcrypt.generate_password_hash(password).decode('utf-8')
try:
# Create a new User instance
new_user = User(
email=email,
password_hash=hashed_password,
name=name,
gender=gender
)
# Create a basic profile for the new user.
new_profile = Profile(user=new_user)
# Add the new user and profile to the database session
db.session.add(new_user)
db.session.add(new_profile)
db.session.commit()
return jsonify({
"message": "User created successfully",
"user_id": new_user.id
}), 201
except Exception as e:
# Rollback the session in case of an error
db.session.rollback()
return jsonify({"message": f"An error occurred:
{str(e)}"}), 500
if __name__ == '__main__':
# This block is for local development only.
# It creates the database tables if they don't exist.
with app.app_context():
db.create_all()
app.run(debug=True)
API Reference
This document provides a comprehensive, in-depth reference for all API endpoints of the Amore
Dating Application backend. All requests and responses are in JSON format. All protected endpoints
require a valid JWT in the Authorization header.
Authentication Endpoints
These endpoints are responsible for user registration and authentication.
POST /api/v1/register
Description: Creates a new user account and an associated profile. The password is
automatically hashed using Bcrypt before being stored in the database.
Authentication: Public endpoint.
Request Body:
{
"email": "user@example.com", // Required: A unique, valid email address.
"password": "strong-password", // Required: The user's password. Must be at least 8
characters.
"name": "Jane Doe", // Required: The user's full name.
"gender": "female" // Required: The user's self-identified gender.
}
Success Response (201 Created):
{
"message": "User created successfully",
"user_id": 123
}
Error Responses:
o 400 Bad Request:
o {
o "message": "Validation error: 'email' or 'password' field is missing."
o }
o 409 Conflict:
o {
o "message": "Email address is already registered."
o }
Notes: A new user profile is created automatically upon successful registration.
POST /api/v1/login
Description: Authenticates a user with their email and password. Upon successful
authentication, a JSON Web Token (JWT) is returned. This token must be used for subsequent
requests to protected endpoints.
Authentication: Public endpoint.
Request Body:
{
"email": "user@example.com",
"password": "strong-password"
}
Success Response (200 OK):
{
"access_token":
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4g
RG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
Error Response (401 Unauthorized):
{
"message": "Invalid email or password."
}
Notes: The JWT token has a short expiration time (e.g., 1 hour) for security. A robust
application would also implement a refresh token flow.
Profile and Discovery Endpoints
These endpoints handle user profile data and the discovery of potential matches.
GET /api/v1/profile
Description: Retrieves the profile details for the authenticated user.
Authentication: Requires a valid JWT. The token must be sent in the Authorization header as
Bearer <token>.
Success Response (200 OK):
{
"id": 123,
"name": "Jane Doe",
"age": 25,
"bio": "Loves hiking, coding, and good coffee.",
"interests": ["hiking", "coding", "movies"],
"photo_url": "[https://example.com/photos/jane_doe.jpg](https://example.com/photos/
jane_doe.jpg)",
"last_active": "2023-10-27T14:30:00Z"
}
Error Responses:
o 401 Unauthorized:
o {
o "message": "Missing Authorization Header"
o }
or
"message": "Token is invalid or expired."
o 404 Not Found:
o {
o "message": "Profile not found."
o }
PUT /api/v1/profile
Description: Updates the profile of the authenticated user. Any field in the request body is
optional.
Authentication: Requires a valid JWT.
Request Body (Partial Update):
{
"bio": "I'm a software engineer who loves the outdoors."
}
Success Response (200 OK):
{
"message": "Profile updated successfully."
}
Error Responses:
o 401 Unauthorized:
o {
o "message": "Invalid or missing token."
o }
o 400 Bad Request:
o {
o "message": "Validation error: 'age' must be a positive integer."
o }
GET /api/v1/users
Description: Fetches a paginated list of potential matches for the authenticated user. The
algorithm prioritizes users based on shared interests and filters out users the authenticated
user has already swiped on. The default sort order is by proximity and last active date.
Authentication: Requires a valid JWT.
Request Query Parameters:
o limit: Optional. The maximum number of users to return (default: 20).
o offset: Optional. The number of users to skip for pagination (default: 0).
Success Response (200 OK):
[
{
"id": 456,
"name": "John Smith",
"age": 28,
"bio": "Into sci-fi and gaming.",
"interests": ["sci-fi", "gaming"]
},
{
"id": 789,
"name": "Sarah Lee",
"age": 26,
"bio": "A coffee lover and a bookworm.",
"interests": ["reading", "coffee"]
}
]
POST /api/v1/swipe
Description: Records a user's "like" or "dislike" action on another user and triggers a match
check.
Authentication: Requires a valid JWT.
Request Body:
{
"target_user_id": 456, // Required: The ID of the user being swiped on.
"action": "like" // Required: The action, must be "like" or "dislike".
}
Success Responses (200 OK):
o If a new match occurs:
o {
o "message": "Match successful!",
o "match_id": 789
o }
o If no match is found:
o {
o "message": "Swipe recorded."
o }
Error Responses:
o 400 Bad Request:
o {
o "message": "Validation error: 'action' must be 'like' or 'dislike'."
o }
o 404 Not Found:
o {
o "message": "Target user not found."
o }
o 409 Conflict:
o {
o "message": "You have already swiped on this user."
o }
GET /api/v1/matches
Description: Retrieves a list of all users who have mutually "liked" the authenticated user.
Authentication: Requires a valid JWT.
Success Response (200 OK):
[
{
"match_id": 789,
"user_id": 456,
"name": "John Smith",
"match_date": "2023-10-27T10:00:00Z",
"bio": "John's full bio here..."
}
]
Notes: This endpoint joins data from the matches and users tables to provide a complete
view of the matched user's profile.
Endpoint Flowcharts
This section provides a detailed step-by-step breakdown of the logic for key API endpoints.
User Registration (POST /api/v1/register)
1. Start: An incoming POST request is received at /api/v1/register.
2. Validate Request Body: The system checks if the request contains email, password, name,
and gender.
o If Validation Fails: A 400 Bad Request response is returned.
o If Validation Succeeds: Continue to the next step.
3. Check for Existing User: The database is queried to see if an account with the provided email
already exists.
o If User Exists: A 409 Conflict response is returned.
o If User Does Not Exist: Continue to the next step.
4. Hash Password: The provided password is sent to the Bcrypt hashing algorithm.
5. Store User in Database: A new User record is created in the users table with the hashed
password. The created_at timestamp is set automatically.
6. Create Profile: A new Profile record is created in the profiles table, linked to the new User via
the user_id foreign key.
7. Return Success: A 201 Created response is returned, confirming the account and profile
were successfully created.
8. End.
User Login (POST /api/v1/login)
1. Start: An incoming POST request is received at /api/v1/login.
2. Validate Request Body: The system checks for the presence of email and password.
o If Validation Fails: A 400 Bad Request response is returned.
o If Validation Succeeds: Continue to the next step.
3. Retrieve User: The database is queried for the user with the provided email.
o If User Not Found: A 401 Unauthorized response is returned.
o If User Found: Continue to the next step.
4. Verify Password: The provided password is compared against the stored password_hash
using Bcrypt.
o If Password Mismatches: A 401 Unauthorized response is returned.
o If Password Matches: Continue to the next step.
5. Generate JWT: A new JSON Web Token is generated, containing the user's ID as a payload.
6. Return Success: A 200 OK response is returned, with the access_token in the response body.
7. End.
Swipe and Match Logic (POST /api/v1/swipe)
1. Start: An incoming POST request is received at /api/v1/swipe from the authenticated user.
2. Validate Request Body: The system checks for target_user_id and a valid action ("like" or
"dislike").
o If Validation Fails: A 400 Bad Request response is returned.
o If Validation Succeeds: Continue to the next step.
3. Check for Existing Swipe: The database is queried to see if the authenticated user has
already swiped on the target_user_id.
o If Swipe Exists: A 409 Conflict response is returned.
o If Swipe Does Not Exist: Continue to the next step.
4. Create Swipe Record: A new Like record is created in the likes table with the liker_id,
liked_id, and status.
5. Check for Match (if action is "like"):
o If action is "dislike": Skip to step 8.
o If action is "like": The database is queried to see if the target_user_id has also
"liked" the authenticated user.
6. Match Found?
o If No Match Found: A 200 OK response is returned with a "Swipe recorded"
message.
o If Match Found: Continue to the next step.
7. Create Match Record: A new Match record is created in the matches table, with the user1_id
and user2_id sorted canonically to prevent duplicate entries.
8. Return Success: A 200 OK response is returned, either with a "Match successful!" or "Swipe
recorded" message.
9. End.