Best practices for making API calls in AngularJS

Loading

Making efficient and optimized API calls in AngularJS is essential for performance, maintainability, and scalability. Below are the best practices to follow when handling API requests.


1. Use $http or $resource Based on the Use Case

When to Use $http

  • If you need complete control over request and response.
  • When working with non-RESTful APIs (e.g., GraphQL, WebSockets).
  • When you need custom request transformations.
$http.get('/api/users')
.then(function(response) {
$scope.users = response.data;
})
.catch(function(error) {
console.error('API Error:', error);
});

When to Use $resource

  • If you are working with RESTful APIs.
  • When you need built-in CRUD functionality.
var UserService = $resource('/api/users/:id', { id: '@id' }, {
update: { method: 'PUT' }
});

$scope.users = UserService.query(); // Fetch all users
$scope.user = UserService.get({ id: 1 }); // Fetch one user

2. Use Services or Factories to Encapsulate API Calls

Never make API calls directly inside controllers; instead, create a service.

myApp.factory('UserService', function($http) {
return {
getUsers: function() {
return $http.get('/api/users');
},
createUser: function(user) {
return $http.post('/api/users', user);
}
};
});

Usage in Controller

myApp.controller('UserController', function($scope, UserService) {
UserService.getUsers()
.then(function(response) {
$scope.users = response.data;
});
});

Benefits:
✔ Improves maintainability.
✔ Follows Separation of Concerns (SoC).
✔ Makes testing easier.


3. Use $httpInterceptor for Global API Error Handling

Instead of handling errors in every API call, use an $httpInterceptor.

Interceptor Example

myApp.factory('ApiInterceptor', function($q, $rootScope) {
return {
responseError: function(response) {
if (response.status === 401) {
$rootScope.$broadcast('unauthorized'); // Handle authentication errors
} else if (response.status === 500) {
console.error('Server Error:', response);
}
return $q.reject(response);
}
};
});

myApp.config(function($httpProvider) {
$httpProvider.interceptors.push('ApiInterceptor');
});

Advantages:
✔ Centralized error handling.
✔ Avoids redundant error-handling code in controllers.


4. Use $cacheFactory to Cache API Responses

For frequent API requests, caching can reduce server load and improve performance.

Enable Caching in $http

myApp.factory('UserService', function($http, $cacheFactory) {
var userCache = $cacheFactory('userCache');

return {
getUsers: function() {
return $http.get('/api/users', { cache: userCache });
}
};
});

Advantages:
✔ Reduces redundant API calls.
✔ Improves performance.


5. Use track by in ng-repeat for Large Lists

If you display large datasets, use track by to improve rendering performance.

<li ng-repeat="user in users track by user.id">
{{ user.name }}
</li>

Advantages:
✔ Prevents unnecessary DOM re-renders.
✔ Improves UI responsiveness.


6. Implement Retry Logic for Failed API Calls

Sometimes, API requests fail due to temporary network issues. Implementing a retry mechanism can improve reliability.

function retryRequest(apiFunction, retries) {
return apiFunction().catch(function(error) {
if (retries > 0) {
console.log(`Retrying... Attempts left: ${retries}`);
return retryRequest(apiFunction, retries - 1);
}
return Promise.reject(error);
});
}

// Example Usage
retryRequest(() => $http.get('/api/users'), 3)
.then(response => console.log('Data:', response.data))
.catch(error => console.error('API Call Failed:', error));

Advantages:
✔ Handles transient failures gracefully.
✔ Improves reliability.


7. Optimize API Calls with Debouncing

When making API requests on user input (e.g., search boxes), debounce requests to avoid excessive API calls.

Debounce Example Using $timeout

myApp.controller('SearchController', function($scope, $timeout) {
var timeoutPromise;

$scope.search = function(query) {
if (timeoutPromise) {
$timeout.cancel(timeoutPromise);
}

timeoutPromise = $timeout(function() {
$http.get('/api/search?q=' + query).then(function(response) {
$scope.results = response.data;
});
}, 300); // Delay of 300ms
};
});

Advantages:
✔ Reduces API load.
✔ Improves user experience by preventing lag.


8. Avoid Unnecessary Watchers ($watch)

Too many $watch expressions can slow down AngularJS performance.

Use $watchCollection Instead of $watch for Objects

$scope.$watchCollection('users', function(newUsers, oldUsers) {
console.log('Users changed:', newUsers);
});

Use One-Time Binding (::) for Static Data

<p>{{ ::user.name }}</p>

Use Event-Based Updates Instead of $watch

$rootScope.$broadcast('updateData');

9. Persist API Data in localStorage

For stateful applications, store API responses in localStorage to reduce API requests.

myApp.factory('StorageService', function() {
return {
set: function(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
get: function(key) {
return JSON.parse(localStorage.getItem(key) || 'null');
}
};
});

Example Usage

UserService.getUsers().then(function(response) {
StorageService.set('users', response.data);
});

Advantages:
✔ Reduces unnecessary API calls.
✔ Improves offline support.


10. Use Pagination for Large Data Sets

Fetching all data at once can slow down the app. Use pagination instead.

$http.get('/api/users?page=1&limit=10')
.then(function(response) {
$scope.users = response.data;
});

Advantages:
✔ Improves performance.
✔ Reduces API response time.

Leave a Reply

Your email address will not be published. Required fields are marked *