-
Notifications
You must be signed in to change notification settings - Fork 3
docs: add canister discovery, binding generation, and frontend development docs #338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+649
−76
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
cf8e363
docs: add canister discovery, binding generation, and frontend develo…
marc0olo fd747a1
Merge branch 'main' into marc0olo/docs-bindings-env-variables
marc0olo 61161d1
docs: refine concepts, fix anchor link handling and improve CLAUDE.md
marc0olo 877b911
Merge branch 'main' into marc0olo/docs-bindings-env-variables
marc0olo e70bba0
Merge branch 'main' into marc0olo/docs-bindings-env-variables
marc0olo 6675ccc
Merge remote-tracking branch 'origin/main' into marc0olo/docs-binding…
marc0olo e3f0d48
Merge remote-tracking branch 'origin/main' into marc0olo/docs-binding…
marc0olo cb6ef72
Merge remote-tracking branch 'origin/main' into marc0olo/docs-binding…
marc0olo edf818a
fix(examples): only configure ic_env cookie in vite dev server mode
marc0olo 8c71cc4
refactor(examples): improve frontend example code quality and fix docs
marc0olo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # Binding Generation | ||
|
|
||
| Understanding and using type-safe client code for calling canisters. | ||
|
|
||
| ## What Are Bindings? | ||
|
|
||
| Bindings are generated code that provides type-safe access to canister methods. They're created from Candid interface files (`.did`), which define a canister's public API. | ||
|
|
||
| ## Candid Interface Files | ||
|
|
||
| Candid is the interface description language for the Internet Computer. A `.did` file defines the public methods and types a canister exposes — it's the contract between a canister and its callers. | ||
|
|
||
| `.did` files can be: | ||
| - **Manually authored** — Recommended for stable APIs where backward compatibility matters | ||
| - **Generated from code** — Convenient during development, but review before publishing | ||
|
|
||
| For Candid syntax and best practices, see the [Candid specification](https://github.com/dfinity/candid/blob/master/spec/Candid.md). | ||
|
|
||
| ## Generating Client Bindings | ||
|
|
||
| icp-cli focuses on deployment — use these dedicated tools to generate bindings: | ||
|
|
||
| | Language | Tool | Documentation | | ||
| |----------|------|---------------| | ||
| | TypeScript/JavaScript | `@icp-sdk/bindgen` | [js.icp.build/bindgen](https://js.icp.build/bindgen) | | ||
| | Rust | `candid` crate | [docs.rs/candid](https://docs.rs/candid) | | ||
| | Other languages | `didc` CLI | [github.com/dfinity/candid](https://github.com/dfinity/candid) | | ||
|
|
||
| > **Note:** Generated bindings typically hardcode a canister ID or require one at initialization. With icp-cli, canister IDs differ between environments. You can look up IDs with `icp canister status <name> -i`, or read them from canister environment variables at runtime. See [Canister Discovery](canister-discovery.md) for details. | ||
|
|
||
| ### TypeScript/JavaScript | ||
|
|
||
| Use `@icp-sdk/bindgen` to generate TypeScript bindings from Candid files. See the [@icp-sdk/bindgen documentation](https://js.icp.build/bindgen) for usage and build tool integration. | ||
|
|
||
| ### Rust | ||
|
|
||
| The `candid` crate provides Candid serialization and code generation macros. See the [candid crate documentation](https://docs.rs/candid). | ||
|
|
||
| ### Other Languages | ||
|
|
||
| The `didc` CLI generates bindings for various languages. See the [Candid repository](https://github.com/dfinity/candid) for available targets. | ||
|
|
||
| ## See Also | ||
|
|
||
| - [Canister Discovery](canister-discovery.md) — How canisters find each other's IDs | ||
| - [Local Development](../guides/local-development.md) — Development workflow | ||
|
|
||
| [Browse all documentation →](../index.md) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| # Canister Discovery | ||
|
|
||
| How icp-cli enables canisters to discover each other through automatic ID injection. | ||
|
|
||
| ## The Discovery Problem | ||
|
|
||
| Canister IDs are assigned at deployment time and differ between environments: | ||
|
|
||
| | Environment | Backend ID | | ||
| |-------------|-----------| | ||
| | local | `bkyz2-fmaaa-aaaaa-qaaaq-cai` | | ||
| | staging | `rrkah-fqaaa-aaaaa-aaaaq-cai` | | ||
| | ic (mainnet) | `xxxxx-xxxxx-xxxxx-xxxxx-cai` | | ||
|
|
||
| Hardcoding IDs creates problems: | ||
|
|
||
| - Deploying to a new environment requires code changes | ||
| - Recreating a canister invalidates hardcoded references | ||
| - Sharing code with others fails because IDs don't match | ||
|
|
||
| ## Automatic Canister ID Injection | ||
|
|
||
| icp-cli solves this by automatically injecting canister IDs as [canister environment variables](../reference/environment-variables.md#canister-runtime-environment-variables) during deployment. | ||
|
|
||
| ### How It Works | ||
|
|
||
| During `icp deploy`, icp-cli automatically: | ||
|
|
||
| 1. Collects all canister IDs in the current environment | ||
| 2. Creates a variable for each: `PUBLIC_CANISTER_ID:<canister-name>` → `<principal>` | ||
| 3. Injects **all** these variables into **every** canister in the environment | ||
|
|
||
| This means each canister receives the IDs of all other canisters, enabling any canister to call any other canister without hardcoding IDs. | ||
|
|
||
| > **Note:** Variables are only updated for the canisters being deployed. If you deploy a single canister (`icp deploy backend`), only that canister receives updated variables. When adding new canisters to an existing project, run `icp deploy` without arguments to update all canisters with the complete set of IDs. | ||
|
|
||
| ### Variable Format | ||
|
|
||
| For an environment with `backend`, `frontend`, and `worker` canisters: | ||
|
|
||
| ``` | ||
| PUBLIC_CANISTER_ID:backend → bkyz2-fmaaa-aaaaa-qaaaq-cai | ||
| PUBLIC_CANISTER_ID:frontend → bd3sg-teaaa-aaaaa-qaaba-cai | ||
| PUBLIC_CANISTER_ID:worker → b77ix-eeaaa-aaaaa-qaada-cai | ||
| ``` | ||
|
|
||
| These variables are stored in canister settings, not baked into the WASM. The same WASM can run in different environments with different canister IDs. | ||
|
|
||
| ### Deployment Order | ||
|
|
||
| When deploying multiple canisters: | ||
|
|
||
| 1. `icp deploy` creates all canisters first (getting their IDs) | ||
| 2. Then injects `PUBLIC_CANISTER_ID:*` variables into all canisters | ||
| 3. Then installs WASM code | ||
|
|
||
| All canisters can reference each other's IDs regardless of declaration order in `icp.yaml`. | ||
|
|
||
| ## Frontend to Backend Communication | ||
|
|
||
| When your frontend is deployed to an asset canister: | ||
|
|
||
| 1. The asset canister receives `PUBLIC_CANISTER_ID:*` variables | ||
| 2. It exposes them via a cookie named `ic_env`, along with the network's root key (`IC_ROOT_KEY`) | ||
| 3. Your frontend JavaScript reads the cookie to get canister IDs and root key | ||
|
|
||
| This mechanism works identically on local networks and mainnet — your frontend code doesn't need to change between environments. | ||
|
|
||
| ### Working Examples | ||
|
|
||
| - **hello-world template** — The template from `icp new` demonstrates this pattern. Look at the frontend source code to see how it reads the backend canister ID. | ||
| - **[frontend-environment-variables example](https://github.com/dfinity/icp-cli/tree/main/examples/icp-frontend-environment-variables)** — A detailed example showing dev server configuration with Vite. | ||
|
|
||
| ### Implementation | ||
|
|
||
| Use [@icp-sdk/core](https://www.npmjs.com/package/@icp-sdk/core) to read the cookie: | ||
|
|
||
| ```typescript | ||
| import { getCanisterEnv } from "@icp-sdk/core/agent/canister-env"; | ||
|
|
||
| interface CanisterEnv { | ||
| "PUBLIC_CANISTER_ID:backend": string; | ||
| IC_ROOT_KEY: Uint8Array; // Parsed from hex by the library | ||
| } | ||
|
|
||
| const env = getCanisterEnv<CanisterEnv>(); | ||
| ``` | ||
|
|
||
| For local development with a dev server, see the [Local Development Guide](../guides/local-development.md#frontend-development). | ||
|
|
||
| ## Backend to Backend Communication | ||
|
|
||
| Since all canisters receive `PUBLIC_CANISTER_ID:*` variables for every canister in the environment, backend canisters can discover each other's IDs at runtime. | ||
|
|
||
| ### Reading Canister Environment Variables | ||
|
|
||
| **Rust** canisters can read the injected canister IDs using [`ic_cdk::api::env_var_value`](https://docs.rs/ic-cdk/latest/ic_cdk/api/fn.env_var_value.html): | ||
|
|
||
| ```rust | ||
| use candid::Principal; | ||
|
|
||
| let backend_id = Principal::from_text( | ||
| &ic_cdk::api::env_var_value("PUBLIC_CANISTER_ID:backend") | ||
| ).unwrap(); | ||
| ``` | ||
|
|
||
| **Motoko** canisters can read canister environment variables using `Prim.envVar` (since Motoko 0.16.2): | ||
|
|
||
| ```motoko | ||
| import Prim "mo:⛔"; | ||
| import Principal "mo:core/Principal"; | ||
|
|
||
| let ?backendIdText = Prim.envVar<system>("PUBLIC_CANISTER_ID:backend") else { | ||
| return #err("backend canister ID not set"); | ||
| }; | ||
| let backendId = Principal.fromText(backendIdText); | ||
| ``` | ||
|
|
||
| > **Note:** `Prim` is an internal module not intended for general use. This functionality will be available in the Motoko core package in a future release. | ||
|
|
||
| ### Making Inter-Canister Calls | ||
|
|
||
| Once you have the target canister ID, make calls using your language's CDK: | ||
|
|
||
| - **Rust**: [`ic_cdk::call`](https://docs.rs/ic-cdk/latest/ic_cdk/call/index.html) API | ||
| - **Motoko**: [Inter-canister calls](https://docs.internetcomputer.org/motoko/fundamentals/actors/messaging#inter-canister-calls) | ||
|
|
||
| ### Alternative Patterns | ||
|
|
||
| If you prefer not to use canister environment variables: | ||
|
|
||
| 1. **Init arguments** — Pass canister IDs as initialization parameters | ||
| 2. **Configuration** — Store IDs in canister state during setup | ||
|
|
||
| ## Custom Canister Environment Variables | ||
|
|
||
| Beyond automatic `PUBLIC_CANISTER_ID:*` variables, you can define custom canister environment variables in `icp.yaml`. See the [Environment Variables Reference](../reference/environment-variables.md#custom-variables) for configuration syntax. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### "Canister not found" errors | ||
|
|
||
| Ensure the target canister is deployed: | ||
|
|
||
| ```bash | ||
| icp canister list # Check what's deployed | ||
| icp deploy # Deploy all canisters | ||
| ``` | ||
|
|
||
| ### Canister environment variables not available | ||
|
|
||
| Canister environment variables are set automatically during `icp deploy`. If you're using `icp canister install` directly, variables won't be set. Use `icp deploy` instead. | ||
|
|
||
| ### Wrong canister ID in different environment | ||
|
|
||
| Check which environment you're targeting: | ||
|
|
||
| ```bash | ||
| icp canister list -e local # Local environment | ||
| icp canister list -e production # Production environment | ||
| ``` | ||
|
|
||
| ## See Also | ||
|
|
||
| - [Binding Generation](binding-generation.md) — Type-safe canister interfaces | ||
| - [Environment Variables Reference](../reference/environment-variables.md) — Complete variable documentation | ||
| - [Canister Settings Reference](../reference/canister-settings.md) — Settings configuration | ||
| - [Build, Deploy, Sync](build-deploy-sync.md) — Deployment lifecycle details | ||
| - [Local Development](../guides/local-development.md) — Frontend local dev setup | ||
|
|
||
| [Browse all documentation →](../index.md) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have a note somewhere that not all generated bindings will work well with icp-cli.