0011: Targeted API Resource Access Pattern
STATUS
Approved
CONTEXT
AdAction wants to offer a product that serves single- and multi-reward targeted offers to app users. This API should allow apps to filter offers using player-level data.
Considered Options
- REST
- GraphQL - DECISION
Option 1: REST
Description
The REST API architectural style "enables client applications to exchange data with a server using HTTP verbs, which is the standard communication protocol of the internet" (What's the Difference Between GraphQL and REST?).
Example Request
GET /v1/offers/targeted?player_id=testplayer
Host: https://offer-api.adgem.com
Accept: application/json
Authorization: Bearer 0|123456789abcdefghi // authentication approach to be determined
Example Response
// response schema to be determined
{
"status": "success",
"data": {
"offers": [
{
"id": "61918292528467968",
"campaign_id": 20391,
"name": "Prison Escape",
"store_id": "1525116042",
"tracking_type": "CPE",
"total_payout_usd": 0.3,
"total_amount": 0.3,
"start_datetime": "2023-03-22 17:04:30",
"is_multi_reward": false,
"completion_difficulty": 6,
"is_featured_campaign": false,
"campaign_vertical": "games_casual_general",
"updated_at": "2024-11-19T06:41:09.000000Z",
"creatives": {
"name": "Prison Escape",
"icon_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/icons/OrOJ6ksQZqJzQxh.png",
"description": "Pin the pulls in the correct order to help Andy overcome obstacles and get free from prison!",
"short_description": "Collect 10k Coins to Earn!",
"instructions": [
"Play Prison Escape",
"Collect 10k coins within 5 days after installing"
],
"disclaimer": "New Users Only!, Offer must be completed within 5 days!",
"currency_name": "Coin",
"currency_name_plural": "Coins",
"categories": [
"app",
"free"
],
"sort_order_setting": null,
"stickers": [],
"hero_image_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/hero_images/jkgkzGXMrgCXZsj"
},
"goals": [
{
"id": "61918292553633792",
"name": "Collect 10k coins",
"description": "Collect 10k coins",
"amount": 0.3,
"payout_usd": 0.3,
"order": 1
}
],
"links": {
"click_url": "https://api.adgem.com/v1/click?all=1&integration=offer-api&appid=29522&cid=20391&playerid={playerid}&offer_id=61918292528467968",
"support_url": "https://api.adgem.com/support/29522/20391/{playerid}"
},
"stats": {
"network_epc": "0.0052"
},
},
...
]
},
"links": {
"support_portal": "https://api.adgem.com/support/player?appid=29522&playerid={playerid}"
}
}
Pros
- Minimal infrastructure (follow established pattern)
- Publisher engineering teams likely have more experience with REST
- Publisher Integrations team has more experience with REST
Cons
- Overfetching data
- Backward compatibility not required, but can be alleviated through versioning
- Non-encoded filters (assuming
GETrequest) - Weakly typed
- Possibly slower performance
Option 2: GraphQL
Description
"GraphQL is an API query language that defines specifications of how a client application should request data from a remote server. You can use GraphQL in your API calls without relying on the server-side application to define the request" (What's the Difference Between GraphQL and REST?).
Example Request (No Specified Fields)
POST /v1/offers/targeted?player_id=testplayer
Host: https://offer-api.adgem.com
Accept: application/json
Authorization: Bearer 0|123456789abcdefghi // authentication approach to be determined
// query schema to be deteremined
Body:
{
query: query {
player(id:"testplayer")
offers {
id
campaign_id
name
store_id
tracking_type
total_payout_usd
total_amount
start_datetime
is_multi_reward
completion_difficulty
is_featured_campaign
campaign_vertical
updated_at
creatives {
name
icon_url
description
short_description
instructions
disclaimer
currency_name
currency_name_plural
categories
sort_order_setting
stickers
hero_image_url
}
goals {
id
name
description
amount
payout_usd
order
}
links {
click_url
support_url
}
stats {
network_epc
}
}
}
}
Example Response (No Specified Fields)
// response schema to be determined
{
"status": "success",
"data": {
"offers": [
{
"id": "61918292528467968",
"campaign_id": 20391,
"name": "Prison Escape",
"store_id": "1525116042",
"tracking_type": "CPE",
"total_payout_usd": 0.3,
"total_amount": 0.3,
"start_datetime": "2023-03-22 17:04:30",
"is_multi_reward": false,
"completion_difficulty": 6,
"is_featured_campaign": false,
"campaign_vertical": "games_casual_general",
"updated_at": "2024-11-19T06:41:09.000000Z",
"creatives": {
"name": "Prison Escape",
"icon_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/icons/OrOJ6ksQZqJzQxh.png",
"description": "Pin the pulls in the correct order to help Andy overcome obstacles and get free from prison!",
"short_description": "Collect 10k Coins to Earn!",
"instructions": [
"Play Prison Escape",
"Collect 10k coins within 5 days after installing"
],
"disclaimer": "New Users Only!, Offer must be completed within 5 days!",
"currency_name": "Coin",
"currency_name_plural": "Coins",
"categories": [
"app",
"free"
],
"sort_order_setting": null,
"stickers": [],
"hero_image_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/hero_images/jkgkzGXMrgCXZsj"
},
"goals": [
{
"id": "61918292553633792",
"name": "Collect 10k coins",
"description": "Collect 10k coins",
"amount": 0.3,
"payout_usd": 0.3,
"order": 1
}
],
"links": {
"click_url": "https://api.adgem.com/v1/click?all=1&integration=offer-api&appid=29522&cid=20391&playerid={playerid}&offer_id=61918292528467968",
"support_url": "https://api.adgem.com/support/29522/20391/{playerid}"
},
"stats": {
"network_epc": "0.0052"
},
},
...
]
},
"links": {
"support_portal": "https://api.adgem.com/support/player?appid=29522&playerid={playerid}"
}
}
Example Request (With Specified Fields)
POST /v1/offers/targeted?player_id=testplayer
Host: https://offer-api.adgem.com
Accept: application/json
Authorization: Bearer 0|123456789abcdefghi // authentication approach to be determined
// query schema to be deteremined
Body:
{
query: query {
player(id:"testplayer")
offers {
id
name
total_amount
campaign_vertical
creatives {
name
icon_url
description
instructions
disclaimer
currency_name
currency_name_plural
hero_image_url
}
goals {
id
name
description
amount
order
}
links {
click_url
support_url
}
}
}
}
Example Response (With Specified Fields)
// response schema to be determined
{
"status": "success",
"data": {
"offers": [
{
"id": "61918292528467968",
"name": "Prison Escape",
"total_amount": 0.3,
"campaign_vertical": "games_casual_general",
"creatives": {
"name": "Prison Escape",
"icon_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/icons/OrOJ6ksQZqJzQxh.png",
"description": "Pin the pulls in the correct order to help Andy overcome obstacles and get free from prison!",
"instructions": [
"Play Prison Escape",
"Collect 10k coins within 5 days after installing"
],
"disclaimer": "New Users Only!, Offer must be completed within 5 days!",
"currency_name": "Coin",
"currency_name_plural": "Coins",
"hero_image_url": "https://adgem-dashboard-production.s3.us-east-2.amazonaws.com/campaigns/20391/campaign-offerwall-creatives/hero_images/jkgkzGXMrgCXZsj"
},
"goals": [
{
"id": "61918292553633792",
"name": "Collect 10k coins",
"description": "Collect 10k coins",
"amount": 0.3,
"order": 1
}
],
"links": {
"click_url": "https://api.adgem.com/v1/click?all=1&integration=offer-api&appid=29522&cid=20391&playerid={playerid}&offer_id=61918292528467968",
"support_url": "https://api.adgem.com/support/29522/20391/{playerid}"
},
},
...
]
},
"links": {
"support_portal": "https://api.adgem.com/support/player?appid=29522&playerid={playerid}"
}
}
Pros
- Flexibility
- Event subscription
- Encoded payload
- Requires backward compatibility
- Built-in strongly-typed architecture
- Built-in error handling
Cons
- Publisher engineering teams likely have less experience with GraphQL (see "Popularity" in GraphQL vs. REST)
- Publisher Integrations team has less experience with GraphQL
Laravel GraphQL Package Options
DECISION
Choosing option 2 (GraphQL) would give publishers more flexibility to request only the data they want to meet the needs of their apps and users.
CONSEQUENCES
Choosing option 2 (GraphQL) will require installing new libraries, as well as expecting a longer integration time due to the fact that the team lacks experience with this technology.
Risks
Because the team lacks experience with GraphQL, there's a risk of less-than-ideal integration. We will want to take extra time to investigate best practices, especially with comparable APIs.
NOTES
References
- GraphQL documentation
- What's the Difference Between GraphQL and REST? (AWS)
- GraphQL vs. Rest (Postman)
- PR #45: docs(AGPI-1083): creates draft of Targeted API Resource Access Pattern adr
- PR #68: fix: Flatten indexes of docs to prep for auto-index merge
- PR #127: docs: backfill PR reference links for existing ADRs
Original Author
Micah Wierenga