Azle Cloud Development Kit

repository·main·Indexed 42 days ago

https://github.com/demergent-labs/azle

A TypeScript and JavaScript Cloud Development Kit (CDK) for the Internet Computer (ICP) that allows developers to build canisters using web technologies. It offers a Stable Mode for production stability and an Experimental Mode for access to the Node.js standard library, npm ecosystem, and HTTP server functionality.

Tokens
98.7K
Snippets
271
Records
374
Agent score
48%

What's inside Azle

  1. Access Internet Computer platform capabilities via Azle IC API

    main

    The IC API is exposed as functions exported from the azle package. These functions allow your canister to interact with the Internet Computer platform, providing access to system time, canister identity, cycles management, timers, and inter-canister communication.

    import {
        msgCaller,
        time,
        canisterCycleBalance,
        call,
        IDL,
        query,
        update
    } from 'azle';
    
    export default class {
        @query([], IDL.Text)
        whoCalledWhen(): string {
            const caller = msgCaller().toText();
            const now = time();
            return `Called by ${caller} at ${now}`;
        }
    
        @query([], IDL.Nat)
        getBalance(): bigint {
            return canisterCycleBalance();
        }
    
        @update([IDL.Principal, IDL.Text], IDL.Text)
        async callOther(canisterId: Principal, message: string): Promise<string> {
            const result = await call(canisterId, 'echo', {
                args: [message],
                paramIdlTypes: [IDL.Text],
                returnIdlType: IDL.Text
            });
            return result;
        }
    }
  2. Understand Azle performance benchmarks

    main

    Azle provides high-level performance tracking to compare efficiency across different releases. This document tracks changes in metrics like 'Baseline Weighted Efficiency' and 'Number of Instructions' for both Stable and Experimental modes.

    Note: For granular performance data regarding how specific Candid data types perform, refer to the benchmarks example. This is the recommended resource for optimizing day-to-day performance in your Azle applications.

  3. Understand Azle Stable Mode

    main

    Azle runs in Stable Mode by default. This mode is designed for production use (targeting the 1.0 release) and focuses on API and runtime stability, security, performance, TypeScript/JavaScript support, ICP APIs, and Candid RPC.

    Note: In Stable Mode, there is minimal support for the Node.js standard library, the npm ecosystem, and HTTP server functionality.

  4. Choose between Candid RPC and HTTP Server methodologies

    main

    Azle applications (canisters) can be developed using one of two primary methodologies depending on your target client and serialization needs:

    1. Candid RPC: This approach uses the Internet Computer's (ICP) Candid language. It exposes canister methods directly to Candid-speaking clients and uses Candid for serialization and deserialization. This is the standard approach for canister-to-canister or specialized client interaction.

      • Status: Release Candidate (heading towards 1.0).
    2. HTTP Server: This approach uses traditional web server techniques. It allows you to write HTTP servers using popular libraries like Express and uses JSON for serialization and deserialization. This is ideal for standard web integration.

      • Status: Experimental.
  5. Organize complex canisters with Multiple Canister Classes

    main

    For modular canister design, you can split functionality across multiple classes in separate files and export them as an array in your main index file.

    Key behaviors:

    • Array Export: Use export default [Class1, Class2, ...] to combine them.
    • Method Merging: All decorated methods from all classes in the array are merged into a single Candid interface.
    • Independent State: Each class maintains its own state within the same canister.
    • Automatic Instantiation: Azle automatically instantiates these classes; you do not need to call new manually.
    // user_service.ts
    import { IDL, query, update } from 'azle';
    
    export class UserService {
        users: Map<string, string> = new Map();
    
        @update([IDL.Text, IDL.Text], IDL.Bool)
        createUser(id: string, name: string): boolean {
            if (this.users.has(id)) {
                return false;
            }
            this.users.set(id, name);
            return true;
        }
    
        @query([IDL.Text], IDL.Opt(IDL.Text))
        getUser(id: string): string | undefined {
            return this.users.get(id);
        }
    }
    // notification_service.ts
    import { IDL, query, update } from 'azle';
    
    export class NotificationService {
        notifications: string[] = [];
    
        @update([IDL.Text], IDL.Nat)
        addNotification(message: string): number {
            this.notifications.push(message);
            return this.notifications.length;
        }
    
        @query([], IDL.Vec(IDL.Text))
        getNotifications(): string[] {
            return this.notifications;
        }
    }
    // index.ts
    import { UserService } from './user_service';
    import { NotificationService } from './notification_service';
    
    export default [UserService, NotificationService];
  6. Enable cross-canister calls with Composite Queries

    main

    By default, @query methods cannot perform cross-canister calls. To enable this capability, you must set the composite option to true in the decorator. This allows the method to be async and use the call function to interact with other canisters.

    import { IDL, query, call } from 'azle';
    
    export default class {
        @query([], IDL.Text, { composite: true })
        async crossCanisterQuery(): Promise<string> {
            const result = await call('canister-id', 'method_name', {
                returnIdlType: IDL.Text
            });
            return result;
        }
    }
  7. Use the HTTP Server methodology in Azle

    main

    Azle provides an experimental HTTP Server methodology that allows you to develop applications using traditional web server techniques. This approach enables you to write servers using popular libraries like Express and use JSON for simple serialization and deserialization of data.

    Note: HTTP Server functionality is currently marked as experimental and its stability/availability is not guaranteed for the long term.

  8. Understand Azle's fetch implementations

    main

    Azle provides custom fetch implementations tailored for different contexts:

    1. Client fetch: Used for authentication purposes (see Authentication).
    2. Canister fetch: Used within a canister to perform:
      • Cross-canister calls to a Candid canister: Interacting with other canisters via their Candid interface.
      • Cross-canister calls to an HTTP canister: Interacting with canisters that expose HTTP interfaces.
      • HTTPS outcalls: Making requests to the external internet.
  9. Manage persistent canister state via class properties

    main

    Class properties defined within your canister class automatically become persistent canister state. This allows you to store and modify data across different method calls.

    import { IDL, query, update } from 'azle';
    
    export default class {
        // This becomes persistent canister state
        counter: number = 0;
        users: Map<string, string> = new Map();
    
        @query([], IDL.Nat)
        getCounter(): number {
            return this.counter;
        }
    
        @update([], IDL.Nat)
        increment(): number {
            this.counter += 1;
            return this.counter;
        }
    }
  10. Implement certified data sets with certifiedDataSet

    main

    To enable cryptographic proofs for your query responses, you must use certifiedDataSet(data) inside an @update method. This function binds a specific piece of data (up to 32 bytes) to the canister's state so that subsequent @query calls can retrieve a corresponding certificate via dataCertificate().

    Workflow:

    1. In an @update call, prepare a Uint8Array of the data you wish to certify.
    2. Call certifiedDataSet(data).
    3. In a @query call, use dataCertificate() to return the proof alongside your data.
    const encoder = new TextEncoder();
    const data = encoder.encode(value.slice(0, 32));
    certifiedDataSet(data);
  11. Enable Azle Experimental Mode

    main

    Azle's experimental mode enables features like the Node.js standard library, the npm ecosystem, and HTTP server functionality. This mode is intended for developers who need early access to new features but must accept higher risks of bugs, security exploits, and breaking API changes.

    To use experimental mode, you must explicitly enable it via a flag in dfx.json or through specific CLI commands during project creation.

  12. Use cases for candidDecode

    main

    The candidDecode function is intended for scenarios where you need to manually handle Candid-encoded data rather than relying on Azle's automatic argument decoding. Common use cases include:

    • Processing raw argument data: Using msgArgData() to get the raw bytes of a call and then decoding them manually.
    • Decoding external data: Handling data received from sources outside the standard Azle request lifecycle.
    • Inter-canister calls: Manually parsing responses from other canisters.
    • Stored data: Decoding Candid-encoded blobs retrieved from stable memory or other storage mechanisms.