Import library modules

In this tutorial, you are going to write a simple program that enables you to store and look up telephone numbers. This tutorial illustrates how to import and use a few basic Motoko library functions.

For this tutorial, the Motoko base library functions are defined in the List and AssocList modules and enable you to work with lists as linked key-value pairs. In this example, the key is a name and the value is the phone text string associated with that name.

This program supports the following function calls:

  • The insert function accepts the name and phone key-value pair as input stored in the book variable.

  • The lookup function is a query that uses the specified name key as input to find the associated phone number.

Before you begin

Before starting the 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 10 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 projects, if you are using one.

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

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

    cd phonebook

Modify the default program

For this tutorial, let’s create a new main.mo file for the simple phone number lookup program.

To modify the default template:

  1. Open the src/phonebook/main.mo file in a text editor and delete the existing content.

  2. Copy and paste the following sample code into the main.mo file:

    // Import standard library functions for lists
    
    import L "mo:base/List";
    import A "mo:base/AssocList";
    
    // The PhoneBook actor.
    actor {
    
        // Type aliases make the rest of the code easier to read.
        public type Name = Text;
        public type Phone = Text;
    
        // The actor maps names to phone numbers.
        flexible var book: A.AssocList<Name, Phone> = L.nil<(Name, Phone)>();
    
        // An auxiliary function checks whether two names are equal.
        func nameEq(l: Name, r: Name): Bool {
            return l == r;
        };
    
        // A shared invokable function that inserts a new entry
        // into the phone book or replaces the previous one.
        public func insert(name: Name, phone: Phone): async () {
            let (newBook, _) = A.replace<Name, Phone>(book, name, nameEq, ?phone);
            book := newBook;
        };
    
        // A shared read-only query function that returns the (optional)
        // phone number corresponding to the person with the given name.
        public query func lookup(name: Name): async ?Phone {
            return A.find<Name, Phone>(book, name, nameEq);
        };
    };

    In looking at this sample program, you might notice the following key elements:

    • The code defines Name and Phone as custom Text types. Creating user-defined types improves the readability of the code.

    • The insert function is an update call and the lookup function is a query call.

    • The Phone type is identified as an optional value by using the ?Phone syntax.

Start the local network

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

Starting the network locally requires a dfx.json file, so you should be sure you are in your project’s root directory. For this tutorial, you should have two separate terminal shells, so that you can start and see network operations in one terminal and manage your project in another.

To start the network locally:

  1. Open a new terminal window or tab on your local computer.

  2. Navigate to the root directory for your project, if necessary.

    • You should now have two terminals open.

    • You should have the project directory as your current working directory.

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

    dfx start --clean

    For this tutorial, we’re using the --clean option to start the Internet Computer network in a clean state.

    This option removes any orphan background processes or canister identifiers that might disrupt normal operations. For example, if you forgot to issue a dfx stop when moving between projects, you might have a process running in the background or in another terminal. The --clean option ensures the you can start the Internet Computer network and continue to the next step without manually finding and terminating any running processes.

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

Register, build, and deploy the application

After you connect to the Internet Computer network running locally in your development environment, you can register, build, and deploy your application locally.

To deploy the application locally:

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

  2. Register, build, and deploy your application by running the following command:

    dfx deploy phonebook

    The dfx.json file provides default settings for creating an application front-end entry point and assets canister.

    In previous tutorials, we deleted the entries for the asset canister because we weren’t adding a front-end for the sample application. That change kept our project workspace tidy by eliminating files that would go unused. There’s no requirement to do this, however, and there’s no harm in leaving the asset canister description in the dfx.json file. For example, you might want to use it as a placeholder if you intend to add front-end assets later.

    For this tutorial, you can deploy just the phonebook back-end canister using the dfx deploy phonebook command because the project is a terminal-based application that doesn’t include any front-end assets.

    Although this tutorial illustrates how to skip compiling a front-end canister, you can add a simple user interface to this application later by exploring the phone-book project in the examples repository.

Add names and numbers using the insert function

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

To test the program you have deployed on the local replica network:

  1. Use the dfx canister call command to call the canister phonebook using the insert function and pass it a name and phone number by running the following command:

    dfx canister call phonebook insert '("Chris Lynn", "01 415 792 1333")'
  2. Add a second name and number pair by running the following command:

    dfx canister call phonebook insert '("Maya Garcia", "01 408 395 7276")'
  3. Verify that the command returns the number associated with "Chris Lynn" using the lookup function by running the following command:

    dfx canister call phonebook lookup '("Chris Lynn")'

    The command returns output similar to the following:

    (opt "01 415 792 1333")
  4. Try to call the lookup function with the number associated with "Maya Garcia" by running the following command:

    dfx canister call phonebook lookup '("01 408 395 7276")'

    Note that, in this case, the command returns (null) because the phone number is not a key associated with the "Maya Garcia" name entry.

  5. Try to call the lookup function again to return the phone numbers for both "Maya Garcia" and "Chris Lynn" by running the following command:

    dfx canister call phonebook lookup '("Maya Garcia","Chris Lynn")'

    Because the program is written to return one value for one key, the command only returns information associated with the first key, in this example the phone number for Maya Garcia.

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. Candid provides a unified way for you to interact with canisters that are written in different languages or accessed using different tools. For example, Candid provides a consistent view of a service whether the underlying program is native Rust, JavaScript, or Motoko. Candid also enables different tools—such as the dfx command-line interface and the Network Nervous System application—to share a common description for a service.

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

After you have deployed your project locally using the dfx deploy or dfx canister install command, you can access the Candid web interface endpoint in a browser. This web interface—the Candid UI—exposes the service description in a form, enabling you to quickly view and test functions and experiment with entering different data types without writing any front-end code.

To use the Candid web interface to test canister functions:

  1. Find the Candid UI canister identifier associated with the current project using the dfx canister id __Candid_UI command.

    dfx canister id __Candid_UI

    The command displays the canister identifier for the Candid UI with output similar to the following:

    r7inp-6aaaa-aaaaa-aaabq-cai
  2. Copy the Candid UI canister identifier so that it is available in the clipboard.

  3. If you’ve stopped the Internet Computer, restart it locally by running the following command:

    dfx start --background
  4. 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.

  5. Add the required canisterId parameter and the Candid UI canister identifier returned by the dfx canister id command to the URL.

    For example, the full URL should look similar to the following but with the CANDID-UI-CANISTER-IDENTIFIER that was returned by the dfx canister id command:

    http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>

    The browser displays a form for you to specify a canister identifier or choose a Candid description (.did) file.

    If you aren’t sure which canister identifier to use, you can run the dfx canister id command to look up the identifier for a specific canister name.

  6. Specify the canister identifier or description file for your application into the Provide a canister ID field, then click Go to display the service description.

  7. Review the list of function calls and types defined in the program.

  8. Type a value of the appropriate type for a function or click Random 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.

    Phonebook functions

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 change the source code so that instead of a program that inserts and looks up a current key-value (name-phone) pair to create a program that stores contact information similar to a database "record" in which a primary key is associated with multiple fields. In this example, your program might enable users or another program to add information—such as a home phone number, a cell phone number, an email address, and a street address—and selectively return all or specific field values.

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