20 minlesson

Final Project: Address Service API

Final Project: Address Service API

Build a comprehensive Address Service that combines all the concepts from this course into a unified API.

Project Overview

You'll create an AddressService class that provides:

  • Address parsing for multiple countries
  • Postal code validation with GeoNames
  • Address normalization (USPS standards)
  • Geocoding with Nominatim
  • Address deduplication

Architecture

1┌─────────────────────────────────────────────────────────┐
2│ AddressService │
3├─────────────────────────────────────────────────────────┤
4│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
5│ │ Parser │ │ Validator │ │ Normalizer │ │
6│ └─────────────┘ └─────────────┘ └─────────────────┘ │
7│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
8│ │ Geocoder │ │ Deduplicator│ │ Formatter │ │
9│ └─────────────┘ └─────────────┘ └─────────────────┘ │
10└─────────────────────────────────────────────────────────┘

Core Components

1. Address Parser

javascript
1class AddressParser {
2 parse(rawAddress, country = 'US') {
3 // Extract components from raw address string
4 return {
5 street: '123 Main St',
6 unit: 'Apt 4B',
7 city: 'New York',
8 state: 'NY',
9 postalCode: '10001',
10 country: 'US'
11 };
12 }
13}

2. Postal Code Validator

javascript
1class PostalCodeValidator {
2 validate(postalCode, countryCode) {
3 // Format validation using country-specific regex patterns
4 if (!this.validateFormat(postalCode, countryCode)) {
5 return { valid: false, reason: 'Invalid format' };
6 }
7
8 return { valid: true, postalCode, countryCode };
9 }
10}

3. Address Normalizer

javascript
1class AddressNormalizer {
2 normalize(address) {
3 return {
4 ...address,
5 street: this.normalizeStreet(address.street),
6 state: this.normalizeState(address.state),
7 postalCode: this.normalizePostalCode(address.postalCode)
8 };
9 }
10}

4. Geocoder

javascript
1class Geocoder {
2 async geocode(address) {
3 // Convert address to coordinates
4 return { lat: 40.7484, lon: -73.9967 };
5 }
6
7 async reverseGeocode(lat, lon) {
8 // Convert coordinates to address
9 return { displayName: '123 Main St, NYC' };
10 }
11}

5. Address Deduplicator

javascript
1class AddressDeduplicator {
2 findDuplicates(addresses, threshold = 0.85) {
3 // Find duplicate pairs above similarity threshold
4 // Returns array of { address1, address2, similarity }
5 return [
6 { address1: addresses[0], address2: addresses[2], similarity: 0.92 }
7 ];
8 }
9}

Unified Service

javascript
1class AddressService {
2 constructor(options = {}) {
3 this.parser = new AddressParser();
4 this.validator = new PostalCodeValidator(options.geonamesUsername);
5 this.normalizer = new AddressNormalizer();
6 this.geocoder = new Geocoder();
7 this.deduplicator = new AddressDeduplicator();
8 }
9
10 async processAddress(rawAddress, country = 'US') {
11 // 1. Parse
12 const parsed = this.parser.parse(rawAddress, country);
13
14 // 2. Normalize
15 const normalized = this.normalizer.normalize(parsed);
16
17 // 3. Validate postal code
18 const validation = this.validator.validate(
19 normalized.postalCode,
20 country
21 );
22
23 // 4. Geocode
24 const coordinates = await this.geocoder.geocode(normalized);
25
26 return {
27 original: rawAddress,
28 parsed,
29 normalized,
30 validation,
31 coordinates,
32 confidence: this.calculateConfidence(validation, coordinates)
33 };
34 }
35
36 async processBatch(addresses, country = 'US') {
37 // Process multiple addresses
38 const results = [];
39 for (const address of addresses) {
40 results.push(await this.processAddress(address, country));
41 }
42
43 // Find duplicates
44 const duplicates = this.deduplicator.findDuplicates(
45 results.map(r => r.normalized)
46 );
47
48 return { results, duplicates };
49 }
50}

Error Handling

javascript
1class AddressServiceError extends Error {
2 constructor(message, code, details = {}) {
3 super(message);
4 this.code = code;
5 this.details = details;
6 }
7}
8
9// Error codes
10const ErrorCodes = {
11 PARSE_FAILED: 'PARSE_FAILED',
12 VALIDATION_FAILED: 'VALIDATION_FAILED',
13 GEOCODE_FAILED: 'GEOCODE_FAILED',
14 RATE_LIMITED: 'RATE_LIMITED',
15 API_ERROR: 'API_ERROR'
16};

Rate Limiting Strategy

javascript
1class RateLimiter {
2 constructor(requestsPerSecond = 1) {
3 this.minInterval = 1000 / requestsPerSecond;
4 this.lastRequest = 0;
5 }
6
7 async wait() {
8 const now = Date.now();
9 const elapsed = now - this.lastRequest;
10 const wait = Math.max(0, this.minInterval - elapsed);
11
12 if (wait > 0) {
13 await new Promise(r => setTimeout(r, wait));
14 }
15
16 this.lastRequest = Date.now();
17 }
18}

Caching Strategy

javascript
1class AddressCache {
2 constructor(ttlMs = 24 * 60 * 60 * 1000) { // 24 hours
3 this.cache = new Map();
4 this.ttl = ttlMs;
5 }
6
7 generateKey(address) {
8 const normalized = JSON.stringify(address).toLowerCase();
9 return this.hash(normalized);
10 }
11
12 get(address) {
13 const key = this.generateKey(address);
14 const entry = this.cache.get(key);
15
16 if (!entry) return null;
17 if (Date.now() > entry.expires) {
18 this.cache.delete(key);
19 return null;
20 }
21
22 return entry.value;
23 }
24
25 set(address, value) {
26 const key = this.generateKey(address);
27 this.cache.set(key, {
28 value,
29 expires: Date.now() + this.ttl
30 });
31 }
32}

Testing Strategy

  1. Unit Tests: Test each component in isolation
  2. Integration Tests: Test component interactions
  3. Mock External APIs: Use mocks for GeoNames/Nominatim
  4. Edge Cases: International addresses, malformed input

Project Requirements

Your final project must:

  1. Parse addresses for at least 3 countries (US, UK, DE)
  2. Validate postal codes against format rules
  3. Normalize street types and directionals
  4. Geocode addresses using Nominatim
  5. Detect duplicate addresses with fuzzy matching
  6. Handle rate limiting gracefully
  7. Cache results to minimize API calls
  8. Include comprehensive error handling
  9. Pass all provided unit tests

Success Criteria

RequirementPoints
Address parsing (3 countries)20
Postal code validation15
Address normalization15
Geocoding integration15
Deduplication15
Error handling10
Caching10
Total100