0.6 Deploying your first dapp
Now that you've covered the fundamentals of ICP, it's time to start developing your first application. To get started quickly and seamlessly, you'll use ICP Ninja, which was introduced in the previous module, 0.5: Introduction to tooling.
The video corresponding to this tutorial is outdated.
Projects on ICP have a default architecture of two canisters: a frontend canister and a backend canister. The frontend canister is essentially a web server. It stores the web assets for the application’s user interface and interacts with the backend through the use of an agent. The backend canister stores the application’s data and provides endpoints to access and modify the data. Backend canisters can be written in a variety of different languages (Motoko, Rust, TypeScript, and more) through the use of canister development kits (CDKs).
Hello, world!
This tutorial will use the Motoko "Hello, world!" example on ICP Ninja.
Let's review the project's file structure:
├── backend # Folder containing the source code of your dapp's backend.
│ ├── app.mo # The default source code file; this is the file you'll be working with in this tutorial.
├── frontend # Folder containing the asset files for your dapp's frontend.
│ ├── index.html # Entrypoint file for the frontend.
│ ├── package.json
│ ├── vite.config.js
├── dfx.json # The configuration file for your Internet Computer dapp.
├── mops.toml # Package configuration file for Motoko.
├── README.md # Information about the project.
├── WELCOME.md # Information about using ICP Ninja.
dfx.json
The dfx.json
file in the root directory defines the canisters that make up the application and where their source code is located. This project uses the following dfx.json
file:
{
"canisters": {
"backend": {
"main": "backend/app.mo", # Defines the backend canister's source code file
"type": "motoko", # Defines the type of canister
"args": "--enhanced-orthogonal-persistence"
},
"frontend": {
"dependencies": ["backend"], # Defines a dependency of the backend canister
"frontend": {
"entrypoint": "frontend/index.html" # Defines the file that should be opened by default for the frontend
},
"source": ["frontend/dist"],
"type": "assets"
}
},
"output_env_file": ".env",
"defaults": {
"build": {
"packtool": "mops sources"
}
}
}
Learn more about dfx.json.
Backend
Now, let's review the backend/app.mo
file to learn about the application's functions.
// First define an actor named HelloWorld.
// An actor is an object that can hold state and interact with the world through messages.
persistent actor HelloWorld {
// Store the greeting in a stable variable such that it gets persisted over canister upgrades.
var greeting : Text = "Hello, ";
// This update method stores the greeting prefix in stable memory.
// Update methods alter the canister's state and data.
// This method specifically will update the text stored in the `greeting` variable.
public func setGreeting(prefix : Text) : async () {
greeting := prefix;
};
// This query method returns the currently persisted greeting with the given name.
// Query methods do not alter the canister's state or data. They can be thought of as 'ready-only' operations.
// This query method will return the text stored in greeting, followed by the text passed to the method.
public query func greet(name : Text) : async Text {
// The method’s body returns Text concatenating 'Hello ,' the input value 'name', and an exclamation mark.
return greeting # name # "!";
};
};
If you want to learn more about this code, you can right-click and select "Ask AI - Explain" to get a more detailed breakdown of the code's syntax and components.
Calling the backend canister
Click the "Deploy" button in the upper right corner of the code editor. ICP Ninja will deploy the project, then return the project's canister URLs:
Deploying code onchain...
→ Reserving canisters onchain
→ Building backend
→ Building frontend
→ Uploading frontend assets
Backend Internet Computer URL:
https://a4gq6-oaaaa-aaaab-qaa4q-cai.icp1.io/?id=6kwk6-qqaaa-aaaab-qbmga-cai
🥷🚀🎉 Your dapp's Internet Computer URL is ready:
https://4u45u-3aaaa-aaaab-qbluq-cai.icp1.io
⏰ Your dapp will be available for 20 minutes
The backend canister URL will bring you to a Candid API interface that allows you to call the methods defined in the backend.
In the greet
method input box, insert the input "ICP," then select the "Query" option. The call will be sent to the greet
method, which was given the input "ICP."
The backend canister will process the call and return the response defined in the method. Since the method is defined as a query
method, the canister simply returns data to the caller; it does not alter the canister's state.
("Hello, ICP!")
In contrast, setGreeting
is an update call that does alter the canister’s state. Calling it will change the data stored in the greeting
variable, which will then result in the greet
function returning a different response when called.
Insert "Hi, " into the setGreeting
function's input, then select "Call." Then, call the greet
function again and observe the new output.
("Hi, ICP!")
Frontend
The frontend of an application is used to facilitate user interaction with the methods defined in the backend. It is made up of assets, most commonly HTML, CSS, and JavaScript.
On ICP, an application's frontend is created through these steps:
A developer creates frontend assets.
The project's
dfx.json
file defines the frontend canister and specifies it as"type": "asset"
.When the project is deployed,
dfx
deploys an implementation of the asset canister interface to the canister. It then adds an API client called the ICP JavaScript agent to the frontend assets and uploads all assets to the canister. The ICP JavaScript agent facilitates the communication between the frontend and the backend. In general, ICP agents are libraries for interacting with canisters' public interfaces.
How does the frontend communicate with the backend?
When a user loads the application, the web browser fetches the user interface from the frontend canister. Once the frontend is loaded into the browser, the user can interact with it, triggering messages to be sent to the backend.

For example, the src/hello_frontend/index.html
file defines a simple HTML page that embeds the JavaScript code used to communicate with the backend canister:
...
<script type="module">
// Import the backend actor
import { backend } from 'declarations/backend';
// Add an event listener to the form
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
// Get the name from the input field
const name = document.getElementById('name').value.toString();
// Calling the method "greet" on the backend actor with the name
const greeting = await backend.greet(name);
// Display the greeting returned by the backend actor
document.getElementById('greeting').innerText = greeting;
});
</script>
...
In this script, the backend declarations are imported with import { backend } from 'declarations/backend';
. Declaration files define the public methods of a canister and their input and output types. Declaration files are generated by dfx
during the build process. If you build this project locally, you will see them; they are not shown in the ICP Ninja file viewer.
When the agent makes a call to the backend canister, it uses these declaration files to determine which public methods it can submit requests to. Then it will create and send the request containing the request type, canister ID, method name, and any input or arguments to be passed to the method.
In this example, the user interface includes a text input box and a button to submit the input. When the button is pressed, the ICP JavaScript agent sends a request to the backend
canister's greet
method that includes the user's text input. The backend processes the request, then responds to the agent with an outgoing message that includes the result of the method.
Viewing the dapp on the mainnet
In the deployment output, there are two canister URLs. Open the second URL to view the frontend of your application deployed on the mainnet. You should see a very simple "Enter your name" input box. Insert your name, then click the "Click me!" button. You will see the response, "Hello, <name>!"
, indicating that your text input was successfully received and processed by the backend's greet
method.
Want to share your new dapp with a friend? Simply send them the frontend canister's URL, such as: https://7azie-saaaa-aaaab-qbmba-cai.icp1.io
Continue developing with this dapp, pick another ICP Ninja template to explore, or create a new project to get started building your next project!

Did you get stuck somewhere in this tutorial, or do you feel like you need additional help understanding some of the concepts? The ICP community has several resources available for developers, like working groups and bootcamps, along with our Discord community, forum, and events such as hackathons. Here are a few to check out:
- Developer Discord
- Developer Liftoff forum discussion
- Developer tooling working group
- Motoko Bootcamp - The DAO Adventure
- Motoko Bootcamp - Discord community
- Motoko developer working group
- Upcoming events and conferences
- Upcoming hackathons
- Weekly developer office hours to ask questions, get clarification, and chat with other developers live via voice chat.
- Submit your feedback to the ICP Developer feedback board