5: Writing and deploying canisters
Overview
This guide covers the basics of writing and deploying Motoko canisters. For detailed information about Motoko's fundamentals, check out the Motoko fundamentals page.
Prerequisites
Before getting started, assure you have set up your developer environment according to the instructions in the developer environment guide.
Create a new project
Open a terminal window on your local computer, if you don’t already have one open.
- dfx v0.17.0 or newer
- dfx v0.16.1 or older
Use dfx new <project_name>
to create a new project:
dfx new explore_hello
You will be prompted to select the language that your backend canister will use. Select 'Motoko':
? Select a backend language: ›
❯ Motoko
Rust
TypeScript (Azle)
Python (Kybra)
Then, select a frontend framework for your frontend canister. Select 'Vanilla JS':
? Select a frontend framework: ›
SvelteKit
React
Vue
❯ Vanilla JS
No JS template
No frontend canister
Lastly, you can include extra features to be added to your project:
? Add extra features (space to select, enter to confirm) ›
⬚ Internet Identity
⬚ Bitcoin (Regtest)
⬚ Frontend tests
dfx new explore_hello
For projects created with dfx new
(Motoko and Rust) the command automatically generates the project's default configuration and two default smart contracts.
Then, navigate into the project with the command:
cd explore_hello
Review the default configuration
By default, creating a new project adds some template files to your project directory. You can edit these template files to customize the configuration settings for your project and to include your own code to speed up the development cycle.
Open the dfx.json
configuration file in a text editor to review the default settings.
It may look like this:
{
"canisters": {
"explore_hello_backend": {
"main": "src/explore_hello_backend/main.mo",
"type": "motoko"
},
"explore_hello_frontend": {
"dependencies": [
"explore_hello_backend"
],
"frontend": {
"entrypoint": "src/explore_hello_frontend/src/index.html"
},
"source": [
"src/explore_hello_frontend/assets",
"dist/explore_hello_frontend/"
],
"type": "assets"
}
},
"defaults": {
"build": {
"args": "",
"packtool": ""
}
},
"output_env_file": ".env",
"version": 1
}
Let’s take a look at a few of the default settings.
The
settings
section specifies the name of the WebAssembly module for yourexplore_hello
project isexplore_hello
.The
canisters.explore_hello
key specifies that the main program to be compiled is located in the path specified by themain
setting, in this case,src/explore_hello/main.mo
and thetype
setting indicates that this is amotoko
program.The
canisters.explore_hello_assets
key specifies configuration details about frontend assets for this project. Let’s skip those for now.The
dfx
setting is used to identify the version of the software used to create the project.The
networks
section specifies information about the networks to which you connect. The default settings bind the local canister execution environment to the local host address127.0.0.1
and port4943
.
If you have access to other Internet Computer network providers, the networks
section can include network aliases and URLs for connecting to those providers.
You can leave the default settings as they are.
Close the dfx.json
file to continue.
Writing canister code
New projects always include a template main.mo
source code file. You can edit this file to include your own code to speed up the development cycle.
Let’s take a look at the sample program in the default main.mo
template file as a starting point for creating simple dapp using the Motoko programming language.
Open the src/explore_hello_backend/main.mo
file in a text editor and review the code in the template:
actor {
public func greet(name : Text) : async Text {
return "Hello, " # name # "!";
};
};
Let’s take a look at a few key elements of this program:
You might notice that this sample code defines an
actor
instead of amain
function, which some programming languages require. For Motoko, themain
function is implicit in the file itself.Although the traditional "Hello, World!" program illustrates how you can print a string using a
print
orprintln
function, that traditional program would not represent a typical use case for Motoko dapps that run on the Internet Computer.Instead of a print function, this sample program defines an
actor
with a publicgreet
function that takes aname
argument with a type ofText
.The program then uses the
async
keyword to indicate that the program returns an asynchronous message consisting of a concatenated text string constructed using"Hello, "
, the#
operator, thename
argument, and"!"
.
We’ll explore code that uses actor
objects and asynchronous message handling more a little later. For now, you can continue to the next section.
Close the main.mo
file to continue.
Start the local canister execution environment
Before you can deploy the default project, you need to connect to either the local canister execution environment, or to the Internet Computer blockchain mainnet.
Starting the local canister execution environment requires a dfx.json
file, so you should be sure you are in your project’s root directory. For this guide, 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 local canister execution environment, first open a new terminal window or a new terminal tab on your local computer.
- You should now have **two terminals** open.
- You should have the **project directory** as your **current working directory**.
Start the local canister execution environment 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.
After you start the local canister execution environment, you have one terminal that displays messages about network operations and another for performing project-related tasks.
Leave the terminal that displays network operations open and switch your focus to the terminal where you created your new project.
Register canister identifiers
After you connect to the local canister execution 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, 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 identifiers for the canisters defined in the dfx.json
configuration file.
Creating canister explore_hello_backend...
explore_hello_backend canister created with canister id: br5f7-7uaaa-aaaaa-qaaca-cai
Creating canister explore_hello_frontend...
explore_hello_frontend canister created with canister id: bw4dl-smaaa-aaaaa-qaacq-cai
Because you are connected to the local canister execution environment, these canister identifiers are only valid locally and are stored for the project in the .dfx/local/canister_ids.json
file.
For example:
{
"explore_hello_backend": {
"local": "br5f7-7uaaa-aaaaa-qaaca-cai"
},
"explore_hello_frontend": {
"local": "bw4dl-smaaa-aaaaa-qaacq-cai"
}
}
Build the dapp
Now that you have explored the default configuration settings and program code and have started the local canister execution environment, let’s compile the default program into an executable WebAssembly module.
In the terminal window on your local computer, navigate to your explore_hello
project directory.
Build the executable canister by running the following command:
dfx build
You should see output similar to the following:
Building canisters...
Building frontend...
WARN: Building canisters before generate for Motoko
Generating type declarations for canister explore_hello_frontend:
src/declarations/explore_hello_frontend/explore_hello_frontend.did.d.ts
src/declarations/explore_hello_frontend/explore_hello_frontend.did.js
src/declarations/explore_hello_frontend/explore_hello_frontend.did
Generating type declarations for canister explore_hello_backend:
src/declarations/explore_hello_backend/explore_hello_backend.did.d.ts
src/declarations/explore_hello_backend/explore_hello_backend.did.js
src/declarations/explore_hello_backend/explore_hello_backend.did
Because you are connected to the local canister execution environment, the dfx build
command adds the canisters
directory under the .dfx/local/
directory for the project.
Verify that the .dfx/local/canisters/explore_hello_backend
directory created by the dfx build
command contains the WebAssembly and related application files by running the following command.
ls -l .dfx/local/canisters/explore_hello_backend/
For example, the command returns output similar to the following:
-rw-rw-rw- 1 pubs staff 47 Jun 14 15:43 constructor.did
-rw-r--r-- 1 pubs staff 47 Jun 14 15:43 explore_hello_backend.did
-rw-r--r-- 1 pubs staff 32 Jun 14 15:43 explore_hello_backend.most
-rw-r--r-- 1 pubs staff 134640 Jun 14 15:43 explore_hello_backend.wasm
-rw-r--r-- 1 pubs staff 2057 Jun 14 15:43 index.js
-rw-rw-rw- 1 pubs staff 2 Jun 14 15:43 init_args.txt
-rw-rw-rw- 1 pubs staff 47 Jun 14 15:43 service.did
-rw-r--r-- 1 pubs staff 175 Jun 14 15:43 service.did.d.ts
-rw-r--r-- 1 pubs staff 174 Jun 14 15:43 service.did.js
The canisters/explore_hello_backend
directory contains the following key files:
The
explore_hello_backend.did
file contains an interface description for your main dapp.The
index.js
file contains a JavaScript representation of the canister interface for the functions in your dapp.The
explore_hello_backend.wasm
file contains the compiled WebAssembly for the assets used in your project.
The canisters/explore_hello_frontend
directory contains similar files to describe the frontend assets associated with your project.
In addition to the files in the canisters/explore_hello_backend
and the canisters/explore_hello_frontend
directories, the dfx build
command creates an idl
directory.
Verify that a new folder has been created, src/declarations
. This folder will include copies of the folders from .dfx/local
, except for the wasm. They do not contain any secrets, and it is recommended to commit these files along with the rest of your source code.
Deploy the project locally
You’ve seen that the dfx build
command creates several artifacts in a canisters
directory for your project. The WebAssembly modules and the canister_manifest.json
file are required for your dapp to be deployed on the Internet Computer network.
In a terminal shell on your local computer, navigate to your explore_hello
project directory.
Deploy your explore_hello
project on the local network by running the following command:
dfx canister install --all
The command displays output similar to the following:
Installing code for canister explore_hello_backend, with canister ID br5f7-7uaaa-aaaaa-qaaca-cai
Installing code for canister explore_hello_frontend, with canister ID bw4dl-smaaa-aaaaa-qaacq-cai
Uploading assets to asset canister...
Fetching properties for all assets in the canister.
Starting batch.
Run the dfx canister call
command and specify the dapp and function to call by running the following command:
dfx canister call explore_hello_backend greet '("everyone": text)'
This command specifies:
explore_hello
as the name of the canister or dapp you want to call.greet
as the specific method or function you want to call.everyone
as the argument to pass to thegreet
function.
Verify the command displays the return value of the greet
function.
For example:
("Hello, everyone!")
View the default frontend
If you have node.js
installed in your development environment, your project includes a simple frontend example that uses a template index.js
JavaScript file for accessing the explore_hello
dapp in a browser.
Open a terminal window on your local computer, if you don’t already have one open, and navigate to your explore_hello
project directory.
Open the src/explore_hello_frontend/src/index.js
file in a text editor and review the code in the template script:
import { explore_hello } from "../../declarations/explore_hello_backend";
document.getElementById("clickMeBtn").addEventListener("click", async () => {
const name = document.getElementById("name").value.toString();
// Interact with explore_hello actor, calling the greet method
const greeting = await explore_hello_backend.greet(name);
document.getElementById("greeting").innerText = greeting;
});
The template index.js
imports an explore_hello
agent from our newly created declarations
directory. The agent is automatically configured to interact with the interface you created in Main.mo
, and makes calls to our canister using an AnonymousIdentity
when the user clicks the greeting
button.
This file works in conjunction with the template index.html
file to display an HTML page with an image asset, input field, and button for the greet
function.
Close the index.js
file to continue.
View the frontend assets created for the project by running following command:
ls -l .dfx/local/canisters/explore_hello_frontend/
The command displays output similar to the following:
-rw-r--r-- 1 pubs staff 6269 Dec 31 1969 assetstorage.did
-rw-r--r-- 1 pubs staff 432762 Jun 14 15:47 assetstorage.wasm.gz
-rw-rw-rw- 1 pubs staff 6269 Dec 31 1969 constructor.did
-rw-r--r-- 1 pubs staff 432762 Jun 14 15:47 explore_hello_frontend.wasm.gz
-rw-r--r-- 1 pubs staff 2064 Jun 14 15:47 index.js
-rw-rw-rw- 1 pubs staff 2 Jun 14 15:47 init_args.txt
-rw-rw-rw- 1 pubs staff 6269 Jun 14 15:47 service.did
-rw-r--r-- 1 pubs staff 6582 Jun 14 15:47 service.did.d.ts
-rw-r--r-- 1 pubs staff 7918 Jun 14 15:47 service.did.js
These files were generated automatically by the dfx build
command using node modules and the template index.js
file.
Then, start a development server with npm start
.
The output should resemble the following:
> explore_hello_frontend@0.2.0 start
> webpack serve --mode development --env development
<i> [webpack-dev-server] [HPM] Proxy created: /api -> http://127.0.0.1:4943
<i> [webpack-dev-server] [HPM] Proxy rewrite rule created: "^/api" ~> "/api"
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8083/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.0.144:8083/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8083/
Open a browser and navigate to the "Loopback" or "On Your Network (IPv4)" URL address in a web browser.
Verify that you see the HTML page for the sample application.
For example:
Type a greeting, then click Click Me to return the greeting.
For example:
Next steps
For more information on deploying, deleting or managing canisters, view the managing canisters documentation.
For the next step in this guide, check out the upgrading canisters guide