Skip to content

Commit d109675

Browse files
docs: review HTTPs outcalls in Rust
Signed-off-by: David Dal Busco <david.dalbusco@outlook.com>
1 parent 8c42fc2 commit d109675

1 file changed

Lines changed: 24 additions & 43 deletions

File tree

docs/guides/rust.mdx

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,9 @@ To encode and decode the call, you need Rust structures that match the Candid ty
241241

242242
---
243243

244-
## HTTPS outcalls
244+
## HTTPS Outcalls
245245

246-
[HTTPS outcalls](https://internetcomputer.org/https-outcalls) are a feature of the Internet Computer, enabling smart contracts to directly connect to the Web 2.0 world by querying APIs with HTTP requests.
246+
[HTTPS outcalls](https://internetcomputer.org/https-outcalls) are a feature that enables your serverless functions to make HTTP requests to any external API.
247247

248248
:::tip
249249

@@ -260,18 +260,10 @@ For this example, we'll skip a few steps as the logic remains consistent:
260260
Here is an example of an `on_set_doc` hook which fetches an API to get the link to an image of a dog and saves that information within the Datastore. While this might not be a practical real-world use case, it is simple enough to demonstrate the feature.
261261

262262
```rust
263-
use ic_cdk::api::management_canister::http_request::{
264-
http_request as http_request_outcall, CanisterHttpRequestArgument, HttpMethod,
265-
};
266-
use junobuild_macros::{
267-
on_delete_asset, on_delete_doc, on_delete_many_assets, on_delete_many_docs, on_set_doc,
268-
on_set_many_docs, on_upload_asset,
269-
};
270-
use junobuild_satellite::{
271-
include_satellite, set_doc_store, OnDeleteAssetContext, OnDeleteDocContext,
272-
OnDeleteManyAssetsContext, OnDeleteManyDocsContext, OnSetDocContext, OnSetManyDocsContext,
273-
OnUploadAssetContext, SetDoc,
274-
};
263+
use ic_cdk::management_canister::http_request as http_request_outcall;
264+
use ic_cdk::management_canister::{HttpMethod, HttpRequestArgs};
265+
use junobuild_macros::on_set_doc;
266+
use junobuild_satellite::{include_satellite, set_doc_store, OnSetDocContext, SetDoc};
275267
use junobuild_utils::encode_doc_data;
276268
use serde::{Deserialize, Serialize};
277269

@@ -304,7 +296,7 @@ async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> {
304296

305297
let request_headers = vec![];
306298

307-
let request = CanisterHttpRequestArgument {
299+
let request = HttpRequestArgs {
308300
url,
309301
method: HttpMethod::GET,
310302
body: None,
@@ -313,18 +305,14 @@ async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> {
313305
transform: None,
314306
// We do not require any particular HTTP headers in this example.
315307
headers: request_headers,
308+
// Use a single node as we do not require that a trust level for fetching a dog image for demo purposes. 😉
309+
is_replicated: Some(false),
316310
};
317311

318-
// 2. Execute the HTTP request. A request consumes Cycles(!). In this example we provide 2_000_000_000 Cycles (= 0.002 TCycles).
319-
// To estimate the costs see documentation:
320-
// - https://internetcomputer.org/docs/current/developer-docs/gas-cost#special-features
321-
// - https://internetcomputer.org/docs/current/developer-docs/integrations/https-outcalls/https-outcalls-how-it-works#pricing
322-
// Total amount of cycles depends on the subnet size. Therefore, on mainnet it might cost ~13x more than what's required when developing locally. Source: https://forum.dfinity.org/t/http-outcalls-cycles/27439/4
323-
// Note: In the future we will have a UI logging panel in console.juno.build to help debug on production. Follow PR https://github.com/junobuild/juno/issues/415.
324-
//
325-
// We rename ic_cdk::api::management_canister::http_request::http_request to http_request_outcall because the Satellite already includes such a function's name.
326-
match http_request_outcall(request, 2_000_000_000).await {
327-
Ok((response,)) => {
312+
// 2. Execute the HTTP request.
313+
// Note: we alias the function http_request to http_request_outcall just to prevent naming conflicts.
314+
match http_request_outcall(&request).await {
315+
Ok(response) => {
328316
// 3. Use serde_json to transform the response to a structured object.
329317
let str_body = String::from_utf8(response.body)
330318
.expect("Transformed response is not UTF-8 encoded.");
@@ -357,43 +345,36 @@ async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> {
357345

358346
Ok(())
359347
}
360-
Err((r, m)) => {
361-
let message =
362-
format!("The http_request resulted into error. RejectionCode: {r:?}, Error: {m}");
348+
Err(error) => {
349+
let message = format!("The http_request resulted into error: {error:?}");
363350

364351
Err(message)
365352
}
366353
}
367354
}
368355

369-
// Other hooks
370-
371356
include_satellite!();
372357
```
373358

374359
As with the previous example, the hook will asynchronously update the document. If you wait a bit before retrieving the document in your frontend, you might notice that the source of the image has been updated by your hook.
375360

376-
### Costs
361+
### Replication
377362

378-
HTTPS outcalls require cycles to execute the request. At the time of writing this example, the cost was calculated using the formula `(3_000_000 + 60_000 * n) * n` for the base fee and `400 * n` each request byte and `800 * n` for each response byte, where n is the number of nodes in the subnet.
363+
By default, all nodes that run your Satellite execute the same request and must agree on the response for the call to succeed. This ensures the result is verified but means the target API must return identical responses across repeated calls.
379364

380-
You can use the [HTTPS Outcalls Cost Calculator](https://7joko-hiaaa-aaaal-ajz7a-cai.icp0.io/) to estimate the cost of your request (source code is available on [GitHub](https://github.com/domwoe/HTTPS-Outcalls-Calculator)).
365+
Setting `is_replicated: Some(false)` switches to a single-node mode which by extension skips such assertion. It's also cheaper, but the response is not verified by others. Suitable when you trust the data source or consistency is not critical.
381366

382-
Alternatively, refer to the [documentation](https://internetcomputer.org/docs/current/references/https-outcalls-how-it-works#pricing) for the actual calculation method and costs.
383-
384-
### Technical Requirements
367+
### Costs
385368

386-
The goal of HTTPS outcalls is to ensure that a request to the Web2 world returns a valid and verifiable response. To achieve this, calls are replicated when executed on mainnet. This means the blockchain will perform multiple identical requests and compare their results. The response will only succeed if all returned results are exactly the same.
369+
HTTPS outcalls consume cycles to execute. Refer to the [ICP documentation](https://internetcomputer.org/docs/current/references/https-outcalls-how-it-works#pricing) for the current pricing model.
387370

388-
Many Web APIs do not natively support such replicated calls. More advanced APIs offer a way to handle this by using an **idempotency key**, a unique key that allows the server to recognize and return the same response for repeated requests.
371+
You can also use the [HTTPS Outcalls Cost Calculator](https://7joko-hiaaa-aaaal-ajz7a-cai.icp0.io/) to estimate the cost of a specific request.
389372

390-
Another requirement is that the API must be accessible over **IPv6**.
373+
### Technical Requirements
391374

392-
If replication or IPv6 support is not available, a common workaround is to use a proxy service. Developers can consider the following solutions:
375+
HTTPS outcalls support both IPv4 and IPv6.
393376

394-
- [`ic-http-proxy`](https://github.com/omnia-network/ic-http-proxy) from Omnia Network ([announcement](https://forum.dfinity.org/t/non-replicated-https-outcalls/26627))
395-
- [`idempotent-proxy`](https://github.com/ldclabs/idempotent-proxy) from LDC Labs ([announcement](https://forum.dfinity.org/t/idempotent-proxy-show-proxy-https-outcalls-to-any-web2-service/32754))
396-
- A similar approach to the optimistic solution we currently used for transmitting emails ([repo](https://github.com/junobuild/proxy))
377+
One consideration when using replicated mode: since all nodes execute the same request, the API must return an identical response each time. Many APIs support this via an idempotency key. If that's not an option, non-replicated mode is usually the practical alternative.
397378

398379
---
399380

0 commit comments

Comments
 (0)