How to update this simple example for new HTTP_TRANSFORM

Hey! @qijing_yu

I often use the example apps and simplify them, and then change them to my use case.
I know that the most recent changes are to do with the transform function. Can you provide a tip on how I could just change that to allow this to keep working?

use candid::{CandidType, Principal};
use ic_cdk_macros::{self, query, update};
use ic_cdk::api::management_canister::http_request::{
    HttpHeader, HttpMethod, TransformFunc, TransformType,
use serde::{Deserialize, Serialize};
use serde_json::{self, Value};
use std::collections::{HashMap};

#[derive(CandidType, Deserialize, Debug, Clone)]
pub struct CanisterHttpRequestArgs {
    pub url: String,
    pub max_response_bytes: Option<u64>,
    pub headers: Vec<HttpHeader>,
    pub body: Option<Vec<u8>>,
    pub method: HttpMethod,
    pub transform: Option<TransformType>,

#[derive(CandidType, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CanisterHttpResponsePayload {
    pub status: u128,
    pub headers: Vec<HttpHeader>,
    pub body: Vec<u8>,

// How many data points in each Coinbase API call. Maximum allowed is 300
pub const DATA_POINTS_PER_API: u64 = 200;
pub const MAX_RESPONSE_BYTES: u64 = 10 * 6 * DATA_POINTS_PER_API;

A function to call IC http_request function with sample interval of REMOTE_FETCH_GRANULARITY seconds. Each API
call fetches DATA_POINTS_PER_API data points, which is equivalent of DATA_POINTS_PER_API minutes of data.
async fn get_rate() -> String {

    let host = "";
    let mut host_header = host.clone().to_owned();
    // prepare system http_request call
    let request_headers = vec![
        HttpHeader {
            name: "Host".to_string(),
            value: host_header,
        HttpHeader {
            name: "User-Agent".to_string(),
            value: "exchange_rate_canister".to_string(),
    let url = format!("https://{host}/v2/prices/icp-usd/spot");

    let request = CanisterHttpRequestArgs {
        url: url,
        method: HttpMethod::GET,
        body: None,
        max_response_bytes: Some(MAX_RESPONSE_BYTES),
        transform: Some(TransformType::Function(TransformFunc(candid::Func {
            principal: ic_cdk::api::id(),
            method: "transform".to_string(),
        headers: request_headers,

    let body = candid::utils::encode_one(&request).unwrap();
    ic_cdk::api::print(format!("Making IC http_request call now."));

    match ic_cdk::api::call::call_raw(
        Ok(result) => {
            // decode the result
            let decoded_result: CanisterHttpResponsePayload =
                candid::utils::decode_one(&result).expect("IC http_request failed!");

            let decoded_body = String::from_utf8(decoded_result.body)
                .expect("Remote service response is not UTF-8 encoded.");
            let fetch = decode_body_to_rates(&decoded_body);
            return fetch.get("data").unwrap().get("amount").unwrap().to_string();
        Err((r, m)) => {
            let message =
                format!("The http_request resulted into error. RejectionCode: {r:?}, Error: {m}");
            return message.clone().to_string();

fn decode_body_to_rates(body: &str) -> Value {
    let json: serde_json::Value =
    return json

async fn transform(raw: CanisterHttpResponsePayload) -> CanisterHttpResponsePayload {
    let mut sanitized = raw.clone();
    sanitized.headers = vec![];

fn main() {}

mod tests {
    use super::*;

    fn test_decode_body_to_rates() {
        let body = "{\"data\":{\"base\":\"ICP\",\"currency\":\"USD\",\"amount\":\"5.96\"}}";
        let fetched = decode_body_to_rates(body);
        println!("{:?}", fetched.get("data").unwrap().get("amount").unwrap());
        assert_eq!(*fetched.get("data").unwrap().get("amount").unwrap(), serde_json::json!("5.96"));

I don’t know the specifics, but the diff for the update to the examples could give useful hints.

1 Like

I think I got it.

Also FYI in the public sample Dfinity uses which does not work without an API key? Worked when I changed it back to


Like this? Switch to free coinbase API by sesi200 · Pull Request #367 · dfinity/examples · GitHub

That’s what happens if people don’t write tests…

1 Like

Good morning @apotheosis @Severin! Thanks for answering the questions while it was night time at my side. Glad you figured out how to update your dapp to work with transform context.

WRT, I did testing right before publishing sample dapp updates, and did another testing just now. I was able to download rates from Coinbase Pro without API key. is accessible if you just hit the link without supplying an API key. The API you are trying to call: on the other hand, shows an Route not found error though. Looking at the Pro API documentation it does look like they don’t have spot price served in Pro/Exchange section.

The PR-367 unfortunately breaks the sample dapp. Curling doesn’t seem to exist, and requests are being redirected to documentation website.

1 Like


Another that may be confusing to people…

ic-cdk = { path = "../../../cdk-rs/src/ic-cdk" }

I changed this to

ic-cdk = { path = "0.6.5" }
1 Like

Thanks for the correction @qijing_yu, I fixed it. And thank you for the dependencies thing @apotheosis, that’s also fixed.

The example is now also under a minimal e2e test that checks if deployment works properly.

Thanks all! My subnet was updated today and fiat to canister deploy payments were down for a very short time. Smooth transition :pray:

1 Like