JAVASCRIPT

Normalizing Heterogeneous API Responses with Data Transformation in JavaScript

Transform and map varied API data structures into a consistent, application-specific format using JavaScript for improved data consistency.

/**
 * Simulates an API response from a 'users' service with a specific structure.
 */
const userApiResponse = {
  status: 'success',
  data: {
    id: 'usr_101',
    username: 'jsmith',
    emailAddress: '[email protected]',
    creationDate: '2023-01-15T10:30:00Z',
    isActiveUser: true,
    addressInfo: {
      street: '123 Main St',
      city: 'Anytown',
      zip: '12345'
    }
  }
};

/**
 * Simulates an API response from a 'profiles' service with a different structure.
 */
const profileApiResponse = {
  code: 200,
  result: {
    profileId: 'prof_202',
    userIdRef: 'usr_101',
    displayName: 'Johnny S.',
    contactEmail: '[email protected]',
    registrationTimestamp: 1673775600000, // Unix timestamp in ms
    status: 'active'
  }
};

/**
 * Defines the desired standardized data model for a 'User' in our application.
 */
// Desired output structure:
// {
//   id: string,
//   userId: string, // if different from id
//   name: string,
//   email: string,
//   registeredAt: Date,
//   status: 'active' | 'inactive',
//   location: { street: string, city: string, postalCode: string }
// }

/**
 * Function to transform the user API response into the standardized model.
 * @param {object} apiResponse - The raw response from the user API.
 * @returns {object|null} The normalized user object or null if data is invalid.
 */
function normalizeUser(apiResponse) {
  const userData = apiResponse?.data;
  if (!userData) return null;

  return {
    id: userData.id,
    userId: userData.id, // Assuming id and userId are the same for this source
    name: userData.username, // Mapping 'username' to 'name'
    email: userData.emailAddress,
    registeredAt: new Date(userData.creationDate),
    status: userData.isActiveUser ? 'active' : 'inactive',
    location: {
      street: userData.addressInfo?.street || '',
      city: userData.addressInfo?.city || '',
      postalCode: userData.addressInfo?.zip || '' // Renaming 'zip' to 'postalCode'
    }
  };
}

/**
 * Function to transform the profile API response into the standardized model.
 * Note: This might represent a partial user profile that needs to be merged.
 * @param {object} apiResponse - The raw response from the profile API.
 * @returns {object|null} The normalized profile object or null if data is invalid.
 */
function normalizeProfile(apiResponse) {
  const profileData = apiResponse?.result;
  if (!profileData) return null;

  return {
    id: profileData.profileId, // Using profileId as the primary ID for this source
    userId: profileData.userIdRef,
    name: profileData.displayName, // Mapping 'displayName' to 'name'
    email: profileData.contactEmail,
    registeredAt: new Date(profileData.registrationTimestamp),
    status: profileData.status === 'active' ? 'active' : 'inactive'
  };
}

// --- Usage Example ---
const normalizedUser = normalizeUser(userApiResponse);
console.log('Normalized User from User API:');
console.log(JSON.stringify(normalizedUser, null, 2));

const normalizedProfile = normalizeProfile(profileApiResponse);
console.log('
Normalized Profile from Profile API:');
console.log(JSON.stringify(normalizedProfile, null, 2));

// If we need to merge them (common scenario):
const combinedUser = {
  ...normalizedUser, // Start with user data
  ...(normalizedProfile && { // Overlay with profile data, ensuring profileData exists
    id: normalizedUser.id, // Preserve primary ID from user API, or decide on a merging strategy
    name: normalizedProfile.name, // Profile name might be preferred
    email: normalizedProfile.email, // Profile email might be more current
    // ... other fields from profile that should override or complement
  })
};

console.log('
Combined (Normalized) User Data:');
console.log(JSON.stringify(combinedUser, null, 2));
How it works: This JavaScript snippet demonstrates how to normalize data from different API responses into a consistent, application-specific data model. It defines two transformation functions, `normalizeUser` and `normalizeProfile`, each responsible for taking a raw API response with a unique structure and mapping its fields to a predefined standard format. This process involves selecting relevant fields, renaming them (e.g., `emailAddress` to `email`), converting data types (e.g., timestamp to `Date` object), and restructuring nested objects. Data normalization is crucial when integrating with multiple APIs that return heterogeneous data, ensuring uniformity and simplifying data consumption within your application.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs