Skip to main content

Using Candid

Beginner
Tutorial

Candid provides a language-agnostic way to interact with canisters.

While Candid is a robust, type-safe, and future-proof way to facilitate communication between canisters, it is not required. Arbitrary bytes can be exchanged if all canisters that are part of the communication stream are designed to understand them (such that they are using the same underlying protocol, written in the same language, etc.).

Learn more in the Candid reference documentation.

By using Candid, you can specify input argument values and display return values from canister methods regardless of whether you interact with ICP from a terminal using the IC SDK, through a web browser, or from a program written in JavaScript, Motoko, Rust, or any other language.

The .did file

Candid types can be used to describe a service via a Candid service description file (.did file), which can either be manually written or generated from a service implementation.

Auto-generated .did files

If you write a canister in Motoko, the compiler automatically generates a Candid description when you compile the program. If you use dfx, you will typically see the auto-generated .did files in the /declarations directory of your project. Since these files are auto-generated, it is recommended that you do not manually edit them; they will be overwritten in the next dfx build.

For canisters written in Rust, the Candid extractor tool can be used with the Rust IC CDK versions 0.11.0 and newer. View the full instructions for using the Candid extractor.

Writing .did files

For other languages, you will have to write the Candid interface description manually. Define a service, then add a public method's name, followed by the data types that the method accepts and returns:

Here is an example that defines the public method greet, which accepts type text and returns type text using a query call.

service : {
    "greet" : (text) -> (text) query;
};

Learn more about supported data types in the Candid reference documentation.

Splitting interface descriptions across multiple files (Motoko)

In some workflows, it may be beneficial to split interface descriptions across multiple .did files. For example, one file may be used to define type definitions that are used by several canisters, while another file is used to define the main service for one of those canisters.

You can import both interface descriptions into your canister with two import statements:

import A "a.did";  // Imports only type definitions
import service B "b.did";  // Imports both type definitions and main service

Service InitArgs

Consider the following service definition:

service : (InitArgs) -> {
   ...
};

This definition can be thought of as a deployment configuration. To initialize this service, you must supply (InitArgs) as an init argument.

An example of this service definition can be found in the EVM RPC canister's Candid file canister.

Naming arguments and results

Naming the arguments or results for a method is purely for documentation purposes. The name you use does not change the method’s type or the values being passed. Instead, arguments and results are identified by their position, independent of the name. This is thus very different from named record fields, which are semantically relevant.

Reusing complex types

Often, multiple methods in a service may refer to the same complex type. In that case, the type can be named and reused multiple times. For example:

type address = record {
  street : text;
  city : text;
  zip_code : nat;
  country : text;
};
service address_book : {
  set_address: (name : text, addr : address) -> ();
  get_address: (name : text) -> (opt address) query;
}

These type definitions merely abbreviate an existing type; they do not define a new type. It does not matter whether you use address in the function signature or write out the records. Also, two abbreviations with different names but equivalent definitions describe the same type and are interchangeable. In other words, Candid uses structural typing.

Specifying a query method

In the last example, you might have noticed the use of the query annotation for the get_address method. For example:

service address_book : {
  set_address: (name : text, addr : address) -> ();
  get_address: (name : text) -> (opt address) query;
}

This annotation indicates that the get_address method can be invoked as an ICP query call. As discussed in query and update methods, a query provides an efficient way to retrieve information from a canister without going through consensus, so being able to identify a method as a query is one of the key benefits of using Candid to interact with ICP.

Encoding and decoding

The point of Candid is to allow seamless invocation of service methods, passing arguments encoded to a binary format and transferred by an underlying transportation method (such as messages into or within the Internet Computer) and decoded on the other side.

As a Candid user, you do not have to worry about the details of this binary format. If you plan to implement Candid yourself (for example, to support a new host language), you can consult the Candid specification for details. However, some aspects of the format are worth knowing:

  •   The Candid binary format starts with DIDL… (or, in hex, 4449444c…). If you see this in some low-level log output, you are very likely observing a Candid-encoded value.

  •   The Candid binary format always encodes sequences of values, because method parameters and results are sequences of types.

  •   The binary format is quite compact. A (vec nat64) with 125_000 entries takes 1_000_007 bytes.

  •   The binary is self-describing and includes a (condensed) type description of the type of the values therein. This allows the receiving side to detect if a message was sent as a different, incompatible type.

  •   As long as the sender serializes the arguments as the type that the receiving side expects, deserialization will succeed.

Service upgrades

Services evolve over time; they gain new methods, existing methods return more data, or expect additional arguments. Usually, developers want to do that without breaking existing clients.

Candid supports such evolution by defining precise rules that indicate when the new service type will still be able to communicate with all other parties that are using the previous interface description. The underlying formalism is that of subtyping.

Services can safely evolve in the following ways:

  •   New methods can be added.

  •   Existing methods can return additional values; that is, the sequence of result types can be extended. Old clients will simply ignore additional values.

  •   Existing methods can shorten their parameter list. Old clients may still send the extra arguments, but they will be ignored.

  •   Existing methods can extend their parameter list with optional arguments (type opt …). When reading messages from old clients, who do not pass that argument, a null value is assumed.

  •   Existing parameter types may be changed, but only to a supertype of the previous type.

  •   Existing result types may be changed, but only to a subtype of the previous type.

For information about the supertypes and subtypes of a given type, see the corresponding reference section for that type.

Let’s look at a concrete example of how a service might evolve. Consider a service with the following API:

service counter : {
  add : (nat) -> ();
  subtract : (nat) -> ();
  get : () -> (int) query;
  subscribe : (func (int) -> ()) -> ();
}

This service can evolve to the following interface:

type timestamp = nat;
service counter : {
  set : (nat) -> ();
  add : (int) -> (new_val : nat);
  subtract : (nat, trap_on_underflow : opt bool) -> (new_val : nat);
  get : () -> (nat, last_change : timestamp) query;
  subscribe : (func (nat) -> (unregister : opt bool)) -> ();
}

Candid textual values

The main purpose of Candid is to connect programs written in some host language—Motoko, Rust, or JavaScript, for example—with ICP. In most cases, therefore, you do not have to deal with your program data as Candid values. Instead, you work with a host language like JavaScript using familiar JavaScript values and then rely on Candid to transparently transport those values to a canister written in Rust or Motoko. The canister receiving the values treats them as native Rust or Motoko values.

However, there are some cases, for example, when logging, debugging, or interacting with a service on the command line, where it is useful to see the Candid values directly in a human-readable form. In these scenarios, you can use the textual presentation for Candid values.

The syntax is similar to that for Candid types. For example, a typical textual presentation for a Candid value might look like this:

(record {
  first_name = "John";
  last_name = "Doe";
  age = 41;
  membership_status = variant { active };
  email_addresses =
    vec { "john@doe.com"; "john.doe@example.com" };
})

The Candid binary format does not include the actual field names, merely numeric hashes. So pretty-printing such a value without knowledge of the expected type will not include the field names of records and variants. The above value might then be printed as follows:

(record {
   4846783 = 14;
   456245371 = variant {373703110};
   1443915007 = vec {"john@doe.com"; "john.doe@example.com"};
   2797692922 = "John"; 3046132756 = "Doe"
})

Best practices

Use descriptive type names

It is recommended to use Candid records with descriptive names for types:

type Recycler =
 record {
   identity: principal;
   location: record {
               Country__1; // <-- Use descriptive names for types,
               text; // <----- Rather than generic terms.
             };
   name: text;
   nick: text;
 };

Deprecating fields

To deprecate a Candid field, you can update the field's type to opt empty, indicating the field is not used:

record {
  first_name : text; middle_name : opt empty; second_name : text; score : nat; country : text
}

However, it is best practice to mark a field as reserved to prevent future developers from using the field in an unexpected manner:

record {
  first_name : text; middle_name : reserved; second_name : text; score : nat; country : text
}

For more information, refer to the blog post on Candid for engineers.

Interact with a service

Command line

To interact with a service, use dfx canister call.

To pass Candid arguments to the dfx canister call command, please refer to the Candid reference, or, for Candid arguments too long to fit the command line, please use the --argument-file flag.

You can also omit the arguments and let the IC SDK generate a random value that matches the method type.

Browser

The Candid interface description language provides a common language for specifying the signature of a canister. Based on the type signature of the service offered by the smart contract, Candid provides a web interface (the Candid UI) that allows you to call canister functions for testing and debugging from a web browser without writing any frontend code.

When a canister is deployed, it will return a Candid UI URL as part of the output, indicated as the "backend canister URL." The full URL should look similar to the following, but with the canister_id reflecting your project's canister ID.

http://127.0.0.1:4943/?canisterId=<canister_id> // Local URL
https://a4gq6-oaaaa-aaaab-qaa4q-cai.icp0.io/?id=ts425-saaaa-aaaab-qbksq-cai // Playground or Mainnet URL

For more information about the tool that creates a web interface from the Candid interface of any canister, see the Candid UI repository.

At this time, it is not possible to disable the Candid UI from being installed and deployed for your canister.

Interact with a service from a Motoko canister

If you are writing a canister in Motoko, the Motoko compiler automatically translates the signature of your canister’s top-level actor or actor class into a Candid description, and the dfx build command ensures that the service description is properly referenced where it needs to be. It ensures that the canister identifier and the Candid description are passed to the Motoko compiler correctly. The Motoko compiler then translates the Candid type into the appropriate native Motoko type. This translation enables you to call the canister's method natively. For additional information on the type mapping between Candid and Motoko, you can consult the supported types reference section.

Motoko canisters auto-generate a Candid description file, located in your project build directory at .dfx/local/canisters/hello/hello.did.

Interact with a service from a Rust canister

If you write a canister in Rust, the dfx build command ensures that the service description is properly referenced where it needs to be. However, you need to write the Candid service description manually, following the conventions described in the Candid specification.

The Rust CDK then translates the Candid type into the appropriate native Rust type. This translation enables you to call the canister's method natively. For additional information on the type mapping between Candid and Rust, you can consult the supported types reference section.

There is also an experimental feature to generate a Candid service description automatically; see this test case as an example.

For additional information and libraries to help you create Candid services or canisters in Rust, see the documentation for the Candid crate, Rust CDK examples and the Rust tutorials.

Interact with a service from JavaScript

The dfinity/agent npm package includes support for importing canisters using Candid.

When the import dependency of a canister is processed by the dfx build command and the webpack configuration, this processing ensures that the canister identifier and the Candid description are passed to the JavaScript program correctly. Behind the scenes, the Candid service description is translated into a JavaScript module, located at .dfx/local/canister/counter/counter.did.js by dfx build. The dfinity/agent package then translates the Candid type into native JavaScript values and enables you to call the canister's method natively. For additional information on the type mapping between Candid and JavaScript, you can consult the supported types reference section.

Publish Candid interfaces privately

By default, dfx makes the candid:service metadata information public to promote interoperability and make it convenient to use the Candid UI. The Candid UI reads this metadata to determine the interface of your canister. It doesn’t publish any more information than what is already public. This metadata can be returned through the dfx canister metadata command:

dfx canister metadata <canister_id> candid:service

However, you can make this metadata information private so that it is only readable by the controllers of your canister. To do this, add the following configuration information to your canister's definition in the project's dfx.json file:

dfx.json
      "metadata": [
        {
          "name": "candid:service",
          "visibility": "private"
        }
      ]

Then, redeploy your canister and confirm that the interface is no longer public.

Create a new Candid implementation

There are community-supported Candid libraries for the following host languages:

If you want to create a Candid implementation to support a language or tool for which an implementation is not currently available, you should consult the Candid specification.

If you add a Candid implementation for a new language or tool, you can use the official Candid test data to test and verify that your implementation is compatible with Candid, even in slightly more obscure corner cases.