Passing arguments in a terminal

This tutorial provides a simple variation on the default program that lets you pass a single string argument to a single actor, compile the code to create a canister, then retrieve the argument.

This tutorial illustrates how to pass arguments on the command-line in a terminal using the Candid interface description language (IDL) and how to modify the program to allow it to accept more than one value for the string argument.

Before you begin

Before you start this tutorial, verify the following:

  • You have downloaded and installed the DFINITY Canister SDK package as described in Download and install.

  • You have stopped any Internet Computer network processes running on the local computer.

This tutorial takes approximately 20 minutes to complete.

Create a new project

To create a new project for this tutorial:

  1. Open a terminal shell on your local computer, if you don’t already have one open.

  2. Change to the folder you are using for your Internet Computer sample projects.

  3. Create a new project by running the following command:

    dfx new location_hello
  4. Change to your project directory by running the following command:

    cd location_hello

Modify the default configuration

In the Exploring the default project tutorial, you saw that creating a new project adds a default dfx.json configuration file to your project directory. You should always review the default settings in the file to verify the information accurately reflects the project settings you want to use. For this tutorial, you’ll modify the default configuration to remove settings that aren’t used.

To modify settings in the dfx.json configuration file:

  1. Open the dfx.json configuration file in a text editor.

  2. Check the default settings for the location_hello project.

  3. Remove all of the unnecessary configuration settings.

    Because this tutorial does not involve creating any front-end assets, you can remove all of the location_hello_assets configuration settings from the file.

  4. Save your changes and close the file to continue.

Modify the default program

In the Exploring the default project tutorial, you saw that creating a new project creates a default src directory with a template main.mo file.

To modify the default template source code:

  1. Open the src/location_hello/main.mo source code file in a text editor.

  2. Modify the default source code to replace the greet function with a location function and the name argument with a city argument.

    For example:

    actor {
        public func location(city : Text) : async Text {
            return "Hello, " # city # "!";
        };
    };
  3. Save your changes and close the file to continue.

Start the local network

Before you can build your project, you need to connect to the Internet Computer network either running locally in your development environment or running remotely on a sub-network that you can access.

As a best practice, this step requires you to have two terminal shells open, so that you can start and see network operations in one terminal and manage your project in another.

To start the local network:

  1. Open a new terminal window or tab on your local computer and navigate to your project directory.

    For example, you can do either of the following if running Terminal on macOS:

    • Click Shell, then select New Tab to open a new terminal in your current working directory.

    • Click Shell and select New Window, then run cd ~/ic-projects/location_hello in the new terminal if your location_hello project is in the ic-projects working folder.

    You should now have two terminals open with your project directory as your current working directory.

  2. Start the Internet Computer network on your local computer by running the following command:

    dfx start

    Depending on your platform and local security settings, you might see a warning displayed. If you are prompted to allow or deny incoming network connections, click Allow.

  3. Leave the terminal that displays network operations open and switch your focus to your original terminal where you created your project.

Register canister identifiers

After you connect to the Internet Computer network running locally in your development environment, you can register with the network to generate unique, network-specific canister identifiers for your project.

To register canister identifiers for the local network:

  1. Check that you are still in your project directory, if needed.

  2. Register unique canister identifiers for the canisters in the project by running the following command:

    dfx canister create --all

    The command displays the network-specific canister identifier for the canister defined in the dfx.json configuration file.

    "location_hello" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"

Build and deploy the program

You now have a simple program that you can compile into an executable WebAssembly module, then deploy as a canister on your local Internet Computer network.

To build and deploy the program executable:

  1. Build the WebAssembly executable by running the following command:

    dfx build
  2. Deploy your location_hello project on the local network by running the following command:

    dfx canister install location_hello

    The command displays output similar to the following:

    Installing code for canister location_hello, with canister_id 75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q

Pass a string argument

You now have a program deployed as a canister on your local Internet Computer network and can test your program by using dfx canister call commands.

To test the program you have deployed locally:

  1. Call the location method in the program and pass your city argument of type string by running the following command:

    dfx canister call location_hello location "San Francisco"

    Because the argument in this case includes a space between San and Francisco, you need to enclose the argument in quotes. The command displays output similar to the following:

    ("Hello, San Francisco!")

    If the argument did not contain a space that required enclosing the text inside of quotation marks, you could allow the Candid interface description language to infer the data type like this:

    dfx canister call location_hello location Paris
  2. Call the location method in the program and pass your city argument explicitly using the Candid interface description language syntax for Text arguments:

    dfx canister call location_hello location '("San Francisco and Paris")'

    The command displays output similar to the following:

    ("Hello, San Francisco and Paris!")

    Because your program only accepts a single string argument, specifying multiple strings returns only the first argument or a mismatch error.

    For example, if you try this command:

    dfx canister call location_hello location '("San Francisco","Paris","Rome")'

    Only the first argument—("Hello, San Francisco!")—is returned or you’ll see an error similar to the following:

    Invalid data: Unable to serialize Candid values: length mismatch for types and values

Revise the source code in your program

To extend what you have learned in this tutorial, you might want to try modifying the source code to return different results. For example, you might want to modify the location function to return multiple city names.

To experiment with modifying the source code for this tutorial:

  1. Open the dfx.json configuration file in a text editor and change the default location_hello settings to favorite_cities.

  2. Save your changes and close the dfx.json file to continue.

  3. Copy the location_hello source file directory to match the name specified in the dfx.json configuration file by running the following command:

    cp -r src/location_hello src/favorite_cities
  4. Open the src/favorite_cities/main.mo file in a text editor.

  5. Copy and paste the following code sample to replace the location function with two new functions.

    For example:

    actor {
        public func location(cities : [Text]) : async Text {
            return "Hello, from " # (debug_show cities) # "!";
        };
        public func location_pretty(cities : [Text]) : async Text {
            var str = "Hello from ";
            for (city in cities.vals()) {
                    str := str # city #", ";
            };
            return str # "bon voyage!";
        }
    };

    You might notice that Text in this code example is enclosed by square ([ ]) brackets. By itself, Text represents a collection of UTF-8 characters. The square brackets around a type indicate that it is an array of that type. In this context, therefore, [Text] indicates an array of a collection of UTF-8 characters, enabling the program to accept and return multiple text strings.

    The code sample also uses the basic format of an apply operation for the array, which can be abstracted as:

    public func apply<A, B>(fs : [A -> B], xs : [A]) : [B] {
        var ys : [B] = [];
        for (f in fs.vals()) {
          ys := append<B>(ys, map<A, B>(f, xs));
        };
        ys;
    };

    For information about the functions that perform operations on arrays, see the description of the Array module in the Motoko base library or the Motoko Programming Language Reference.

  6. Register a network-specific identifier for the new program by running the following command:

    dfx canister create favorite_cities

    The command returns output similar to the following:

    "favorite_cities" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
  7. Build the program by running the following command:

    dfx build

    For this tutorial, you have already removed the front-end assets canister, so running dfx build only compiles the favorite_cities canister.

  8. Deploy your favorite_cities program on the local network by running the following command:

    dfx canister install favorite_cities
  9. Call the location method in the program and pass your city argument using the Candid interface description syntax by running the following command:

    dfx canister call favorite_cities location '(vec {"San Francisco";"Paris";"Rome"})'

    The command uses the Candid interface description syntax (vec { val1; val2; val3; }) to return a vector of values. For more information about the Candid interface description syntax, see Interface description syntax for arguments.

    This command displays output similar to the following:

    ("Hello, from ["San Francisco", "Paris", "Rome"]!")
  10. Call the location_pretty method in the program and pass your city argument using the interface description syntax by running the following command:

    dfx canister call favorite_cities location_pretty '(vec {"San Francisco";"Paris";"Rome"})'

    The command displays output similar to the following:

    ("Hello from San Francisco, Paris, Rome, bon voyage!")

Test functions in a browser

The canister interface description language—often referred to as Candid or more generally as the IDL—provides a common language for specifying the signature of a canister service. The language provides a unified way of interacting with canisters in various languages and tools, such as the dfx command-line interface, JavaScript, and Motoko.

Based on the type signature of the actor, Candid provides a web interface that allows you to call canister functions for testing and debugging.

To use the Candid web interface to test canister functions:

  1. Deploy your project using the dfx canister install command and copy the canister identifier associated with the main actor for your application.

  2. Open a browser and navigate to the address and port number specified in the dfx.json configuration file.

    By default, the local network binds to the 127.0.0.1:8000 address and port number.

  3. Add the candid endpoint to access the Candid web interface followed by the required canisterId parameter and canister identifier.

    For example, the full URL should look similar to the following but with the canister_identifier that was returned by the dfx canister install command:

    http://127.0.0.1:8000/candid?canisterId=<YOUR-CANISTER-IDENTIFIER>
  4. Review the list of function calls and types defined in the program.

  5. Type a value of the appropriate type for a function or click Lucky to generate a value, then click Call or Query to see the result.

    Note that depending on the data type, the Candid interface might display additional configuration settings for testing functions. For example, if a function takes an array, you might need to specify the number of items in the array before entering values.

    In this example, each function accepts an array of text strings. Therefore, you first select the length of the array, then set values for each item before clicking Call.

    Specifying an array

Stop the local network

After you finish experimenting with your program, you can stop the local Internet Computer network so that it doesn’t continue running in the background.

To stop the local network:

  1. In the terminal that displays network operations, press Control-C to interrupt the local network process.

  2. Stop the Internet Computer network by running the following command:

    dfx stop