Backend API Routes - esketchandu/TrailAndOutdoorRecreationHub GitHub Wiki

Backend API Routes Documentation

Overview

This document outlines all the API endpoints for the Trail & Outdoor Recreation Hub backend built with Flask and Python. The API follows RESTful conventions and supports full CRUD operations for trails, reviews, points of interest, and trip plans. All endpoints return JSON responses and use standard HTTP status codes.

Base URL: http://localhost:5000/api


Authentication Routes

Register a User

Creates a new user account and returns the user's information with authentication token.

  • Require Authentication: false

  • Request

    • Method: POST

    • Route path: /api/auth/signup

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "username": "hikerjane",
        "email": "[email protected]",
        "password": "securepass123",
        "first_name": "Jane",
        "last_name": "Hiker",
        "hiking_level": "intermediate"
      }
      
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": {
          "id": 1,
          "username": "hikerjane",
          "email": "[email protected]",
          "first_name": "Jane",
          "last_name": "Hiker",
          "hiking_level": "intermediate",
          "is_active": true,
          "created_at": "2024-01-15T10:30:00Z"
        },
        "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
      }
      
  • Error Response: User already exists

    • Status Code: 409

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "User already exists",
        "errors": {
          "email": "Email address is already in use"
        }
      }
      
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation error",
        "errors": {
          "username": "Username is required",
          "email": "Valid email is required",
          "password": "Password must be at least 6 characters",
          "hiking_level": "Must be one of: beginner, intermediate, advanced, expert"
        }
      }
      

Log In a User

Logs in a current user with valid credentials and returns the current user's information with authentication token.

  • Require Authentication: false

  • Request

    • Method: POST

    • Route path: /api/auth/login

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "email": "[email protected]",
        "password": "securepass123"
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": {
          "id": 1,
          "username": "hikerjane",
          "email": "[email protected]",
          "first_name": "Jane",
          "last_name": "Hiker",
          "hiking_level": "intermediate",
          "avatar_url": "https://s3.amazonaws.com/trail-hub/avatars/user1.jpg",
          "created_at": "2024-01-15T10:30:00Z"
        },
        "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
      }
      
  • Error Response: Invalid credentials

    • Status Code: 401

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Invalid credentials"
      }
      

Log Out a User

Logs out the current user by invalidating their authentication token.

  • Require Authentication: true

  • Request

    • Method: POST
    • Route path: /api/auth/logout
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully logged out"
      }
      

Get Current User Session

Returns the current user's information if authenticated.

  • Require Authentication: true

  • Request

    • Method: GET
    • Route path: /api/auth/session
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": {
          "id": 1,
          "username": "hikerjane",
          "email": "[email protected]",
          "first_name": "Jane",
          "last_name": "Hiker",
          "hiking_level": "intermediate",
          "is_active": true,
          "is_admin": false,
          "created_at": "2024-01-15T10:30:00Z"
        }
      }
      

Trail Routes

Get All Trails

Returns all trails with pagination and optional filtering.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trails
    • Query Parameters:
      • page (optional): Page number (default: 1)
      • limit (optional): Items per page (default: 20)
      • difficulty (optional): Filter by difficulty (easy, moderate, hard, expert)
      • min_length (optional): Minimum trail length in km
      • max_length (optional): Maximum trail length in km
      • region (optional): Filter by region
      • bounds (optional): Bounding box for map view (format: "minLat,minLon,maxLat,maxLon")
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "trails": [
          {
            "id": 1,
            "name": "Eagle Peak Trail",
            "description": "A challenging hike with stunning summit views",
            "difficulty": "hard",
            "length_km": 12.5,
            "elevation_gain_m": 850,
            "region": "Yosemite National Park",
            "avg_rating": 4.8,
            "total_reviews": 156,
            "geometry": {
              "type": "LineString",
              "coordinates": [[-119.5383, 37.7459], [-119.5384, 37.7460], ...]
            },
            "created_by": {
              "id": 2,
              "username": "trailmaster"
            },
            "created_at": "2024-01-10T09:15:00Z"
          }
        ],
        "pagination": {
          "page": 1,
          "pages": 5,
          "per_page": 20,
          "total": 87
        }
      }
      

Get Trail by ID

Returns detailed information about a specific trail.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trails/:id
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "name": "Eagle Peak Trail",
        "description": "A challenging hike with stunning summit views. The trail starts at the valley floor and climbs steadily through pine forests before reaching alpine meadows.",
        "difficulty": "hard",
        "length_km": 12.5,
        "elevation_gain_m": 850,
        "region": "Yosemite National Park",
        "parking_info": "Park at the Eagle Peak Trailhead parking lot. Arrives early on weekends as it fills up by 8 AM. $30 park entrance fee required.",
        "avg_rating": 4.8,
        "total_reviews": 156,
        "geometry": {
          "type": "LineString",
          "coordinates": [[-119.5383, 37.7459], [-119.5384, 37.7460], ...]
        },
        "created_by": {
          "id": 2,
          "username": "trailmaster",
          "first_name": "Mike",
          "last_name": "Johnson"
        },
        "created_at": "2024-01-10T09:15:00Z",
        "updated_at": "2024-01-15T16:45:00Z"
      }
      
  • Error Response: Trail not found

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Trail not found"
      }
      

Create a Trail

Creates a new trail. Requires GPS track data as LineString geometry.

  • Require Authentication: true

  • Request

    • Method: POST

    • Route path: /api/trails

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "name": "Hidden Lake Trail",
        "description": "A moderate hike through forests to a pristine alpine lake",
        "difficulty": "moderate",
        "length_km": 8.2,
        "elevation_gain_m": 450,
        "region": "North Cascades",
        "parking_info": "Free parking at the trailhead. No facilities available.",
        "geometry": {
          "type": "LineString",
          "coordinates": [
            [-121.1234, 48.7890],
            [-121.1235, 48.7891],
            [-121.1236, 48.7892]
          ]
        }
      }
      
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 88,
        "name": "Hidden Lake Trail",
        "description": "A moderate hike through forests to a pristine alpine lake",
        "difficulty": "moderate",
        "length_km": 8.2,
        "elevation_gain_m": 450,
        "region": "North Cascades",
        "parking_info": "Free parking at the trailhead. No facilities available.",
        "geometry": {
          "type": "LineString",
          "coordinates": [-121.1234, 48.7890], [-121.1235, 48.7891], [-121.1236, 48.7892](/esketchandu/TrailAndOutdoorRecreationHub/wiki/-121.1234,-48.7890],-[-121.1235,-48.7891],-[-121.1236,-48.7892)
        },
        "created_by": 1,
        "created_at": "2024-01-16T10:30:00Z"
      }
      
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation error",
        "errors": {
          "name": "Trail name is required",
          "difficulty": "Difficulty must be one of: easy, moderate, hard, expert",
          "length_km": "Trail length must be a positive number",
          "geometry": "Valid LineString geometry is required"
        }
      }
      

Update a Trail

Updates an existing trail. Only the trail creator or admin can update.

  • Require Authentication: true

  • Request

    • Method: PUT

    • Route path: /api/trails/:id

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "description": "Updated description with seasonal information. Best hiked in summer months.",
        "parking_info": "Updated: Parking lot expanded. Now has restroom facilities.",
        "elevation_gain_m": 455
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "name": "Eagle Peak Trail",
        "description": "Updated description with seasonal information. Best hiked in summer months.",
        "difficulty": "hard",
        "length_km": 12.5,
        "elevation_gain_m": 455,
        "parking_info": "Updated: Parking lot expanded. Now has restroom facilities.",
        "updated_at": "2024-01-16T14:20:00Z"
      }
      
  • Error Response: Forbidden

    • Status Code: 403

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Access denied. You can only edit trails you created."
      }
      

Delete a Trail

Deletes a trail and all associated data. Only the trail creator or admin can delete.

  • Require Authentication: true

  • Request

    • Method: DELETE
    • Route path: /api/trails/:id
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 204
    • Headers:
      • Content-Type: application/json
    • Body: None
  • Error Response: Forbidden

    • Status Code: 403

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Access denied. You can only delete trails you created."
      }
      

Review Routes

Get Trail Reviews

Returns all reviews for a specific trail.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trails/:id/reviews
    • Query Parameters:
      • page (optional): Page number (default: 1)
      • limit (optional): Items per page (default: 10)
      • sort (optional): Sort by 'newest', 'oldest', 'highest', 'lowest' (default: 'newest')
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "reviews": [
          {
            "id": 1,
            "rating": 5,
            "title": "Absolutely breathtaking!",
            "content": "This trail exceeded all expectations. The summit views were incredible and the wildflowers in the meadow section were amazing.",
            "hiked_date": "2024-01-12",
            "weather_condition": "sunny",
            "trail_condition": "excellent",
            "crowd_level": "moderate",
            "helpful_count": 23,
            "is_verified_hike": true,
            "created_at": "2024-01-13T10:30:00Z",
            "user": {
              "id": 3,
              "username": "mountainlover",
              "first_name": "Sarah",
              "last_name": "Chen",
              "hiking_level": "advanced"
            }
          }
        ],
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail"
        },
        "pagination": {
          "page": 1,
          "pages": 16,
          "per_page": 10,
          "total": 156
        }
      }
      

Get Review by ID

Returns detailed information about a specific review.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/reviews/:id
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "trail_id": 1,
        "rating": 5,
        "title": "Absolutely breathtaking!",
        "content": "This trail exceeded all expectations. The summit views were incredible and the wildflowers in the meadow section were amazing.",
        "hiked_date": "2024-01-12",
        "weather_condition": "sunny",
        "trail_condition": "excellent",
        "crowd_level": "moderate",
        "helpful_count": 23,
        "is_verified_hike": true,
        "created_at": "2024-01-13T10:30:00Z",
        "updated_at": "2024-01-13T10:30:00Z",
        "user": {
          "id": 3,
          "username": "mountainlover",
          "first_name": "Sarah",
          "last_name": "Chen"
        },
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail",
          "difficulty": "hard"
        }
      }
      

Create a Review

Creates a new review for a trail. Users can only review each trail once.

  • Require Authentication: true

  • Request

    • Method: POST

    • Route path: /api/trails/:id/reviews

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "rating": 4,
        "title": "Great hike but crowded",
        "content": "Beautiful trail with amazing views. The trail was well-maintained but quite crowded on Saturday morning. Recommend going early on weekdays.",
        "hiked_date": "2024-01-14",
        "weather_condition": "cloudy",
        "trail_condition": "good",
        "crowd_level": "crowded"
      }
      
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 157,
        "trail_id": 1,
        "user_id": 1,
        "rating": 4,
        "title": "Great hike but crowded",
        "content": "Beautiful trail with amazing views. The trail was well-maintained but quite crowded on Saturday morning. Recommend going early on weekdays.",
        "hiked_date": "2024-01-14",
        "weather_condition": "cloudy",
        "trail_condition": "good",
        "crowd_level": "crowded",
        "helpful_count": 0,
        "is_verified_hike": false,
        "created_at": "2024-01-16T10:30:00Z"
      }
      
  • Error Response: Review already exists

    • Status Code: 409

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "You have already reviewed this trail"
      }
      
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation error",
        "errors": {
          "rating": "Rating must be between 1 and 5",
          "content": "Review content cannot be empty",
          "hiked_date": "Hiked date is required and cannot be in the future"
        }
      }
      

Update a Review

Updates an existing review. Only the review author can update.

  • Require Authentication: true

  • Request

    • Method: PUT

    • Route path: /api/reviews/:id

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "content": "Updated: Beautiful trail with amazing views. The trail was well-maintained but quite crowded on Saturday morning. Recommend going early on weekdays. Update: I've hiked this trail several more times and early mornings are definitely best.",
        "trail_condition": "excellent"
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 157,
        "rating": 4,
        "title": "Great hike but crowded",
        "content": "Updated: Beautiful trail with amazing views. The trail was well-maintained but quite crowded on Saturday morning. Recommend going early on weekdays. Update: I've hiked this trail several more times and early mornings are definitely best.",
        "trail_condition": "excellent",
        "updated_at": "2024-01-17T09:15:00Z"
      }
      
  • Error Response: Forbidden

    • Status Code: 403

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Access denied. You can only edit your own reviews."
      }
      

Delete a Review

Deletes a review. Only the review author or admin can delete.

  • Require Authentication: true

  • Request

    • Method: DELETE
    • Route path: /api/reviews/:id
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 204
    • Headers:
      • Content-Type: application/json
    • Body: None

Points of Interest Routes

Get Trail Points of Interest

Returns all points of interest for a specific trail.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trails/:id/points-of-interest
    • Query Parameters:
      • type (optional): Filter by type (viewpoint, waterfall, campsite, water_source, parking, warning, junction)
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "points_of_interest": [
          {
            "id": 1,
            "name": "Summit Viewpoint",
            "description": "360-degree panoramic views of the entire valley",
            "type": "viewpoint",
            "latitude": 37.7459,
            "longitude": -119.5383,
            "elevation_m": 2850,
            "distance_from_start_km": 6.2,
            "amenities": {},
            "is_verified": true,
            "created_by": {
              "id": 2,
              "username": "trailmaster"
            },
            "created_at": "2024-01-10T09:15:00Z"
          },
          {
            "id": 2,
            "name": "Alpine Lake Campsite",
            "description": "Beautiful lakeside camping with 6 designated sites",
            "type": "campsite",
            "latitude": 37.7234,
            "longitude": -119.5234,
            "elevation_m": 2200,
            "distance_from_start_km": 4.8,
            "amenities": {
              "toilets": true,
              "fire_rings": true,
              "bear_boxes": true,
              "max_tents": 6
            },
            "seasonal_info": "Snow-free typically from June to October",
            "is_verified": true,
            "created_by": {
              "id": 5,
              "username": "campexpert"
            },
            "created_at": "2024-01-12T14:20:00Z"
          }
        ],
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail"
        }
      }
      

Get Point of Interest by ID

Returns detailed information about a specific point of interest.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/points-of-interest/:id
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 2,
        "trail_id": 1,
        "name": "Alpine Lake Campsite",
        "description": "Beautiful lakeside camping with 6 designated sites. First-come, first-served. Popular on weekends.",
        "type": "campsite",
        "location": {
          "type": "Point",
          "coordinates": [-119.5234, 37.7234]
        },
        "latitude": 37.7234,
        "longitude": -119.5234,
        "elevation_m": 2200,
        "distance_from_start_km": 4.8,
        "amenities": {
          "toilets": true,
          "fire_rings": true,
          "bear_boxes": true,
          "max_tents": 6,
          "water_access": "seasonal"
        },
        "seasonal_info": "Snow-free typically from June to October. Water source may dry up in late summer.",
        "is_verified": true,
        "created_by": {
          "id": 5,
          "username": "campexpert",
          "first_name": "Lisa",
          "last_name": "Wong"
        },
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail"
        },
        "created_at": "2024-01-12T14:20:00Z",
        "updated_at": "2024-01-14T09:30:00Z"
      }
      

Create a Point of Interest

Creates a new point of interest on a trail.

  • Require Authentication: true

  • Request

    • Method: POST

    • Route path: /api/trails/:id/points-of-interest

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "name": "Hidden Waterfall",
        "description": "Beautiful 30-foot waterfall, best flow in spring",
        "type": "waterfall",
        "latitude": 37.7345,
        "longitude": -119.5289,
        "elevation_m": 1950,
        "distance_from_start_km": 3.2,
        "seasonal_info": "Best viewed April through June when snowmelt is high"
      }
      
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 45,
        "trail_id": 1,
        "name": "Hidden Waterfall",
        "description": "Beautiful 30-foot waterfall, best flow in spring",
        "type": "waterfall",
        "location": {
          "type": "Point",
          "coordinates": [-119.5289, 37.7345]
        },
        "latitude": 37.7345,
        "longitude": -119.5289,
        "elevation_m": 1950,
        "distance_from_start_km": 3.2,
        "amenities": {},
        "seasonal_info": "Best viewed April through June when snowmelt is high",
        "is_verified": false,
        "created_by": 1,
        "created_at": "2024-01-16T10:30:00Z"
      }
      
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation error",
        "errors": {
          "name": "POI name is required",
          "type": "Type must be one of: viewpoint, waterfall, campsite, water_source, parking, warning, junction",
          "latitude": "Valid latitude is required",
          "longitude": "Valid longitude is required"
        }
      }
      

Update a Point of Interest

Updates an existing point of interest. Only the creator or admin can update.

  • Require Authentication: true

  • Request

    • Method: PUT

    • Route path: /api/points-of-interest/:id

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "description": "Beautiful 30-foot waterfall. Update: Trail to waterfall can be slippery, use caution.",
        "amenities": {
          "viewing_platform": true,
          "safety_railing": false
        }
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 45,
        "name": "Hidden Waterfall",
        "description": "Beautiful 30-foot waterfall. Update: Trail to waterfall can be slippery, use caution.",
        "amenities": {
          "viewing_platform": true,
          "safety_railing": false
        },
        "updated_at": "2024-01-17T11:45:00Z"
      }
      

Delete a Point of Interest

Deletes a point of interest. Only the creator or admin can delete.

  • Require Authentication: true

  • Request

    • Method: DELETE
    • Route path: /api/points-of-interest/:id
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 204
    • Headers:
      • Content-Type: application/json
    • Body: None

Trip Plan Routes

Get All Trip Plans

Returns all upcoming trip plans with pagination.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trip-plans
    • Query Parameters:
      • page (optional): Page number (default: 1)
      • limit (optional): Items per page (default: 20)
      • trail_id (optional): Filter by specific trail
      • upcoming (optional): Show only future trips (default: true)
      • my_trips (optional): Show only trips user is participating in (requires auth)
    • Headers:
      • Authorization: Bearer <access_token> (optional, for my_trips filter)
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "trip_plans": [
          {
            "id": 1,
            "trail_id": 1,
            "title": "Sunrise Hike to Eagle Peak",
            "description": "Early morning hike to catch the sunrise from the summit",
            "planned_date": "2024-01-20",
            "start_time": "05:00:00",
            "estimated_end_time": "14:00:00",
            "meeting_point": "Main parking lot near the visitor center",
            "meeting_lat": 37.7400,
            "meeting_lon": -119.5400,
            "max_participants": 12,
            "current_participants": 8,
            "difficulty_level": "hard",
            "pace": "moderate",
            "status": "active",
            "is_public": true,
            "organizer": {
              "id": 3,
              "username": "mountainlover",
              "first_name": "Sarah",
              "last_name": "Chen"
            },
            "trail": {
              "id": 1,
              "name": "Eagle Peak Trail"
            },
            "created_at": "2024-01-14T09:00:00Z"
          }
        ],
        "pagination": {
          "page": 1,
          "pages": 3,
          "per_page": 20,
          "total": 45
        }
      }
      

Get Trip Plan by ID

Returns detailed information about a specific trip plan.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trip-plans/:id
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "trail_id": 1,
        "title": "Sunrise Hike to Eagle Peak",
        "description": "Early morning hike to catch the sunrise from the summit. We'll meet at 5 AM and start hiking in the dark, so bring headlamps!",
        "planned_date": "2024-01-20",
        "start_time": "05:00:00",
        "estimated_end_time": "14:00:00",
        "meeting_point": "Main parking lot near the visitor center",
        "meeting_point_location": {
          "type": "Point",
          "coordinates": [-119.5400, 37.7400]
        },
        "meeting_lat": 37.7400,
        "meeting_lon": -119.5400,
        "max_participants": 12,
        "current_participants": 8,
        "difficulty_level": "hard",
        "pace": "moderate",
        "requirements": [
          "Headlamp or flashlight",
          "3L water minimum",
          "Lunch and snacks",
          "Layers for cold morning",
          "Hiking boots"
        ],
        "notes": "Dogs welcome but must be leashed. Carpool options available.",
        "status": "active",
        "is_public": true,
        "organizer": {
          "id": 3,
          "username": "mountainlover",
          "first_name": "Sarah",
          "last_name": "Chen",
          "hiking_level": "advanced"
        },
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail",
          "difficulty": "hard",
          "length_km": 12.5
        },
        "participants": [
          {
            "id": 5,
            "username": "hikerjane",
            "first_name": "Jane",
            "status": "confirmed"
          },
          {
            "id": 7,
            "username": "trailfinder",
            "first_name": "Mike",
            "status": "confirmed"
          }
        ],
        "created_at": "2024-01-14T09:00:00Z",
        "updated_at": "2024-01-15T10:30:00Z"
      }
      

Create a Trip Plan

Creates a new trip plan for a trail.

  • Require Authentication: true

  • Request

    • Method: POST

    • Route path: /api/trip-plans

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "trail_id": 1,
        "title": "Full Moon Hike",
        "description": "Night hike under the full moon. Experience the trail in a completely different way!",
        "planned_date": "2024-01-25",
        "start_time": "19:00:00",
        "estimated_end_time": "23:00:00",
        "meeting_point": "Lower parking lot",
        "meeting_lat": 37.7380,
        "meeting_lon": -119.5420,
        "max_participants": 8,
        "difficulty_level": "moderate",
        "pace": "leisurely",
        "requirements": [
          "Headlamp with extra batteries",
          "Warm layers",
          "Hot beverage in thermos"
        ],
        "notes": "No dogs for this night hike please. We'll take it slow and enjoy the moonlight.",
        "is_public": true
      }
      
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 46,
        "trail_id": 1,
        "organizer_id": 1,
        "title": "Full Moon Hike",
        "description": "Night hike under the full moon. Experience the trail in a completely different way!",
        "planned_date": "2024-01-25",
        "start_time": "19:00:00",
        "estimated_end_time": "23:00:00",
        "meeting_point": "Lower parking lot",
        "meeting_point_location": {
          "type": "Point",
          "coordinates": [-119.5420, 37.7380]
        },
        "max_participants": 8,
        "current_participants": 1,
        "difficulty_level": "moderate",
        "pace": "leisurely",
        "requirements": [
          "Headlamp with extra batteries",
          "Warm layers",
          "Hot beverage in thermos"
        ],
        "notes": "No dogs for this night hike please. We'll take it slow and enjoy the moonlight.",
        "status": "active",
        "is_public": true,
        "created_at": "2024-01-16T10:30:00Z"
      }
      
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation error",
        "errors": {
          "title": "Trip title is required",
          "planned_date": "Date must be in the future",
          "start_time": "Start time is required",
          "max_participants": "Must be a positive number"
        }
      }
      

Update a Trip Plan

Updates an existing trip plan. Only the organizer can update.

  • Require Authentication: true

  • Request

    • Method: PUT

    • Route path: /api/trip-plans/:id

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "description": "Night hike under the full moon. Update: Weather looks great! Experience the trail in a completely different way!",
        "max_participants": 10,
        "notes": "No dogs for this night hike please. We'll take it slow and enjoy the moonlight. Update: 2 spots just opened up!"
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 46,
        "title": "Full Moon Hike",
        "description": "Night hike under the full moon. Update: Weather looks great! Experience the trail in a completely different way!",
        "max_participants": 10,
        "notes": "No dogs for this night hike please. We'll take it slow and enjoy the moonlight. Update: 2 spots just opened up!",
        "updated_at": "2024-01-17T15:20:00Z"
      }
      

Delete/Cancel a Trip Plan

Cancels a trip plan. Only the organizer can cancel.

  • Require Authentication: true

  • Request

    • Method: DELETE
    • Route path: /api/trip-plans/:id
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Trip plan cancelled successfully",
        "notifications_sent": 7
      }
      

Join a Trip Plan

Join an existing trip plan as a participant.

  • Require Authentication: true

  • Request

    • Method: POST
    • Route path: /api/trip-plans/:id/join
    • Headers:
      • Authorization: Bearer <access_token>
    • Body: None
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully joined trip",
        "status": "confirmed",
        "trip_plan_id": 1,
        "participant_count": 9
      }
      
  • Error Response: Trip full

    • Status Code: 409

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Trip is already full"
      }
      

Leave a Trip Plan

Leave a trip plan you previously joined.

  • Require Authentication: true

  • Request

    • Method: POST
    • Route path: /api/trip-plans/:id/leave
    • Headers:
      • Authorization: Bearer <access_token>
    • Body: None
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully left trip",
        "trip_plan_id": 1,
        "participant_count": 8
      }
      

Hazard Routes

Get Trail Hazards

Returns all hazards for a specific trail, including both active and resolved.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trails/:id/hazards
    • Query Parameters:
      • status (optional): Filter by status (active, monitoring, resolved) - default: active
      • severity (optional): Filter by severity (low, medium, high, extreme)
      • verified_only (optional): Show only admin-verified hazards (true/false)
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "hazards": [
          {
            "id": 12,
            "type": "fallen_tree",
            "severity": "high",
            "title": "Large tree blocking trail at mile 3.5",
            "description": "A massive pine tree has fallen across the trail. Difficult but possible to climb over. Use caution.",
            "latitude": 37.7234,
            "longitude": -119.5234,
            "status": "active",
            "verified_by_admin": true,
            "reported_at": "2024-01-15T14:30:00Z",
            "reporter": {
              "id": 8,
              "username": "trailwatch",
              "first_name": "Tom"
            }
          },
          {
            "id": 15,
            "type": "ice",
            "severity": "extreme",
            "title": "Dangerous ice on summit approach",
            "description": "The final 0.5 miles to summit are covered in sheet ice. Microspikes or crampons absolutely required. Several hikers have fallen.",
            "latitude": 37.7456,
            "longitude": -119.5380,
            "status": "active",
            "expires_at": "2024-03-15T00:00:00Z",
            "verified_by_admin": true,
            "reported_at": "2024-01-16T09:15:00Z",
            "reporter": {
              "id": 3,
              "username": "mountainlover"
            }
          }
        ],
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail"
        },
        "active_count": 2,
        "total_count": 5
      }
      

Get All Recent Hazards

Returns recent hazards across all trails for map view.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/hazards
    • Query Parameters:
      • bounds (optional): Bounding box "minLat,minLon,maxLat,maxLon"
      • days (optional): Show hazards from last N days (default: 7)
      • severity (optional): Filter by severity level
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "hazards": [
          {
            "id": 15,
            "trail_id": 1,
            "trail_name": "Eagle Peak Trail",
            "type": "ice",
            "severity": "extreme",
            "title": "Dangerous ice on summit approach",
            "latitude": 37.7456,
            "longitude": -119.5380,
            "reported_at": "2024-01-16T09:15:00Z"
          },
          {
            "id": 18,
            "trail_id": 5,
            "trail_name": "River Loop Trail",
            "type": "flooding",
            "severity": "high",
            "title": "Creek crossing impassable",
            "latitude": 37.6890,
            "longitude": -119.4567,
            "reported_at": "2024-01-16T11:20:00Z"
          }
        ],
        "filter_applied": {
          "days": 7,
          "bounds": "37.6000,-119.6000,37.8000,-119.4000"
        },
        "total": 15
      }
      

Get Hazard by ID

Returns detailed information about a specific hazard.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/hazards/:id
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 15,
        "trail_id": 1,
        "type": "ice",
        "severity": "extreme",
        "title": "Dangerous ice on summit approach",
        "description": "The final 0.5 miles to summit are covered in sheet ice. Microspikes or crampons absolutely required. Several hikers have fallen. Alternative route: Consider turning back at the alpine lake if not properly equipped.",
        "location": {
          "type": "Point",
          "coordinates": [-119.5380, 37.7456]
        },
        "latitude": 37.7456,
        "longitude": -119.5380,
        "status": "active",
        "expires_at": "2024-03-15T00:00:00Z",
        "verified_by_admin": true,
        "reported_at": "2024-01-16T09:15:00Z",
        "updated_at": "2024-01-16T10:30:00Z",
        "reporter": {
          "id": 3,
          "username": "mountainlover",
          "first_name": "Sarah",
          "hiking_level": "advanced"
        },
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail",
          "difficulty": "hard"
        }
      }
      

Report a Hazard

Reports a new hazard on a trail.

  • Require Authentication: true

  • Request

    • Method: POST

    • Route path: /api/trails/:id/hazards

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "type": "rockfall",
        "severity": "medium",
        "title": "Recent rockfall near mile marker 5",
        "description": "Small rockfall has left loose rocks on trail. Careful footing required but passable. Happened during yesterday's rain.",
        "latitude": 37.7345,
        "longitude": -119.5290,
        "expires_at": "2024-02-15T00:00:00Z"
      }
      
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 23,
        "trail_id": 1,
        "reported_by": 1,
        "type": "rockfall",
        "severity": "medium",
        "title": "Recent rockfall near mile marker 5",
        "description": "Small rockfall has left loose rocks on trail. Careful footing required but passable. Happened during yesterday's rain.",
        "location": {
          "type": "Point",
          "coordinates": [-119.5290, 37.7345]
        },
        "latitude": 37.7345,
        "longitude": -119.5290,
        "status": "active",
        "expires_at": "2024-02-15T00:00:00Z",
        "verified_by_admin": false,
        "reported_at": "2024-01-16T10:30:00Z"
      }
      
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation error",
        "errors": {
          "type": "Type must be one of: fallen_tree, flooding, wildlife, ice, rockfall, trail_damage, closure",
          "severity": "Severity must be one of: low, medium, high, extreme",
          "title": "Title is required",
          "description": "Description is required",
          "latitude": "Valid latitude is required",
          "longitude": "Valid longitude is required"
        }
      }
      

Update Hazard Status

Updates a hazard's status or information. Only reporter or admin can update.

  • Require Authentication: true

  • Request

    • Method: PUT

    • Route path: /api/hazards/:id

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "status": "resolved",
        "resolution_notes": "Trail crew cleared the tree on Jan 17. Trail is now fully passable."
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 12,
        "type": "fallen_tree",
        "title": "Large tree blocking trail at mile 3.5",
        "status": "resolved",
        "resolved_at": "2024-01-17T14:20:00Z",
        "resolved_by": 1,
        "resolution_notes": "Trail crew cleared the tree on Jan 17. Trail is now fully passable.",
        "updated_at": "2024-01-17T14:20:00Z"
      }
      

Delete a Hazard

Deletes a hazard report. Only reporter or admin can delete.

  • Require Authentication: true

  • Request

    • Method: DELETE
    • Route path: /api/hazards/:id
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 204
    • Headers:
      • Content-Type: application/json
    • Body: None

Photo Routes

Get Trail Photos

Returns all photos for a specific trail.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/trails/:id/photos
    • Query Parameters:
      • page (optional): Page number (default: 1)
      • limit (optional): Items per page (default: 20)
      • poi_id (optional): Filter photos by specific POI
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "photos": [
          {
            "id": 45,
            "uuid": "550e8400-e29b-41d4-a716-446655440000",
            "caption": "Sunrise from the summit - worth the early start!",
            "thumbnail_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/550e8400/thumb.jpg",
            "medium_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/550e8400/medium.jpg",
            "latitude": 37.7459,
            "longitude": -119.5383,
            "taken_at": "2024-01-15T06:45:00Z",
            "likes_count": 23,
            "is_featured": true,
            "uploader": {
              "id": 3,
              "username": "mountainlover"
            },
            "poi": {
              "id": 1,
              "name": "Summit Viewpoint"
            }
          }
        ],
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail"
        },
        "pagination": {
          "page": 1,
          "pages": 8,
          "per_page": 20,
          "total": 156
        }
      }
      

Get Photo by ID

Returns detailed information about a specific photo.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/photos/:id
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 45,
        "uuid": "550e8400-e29b-41d4-a716-446655440000",
        "trail_id": 1,
        "caption": "Sunrise from the summit - worth the early start!",
        "original_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/550e8400/original.jpg",
        "thumbnail_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/550e8400/thumb.jpg",
        "medium_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/550e8400/medium.jpg",
        "latitude": 37.7459,
        "longitude": -119.5383,
        "location_on_trail": {
          "type": "Point",
          "coordinates": [-119.5383, 37.7459]
        },
        "taken_at": "2024-01-15T06:45:00Z",
        "camera_make": "Apple",
        "camera_model": "iPhone 14 Pro",
        "likes_count": 23,
        "is_featured": true,
        "uploaded_at": "2024-01-15T10:30:00Z",
        "uploader": {
          "id": 3,
          "username": "mountainlover",
          "first_name": "Sarah"
        },
        "trail": {
          "id": 1,
          "name": "Eagle Peak Trail"
        },
        "poi": {
          "id": 1,
          "name": "Summit Viewpoint",
          "type": "viewpoint"
        }
      }
      

Upload Photo

Uploads a photo for a trail with automatic EXIF extraction and image processing.

  • Require Authentication: true

  • Request

    • Method: POST
    • Route path: /api/trails/:id/photos
    • Headers:
      • Content-Type: multipart/form-data
      • Authorization: Bearer <access_token>
    • Body: Form data
      • photo: [Image file] (required)
      • caption: "Beautiful sunrise from the summit" (optional)
      • poi_id: 1 (optional)
      • latitude: 37.7459 (optional - overrides EXIF)
      • longitude: -119.5383 (optional - overrides EXIF)
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 234,
        "uuid": "651f9500-f39c-51e5-b827-557766551111",
        "trail_id": 1,
        "caption": "Beautiful sunrise from the summit",
        "original_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/651f9500/original.jpg",
        "thumbnail_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/651f9500/thumb.jpg",
        "medium_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/651f9500/medium.jpg",
        "latitude": 37.7459,
        "longitude": -119.5383,
        "location_source": "exif",
        "taken_at": "2024-01-16T06:45:00Z",
        "camera_make": "Apple",
        "camera_model": "iPhone 14 Pro",
        "uploaded_at": "2024-01-16T10:30:00Z"
      }
      
  • Error Response: Invalid file type

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Invalid file type. Only JPEG, PNG, and WEBP files are allowed"
      }
      
  • Error Response: File too large

    • Status Code: 413

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "File too large. Maximum size is 10MB"
      }
      

Update Photo

Updates photo caption or location. Only uploader can update.

  • Require Authentication: true

  • Request

    • Method: PUT

    • Route path: /api/photos/:id

    • Headers:

      • Content-Type: application/json
      • Authorization: Bearer <access_token>
    • Body:

      {
        "caption": "Sunrise from Eagle Peak summit - 6:45 AM start was worth it! You can see Half Dome in the distance.",
        "poi_id": 1
      }
      
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 234,
        "caption": "Sunrise from Eagle Peak summit - 6:45 AM start was worth it! You can see Half Dome in the distance.",
        "poi_id": 1,
        "poi": {
          "id": 1,
          "name": "Summit Viewpoint"
        },
        "updated_at": "2024-01-16T14:20:00Z"
      }
      

Delete Photo

Deletes a photo and all its versions from S3. Only uploader or admin can delete.

  • Require Authentication: true

  • Request

    • Method: DELETE
    • Route path: /api/photos/:id
    • Headers:
      • Authorization: Bearer <access_token>
  • Successful Response

    • Status Code: 204
    • Headers:
      • Content-Type: application/json
    • Body: None

Get User's Photos

Returns all photos uploaded by a specific user.

  • Require Authentication: false

  • Request

    • Method: GET
    • Route path: /api/users/:id/photos
    • Query Parameters:
      • page (optional): Page number (default: 1)
      • limit (optional): Items per page (default: 20)
    • Headers: None required
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "photos": [
          {
            "id": 234,
            "caption": "Sunrise from Eagle Peak summit",
            "thumbnail_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/651f9500/thumb.jpg",
            "medium_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/651f9500/medium.jpg",
            "likes_count": 15,
            "trail": {
              "id": 1,
              "name": "Eagle Peak Trail"
            },
            "uploaded_at": "2024-01-16T10:30:00Z"
          }
        ],
        "user": {
          "id": 3,
          "username": "mountainlover",
          "total_photos": 45
        },
        "pagination": {
          "page": 1,
          "pages": 3,
          "per_page": 20,
          "total": 45
        }
      }
      

Upload Hazard Photo

Associates a photo with a hazard report.

  • Require Authentication: true

  • Request

    • Method: POST
    • Route path: /api/hazards/:id/photos
    • Headers:
      • Content-Type: multipart/form-data
      • Authorization: Bearer <access_token>
    • Body: Form data
      • photo: [Image file] (required)
      • caption: "Shows the extent of the tree fall" (optional)
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "photo_id": 235,
        "hazard_id": 12,
        "message": "Photo successfully attached to hazard report",
        "photo": {
          "id": 235,
          "caption": "Shows the extent of the tree fall",
          "thumbnail_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/752fa600/thumb.jpg",
          "medium_url": "https://trail-hub-bucket.s3.amazonaws.com/photos/752fa600/medium.jpg"
        }
      }
      

Status Codes

  • 200 OK - Successful GET, PUT requests
  • 201 Created - Successful POST requests
  • 204 No Content - Successful DELETE requests
  • 400 Bad Request - Validation errors, malformed requests
  • 401 Unauthorized - Authentication required
  • 403 Forbidden - Access denied
  • 404 Not Found - Resource not found
  • 409 Conflict - Duplicate resource (e.g., user already reviewed trail)
  • 413 Payload Too Large - File size exceeds limit
  • 500 Internal Server Error - Server errors

API Structure Overview

Authentication Routes (4 endpoints)

  • Signup, Login, Logout, and Session management - Complete user authentication flow
  • JWT token-based authentication - Secure, stateless authentication using Bearer tokens
  • User hiking levels - Track experience from beginner to expert

Trail Routes (5 endpoints)

  • Full CRUD operations - Create, read, update, and delete trails
  • Spatial data support with GeoJSON - Store and retrieve GPS trail routes
  • Advanced filtering - By difficulty, length, region, and geographic bounds
  • Permission-based editing - Only trail creators or admins can modify/delete

Review Routes (5 endpoints)

  • One review per user per trail - Enforced uniqueness to prevent spam
  • Comprehensive trail conditions - Weather, trail state, and crowd levels
  • Community features - Helpful count for quality reviews
  • GPS verification flag - Ready for future location-based verification

Points of Interest Routes (5 endpoints)

  • 7 different POI types - Viewpoints, waterfalls, campsites, water sources, parking, warnings, junctions
  • Flexible amenities using JSON - Adaptable to different POI types without schema changes
  • Distance tracking - Know how far each POI is from the trailhead
  • Seasonal information - Important details about accessibility throughout the year

Trip Plan Routes (7 endpoints)

  • Full CRUD plus join/leave - Complete trip management functionality
  • Smart participant management - Track RSVPs with status (pending, confirmed, declined)
  • Flexible meeting points - Can differ from trailhead for carpooling
  • Detailed trip planning - Requirements, pace, and notes for clear communication

Hazard Routes (5 endpoints)

  • Real-time safety reporting - 7 hazard types with 4 severity levels
  • Temporal hazards - Auto-expiration for temporary conditions
  • Resolution workflow - Track who resolved hazards and when
  • Geographic filtering - Find hazards within map bounds

Photo Routes (6 endpoints)

  • Multi-size image processing - Automatic generation of thumbnail, medium, and original versions
  • EXIF data extraction - Automatic GPS and camera information retrieval
  • S3 integration - Scalable cloud storage for all photo assets
  • Flexible associations - Link photos to trails, POIs, or hazards

Key Design Decisions

RESTful Conventions

  • Nested routes where logical - /trails/:id/reviews clearly shows the relationship
  • Standard HTTP methods - GET for reading, POST for creating, PUT for updating, DELETE for removing
  • Predictable URL patterns - Consistent structure across all resources

Consistent Error Handling

  • Detailed validation errors - Field-specific messages help developers fix issues quickly
  • Standard HTTP status codes - 200 for success, 400 for client errors, 500 for server errors
  • Structured error responses - Always return JSON with message and errors fields

Flexible Queries

  • Pagination on all list endpoints - Handle large datasets efficiently with page/limit parameters
  • Multiple sort options - newest, oldest, highest rated, most helpful
  • Rich filtering capabilities - Combine multiple filters for precise results

Security & Permissions

  • JWT authentication - Stateless, scalable authentication using Bearer tokens
  • Role-based access - Different permissions for regular users vs admins
  • Resource ownership - Users can only modify their own content (reviews, photos, etc.)
  • Public vs authenticated endpoints - Browse without login, contribute with account

Spatial Data Standards

  • GeoJSON format - Industry standard for geographic data exchange
  • Consistent coordinate system - WGS84 (SRID 4326) for all GPS data
  • Multiple geometry types - LineString for trails, Point for POIs and hazards
  • Efficient spatial queries - Bounding box filters for map-based searches

Performance Optimizations

  • Selective field returns - List views return less data than detail views
  • Efficient pagination - Limit default to 20 items to balance data and performance
  • Indexed queries - Common filters (difficulty, region) use database indexes
  • Caching-friendly design - GET requests are idempotent and cacheable

Developer Experience

  • Comprehensive documentation - Every endpoint includes request/response examples
  • Consistent naming - snake_case for JSON, predictable field names
  • Helpful error messages - Guide developers toward correct API usage
  • Version-ready structure - Easy to add /api/v2 when needed