4. Candid Interface Definition
Candid is an interface description language that is used to describe the public interface of a service. A service is usually in the form of an application deployed as a canister on ICP. Public interfaces of canisters are used to interact with the canister. Candid supports interactions through the IC SDK via the terminal, through a web browser, or through the use of agents. It also provides a way to specify input argument values and display return values from different canister methods, regardless of the manner used to interact with the canister. Recall that a method is a piece of code specifying a task which declares a sequence of arguments and their associated result types.
Candid is language-agnostic, which is a key benefit since it allows for the inter-operation between canisters written in different languages, such as Motoko, Rust, and JavasScript, and client applications.
Candid is unique since it provides functionality and features not available in other technologies, such as JSON or XML. Some of these features include the evolution of service interfaces, direct value mapping for Candid values to the types and values of the canister's language, and the ability to pass complex data such as references to services and methods. Candid also has built-in support for ICP-specific features like query annotation.
Up until now in this developer journey, you've utilized the Candid UI a few times through the web browser. If you recall, in several tutorials you've interacted with the backend canisters in the web browser with the URL from an output such as:
Backend canister via Candid interface:
dependencies_backend: http://127.0.0.1:4943/?canisterId=bd3sg-teaaa-aaaaa-qaaba-cai&id=bkyz2-fmaaa-aaaaa-qaaaq-cai
These URLs take you to the Candid user interface (UI). Now it's time to take a closer look at the fundamentals of Candid to understand how it works and how it can be used for more complex dapp development.
Candid types and values
Candid supports the following set of types in order to provide a natural mapping of data types based on reasonable choices suited for each canister language:
-
Unbounded integral number types (
Nat
,Int
). -
Bounded integral numbers (
Nat8
,Nat16
,Nat32
,Nat64
,Int8
,Int16
,Int32
,Int64
). -
Floating point types (
Float32
,Float64
). -
Boolean type (
Bool
). -
Textual data (
Text
). -
Binary data (
Blob
). -
Container types and variants (
Opt
,Vec
,Record
,Variant
). -
Reference types (
Service
,Func
,Principal
). -
Special types
Null
,Reserved
andEmpty
.
Each of these types is described in detail in the Candid reference documentation.
Candid textual values
Typically, as a developer you may not work with your program data as Candid values; instead, you may work with a host language such as Javascript and utilize Candid to transparently transport your dapp's data to your canister written in Javascript, Motoko or Rust. The receiving canister treats this data as native Motoko or Rust values.
In some scenarios, like debugging, logging, or interacting with a service via the terminal command-line, it may be useful to see the Candid values in a human-readable format. In these situations, the textual presentation of Candid values can be used.
The syntax for the textual presentation of Candid values is similar to that of Candid types. An example of a textual presentation for a Candid value is:
(record {
first_name = "John";
last_name = "Doe";
age = 14;
membership_status = variant { active };
email_addresses =
vec { "john@doe.com"; "john.doe@example.com" };
})
In contrast, the Candid binary format uses numeric hashes in place of field names. As a result, printing a value without the knowledge of what type that value is will not include the field name of records or variants. The above example would be printed as follows:
(record {
4846783 = 14;
456245371 = variant {373703110};
1443915007 = vec {"john@doe.com"; "john.doe@example.com"};
2797692922 = "John"; 3046132756 = "Doe"
})
Candid service descriptions
Now that you've learned about Candid types, you can learn how to use them to describe a service. Candid uses a service description file with the file extension .did
. Let's take a closer look at the structure of a service description.
The .did
file
To define Candid types and describe a service, a Candid service description file can be used. This file has the extension .did
, and can be generated from a service implementation or written manually.
The Candid files you have used thus far in the developer journey have been generated automatically. This is because a Azle canister's Candid files are automatically generated when the canister is compiled. When Candid files are auto-generated, it is not recommended to edit them manually, and any changes made to the file will be overwritten by dfx
the next time the project is built and deployed.
Example Candid service descriptions
The simplest service description defines a service with no public methods. This would look like:
service : {}
Since this service description does not have any pubic methods, it is not very useful. To add a public method, you'll add a simple ping
method:
service : {
ping : () -> ();
}
Now the service description supports a single public method named ping
. Note that method names can be arbitrary strings that can be quoted, such as "this is a method".
In this ping
method, there are no arguments passed to the method and there are no results returned, so the empty sequence of ()
is used for both the arguments and the results.
Service upgrades
As your application changes and evolves over time, the methods will most likely change as well. New methods may be introduced, existing methods may expect additional arguments, or new data may be returned. Ideally, developers want to make these changes without breaking any existing workflows or clients.
Candid supports service upgrades through a set of specific rules that indicate when a new service type will be able to communicate with any running clients that are using the previous interface description. Services can be upgraded in the following ways:
-
New methods can be added.
-
Existing methods can return additional values; clients using the previous interface description will ignore these additional values.
-
Existing methods can shorten their parameter list; clients using the previous interface description may still send extra arguments, but they will be ignored.
-
Existing methods can extend their parameter list with optional arguments; clients using the previous interface description who do not pass that argument will use a
null
value. -
Existing parameter types may be changed, but only as a supertype of the previously used type.
-
Existing result types may be changed, but only as a subtype of the previously used type.
For additional information about the supertypes and subtypes of any given type, check out the Candid interface reference documentation for each type.
To demonstrate an example of how a service might be upgraded, let's use the counter
service example that you looked at in generating service descriptions.
Let's start with the following Candid service description:
service counter : {
add : (nat) -> ();
subtract : (nat) -> ();
get : () -> (int) query;
}
This service might be upgraded in the following manner:
type timestamp = nat; // Define a new type of 'timestamp'
service counter : {
set : (nat) -> (); // Define a new method
add : (int) -> (new_val : nat); // Change the parameter result types
subtract : (nat, trap_on_underflow : opt bool) -> (new_val : nat); // Change the parameter result types
get : () -> (nat, last_change : timestamp) query; // Change the result types
}
Need help?
Did you get stuck somewhere in this tutorial, or feel like you need additional help understanding some of the concepts? The ICP community has several resources available for developers, like working groups and bootcamps, along with our Discord community, forum, and events such as hackathons. Here are a few to check out:
-
Developer Discord community, which is a large chatroom for ICP developers to ask questions, get help, or chat with other developers asynchronously via text chat.
-
Weekly developer office hours to ask questions, get clarification, and chat with other developers live via voice chat. This is hosted on our developer Discord group.
-
Submit your feedback to the ICP Developer feedback board.
Next steps
Next, let's explore how to test and debug projects and canisters.