Skip to content

Commit b02aac2

Browse files
docs: typescript https outcalls guide (#686)
* docs: typescript https outcalls guide Signed-off-by: David Dal Busco <david.dalbusco@outlook.com> * 📄 Update LLMs.txt snapshot for PR review --------- Signed-off-by: David Dal Busco <david.dalbusco@outlook.com> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 508c663 commit b02aac2

5 files changed

Lines changed: 183 additions & 24 deletions

File tree

.llms-snapshots/llms-full.txt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7389,6 +7389,50 @@ To encode and decode these calls, you need JavaScript structures that match the
73897389

73907390
---
73917391

7392+
## HTTPS Outcalls
7393+
7394+
[HTTPS outcalls](https://internetcomputer.org/https-outcalls) are a feature that enables your serverless functions to make HTTP requests to any external API.
7395+
7396+
**Tip:**
7397+
7398+
This example is also available on [GitHub](https://github.com/junobuild/examples/tree/main/functions/typescript/https-outcalls).
7399+
7400+
For this example, we'll skip a few steps as the logic remains consistent:
7401+
7402+
* Your frontend makes a call to the Satellite.
7403+
* The Satellite performs some work, such as asserting and setting a document.
7404+
* If everything succeeds, the Satellite triggers a hook before returning the result of the call.
7405+
7406+
Here is an example of an 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.
7407+
7408+
Here is an example of an `onSetDoc` 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.
7409+
7410+
```
7411+
import { defineHook, type OnSetDoc, SetDoc } from "@junobuild/functions";import { j } from "@junobuild/schema";import { httpRequest, HttpRequestArgs } from "@junobuild/functions/ic-cdk";import { encodeDocData, setDocStore } from "@junobuild/functions/sdk";// The data of the document we are looking to update in the Satellite's Datastore.const DogDataSchema = j.strictObject({ src: j.string().optional()});// We are using the Dog CEO API in this example.// https://dog.ceo/dog-api///// Its endpoint "random" returns such JSON data:// {// "message": "https://images.dog.ceo/breeds/mountain-swiss/n02107574_1118.jpg",// "status": "success"// }//// That's why we declare a struct that matches the structure of the answer.const DogApiResponseSchema = j.strictObject({ message: j.url(), status: j.string()});export const onSetDoc = defineHook<OnSetDoc>({ collections: ["dogs"], run: async ({ caller, data: { collection, key, data: { after: { description, version } } } }) => { // 1. Prepare the HTTP GET request const url = "https://dog.ceo/api/breeds/image/random"; const args: HttpRequestArgs = { url, method: "GET", headers: [], // Use a single node as we do not require that a trust level for fetching a dog image for demo purposes. 😉 isReplicated: false }; // 2. Execute the HTTP request. const result = await httpRequest(args); // 3. Transform the response to a structured data object. const decoder = new TextDecoder(); const body = decoder.decode(result.body); const dogResponse = DogApiResponseSchema.parse(JSON.parse(body)); // 4. Our goal is to update the document in the Datastore with an update that contains the link to the image fetched from the API we just called. const dogData = DogDataSchema.parse({ src: dogResponse.message }); // 5. We encode those data back to blob because the Datastore holds data as blob. const encodedData = encodeDocData(dogData); // 6. Then we construct the parameters required to call the function that save the data in the Datastore. const doc: SetDoc = { description, version, data: encodedData }; // 7. We store the data in the Datastore for the same caller as the one who triggered the original on_set_doc, in the same collection with the same key as well. setDocStore({ caller, collection, key, doc }); }});
7412+
```
7413+
7414+
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.
7415+
7416+
### Replication
7417+
7418+
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.
7419+
7420+
Setting 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.
7421+
7422+
### Costs
7423+
7424+
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.
7425+
7426+
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.
7427+
7428+
### Technical Requirements
7429+
7430+
HTTPS outcalls support both IPv4 and IPv6.
7431+
7432+
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.
7433+
7434+
---
7435+
73927436
## Schema Types
73937437

73947438
The `j` type system is Juno's schema layer for custom functions. It is built on top of [Zod](https://zod.dev/) and extends it with types specific to the Juno and Internet Computer environment, such as `j.principal()`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export const isReplicated = "isReplicated: false";
2+
3+
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.
4+
5+
### Replication
6+
7+
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.
8+
9+
Setting <code>{props.isReplicated}</code> 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.
10+
11+
### Costs
12+
13+
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.
14+
15+
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.
16+
17+
### Technical Requirements
18+
19+
HTTPS outcalls support both IPv4 and IPv6.
20+
21+
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.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const onSetDoc = "onSetDoc";
2+
3+
For this example, we'll skip a few steps as the logic remains consistent:
4+
5+
- Your frontend makes a call to the Satellite.
6+
- The Satellite performs some work, such as asserting and setting a document.
7+
- If everything succeeds, the Satellite triggers a hook before returning the result of the call.
8+
9+
Here is an example of an <code>{props.onSetDoc}</code> 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.

docs/guides/rust.mdx

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,9 @@ This example is also available on [GitHub](https://github.com/junobuild/examples
251251

252252
:::
253253

254-
For this example, we'll skip a few steps as the logic remains consistent:
254+
import HttpsOutcallsIntro from "./components/functions/https-outcalls-intro.mdx";
255255

256-
- Your frontend makes a call to the Satellite.
257-
- The Satellite performs some work, such as asserting and setting a document.
258-
- If everything succeeds, the Satellite triggers a hook before returning the result of the call.
259-
260-
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.
256+
<HttpsOutcallsIntro onSetDoc="on_set_doc" github="rust" />
261257

262258
```rust
263259
use ic_cdk::management_canister::http_request as http_request_outcall;
@@ -356,25 +352,9 @@ async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> {
356352
include_satellite!();
357353
```
358354

359-
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.
360-
361-
### Replication
362-
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.
364-
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.
366-
367-
### Costs
368-
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.
370-
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.
372-
373-
### Technical Requirements
374-
375-
HTTPS outcalls support both IPv4 and IPv6.
355+
import HttpsOutcallsDesc from "./components/functions/https-outcalls-description.mdx";
376356

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.
357+
<HttpsOutcallsDesc isReplicated="is_replicated: Some(false)" />
378358

379359
---
380360

docs/guides/typescript.mdx

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,111 @@ To encode and decode these calls, you need JavaScript structures that match the
307307

308308
---
309309

310+
## HTTPS Outcalls
311+
312+
[HTTPS outcalls](https://internetcomputer.org/https-outcalls) are a feature that enables your serverless functions to make HTTP requests to any external API.
313+
314+
:::tip
315+
316+
This example is also available on [GitHub](https://github.com/junobuild/examples/tree/main/functions/typescript/https-outcalls).
317+
318+
:::
319+
320+
import HttpsOutcallsIntro from "./components/functions/https-outcalls-intro.mdx";
321+
322+
<HttpsOutcallsIntro />
323+
324+
Here is an example of an `onSetDoc` 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.
325+
326+
```typescript
327+
import { defineHook, type OnSetDoc, SetDoc } from "@junobuild/functions";
328+
import { j } from "@junobuild/schema";
329+
import { httpRequest, HttpRequestArgs } from "@junobuild/functions/ic-cdk";
330+
import { encodeDocData, setDocStore } from "@junobuild/functions/sdk";
331+
332+
// The data of the document we are looking to update in the Satellite's Datastore.
333+
const DogDataSchema = j.strictObject({
334+
src: j.string().optional()
335+
});
336+
337+
// We are using the Dog CEO API in this example.
338+
// https://dog.ceo/dog-api/
339+
//
340+
// Its endpoint "random" returns such JSON data:
341+
// {
342+
// "message": "https://images.dog.ceo/breeds/mountain-swiss/n02107574_1118.jpg",
343+
// "status": "success"
344+
// }
345+
//
346+
// That's why we declare a struct that matches the structure of the answer.
347+
const DogApiResponseSchema = j.strictObject({
348+
message: j.url(),
349+
status: j.string()
350+
});
351+
352+
export const onSetDoc = defineHook<OnSetDoc>({
353+
collections: ["dogs"],
354+
run: async ({
355+
caller,
356+
data: {
357+
collection,
358+
key,
359+
data: {
360+
after: { description, version }
361+
}
362+
}
363+
}) => {
364+
// 1. Prepare the HTTP GET request
365+
const url = "https://dog.ceo/api/breeds/image/random";
366+
367+
const args: HttpRequestArgs = {
368+
url,
369+
method: "GET",
370+
headers: [],
371+
// Use a single node as we do not require that a trust level for fetching a dog image for demo purposes. 😉
372+
isReplicated: false
373+
};
374+
375+
// 2. Execute the HTTP request.
376+
const result = await httpRequest(args);
377+
378+
// 3. Transform the response to a structured data object.
379+
const decoder = new TextDecoder();
380+
const body = decoder.decode(result.body);
381+
382+
const dogResponse = DogApiResponseSchema.parse(JSON.parse(body));
383+
// 4. Our goal is to update the document in the Datastore with an update that contains the link to the image fetched from the API we just called.
384+
const dogData = DogDataSchema.parse({
385+
src: dogResponse.message
386+
});
387+
388+
// 5. We encode those data back to blob because the Datastore holds data as blob.
389+
const encodedData = encodeDocData(dogData);
390+
391+
// 6. Then we construct the parameters required to call the function that save the data in the Datastore.
392+
const doc: SetDoc = {
393+
description,
394+
version,
395+
data: encodedData
396+
};
397+
398+
// 7. We store the data in the Datastore for the same caller as the one who triggered the original on_set_doc, in the same collection with the same key as well.
399+
setDocStore({
400+
caller,
401+
collection,
402+
key,
403+
doc
404+
});
405+
}
406+
});
407+
```
408+
409+
import HttpsOutcallsDesc from "./components/functions/https-outcalls-description.mdx";
410+
411+
<HttpsOutcallsDesc />
412+
413+
---
414+
310415
## Schema Types
311416

312417
The `j` type system is Juno's schema layer for custom functions. It is built on top of [Zod](https://zod.dev/) and extends it with types specific to the Juno and Internet Computer environment, such as `j.principal()`.

0 commit comments

Comments
 (0)