Calling Zome Functions

You can call a zome function from various local and remote processes and secure them with capability tokens, letting you implement secure interop easily.

Call a zome function from a front end

To call a zome function, use AppWebsocket.prototype.callZome. It takes a CallZomeRequest and an optional timeout.

This example gets all the movies by a given director, then logs their titles to the console. You’ll need the getHolochainClient example from the Connecting to a Front End page.

import { decodeHashFromBase64, EntryHash } from '@holochain/client';

interface Movie {
    title: string;
    director_hash: EntryHash;
    imdb_id: string | null;
    release_date: Date;
    box_office_revenue: Number;
}

async function getMoviesForDirector(directorHash: EntryHash): Promise<Array<Movie>> {
    // Use the `getHolochainClient` function from /build/connecting-a-front-end/
    const client = await getHolochainClient();
    let movies: Array<Movie> = await client.callZome({
        role_name: "movies",
        zome_name: "movies",
        fn_name: "get_movies_for_director",
        // This should match the input payload struct for the zome function.
        payload: directorHash,
    });
    return movies;
}

The client handles errors (both ExternResult::Err(_) errors from yor zome function and other errors from Holochain itself), turning them into promise rejections, and deserializes the MessagePack return value into JavaScript objects for you on success. It also takes care of the details of capability security for you, so you don’t have to provide any credentials for the zome call.

Call a zome function from another zome in the same hApp

Coordinator zomes call each other’s functions via the hdk::prelude::call host function. It works for any coordinator zome in a hApp, whether in the same cell or another cell.

This example calls a function get_movies_for_director in another zome called movies in the same cell:

use hdk::prelude::*;
// When you want to share a set of entry type structs among multiple
// coordinator zomes, such as with the following zome that wants to
// deserialize the other zome function's return types, consider moving the
// Rust type definitions into a separate crates to be used by both the
// integrity zome and the coordinator zomes.
use movies_types::*;

#[hdk_extern]
pub fn get_movies_for_director_from_movies_zome(director_hash: EntryHash) -> ExternResult<Vec<Movie>> {
    let response = call(
        // This indicates that `bar` is in the same cell.
        CallTargetCell::Local,
        // Call a coordinator zome by its name defined in the DNA manifest.
        "movies",
        "get_movies_for_director".into(),
        // This is a capability secret -- we'll explain later why it's not
        // needed.
        None,
        director_hash,
    )?;
    match response {
        ZomeCallResponse::Ok(output) => output
            .decode()
            .map_err(|e| wasm_error!(e.to_string()))?,
        _ => Err(wasm_error!("Something bad happened")),
    }
}

This example calls that same function in a zome called movies in a different cell whose role name is also movies:

use hdk::prelude::*;
use movies_types::*;

#[hdk_extern]
pub fn get_movies_for_director_from_movies_cell(director_hash: EntryHash) -> ExternResult<Vec<Movie>> {
    let response = call(
        // Address the cell by its role name from the hApp manifest.
        // You can also address it by its cell ID using
        // `CallTargetCell::OtherCell(CellId)`.
        CallTargetCell::OtherRole("movies".into()),
        "movies",
        "get_movies_for_director".into(),
        None,
        director_hash,
    )?;
    match response {
        ZomeCallResponse::Ok(output) => output
            .decode()
            .map_err(|e| wasm_error!(e.to_string()))?,
        _ => Err(wasm_error!("Something bad happened")),
    }
}

Just as with front ends hosted by a supporting Holochain runtime, calls made within one agent’s hApp instance don’t need to supplying the right capability credentials, because under the hood Holochain applies a special capability called the author grant. The author grant automatically gives a caller access to any function if the agent ID of the callee cell matches the agent ID of the calling cell.

Call a zome function from another agent in the network

If two agents have cells running the same DNA — that is, they’re part of the same network — they can call each other’s zome functions in the same DNA using hdk::prelude::call_remote.

A remote cell might not be running the same coordinator zomes

Holochain allows agents to add and remove coordinator zomes from their cells. This permits upgrading and customization. But it also means that the zomes and functions that you think are on the other end might not actually be there.

This example calls a function in the same coordinator zome (or at least one with the same name) in a remote agent’s cell. It assumes that the remote agent has granted access to their get_movies_by_director_from_movies_cell function with an unrestricted grant, which doesn’t require a capability secret.

use hdk::prelude::*;
use movies_types::*;

#[derive(Serialize, Deserialize, Debug)]
pub struct GetMoviesForDirectorRemoteInput {
    pub director_hash: EntryHash,
    pub remote_agent: AgentPubKey,
}

#[hdk_extern]
pub fn get_movies_for_director_remote(input: GetMoviesForDirectorRemoteInput) -> ExternResult<Vec<Movie>> {
    let GetMoviesForDirectorRemoteInput { director_hash, remote_agent} = input;
    let response = call_remote(
        remote_agent,
        // Get this zome's name from the host.
        zome_info()?.name,
        "get_movies_by_director_from_movies_cell".into(),
        // No capability secret needed for unrestricted functions.
        None,
        director_hash,
    )?;
    match response {
        ZomeCallResponse::Ok(output) => output
            .decode()
            .map_err(|e| wasm_error!(e.to_string()))?,
        ZomeCallResponse::Unauthorized(_, _, _, _, _) => Err(wasm_error!("I wasn't allowed to call this function on remote device")),
        _ => Err(wasm_error!("Something bad happened")),
    }
}

All agents have access to the same DHT data

Because every agent in a network has the same view of the network’s shared DHT data (assuming all agents are synced with each other), call_remote isn’t useful for calling functions that get data from the DHT. Instead, it should be used for requesting data that another agent can access but you can’t, such as asking them for private entries from their source chain or for data from a DHT that they can access but you can’t. In the example above, imagine that the caller doesn’t have a movies cell running but the callee does.

Reference

Further reading

It looks like you are using Internet Explorer. While the basic content is available, this is no longer a supported browser by the manufacturer, and no attention is being given to having IE work well here.