Empowering dApp Development: Vue.js and Juno for Web3
Written on
Introduction to Web3 Development
As a frontend JavaScript developer venturing into the decentralized web, you might have encountered various options for Web3 development. Many of these approaches emphasize wallet integration and transaction execution, which can create a steep learning curve and diverge from the typical Web2 development experience.
However, there is a solution that smoothly connects Web2 and Web3 — Juno.
In this article, we will delve into how to utilize Vue and Juno to build decentralized applications (dApps). By integrating these technologies, you'll find an intuitive and effective method to create powerful dApps that leverage the benefits of Web3. Join us as we explore the capabilities of Juno, empowering you to craft exceptional decentralized experiences with ease!
Previous Insights
In earlier articles, I covered similar solutions for React and Angular, two widely-used JavaScript frontend frameworks. If either of these frameworks resonates with you, I recommend checking out those specific posts for insights tailored to your needs.
Understanding Juno
If you haven't yet heard of Juno, it’s a robust open-source Blockchain-as-a-Service platform that simplifies the development of decentralized applications. Picture it as a serverless platform akin to popular services like Google Firebase or AWS Amplify, but enhanced with blockchain technology. Juno manages everything by hosting your applications entirely on the blockchain, offering a fully decentralized and secure infrastructure.
By harnessing the Internet Computer blockchain network, Juno introduces a distinctive feature called “Satellites” for each application you develop. These Satellites function as powerful smart contracts, encapsulating your entire application, which includes web assets like JavaScript, HTML, and image files, along with a straightforward database, file storage, and authentication mechanisms. Juno grants you total control over your application's functionality and data.
Getting Started with dApps
Let's embark on the journey of creating our first decentralized application, commonly referred to as a “dapp.” In this example, we will develop a note-taking app that enables users to save and retrieve data entries, as well as upload files.
Setup
To start integrating Juno into your application, you'll need to create a satellite, as explained in the documentation. Additionally, you must install the SDK using the following command:
npm i @junobuild/core
Once you've completed these steps, initialize Juno with your satellite ID at the root of your Vue app, such as in App.vue. This setup configures the library to interact with your smart contract.
<script setup lang="ts">
import { onMounted } from 'vue'
import { initJuno } from '@junobuild/core'
onMounted(
async () =>
await initJuno({
satelliteId: 'pycrs-xiaaa-aaaal-ab6la-cai'})
)
</script>
<template>
<h1>Hello World</h1>
</template>
Congratulations! Your app is now prepared for Web3!
Authentication Mechanism
To ensure secure and anonymous user identification, users must sign in and out. You can link the necessary functions to buttons placed anywhere in your application.
<script setup lang="ts">
import { signIn, signOut } from '@junobuild/core'
</script>
<button @click="signIn">Sign-in</button>
<button @click="signOut">Sign-out</button>
To seamlessly integrate with other services, the library and satellite components automatically create a new entry in your smart contract upon successful user sign-in. This functionality enables the library to efficiently verify permissions during any data exchange.
To monitor and gain insights into this entry and access information about the user's status, Juno provides an observable function called authSubscribe(). You can use this function as needed. Additionally, creating a store can help propagate user information throughout your application.
import { ref, type Ref } from 'vue'
import { defineStore } from 'pinia'
import { authSubscribe, type User } from '@junobuild/core'
export const useAuthStore = defineStore('auth', () => {
const user: Ref<User | null | undefined> = ref(undefined)
const unsubscribe = authSubscribe((u) => (user.value = u))
return { user, unsubscribe }
})
This makes it easy to subscribe to the user data at the top level of your application.
<script setup lang="ts">
import { useAuthStore } from '../stores/auth.store'
import { storeToRefs } from 'pinia'
const store = useAuthStore()
const { user } = storeToRefs(store)
</script>
<template>
<template v-if="user !== undefined && user !== null">
<slot /></template>
<template v-else>
<p>Not signed in.</p></template>
</template>
Storing Data
Juno includes a feature called “Datastore” for storing data directly on the blockchain. A Datastore consists of collections, with each collection containing documents identified by a unique key of your choice.
In this tutorial, we will create a collection for storing notes, which we will name “notes.” After setting up your application and creating the collection, use the library's setDoc function to securely store data on the blockchain.
import { setDoc } from "@junobuild/core";
// TypeScript example from the documentation
await setDoc<Example>({
collection: "my_collection_key",
doc: {
key: "my_document_key",
data: myExample,
},
});
The documents in the collection are identified by unique keys, which we can generate using nanoid, a tiny string ID generator for JavaScript.
<script lang="ts" setup>
import { ref } from 'vue'
import { setDoc } from '@junobuild/core'
import { nanoid } from 'nanoid'
const inputText = ref('')
const add = async () => {
const key = nanoid()
await setDoc({
collection: 'notes',
doc: {
key,
data: {
text: inputText.value}
}
})
}
</script>
<template>
<textarea rows="5" placeholder="Your diary entry"
v-model="inputText"></textarea>
<button @click="add">Add</button>
</template>
Retrieving Data
To get the collection of documents saved on the blockchain, utilize the library's listDocs function, which allows for various parameters for filtering, ordering, or pagination.
For this tutorial, we will keep it simple by listing all user data when a component mounts.
<script lang="ts" setup>
import { listDocs } from '@junobuild/core'
import { onMounted, ref } from 'vue'
const items = ref([])
const list = async () => {
const { items: data } = await listDocs({
collection: 'notes'})
items.value = data
}
onMounted(async () => await list())
</script>
<template>
<p v-for="(item, index) in items">
<span>
{{ index + 1 }}</span>
<span>{{ item.data.text }}</span>
</p>
</template>
Uploading Files
Handling user-generated content like photos or files can be challenging. However, Juno is designed to simplify this process. To manage documents, begin by creating a collection, which we will name “images” for this tutorial.
Each file needs a unique file name and path, which is crucial since the data is served on the web, and each piece should have a distinct URL. We achieve this by generating a key that combines the user's unique ID and a timestamp for each uploaded file.
<script lang="ts" setup>
import { ref } from 'vue'
import { useAuthStore } from '@/stores/auth.store'
import { storeToRefs } from 'pinia'
import { uploadFile } from '@junobuild/core'
const file = ref(undefined)
const store = useAuthStore()
const { user } = storeToRefs(store)
const setFile = (f) => (file.value = f)
const upload = async () => {
// Demo purpose therefore edge case not properly handled
if ([null, undefined].includes(user.value)) {
return}
const filename = ${user.value.key}-${file.value.name}
const { downloadUrl } = await uploadFile({
collection: 'images',
data: file.value,
filename
})
console.log('Uploaded', downloadUrl)
}
</script>
<template>
<input type="file" @change="(event) => setFile(event.target.files?.[0])" />
<button @click="upload">Upload</button>
</template>
Once an asset is uploaded, a downloadUrl is returned, providing a direct HTTPS link for accessing the asset on the web.
Deployment Process
After developing and building your application, the next step is to deploy it on the blockchain. To do this, install the Juno command line interface (CLI) with the following command:
npm i -g @junobuild/cli
After the installation, you can access your satellite by following the documentation and logging in via the terminal. This allows your machine to control your satellite with the command:
juno login
Finally, deploy your project using this command:
juno deploy
Congratulations! Your Vue dApp is now live and fully powered by the blockchain!
Further Learning
The source code for this tutorial is available in our GitHub Repository.
Thank you for reading! Follow me on Twitter for more engaging coding content. If you enjoyed this article, consider joining the Juno community on Discord. Your support on GitHub is also greatly appreciated!
Join the Journey with Videos
In this section, we will include valuable video resources to enhance your understanding of developing Web3 applications.
This video, titled "How To Create A Web3 App with Vue.js! Getting Started Guide Solidity, Ether.js With Vue," provides an excellent introduction to building Web3 applications using Vue.js.
Another insightful video, "Build A Web3 Domain Name Service With Vue.js, Solidity and Hardhat!" dives deeper into Web3 development, showcasing how to create a domain name service with the mentioned technologies.