// src/services/googleGeocodingService.server.ts import type { Logger } from 'pino'; import { logger as defaultLogger } from './logger.server'; export class GoogleGeocodingService { private readonly baseUrl = 'https://maps.googleapis.com/maps/api/geocode/json'; /** * Geocodes an address using the Google Maps Geocoding API. * @param address The address string to geocode. * @param logger A logger instance. * @returns A promise that resolves to the coordinates or null if not found. */ async geocode( address: string, logger: Logger = defaultLogger, ): Promise<{ lat: number; lng: number } | null> { const apiKey = process.env.GOOGLE_MAPS_API_KEY; if (!apiKey) { logger.error('[GoogleGeocodingService] API key is missing.'); throw new Error('GOOGLE_MAPS_API_KEY is not set.'); } const url = `${this.baseUrl}?address=${encodeURIComponent(address)}&key=${apiKey}`; try { const response = await fetch(url); if (!response.ok) { throw new Error(`Google Maps API returned status ${response.status}`); } const data = await response.json(); if (data.status === 'OK' && data.results.length > 0) { logger.info( { address, result: data.results[0].geometry.location }, `[GoogleGeocodingService] Successfully geocoded address`, ); return data.results[0].geometry.location; } logger.warn( { address, status: data.status }, '[GoogleGeocodingService] Geocoding failed or returned no results.', ); return null; } catch (error) { logger.error( { err: error, address }, '[GoogleGeocodingService] An error occurred while calling the Google Maps API.', ); throw error; // Re-throw to allow the calling service to handle the failure (e.g., by falling back). } } } export const googleGeocodingService = new GoogleGeocodingService();