Composite REST Resource
What Is a Composite Resource?
Section titled “What Is a Composite 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.
Example Scenario
Section titled “Example Scenario”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:
- Local database: Stores player records (name, position, team, jersey number).
- 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.
Data Aggregation: Typical Workflow
Section titled “Data Aggregation: Typical Workflow”-
Composite Resource Endpoint:
GET /api/players -
Client Request:
- The client sends a request to the endpoint, optionally with query parameters to filter players (e.g., by position or team).
-
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.
-
Aggregation:
- The server merges both datasets into a single response object, using descriptive keys to distinguish each data source.
-
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.
Implementing a Composite Resource
Section titled “Implementing a Composite Resource” /*** 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 [];}Example JSON Output
Section titled “Example JSON Output”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:
{ "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" } ]}