CandidAdapter
CandidAdapter provides low-level utilities for fetching Candid definitions from canisters and compiling them to IDL factories. Use it when you need fine-grained control over Candid handling.
Overview
Section titled “Overview”Fetching the Candid source and compiling it to JavaScript are two separate steps, each with its own fallback chain.
Fetching the source:
- Canister metadata (preferred) — reads the canister’s
candidmetadata path __get_candid_interface_tmp_hack(fallback) — query method on the canister
Compiling to JavaScript:
- Local parser (preferred) — the
@ic-reactor/parserWASM module, a dependency of this package, loaded on first use - didjs canister (fallback) — remote
did_to_jscall
import { CandidAdapter } from "@ic-reactor/candid"
const adapter = new CandidAdapter({ clientManager })
// Fetch and parse in one callconst { idlFactory } = await adapter.getCandidDefinition( "ryjl3-tyaaa-aaaaa-aaaba-cai")Import
Section titled “Import”import { CandidAdapter } from "@ic-reactor/candid"Constructor
Section titled “Constructor”new CandidAdapter(config: CandidAdapterParameters)Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
clientManager |
CandidClientManager |
Yes | Client manager with agent access |
didjsCanisterId |
CanisterId |
No | Custom didjs canister ID |
The didjs canister ID is auto-selected based on network:
- IC Mainnet:
a4gq6-oaaaa-aaaab-qaa4q-cai - Local:
bd3sg-teaaa-aaaaa-qaaba-cai(must be deployed to the local replica)
Methods
Section titled “Methods”getCandidDefinition(canisterId)
Section titled “getCandidDefinition(canisterId)”Fetch and parse Candid for a canister in one call.
const { idlFactory, init } = await adapter.getCandidDefinition( "ryjl3-tyaaa-aaaaa-aaaba-cai")
// Use the idlFactoryconst service = idlFactory({ IDL })console.log(service._fields) // Method definitionsReturns
Section titled “Returns”interface CandidDefinition { idlFactory: IDL.InterfaceFactory init?: (args: { IDL: typeof IDL }) => IDL.Type<unknown>[]}fetchCandidSource(canisterId)
Section titled “fetchCandidSource(canisterId)”Fetch raw Candid source text from a canister.
const candidSource = await adapter.fetchCandidSource( "ryjl3-tyaaa-aaaaa-aaaba-cai")console.log(candidSource)// service { icrc1_name : () -> (text) query; ... }parseCandidSource(candidSource)
Section titled “parseCandidSource(candidSource)”Parse Candid source text into an IDL factory.
const candidSource = ` service : { greet : (text) -> (text) query; }`
const { idlFactory } = await adapter.parseCandidSource(candidSource)This method:
- Tries the local parser first, loading
@ic-reactor/parseron first use - Falls back to remote compilation via the didjs canister
It is the only method that returns a CandidDefinition — compileLocal and
compileRemote return the compiled JavaScript source instead.
loadParser()
Section titled “loadParser()”Load the local WASM parser for offline compilation.
import { CandidAdapter } from "@ic-reactor/candid"
const adapter = new CandidAdapter({ clientManager })
// Load the parser (only needed once). It imports @ic-reactor/parser itself:// the package is a dependency of @ic-reactor/candid, so do not import it// from your app unless you also install it directly.await adapter.loadParser()
// Now compileLocal worksconst jsSource = adapter.compileLocal(candidSource)compileLocal(candidSource)
Section titled “compileLocal(candidSource)”Compile Candid using the local WASM parser (synchronous after loading). Returns
the compiled JavaScript source string — not a CandidDefinition. Use
parseCandidSource() when you need an idlFactory.
// Requires loadParser() firstconst jsSource: string = adapter.compileLocal(candidSource)Throws if the parser hasn’t been loaded.
compileRemote(candidSource)
Section titled “compileRemote(candidSource)”Compile Candid using the didjs canister. Also returns the compiled JavaScript source, wrapped in a promise.
const jsSource = await adapter.compileRemote(candidSource) // string | undefinedThis is the fallback method when local parsing isn’t available.
validateCandid(candidSource)
Section titled “validateCandid(candidSource)”Validate Candid syntax without compiling.
await adapter.loadParser()
const isValid = adapter.validateCandid(` service { greet: (text) -> (text) query; }`)console.log(isValid) // trueRequires the parser to be loaded.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
clientManager |
CandidClientManager |
The client manager instance |
didjsCanisterId |
CanisterId |
The didjs canister ID in use |
Examples
Section titled “Examples”Basic Usage
Section titled “Basic Usage”import { CandidAdapter } from "@ic-reactor/candid"import { ClientManager } from "@ic-reactor/core"import { IDL } from "@icp-sdk/core/candid"import { QueryClient } from "@tanstack/query-core"
const clientManager = new ClientManager({ queryClient: new QueryClient() })await clientManager.initialize()
const adapter = new CandidAdapter({ clientManager })
// Get full definitionconst { idlFactory } = await adapter.getCandidDefinition( "ryjl3-tyaaa-aaaaa-aaaba-cai")
// List methodsconst service = idlFactory({ IDL })for (const [name, func] of service._fields) { console.log(`${name}: ${func.toString()}`)}With Local Parser
Section titled “With Local Parser”import { CandidAdapter } from "@ic-reactor/candid"
const adapter = new CandidAdapter({ clientManager })
// Load parser once at startupawait adapter.loadParser()
// All subsequent compilations are local (fast!)const candidSource = await adapter.fetchCandidSource(canisterId)const jsSource = adapter.compileLocal(candidSource)Error Handling
Section titled “Error Handling”try { const definition = await adapter.getCandidDefinition(canisterId)} catch (error) { if (error.message.includes("has no query method")) { console.log("Canister doesn't expose Candid metadata") } else if (error.message.includes("compilation failed")) { console.log("Invalid Candid syntax") } else { throw error }}Method Signature Parsing
Section titled “Method Signature Parsing”For dynamic calls, you can parse individual method signatures:
// Parse a method signature wrapped in a serviceconst methodSignature = "(record { owner : principal }) -> (nat) query"const serviceSource = `service : { my_method : ${methodSignature}; }`
const { idlFactory } = await adapter.parseCandidSource(serviceSource)const service = idlFactory({ IDL })
// Extract the method's type informationconst funcField = service._fields.find(([name]) => name === "my_method")const func = funcField[1]
console.log("Arg types:", func.argTypes)console.log("Return types:", func.retTypes)console.log("Is query:", func.annotations.includes("query"))Best Practices
Section titled “Best Practices”See Also
Section titled “See Also”- CandidReactor — High-level dynamic Reactor
- @ic-reactor/candid Overview — Package overview
- @ic-reactor/parser — Local WASM parser reference
- Reactor — Base Reactor class