Обработка ошибок при обработке данных с помощью значимых журналов

В последнее время я много работал над API и столкнулся с несколькими ошибками, когда искаженные данные, полученные из внешнего источника, вызывали ошибки в Node при попытке преобразовать данные на стороне сервера.

Задача: я хочу получить список местоположений на основе местоположения пользователя. Однако я хочу вернуть только подмножество на основе некоторых фильтров. Скажем, я получаю некоторые данные из источника данных, который я не контролирую (или не имею возможности применять значимые проверки). Например.

const retrievedLocations = [
  {
    name: "Teahupo'o",
    coordinates: {
      latitude: 17.8471,
      longitude: 149.2664
    }
  },
  {
    name: "Pipeline",
    // coordinates are missing
  }
]

Теперь предположим, что я хочу отфильтровать эти данные только для местоположений, которые (ради этого примера) находятся в пределах 10 градусов от текущей широты/долготы моего пользователя. У меня может быть что-то вроде этого:

// Node Controller
LocationsController.fetchLocations = async (req, res) => {
  const { userLat, userLong } = req.body;
  // first get the responses from our external service
  let locations;
  try {
    locations = await externalLocationsService.fetch();
  } catch (e) { ... }
  // now filter that data, assume a function that does my check
  const filteredLocations = locations.filter(loc => {
    const userIsInLongRange = locationProximityCheck(
      userLong, 
      loc.coordinates.longitude
    );
    const userIsInLatRange = locationProximityCheck(
      userLat, 
      loc.coordinates.latitude
    );
    return userIsInLongRange && userIsInLatRange;
  })
  res.status(200).json(filteredLocations)
}

Что происходит, когда этот код запускается? Местоположение, в котором отсутствует объект координат, выдает ошибку типа can't read property longitude of undefined, и мой сервер падает. Как я могу справиться с этим?

Решение. Опять же, предполагая, что я не могу выполнить проверку данных в другом месте, я могу легко добавить try/catch внутри filter, чтобы просто пропустить любые искаженные данные.

const filteredLocations = locations.filter(loc => {
  try {
    const userIsInLongRange = locationProximityCheck(
      userLong, 
      loc.coordinates.longitude
    );
    const userIsInLatRange = locationProximityCheck(
      userLat, 
      loc.coordinates.latitude
    );
    return userIsInLongRange && userIsInLatRange;
  } catch (error) {
    // log the error so that bad data gets noticed
    errorLogger(`Error Filtering Locations at ${loc.name}`);
    // return false from the filter function so the malformed 
    // options is skipped
    return false;
  }
})

Теперь любые проблемные данные удобно игнорировать, и я делаю уведомление, чтобы знать, в каком месте может потребоваться исправление данных.

Сделать то же самое в функции .map немного сложнее, потому что даже если вы ошибетесь в дескрипторе внутри функции .map, вы все равно получите элемент в результирующем массиве. Однако отфильтровать их довольно просто, если вы возвращаете значение, которое не соответствует ни одному из ваших ожидаемых результатов (например, null при возврате массива объектов).

const mappedData = someData.map(d => {
  try {
    ... some code that might throw errors before you return 
    return whateverIsToBeReturned;
  } catch (e) {
    // log the error so that bad data gets noticed
    errorLogger(`Error mapping data at ${d.property}`);
    // return null 
    return null;
  }
}).filter(item => !!item); // <= filter out anything falsy

Примечание. Как всегда, если у кого-то есть лучший подход, оставьте его в комментариях.