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.