Skip to content

Composite REST Resource

  • In REST-based web services, a composite resource is a combination of multiple other resources aggregated into a single cohesive response.
  • It allows you to present a unified interface to the client while fetching and combining data from various data sources and remote APIs behind the scenes.

Imagine a sports application that manages a collection of players stored in a local database. The application also wants to show league information sourced from an external API like TheSportsDB. Rather than making the client call two separate endpoints, a composite resource combines both datasets into a single response:

  1. Local database: Stores player records (name, position, team, jersey number).
  2. External API (TheSportsDB): Provides league and team data (league names, sports, countries).

A single request to /api/players returns the player collection alongside related league information, all in one JSON response.


  1. Composite Resource Endpoint: GET /api/players

  2. Client Request:

    • The client sends a request to the endpoint, optionally with query parameters to filter players (e.g., by position or team).
  3. Backend Processing:

    • The server fetches the list of players from the local database.
    • It then calls TheSportsDB API to retrieve league information for the relevant sport/country.
  4. Aggregation:

    • The server merges both datasets into a single response object, using descriptive keys to distinguish each data source.
  5. Unified Response:

    • The aggregated data is returned to the client as a single JSON response:
    Example composite resource response:
    {
    "players": [
    {
    "player_id": "1",
    "name": "John Doe",
    "position": "Forward",
    "team": "Manchester United",
    "jersey_number": 10
    }
    ],
    "leagues": [
    {
    "idLeague": "4328",
    "strLeague": "English Premier League",
    "strSport": "Soccer",
    "strLeagueAlternate": "Premier League"
    }
    ]
    }

The client gets everything it needs from a single endpoint, while the server handles the complexity of querying multiple data sources behind the scenes.


The following section shows how to implement this pattern in your Slim-based team project.

Example of a composite resource implementation using Slim Framework:
/**
* Handles the HTTP GET request to retrieve the list of players and aggregate them with external data.
*
* This function fetches a list of players from the database based on the filters passed in the request's query parameters,
* and then fetches additional data from an external API (SportsDB) to aggregate both datasets into one.
*
* The aggregated dataset is returned as a JSON response.
*
* @param Request $request The HTTP request object, containing query parameters for filtering players.
* @param Response $response The HTTP response object that will be populated with the aggregated data.
* @param array $uri_args The URI arguments passed with the request, though not used in this method.
*
* @return Response The HTTP response object containing the aggregated data in JSON format.
*/
public function handleGetPlayers(Request $request, Response $response, array $uri_args): Response
{
$filters = $request->getQueryParams();
// Step 1) Pull the list of players from the database
$players = $this->players_model->getAllPlayers($filters);
// Step 2) Fetch a list of all teams in a league belonging to the specified country and/or sport.
$api_data = $this->getListOfLeagues();
// Step 3) Aggregate both datasets into a single dataset to be included in the response.
// Step 3.a) TODO: validate the $api_data before merging it with the players dataset.
$players["REPLACE_ME"] = $api_data;
// NOTE: the REPLACE_ME key needs to be changed.
// You should use a descriptive, meaningful key (e.g., leagues,
// exercises, shows, animals, planets, etc.)
return $this->renderJson($response, $players);
}
/**
* Fetches a list of leagues from the SportsDB API.
*
* This function uses the Guzzle HTTP client to make a GET request to the SportsDB API to retrieve a list of soccer leagues
* in a specified country. The API data is returned in a format that can be aggregated with other datasets.
*
* @return mixed Returns the data fetched from the SportsDB API, typically an array or object containing the list of leagues.
*/
public function getListOfLeagues() : mixed
{
/*
Fetch a list of leagues from the SportsDB API
Endpoint:
https://www.thesportsdb.com/api/v1/json/3/search_all_leagues.php?c=England&s=Soccer
Use Guzzle HTTP client to fetch data from the remote
API that you've selected.
* NOTE: Refer to Lab #5, Step #3).
*/
return [];
}

The following example shows what the composite resource response would look like after aggregating player data from the database with league data from the SportsDB API:

Example response from GET /players endpoint
{
"players": { // 👈 Your collection resource's data (e.g., players)
"meta": {
"current_page": 1,
"page_size": 10,
"total_pages": 5,
"total_records": 47
},
"data": [
{
"player_id": "1",
"name": "John Doe",
"position": "Forward",
"team": "Manchester United",
"jersey_number": 10
},
{
"player_id": "2",
"name": "Jane Smith",
"position": "Midfielder",
"team": "Chelsea FC",
"jersey_number": 8
},
{
"player_id": "3",
"name": "Mike Johnson",
"position": "Defender",
"team": "Arsenal FC",
"jersey_number": 5
}
]
},
"leagues": [ // 👈 Resource data from the external API (e.g., leagues)
{
"idLeague": "4328",
"strLeague": "English Premier League",
"strSport": "Soccer",
"strLeagueAlternate": "Premier League"
},
{
"idLeague": "4329",
"strLeague": "English League Championship",
"strSport": "Soccer",
"strLeagueAlternate": "The Championship"
},
{
"idLeague": "4330",
"strLeague": "Scottish Premier League",
"strSport": "Soccer",
"strLeagueAlternate": "SPL"
}
]
}