Build an AEM Component that fetches data from an external API and displays it dynamically on the…
While browsing through the linkedin, this post got my attention. The interviewer asked this broad question where it involves architectural…
Build an AEM Component that fetches data from an external API and displays it dynamically on the page.
While browsing through the LinkedIn, this post got my attention. The interviewer asked this broad question where it involves architectural understanding of the AEM, development process of the components, cache and the best practices adopted by the developer in completing the task.
Lets discuss the different approaches to achieve this 1.Frontend/Client-side API call 2.Backend/Server-side API call using Sling Model OR Servlet 3.Combining Sling Model and Servlet 4. Cache response in OSGi service (using cacheBuilder)
Frontend/Client-side API call
This is a simple approach where API URL is authored/configured in component dialog then using it in javascript to make the API call and display the data.
<div class="component" data-apiurl="${properties.apiUrl}">
<ul id="list"></ul>
</div>
<script>
document.addEventListener("DOMContentLoaded",()=>{
let component = document.querySelector(".component");
let apiUrl = component.dataset.apiUrl;
if(apiUrl){
fetch(apiUrl)
.then((res)=>res.json())
.then((data)=> {
let list = document.getElementById("list");
data.forEach((listItem)=>{
let li = document.createElement('li').textContent=listItem;
list.appendChild(li);
})
})
}
})
</script>
With this approach you can reduce load on server having low complexity Cached Pages will have the faster response time. This is not SEO friendly as it is not rendered by the server. One can opt for this approach when a situation meeting these conditions occurs low server load, SEO is not priority, low complexity, and cache efficiency.
Backend/Server-side API call
In this server side approach, there are two ways to call the API Sling model and Servlet to fetch the data and passing it to the HTL template then rendering it on the page.
Sling Model Approach
@Model(adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ExternalAPIComponent{
@ValueMapValue(name = "apiUrl")
private String apiUrl;
private List<String> apiData;
@Override
@PostConstruct
protected void init(){
apiData = fetchApiData(apiUrl);
}
//API Data getter
public List<String> getApiData() {
return apiData;
}
private List<String> fetchApiData(String apiUrl){
List<String> data = new ArrayList<>();
try{
URL url = new URL(apiUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
if(connection.getResponseCode() == 200){
try(BufferedReader bfr = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String response = bfr.readLine();
List<String> res = new ArrayList<>();
//parse response into required res format then return res
return res;
}
}
else{
Logger.ERROR("Response is failed to fetch");
return List.of("No data fetched");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
}
Display the data fetched from the Sling Model in HTL
<div class="component">
<sly data-sly-use.externalApiComponent="com.example.core.models.ExternalApiModel">
<sly data-sly-test="${externalApiComponent.apiData.size() > 0}">
<ul>
<sly data-sly-list="${externalApiComponent.apiData}">
<li>${item}</li>
</sly>
</ul>
</sly>
<sly data-sly-test="${externalApiComponent.apiData.size() == 0}">
<p>No data available</p>
</sly>
</sly>
</div>
One Stop API Logic with better SEO as it is rendering through server. When SEO is critical with medium complexity one can opt for this approach.
Increases server-side processing and frequent and heavy API calls will concern the Scalability.
Servlet Approach
Path Based Servlet can be used when a reusable, generic endpoints need to be created. It is not bounded to any resource or component. Resource Based Servlet which is tied to a specific resource type. In a simple term fetching all the data from API related to a component or a resource type.
Path Based Servlet service class servlet path : /bin/externalapi
@Component(service = Servlet.class,
property = {
Constants.SERVICE_DESCRIPTION + "=External API Fetch Servlet",
"sling.servlet.paths=/bin/externalapi",
"sling.servlet.methods=GET"
})
public class ExternalApiServlet extends SlingAllMethodsServlet {
@Override
protected void doGet(SlingHttpServletRequest req, SlingHttpServletResponse res) {
String apiUrl = CONSTANTS.URL
if (apiUrl == null || apiUrl.isEmpty()) {
response.setStatus(SlingHttpServletResponse.SC_BAD_REQUEST);
return;
}
try {
URL url = new URL(apiUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String jsonRes = reader.readLine();
response.setContentType("application/json");
response.getWriter().write(jsonRes);
}
} else {
LOGGER.error("Failed to fetch data.")
response.setStatus(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
} catch (Exception e) {
LOGGER.error("Error while fetching API data: ", e);
response.setStatus(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
Slightly code
<div class="component">
<sly data-sly-use.externalApiComponent="com.example.core.models.ExternalApiModel">
<div class="api=data"></div>
</sly>
</div>
Javascript code to call the servlet to render the data dynamically
document.addEventListener("DOMContentLoaded",()=>{
let result = document.querySelector(".api-data");
if(apiUrl){
fetch("/bin/externalapi")
.then((res)=>res.json())
.then((data)=> {
let list = document.createElement("ul");
data.forEach((listItem)=>{
list.appendChild(document.createElement('li').textContent=listItem);
})
result.appendChild(list)
})
}
})
This servlet can be used across multiple components or pages just by calling the path associated with it. Data can be fetched dynamically which supports real time use cases. As data is fetched Dynamically it can’t be crawled by Search engines. Hence making it not SEO friendly approach.
Resource Based Servlet service class
@Component(service = Servlet.class,
property = {
Constants.SERVICE_DESCRIPTION + "=External API Fetch Servlet",
"sling.servlet.resource= apps/content/components/customcomponent",
"sling.servlet.methods=GET"
})
//fetch data from API URL and return the response
This approach can be used when data is tightly coupled with the component/resource. As said previously this approach is not a SEO friendly.
Combining Sling Model and Servlet
Using Sling Servlet for dynamic content and combining it with Sling Model to leverage SEO friendly content which is efficient in terms of caching and performances.
- Create the servlet to fetch the external api data and return it in JSON format.
- Use Sling Model to fetch the response from the servlet and expose it to the HTL template.
This approach requires managing the servlet, model and htl template which makes it little complex but returns us the optimized performance with SEO-friendly, authors flexible content and reusable logic.
Caching with OSGi Services.
Combining cacheBuilder along with the osgi service gives a high performance and cached response.
private final Cache<String, String> cacheData = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
public String fetchData(String apiUrl) {
try {
return cacheData.get(apiUrl, () -> {
URL url = new URL(apiUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
if (con.getResponseCode() == 200) {
return new Scanner(con.getInputStream()).useDelimiter("\\A").next();
}
return null;
});
} catch (Exception e) {
return null;
}
This will help in avoiding unnecessary api calls overall improving the performance. It also gives the control over the cache making it usable for different use cases.
Comparing with other approaches this will have medium load on the server with high cache efficient and SEO friendly content with a complex development in nature.
메타데이터
- post_id
- b859ea6567c5
- slug
- build-an-aem-component-that-fetches-data-from-an-external-api-and-displays-it-dynamically-on-the-b859ea6567c5
- url
- https://medium.com/@uchihamadara_/build-an-aem-component-that-fetches-data-from-an-external-api-and-displays-it-dynamically-on-the-b859ea6567c5
- canonical_url
- https://medium.com/@uchihamadara_/build-an-aem-component-that-fetches-data-from-an-external-api-and-displays-it-dynamically-on-the-b859ea6567c5
- author_url
- https://medium.com/@uchihamadara_
- status
- ok
- fetched_at
- 2026-08-05 02:20:04