Skip to main content

Persistent storage

View this sample's code on GitHub

Overview

The example dapp shows how to build a simple dapp in Motoko, which will have persistent storage. The dapp is a simple counter, which will increment a counter, retrieve the counter value and reset the counter value by calling backend functions. The functions are exposed through a Candid interface.

Counter Frontend

The purpose of this example dapp is to build a simple counter dapp, where the counter value will persist even after the dapp has changed and been redeployed.

This example covers:

  • Create a new canister smart contract using Motoko.
  • Add backend functions for a counter (increment, get count, and reset count).
  • Deploy the canister smart contract locally.
  • Test the backend with Candid UI and command line using dfx.

Installation

This example project can be cloned, installed, and deployed locally, for learning and testing purposes. The instructions are based on running the example on either macOS or Linux, but when using WSL2 on Windows, the instructions will be the same.

Prerequisites

This example requires an installation of:

  • Install the BIG SDK.
  • Download and install git.
  • Clone the example dapp project: git clone https://github.com/dfinity/examples

Begin by opening a terminal window.

Step 1: Navigate into the folder containing the project's files and start a local instance of the BigFile with the command:

cd examples/motoko/persistent-storage
dfx start --background

Step 2: Build and deploy the canister:

dfx deploy

Step 3: Command usage: dfx canister call <project> <function>

dfx canister call persistent_storage increment

Output:

(1 : Nat)
dfx canister call persistent_storage get

Output:

(1 : Nat)
dfx canister call persistent_storage reset

Output:

(0 : Nat)

The persistence of the stored value can be tested by calling the increment function again and making sure the value is larger than zero. Then make a change in the Motoko code, any minor change like changing the function name get to getCount, and then re-deploy the project and call the getCount function to verify that the counter value did not change back to the initial value (0).

$ dfx canister call persistent_storage increment
(1 : Nat)
// Make code change
$ dfx deploy
$ dfx canister call persistent_storage getCount
(1 : Nat)

Architecture

The two main parts of the example dapp are the backend and the Candid interface. This example project does not have a frontend.

Motoko backend

The backend functions are located in the src/persistent_storage/main.mo Motoko file. The backend stores the counter value and has functions to get, increment, and reset the counter value. Furthermore, the backend ensures the counter value persists in upgrades of the dapp.

Counter variable

The current counter value is stored as a number in the actor.

actor {
stable var counter : Nat = 0;
}

increment()

The increment() function increments the counter variable.

public func increment() : async Nat {
counter += 1;
return counter;
};

The function returns the incremented counter variable.

get()

The get() function returns the current counter value.

public query func get() : async Nat {
return counter;
};

reset()

The reset() function resets the counter value to 0 and returns the value.

public func reset() : async Nat {
counter := 0;
return counter;
};

Candid interface

The Candid UI provides an easy, user-friendly interface for testing the backend. The UI is automatically generated, and the canister ID can be found by using the dfx canister id <canister_name> command:

$ dfx canister id __Candid_UI
r7inp-6aaaa-aaaaa-aaabq-cai
$ dfx canister id persistent_storage
rrkah-fqaaa-aaaaa-aaaaq-cai
http://<candid_canister_id>.localhost:8000/?id=<backend_canister_id>

License

This project is licensed under the Apache 2.0 license, see LICENSE.md for details. See CONTRIBUTE.md for details about how to contribute to this project.

Security considerations and best practices

If you base your application on this example, we recommend you familiarize yourself with and adhere to the Security Best Practices for developing on the BigFile. This example may not implement all the best practices.

For example, the following aspects are particularly relevant for this app: