Candid

Candid is an interface description language created by DFINITY. It can be used to define interfaces between services (canisters), allowing canisters and clients written in various languages to easily interact with each other. This interaction occurs through the serialization/encoding and deserialization/decoding of runtime values to and from Candid bytes.

Azle performs automatic encoding and decoding of JavaScript values to and from Candid bytes through the use of various CandidType objects. For example, CandidType objects are used when defining the parameter and return types of your query and update methods. They are also used to define the keys and values of a StableBTreeMap.

It's important to note that the CandidType objects decode Candid bytes into specific JavaScript runtime data structures that may differ in behavior from the description of the actual Candid type. For example, a float32 Candid type is a JavaScript Number, a nat64 is a JavaScript BigInt, and an int is also a JavaScript BigInt.

Keep this in mind as it may result in unexpected behavior. Each CandidType object and its equivalent JavaScript runtime value is explained in more detail in The Azle Book Candid reference.

A more canonical reference of all Candid types available on the Internet Computer (IC) can be found here.

The following is a simple example showing how to import and use many of the CandidType objects available in Azle:

import {
    blob,
    bool,
    Canister,
    float32,
    float64,
    Func,
    int,
    int16,
    int32,
    int64,
    int8,
    nat,
    nat16,
    nat32,
    nat64,
    nat8,
    None,
    Null,
    Opt,
    Principal,
    query,
    Record,
    Recursive,
    text,
    update,
    Variant,
    Vec
} from 'azle';

const MyCanister = Canister({
    query: query([], bool),
    update: update([], text)
});

const Candid = Record({
    text: text,
    blob: blob,
    nat: nat,
    nat64: nat64,
    nat32: nat32,
    nat16: nat16,
    nat8: nat8,
    int: int,
    int64: int64,
    int32: int32,
    int16: int16,
    int8: int8,
    float64: float64,
    float32: float32,
    bool: bool,
    null: Null,
    vec: Vec(text),
    opt: Opt(nat),
    record: Record({
        firstName: text,
        lastName: text,
        age: nat8
    }),
    variant: Variant({
        Tag1: Null,
        Tag2: Null,
        Tag3: int
    }),
    func: Recursive(() => Func([], Candid, 'query')),
    canister: Canister({
        query: query([], bool),
        update: update([], text)
    }),
    principal: Principal
});

export default Canister({
    candidTypes: query([], Candid, () => {
        return {
            text: 'text',
            blob: Uint8Array.from([]),
            nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
            nat64: 18_446_744_073_709_551_615n,
            nat32: 4_294_967_295,
            nat16: 65_535,
            nat8: 255,
            int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
            int64: 9_223_372_036_854_775_807n,
            int32: 2_147_483_647,
            int16: 32_767,
            int8: 127,
            float64: Math.E,
            float32: Math.PI,
            bool: true,
            null: null,
            vec: ['has one element'],
            opt: None,
            record: {
                firstName: 'John',
                lastName: 'Doe',
                age: 35
            },
            variant: {
                Tag1: null
            },
            func: [
                Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
                'candidTypes'
            ],
            canister: MyCanister(Principal.fromText('aaaaa-aa')),
            principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
        };
    })
});

Calling candidTypes with dfx will return:

(
  record {
    func = func "rrkah-fqaaa-aaaaa-aaaaq-cai".candidTypes;
    text = "text";
    nat16 = 65_535 : nat16;
    nat32 = 4_294_967_295 : nat32;
    nat64 = 18_446_744_073_709_551_615 : nat64;
    record = record { age = 35 : nat8; lastName = "Doe"; firstName = "John" };
    int = 170_141_183_460_469_231_731_687_303_715_884_105_727 : int;
    nat = 340_282_366_920_938_463_463_374_607_431_768_211_455 : nat;
    opt = null;
    vec = vec { "has one element" };
    variant = variant { Tag1 };
    nat8 = 255 : nat8;
    canister = service "aaaaa-aa";
    int16 = 32_767 : int16;
    int32 = 2_147_483_647 : int32;
    int64 = 9_223_372_036_854_775_807 : int64;
    null = null : null;
    blob = vec {};
    bool = true;
    principal = principal "ryjl3-tyaaa-aaaaa-aaaba-cai";
    int8 = 127 : int8;
    float32 = 3.1415927 : float32;
    float64 = 2.718281828459045 : float64;
  },
)