Skip to main content
This document outlines planned features and enhancements for the Address API.

Effective date ranges for ISO 3166 content

Overview

Add temporal validity to ISO 3166 subdivision data to handle historical changes in country subdivisions.

Problem

Currently, the ISO 3166 content has a simple unique constraint:
CONSTRAINT "iso_3166_country_subdivision_unique"
  UNIQUE ("country_code", "subdivision_code")
This doesn’t allow for:
  • Historical subdivision changes (e.g., state name changes)
  • Subdivision mergers or splits
  • Temporal queries (e.g., “What was the state code in 2020?”)

Proposed solution

Add effective_start_date and effective_end_date columns to track when a subdivision record is valid.

Schema changes

ALTER TABLE iso_3166
ADD COLUMN effective_start_date DATE NOT NULL DEFAULT '1900-01-01',
ADD COLUMN effective_end_date DATE NULL;

-- Drop old unique constraint
ALTER TABLE iso_3166
DROP CONSTRAINT iso_3166_country_subdivision_unique;

-- Add new unique constraint with date range
ALTER TABLE iso_3166
ADD CONSTRAINT iso_3166_country_subdivision_date_unique
  EXCLUDE USING gist (
    country_code WITH =,
    subdivision_code WITH =,
    daterange(effective_start_date, effective_end_date, '[]') WITH &&
  );

Example data

-- California (current)
INSERT INTO iso_3166 (country_code, subdivision_code, subdivision_name, effective_start_date, effective_end_date)
VALUES ('US', 'CA', 'California', '1850-09-09', NULL);

-- Historical example: Czechoslovakia split into Czech Republic and Slovakia
INSERT INTO iso_3166 (country_code, subdivision_code, subdivision_name, effective_start_date, effective_end_date)
VALUES ('CS', 'CZ', 'Czech Socialist Republic', '1969-01-01', '1992-12-31');

INSERT INTO iso_3166 (country_code, subdivision_code, subdivision_name, effective_start_date, effective_end_date)
VALUES ('CZ', 'CZ', 'Czech Republic', '1993-01-01', NULL);

API changes

Ingestion endpoint

Update the ingestion endpoint to accept date ranges:
{
  "items": [
    {
      "country_code": "US",
      "subdivision_code": "CA",
      "subdivision_name": "California",
      "subdivision_local_variant": null,
      "effective_start_date": "1850-09-09",
      "effective_end_date": null
    }
  ]
}
Validation rules:
  • effective_start_date is required
  • effective_end_date is optional (NULL means “still valid”)
  • effective_end_date must be after effective_start_date
  • Date ranges cannot overlap for the same (country_code, subdivision_code)

Geocoding endpoint

The geocoding endpoint behavior: Default behavior (no date specified):
  • Use the current date
  • Return the subdivision valid today
With date parameter (future enhancement):
{
  "address": "Prague, Czechoslovakia",
  "as_of_date": "1990-01-01"
}
Returns the subdivision valid on that date.

Migration strategy

  1. Add columns with default values (non-breaking)
  2. Populate existing data with default dates
  3. Update application code to use date ranges
  4. Update ingestion endpoint to accept dates
  5. Drop old constraint and add new one
  6. Deploy with backward compatibility

Benefits

  • Historical accuracy: Track subdivision changes over time
  • Temporal queries: Query data as of a specific date
  • Audit trail: Know when subdivisions changed
  • Compliance: Meet regulatory requirements for historical data

Implementation checklist

  • Design database schema changes
  • Write migration script
  • Update DTO for ingestion endpoint
  • Update validation logic
  • Update service layer to query by date
  • Add tests for date range logic
  • Update documentation
  • Deploy to staging
  • Test with historical data
  • Deploy to production

Estimated effort

  • Database changes: 2 days
  • Application changes: 3 days
  • Testing: 2 days
  • Documentation: 1 day
  • Total: ~2 weeks

Other planned features

1. Batch geocoding

Description: Allow geocoding multiple addresses in a single request. Benefits:
  • Reduced API calls
  • Better performance for bulk operations
  • Lower latency
API design:
POST /api/v1/geoencode/batch
{
  "addresses": [
    "94108, CA, US",
    "10001, NY, US",
    "60601, IL, US"
  ]
}
Response:
{
  "data": [
    { "original_address": "94108, CA, US", ... },
    { "original_address": "10001, NY, US", ... },
    { "original_address": "60601, IL, US", ... }
  ]
}

2. Address validation

Description: Validate addresses without geocoding. Benefits:
  • Faster than full geocoding
  • Lower cost (no Google API call)
  • Useful for form validation
API design:
POST /api/v1/validate
{
  "address_line_1": "123 Main St",
  "city": "San Francisco",
  "state_code": "CA",
  "postal_code": "94108",
  "country_code": "US"
}
Response:
{
  "valid": true,
  "issues": []
}

3. Reverse geocoding

Description: Convert coordinates to addresses. API design:
POST /api/v1/reverse-geocode
{
  "latitude": 37.7909427,
  "longitude": -122.4084994
}
Response:
{
  "data": {
    "address_line_1": "123 Main St",
    "city": "San Francisco",
    "state": "California",
    "state_code": "CA",
    "country": "United States",
    "country_code": "US",
    "postal_code": "94108"
  }
}

4. Cache management

Description: Admin endpoints to manage the geocoding cache. Endpoints:
  • DELETE /api/v1/internal/cache/{address} - Clear specific cache entry
  • DELETE /api/v1/internal/cache - Clear all cache
  • GET /api/v1/internal/cache/stats - Cache statistics

5. Rate limiting

Description: Implement rate limiting per API key. Benefits:
  • Prevent abuse
  • Fair usage
  • Cost control
Implementation:
  • Use Redis for distributed rate limiting
  • Configure limits per API key
  • Return 429 Too Many Requests when exceeded

6. Webhook notifications

Description: Notify external systems when content is updated. Use case: Automatically sync ISO 3166 changes to other services. Implementation:
  • Store webhook URLs in database
  • Trigger webhooks on content ingestion
  • Retry failed webhooks

Contributing to future features

If you’d like to implement any of these features:
  1. Create an issue on GitHub describing your approach
  2. Get approval from the team
  3. Follow the contribution guide for implementation
  4. Write comprehensive tests
  5. Update documentation
  6. Submit a pull request

Next steps