Hello World

Let's build your first application (canister) with Kybra!

Before embarking please ensure you've followed all of the installation instructions.

We'll build a simple Hello World canister that shows the basics of importing Kybra, exposing a query method, exposing an update method, and storing some state in a global variable. We'll then interact with it from the command line and from our web browser.

The project directory and file structure

Assuming you're starting completely from scratch, run these commands to setup your project's directory and file structure:

mkdir kybra_hello_world
cd kybra_hello_world

mkdir src

touch src/main.py
touch dfx.json

Now create and source a virtual environment:

~/.pyenv/versions/3.10.7/bin/python -m venv venv
source venv/bin/activate

Now install Kybra:

pip install kybra

Open up kybra_hello_world in your text editor (we recommend VS Code with the Microsoft Python extension).

main.py

Here's the main code of the project, which you should put in the kybra_hello_world/src/main.py file of your canister:

from kybra import query, update, void

# This is a global variable that is stored on the heap
message: str = ''

# Query calls complete quickly because they do not go through consensus
@query
def get_message() -> str:
    return message

# Update calls take a few seconds to complete
# This is because they persist state changes and go through consensus
@update
def set_message(new_message: str) -> void:
    global message
    message = new_message # This change will be persisted

Let's discuss each section of the code.

from kybra import query, update, void

The code starts off by importing the query and update decorators from kybra, along with the void type. The kybra module provides most of the Internet Computer (IC) APIs for your canister.

# This is a global variable that is stored on the heap
message: str = ''

We have created a global variable to store the state of our application. This variable is in scope to all of the functions defined in this module. We have annotated it with a type and set it equal to an empty string.

# Query calls complete quickly because they do not go through consensus
@query
def get_message() -> str:
    return message

We are exposing a canister query method here. When query methods are called they execute quickly because they do not have to go through consensus. This method simply returns our global message variable.

# Update calls take a few seconds to complete
# This is because they persist state changes and go through consensus
@update
def set_message(new_message: str) -> void:
    global message
    message = new_message # This change will be persisted

We are exposing an update method here. When update methods are called they take a few seconds to complete. This is because they persist changes and go through consensus. A majority of nodes in a subnet must agree on all state changes introduced in calls to update methods. This method accepts a string from the caller and will store it in our global message variable.

That's it! We've created a very simple getter/setter Hello World application. But no Hello World project is complete without actually yelling Hello world!

To do that, we'll need to setup the rest of our project.

dfx.json

Create the following in kybra_hello_world/dfx.json:

{
    "canisters": {
        "kybra_hello_world": {
            "type": "custom",
            "build": "python -m kybra kybra_hello_world src/main.py src/main.did",
            "post_install": ".kybra/kybra_hello_world/post_install.sh",
            "candid": "src/main.did",
            "wasm": ".kybra/kybra_hello_world/kybra_hello_world.wasm",
            "gzip": true
        }
    }
}

Local deployment

Let's deploy to our local replica.

First startup the replica:

dfx start --background

If you want an extra speedy deploy:

dfx start --background --artificial-delay 0

Then deploy the canister:

dfx deploy

If you are asked for a password, you'll need to create a new unencrypted dfx identity:

dfx identity new test_unencrypted --storage-mode plaintext
dfx identity use test_unencrypted

dfx deploy

This is not ideal for security, but Kybra currently does not support encrypted dfx identities.

Interacting with your canister from the command line

Once we've deployed we can ask for our message:

dfx canister call kybra_hello_world get_message

We should see ("") representing an empty message.

Now let's yell Hello World!:

dfx canister call kybra_hello_world set_message '("Hello World!")'

Retrieve the message:

dfx canister call kybra_hello_world get_message

We should see ("Hello World!").

Interacting with your canister from the web UI

After deploying your canister, you should see output similar to the following in your terminal:

Deployed canisters.
URLs:
  Backend canister via Candid interface:
    kybra_hello_world: http://127.0.0.1:8000/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai

Open up http://127.0.0.1:8000/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai or the equivalent URL from your terminal to access the web UI and interact with your canister.