Introduction
User management is a fundamental feature for many decentralized applications (hApps) built on Holochain. In this tutorial, we will walk through the process of creating a basic user management zome that handles user profiles in a decentralized way.
Understanding the User Zome
In Holochain, a zome is a modular component that encapsulates specific functionality. The User Zome is designed to manage user profiles within a Holochain app. It consists of two main parts:
-
Integrity Zome: This zome defines data structures and validation rules, ensuring that all data entries conform to the schema and pass validation checks before being committed to the DHT (Distributed Hash Table). For the User Zome, it defines the
User
entry type and its validation logic, ensuring consistent and reliable user profiles. -
Coordinator Zome: This zome handles business logic and external interactions. It provides functions that other parts of the application or external agents can call to interact with the data. In the User Zome, it implements functions for creating, updating, and retrieving user profiles.
Technical Implementation of the User Zome
Entry Types
The core of our User Zome is the User entry type. Here’s how we define it:
#[hdk_entry_helper]
pub struct User {
/// The full name of the user
pub name: String,
/// Display name for the user
pub nickname: String,
/// User's biographical information
pub bio: String,
/// Optional profile picture (serialized)
pub picture: Option<SerializedBytes>,
/// User's skills
pub skills: Vec<String>,
/// Contact information
pub email: String,
pub phone: Option<String>,
/// Location details
pub time_zone: String,
pub location: String,
}
Each field in the User struct serves a specific purpose:
name
andnickname
: For user identificationbio
: For user descriptionpicture
: An optional profile pictureskills
: A list of user skillsemail
andphone
: Contact informationtime_zone
andlocation
: Geographic information
Validation Rules
In the Integrity Zome, we implement validation rules to ensure data consistency. Here’s an example of validation for the User entry:
pub fn validate_create_user(
_action: EntryCreationAction,
user: User
) -> ExternResult<ValidateCallbackResult> {
// Validate email format
if !is_valid_email(&user.email) {
return Ok(ValidateCallbackResult::Invalid("Invalid email format".into()));
}
// Validate required fields
if user.name.is_empty() || user.nickname.is_empty() {
return Ok(ValidateCallbackResult::Invalid("Name and nickname are required".into()));
}
Ok(ValidateCallbackResult::Valid)
}
Business Logic
The Coordinator Zome implements the core functions for managing user data. Here are the essential functions:
#[hdk_extern]
pub fn create_user(user: User) -> ExternResult<ActionHash> {
create_entry(&user)
}
#[hdk_extern]
pub fn get_user(action_hash: ActionHash) -> ExternResult<Option<User>> {
get(action_hash, GetOptions::default())
}
#[hdk_extern]
pub fn update_user(input: UpdateUserInput) -> ExternResult<ActionHash> {
update_entry(input.original_action_hash, &input.updated_user)
}
#[hdk_extern]
pub fn get_all_users() -> ExternResult<Vec<Record>> {
let path = Path::from("all_users");
let links = get_links(path.path_entry_hash()?, LinkTypes::AllUsers, None)?;
let mut users = Vec::new();
for link in links {
if let Some(record) = get(link.target.clone(), GetOptions::default())? {
users.push(record);
}
}
Ok(users)
}
Step-by-Step Guide to Building the User Zome
-
Set Up the Project Structure
nix develop hc scaffold zome users
-
Define the Entry Types
- Create the User struct in
integrity.rs
- Add necessary derive macros and field definitions
- Create the User struct in
-
Implement Validation Rules
- Add validation functions in the integrity zome
- Define rules for email format, required fields, etc.
-
Create Business Logic
- Implement CRUD operations in the coordinator zome
- Add functions for creating, reading, updating users
- Set up links for user discovery
Integrating the User Zome with the UI
Here’s an example of how to create a user profile component using SvelteKit and Svelte 5 features:
<script lang="ts">
import { createUser } from '$lib/api';
let user = $state({
name: '',
nickname: '',
bio: '',
email: '',
skills: [],
time_zone: '',
location: ''
});
$derived.by(() => {
// Validate form
const isValid = user.name && user.email;
return isValid;
});
async function handleSubmit() {
try {
const result = await createUser(user);
// Handle success
} catch (error) {
// Handle error
}
}
</script>
<form onsubmit={handleSubmit}>
<input type="text" bind:value={user.name} placeholder="Full Name" required />
<input type="text" bind:value={user.nickname} placeholder="Nickname" required />
<textarea bind:value={user.bio} placeholder="Bio"></textarea>
<input type="email" bind:value={user.email} placeholder="Email" required />
<!-- Add other fields -->
<button type="submit">Create Profile</button>
</form>
Testing and Deployment
To test the User Zome:
nix develop --command "cargo test"
This will run the test suite, which should include:
- Validation tests for user creation
- CRUD operation tests
- Link tests for user discovery
Conclusion
We’ve built a basic User Management zome that provides essential functionality for managing user profiles in a Holochain app. This foundation can be extended with additional features such as user search, profile updates, and more complex user relationships depending on your application’s needs.