Quick Start

Get started with TrustWeave in 5 minutes! This guide will walk you through creating your first TrustWeave application.

Version: 1.0.0-SNAPSHOT Kotlin: 2.2.0+ | Java: 21+ See Installation for setup details.

Complete Runnable Example

Here’s a complete, copy-paste ready example that demonstrates the full TrustWeave workflow with proper error handling. This example uses try-catch blocks for error handling, which is the recommended pattern for all TrustWeave operations.

Note: All TrustWeave methods throw domain-specific exceptions on failure (e.g., DidException, CredentialException, WalletException). These extend TrustWeaveException and provide structured error codes and context. Always wrap operations in try-catch blocks for production code. Verification methods return sealed VerificationResult types for exhaustive error handling. See Error Handling Patterns below for details.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package com.example.TrustWeave.quickstart

import com.trustweave.trust.TrustWeave
import com.trustweave.trust.types.IssuerIdentity
import com.trustweave.trust.types.VerificationResult
import com.trustweave.core.util.DigestUtils
import com.trustweave.core.exception.TrustWeaveException
import com.trustweave.did.exception.DidException
import com.trustweave.did.exception.DidException.DidMethodNotRegistered
import com.trustweave.did.exception.DidException.DidNotFound
import com.trustweave.did.exception.DidException.InvalidDidFormat
import com.trustweave.credential.exception.CredentialException
import com.trustweave.credential.exception.CredentialException.CredentialInvalid
import com.trustweave.credential.exception.CredentialException.CredentialIssuanceFailed
import com.trustweave.wallet.exception.WalletException
import com.trustweave.wallet.exception.WalletException.WalletCreationFailed
import com.trustweave.testkit.services.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

fun main() = runBlocking {
    try {
        // Step 1: Create TrustWeave instance with defaults
        val trustWeave = TrustWeave.build {
            factories(
                kmsFactory = TestkitKmsFactory(),
                didMethodFactory = TestkitDidMethodFactory()
            )
            keys {
                provider("inMemory")
                algorithm("Ed25519")
            }
            did {
                method("key") {
                    algorithm("Ed25519")
                }
            }
        }

        // Step 2: Compute a digest (demonstrates canonicalization)
        val credentialSubject = buildJsonObject {
            put("id", "did:key:holder-placeholder")
            put("name", "Alice Example")
            put("role", "Site Reliability Engineer")
        }
        val digest = DigestUtils.sha256DigestMultibase(credentialSubject)
        println("Canonical credential-subject digest: $digest")

        // Step 3: Create an issuer DID
        val didResult = trustWeave.createDid {
            method("key")
            algorithm("Ed25519")
        }
        
        val issuerDid = when (didResult) {
            is DidCreationResult.Success -> {
                println("Issuer DID: ${didResult.did.value}")
                didResult.did
            }
            else -> {
                println("Failed to create DID: ${didResult.reason}")
                return@runBlocking
            }
        }
        
        // Extract key ID from DID document
        val issuerDidResolution = trustWeave.getDslContext().getConfig().registries.didRegistry.resolve(issuerDid.value)
            ?: throw IllegalStateException("Failed to resolve issuer DID")
        val issuerDidDoc = when (issuerDidResolution) {
            is com.trustweave.did.resolver.DidResolutionResult.Success -> issuerDidResolution.document
            else -> throw IllegalStateException("Failed to resolve issuer DID")
        }
        val issuerKeyId = issuerDidDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
            ?: throw IllegalStateException("No verification method in issuer DID")
        
        println("Issuer DID: ${issuerDid.value} (keyId=$issuerKeyId)")

        // Step 4: Issue a verifiable credential
        val issuanceResult = trustWeave.issue {
            credential {
                type(CredentialType.VerifiableCredential, CredentialType.Custom("QuickStartCredential"))
                issuer(issuerDid.value)
                subject {
                    id("did:key:holder-placeholder")
                    "name" to "Alice Example"
                    "role" to "Site Reliability Engineer"
                }
            }
            signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
        }
        
        val credential = when (issuanceResult) {
            is IssuanceResult.Success -> {
                println("Issued credential id: ${issuanceResult.credential.id}")
                issuanceResult.credential
            }
            else -> {
                println("Failed to issue credential: ${issuanceResult.reason}")
                return@runBlocking
            }
        }

        // Step 5: Verify the credential
        val verification = trustWeave.verify {
            credential(credential)
            checkRevocation()
            checkExpiration()
        }

        when (verification) {
            is VerificationResult.Valid -> {
                println("✅ Verification succeeded")
                if (verification.warnings.isNotEmpty()) {
                    println("Warnings: ${verification.warnings.joinToString()}")
                }
            }
            is VerificationResult.Invalid.Expired -> {
                println("❌ Credential expired at ${verification.expiredAt}")
            }
            is VerificationResult.Invalid.Revoked -> {
                println("❌ Credential revoked")
            }
            is VerificationResult.Invalid.InvalidProof -> {
                println("❌ Invalid proof: ${verification.reason}")
            }
            is VerificationResult.Invalid.UntrustedIssuer -> {
                println("❌ Untrusted issuer: ${verification.issuer}")
            }
            is VerificationResult.Invalid.SchemaValidationFailed -> {
                println("❌ Schema validation failed: ${verification.errors.joinToString()}")
            }
            else -> {
                println("❌ Verification failed: ${verification}")
            }
        }

        // Step 6: Create a wallet and store the credential
        val walletResult = trustWeave.wallet {
            holder("did:key:holder-placeholder")
        }
        
        val wallet = when (walletResult) {
            is WalletCreationResult.Success -> walletResult.wallet
            else -> {
                println("Failed to create wallet: ${walletResult.reason}")
                return@runBlocking
            }
        }
        
        val credentialId = wallet.store(credential)
        println("✅ Stored credential: $credentialId")
    // Error handling is now done via sealed results
    // The when expressions above handle all error cases
}

Simplified Example (Testing Only)

For quick testing and prototypes, you can use a simplified version without detailed error handling. Do not use this in production:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import com.trustweave.testkit.services.*

fun main() = runBlocking {
    val trustWeave = TrustWeave.build {
        factories(
            kmsFactory = TestkitKmsFactory(),
            didMethodFactory = TestkitDidMethodFactory()
        )
        keys { provider("inMemory"); algorithm("Ed25519") }
        did { method("key") { algorithm("Ed25519") } }
    }

    // Operations will throw exceptions on failure
    val did = trustWeave.createDid { method("key") }
    val credential = trustWeave.issue { ... }
    // ... rest of code
}

Why not in production? Exceptions will crash your application. Always use try-catch in production code.

Production Pattern with Error Handling

The example above already shows the production pattern. Here’s an enhanced version with more detailed error handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import com.trustweave.did.exception.DidException
import com.trustweave.did.exception.DidException.DidMethodNotRegistered
import com.trustweave.did.exception.DidException.DidNotFound
import com.trustweave.credential.exception.CredentialException
import com.trustweave.credential.exception.CredentialException.CredentialIssuanceFailed
import com.trustweave.core.exception.TrustWeaveException
import com.trustweave.testkit.services.*

fun main() = runBlocking {
    val trustWeave = TrustWeave.build {
        factories(
            kmsFactory = TestkitKmsFactory(),
            didMethodFactory = TestkitDidMethodFactory()
        )
        keys {
            provider("inMemory")
            algorithm("Ed25519")
        }
        did {
            method("key") {
                algorithm("Ed25519")
            }
        }
    }

    // Production pattern: Use try-catch for all operations with domain-specific exceptions
    val issuerDid = try {
        trustWeave.createDid {
            method("key")
            algorithm("Ed25519")
        }
    } catch (error: DidException) {
        when (error) {
            is DidMethodNotRegistered -> {
                println("❌ DID method not registered: ${error.method}")
                println("Available methods: ${error.availableMethods}")
            }
            is DidNotFound -> {
                println("❌ DID not found: ${error.did}")
            }
            else -> {
                println("❌ DID error: ${error.message}")
            }
        }
        return@runBlocking
    } catch (error: TrustWeaveException) {
        println("❌ TrustWeave error [${error.code}]: ${error.message}")
        return@runBlocking
    } catch (error: Exception) {
        println("❌ Unexpected error: ${error.message}")
        return@runBlocking
    }

    val issuerKeyId = "$issuerDid#key-1"

    val credential = try {
        trustWeave.issue {
            credential {
                type(CredentialType.VerifiableCredential, CredentialType.Custom("QuickStartCredential"))
                issuer(issuerDid)
                subject {
                    id("did:key:holder")
                    "name" to "Alice"
                }
            }
            signedBy(IssuerIdentity.from(issuerDid, issuerKeyId))
        }
    } catch (error: CredentialException) {
        when (error) {
            is CredentialIssuanceFailed -> {
                println("❌ Credential issuance failed: ${error.reason}")
                error.issuerDid?.let { println("   Issuer DID: $it") }
            }
            else -> {
                println("❌ Credential error: ${error.message}")
            }
        }
        return@runBlocking
    } catch (error: TrustWeaveException) {
        println("❌ TrustWeave error [${error.code}]: ${error.message}")
        return@runBlocking
    } catch (error: Exception) {
        println("❌ Failed to issue credential: ${error.message}")
        return@runBlocking
    }

    println("✅ Credential issued: ${credential.id}")
}

Expected Output:

1
2
3
4
5
Canonical credential-subject digest: u5v...
Issuer DID: did:key:z6Mk... (keyId=did:key:z6Mk...#key-1)
Issued credential id: urn:uuid:...
Verification succeeded (proof=true, issuer=true, revocation=true)
Anchored credential on inmemory:anchor: tx_...

To run this example:

  1. Add the dependency (see Step 1 below)
  2. Copy the code above into src/main/kotlin/QuickStart.kt
  3. Run with ./gradlew run or execute in your IDE

Step-by-Step Guide

The sections below explain each step in detail.

Step 1: Add a single dependency

Why: trustweave-all bundles every public module (core APIs, DID support, KMS, anchoring, DSLs) so you can get going with one line. How it works: It’s a convenience metapackage that re-exports the same artifacts you would otherwise add one-by-one. How simple: Drop one dependency and you’re done.

Note: For production deployments, consider using individual modules instead of trustweave-all to minimize bundle size. See Installation Guide for details.

1
2
3
4
dependencies {
    implementation("com.trustweave:trustweave-all:1.0.0-SNAPSHOT")
    testImplementation("com.trustweave:trustweave-testkit:1.0.0-SNAPSHOT")
}

What this does

  • Pulls in every public TrustWeave module (core APIs, DID support, KMS, anchoring, DSLs) with a single coordinate so you never chase transitive dependencies.
  • Adds trustweave-testkit for the in-memory DID/KMS/wallet implementations used in the tutorials and automated tests.

Design significance TrustWeave promotes a “batteries included” experience for newcomers. The monolithic artifact keeps onboarding simple; when you graduate to production you can swap in individual modules without changing API usage.

Step 2: Bootstrap TrustWeave and compute a digest

Why: Most flows start by hashing JSON so signatures and anchors are stable. How it works: DigestUtils.sha256DigestMultibase canonicalises JSON and returns a multibase string. How simple: One helper call, no manual canonicalisation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import com.trustweave.trust.TrustWeave
import com.trustweave.core.util.DigestUtils
import com.trustweave.core.TrustWeaveError
import com.trustweave.testkit.services.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

fun main() = runBlocking {
    // Create TrustWeave with sensible defaults (in-memory KMS, did:key method)
    val trustWeave = TrustWeave.build {
        factories(
            kmsFactory = TestkitKmsFactory(),
            didMethodFactory = TestkitDidMethodFactory()
        )
        keys {
            provider("inMemory")
            algorithm("Ed25519")
        }
        did {
            method("key") {
                algorithm("Ed25519")
            }
        }
    }

    // Build credential subject payload
    val credentialSubject = buildJsonObject {
        put("id", "did:key:holder-placeholder")
        put("name", "Alice Example")
        put("role", "Site Reliability Engineer")
    }

    // Compute deterministic digest (canonicalizes JSON first)
    val digest = DigestUtils.sha256DigestMultibase(credentialSubject)
    println("Digest: $digest")
}

What this does

  • Instantiates TrustWeave with sensible defaults (in-memory registries) suitable for playground and unit tests.
  • Builds a credential payload using Kotlinx Serialization builders so the structure is type-safe.
  • Canonicalises and hashes the payload, returning a multibase-encoded digest you can anchor or sign.

Important: The defaults use in-memory components (KMS, wallets, DID methods) suitable for testing only. For production, configure your own KMS, DID methods, and storage backends. See Default Configuration and Production Deployment for details.

Result DigestUtils.sha256DigestMultibase prints a deterministic digest (for example u5v...) that becomes the integrity reference for later steps.

Design significance Everything in TrustWeave assumes deterministic canonicalization, so the very first code sample reinforces the pattern: serialize → canonicalize → hash → sign/anchor. This is the backbone of interoperability.

Step 3: Create a DID with typed options

Why: You need an issuer DID before issuing credentials. How it works: trustWeave.createDid { } uses the configured DID method registry and DSL builder. How simple: Configure only what you need using a fluent builder—defaults cover the rest.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Simple: use defaults (did:key method, ED25519 algorithm)
val issuerDid = trustWeave.createDid {
    method("key")
    algorithm("Ed25519")
}
val issuerKeyId = "$issuerDid#key-1"
println("Issuer DID: $issuerDid (keyId=$issuerKeyId)")

// Advanced: customize with builder
val customDid = trustWeave.createDid {
    method("key")
    algorithm("Ed25519")
}

What this does

  • Calls the facade to provision a DID using the default registry (in this case did:key).
  • Returns the fully materialised DID document with verification methods.
  • Extracts the DID identifier and key ID for use in credential issuance.

Result issuerDid now holds a resolvable DID such as did:key:z6M... that acts as the issuer for credentials. The issuerKeyId is needed for signing credentials.

Design significance Typed builders (DidCreationOptions) are a core design choice: they prevent misconfigured DID creation at compile time and make IDE autocompletion an onboarding tool rather than documentation guesswork.

Step 4: Issue a credential and store it

Why: Credential issuance is the heart of most TrustWeave solutions. How it works: The facade orchestrates KMS, proofs, and registries, returning a Result<VerifiableCredential>. How simple: Provide the issuer DID/key and credential subject JSON; the API handles proof generation and validation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Issue credential using the issuer DID and key ID from Step 3
val credential = trustWeave.issue {
    credential {
        type(CredentialType.VerifiableCredential, CredentialType.Custom("QuickStartCredential"))
        issuer(issuerDid)
        subject {
            id("did:key:holder-placeholder")
            "name" to "Alice Example"
            "role" to "Site Reliability Engineer"
        }
    }
    signedBy(IssuerIdentity.from(issuerDid, issuerKeyId))
}

println("Issued credential id: ${credential.id}")

What this does

  • Invokes the credential issuance facade which orchestrates key lookup/generation, proof creation, and credential assembly.
  • Configures the credential subject payload and credential types.
  • Returns a signed VerifiableCredential with cryptographic proof attached.

Result The printed ID corresponds to a tamper-evident credential JSON object that you can store, present, or anchor.

Design significance The type-safe IssuerIdentity ensures that issuer DID and key ID are properly validated at compile time, reducing runtime errors and improving developer experience.

Run the sample The full quick-start flow lives in distribution/examples/src/main/kotlin/com/trustweave/examples/quickstart/QuickStartSample.kt. Execute it locally with ./gradlew :distribution:examples:runQuickStartSample.

Step 5: Verify the credential

Why: Consumers must trust the credential; verification validates proofs and checks revocation. How it works: verifyCredential rebuilds proofs, resolves issuer DIDs, and performs validity checks. How simple: One call returns a structured result with validation details.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Verify credential
val verification = trustWeave.verify {
    credential(credential)
    checkRevocation()
    checkExpiration()
}

when (verification) {
    is VerificationResult.Valid -> {
        println("✅ Verification succeeded")
        if (verification.warnings.isNotEmpty()) {
            println("Warnings: ${verification.warnings.joinToString()}")
        }
    }
    is VerificationResult.Invalid -> {
        println("❌ Verification failed: $verification")
    }
}

What this does

  • Verifies the credential by rebuilding proofs and performing validity checks.
  • Checks issuer DID resolution, proof validity, and revocation status.
  • Returns a sealed VerificationResult type for exhaustive error handling.

Result You get a VerificationResult sealed class that can be Valid or one of several Invalid subtypes, each providing specific error information. This enables exhaustive when-expressions for type-safe error handling.

Step 6: Anchor to blockchain (optional)

Why: Anchoring provides tamper evidence and timestamping on a blockchain. How it works: Register a blockchain client and use it to anchor credential data. How simple: Register client, serialize credential, write to chain.

1
2
3
4
5
6
7
// Create wallet and store credential
val wallet = trustWeave.wallet {
    holder("did:key:holder-placeholder")
}

val credentialId = wallet.store(credential)
println("✅ Stored credential: $credentialId")

What this does

  • Registers an in-memory blockchain client (for testing; use real clients in production).
  • Serializes the credential to JSON and writes it to the anchor client.
  • Returns an AnchorRef with chain ID and transaction hash.

Result You get an AnchorRef representing the write operation. In production you’d persist this for audits and use a real chain adapter (Algorand, Polygon, etc.).

Design significance Anchoring is abstracted behind the same interface regardless of provider. The sample sticks to the in-memory implementation, but the code path is identical for Algorand, Polygon, Indy, or future adapters—making environment swaps low risk.

Error Handling Patterns

TrustWeave methods throw exceptions on failure. Understanding error handling patterns is important for production code.

Best Practice: Always use try-catch blocks for production code. Only skip error handling in quick prototypes and tests.

When to Skip Error Handling (Testing/Prototyping Only)

Skip error handling only for:

  • ✅ Quick start examples and prototypes
  • ✅ Simple scripts where you can let errors bubble up
  • ✅ Test code where exceptions are acceptable
  • ✅ Learning and experimentation
1
2
3
4
// ⚠️ Simple usage (exceptions will propagate) - Testing/Prototyping Only
// For production, always use try-catch instead
val did = trustWeave.createDid { method("key") }
val credential = trustWeave.issue { ... }

Why not in production? Unhandled exceptions can crash your application. Production code should handle errors gracefully.

When to Use Try-Catch (Production Pattern)

Use try-catch blocks always for:

  • ✅ Production code
  • ✅ When you need to handle specific error types
  • ✅ When you want to provide user-friendly error messages
  • ✅ When you need to log errors before handling
  • ✅ When you need to recover from errors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import com.trustweave.did.exception.DidException
import com.trustweave.did.exception.DidException.DidMethodNotRegistered
import com.trustweave.core.exception.TrustWeaveException

// ✅ Production pattern with domain-specific error handling
try {
    val did = trustWeave.createDid {
        method("key")
        algorithm("Ed25519")
    }
    processDid(did)
} catch (error: DidException) {
    when (error) {
        is DidMethodNotRegistered -> {
            logger.warn("DID method not registered: ${error.method}")
            // Handle method not registered - show available methods
        }
        else -> {
            logger.warn("DID creation failed: ${error.message}")
        }
    }
} catch (error: TrustWeaveException) {
    logger.error("TrustWeave error [${error.code}]: ${error.message}", error)
    // Handle TrustWeave-specific errors
} catch (error: Exception) {
    logger.error("Unexpected error: ${error.message}", error)
    // Handle generic error
}

Why use domain-specific exceptions? They provide structured error information with error codes, context, and type-safe handling. The compiler ensures exhaustive handling in when expressions.

Handling errors and verification failures

TrustWeave methods throw exceptions on failure. Always use try-catch blocks for error handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Verify credential with exhaustive error handling
try {
    val verification = trustWeave.verify {
        credential(credential)
        checkRevocation()
        checkExpiration()
    }

    when (verification) {
        is VerificationResult.Valid -> {
            println("✅ Credential is valid: ${verification.credential.id}")
            if (verification.warnings.isNotEmpty()) {
                verification.warnings.forEach { println("Warning: $it") }
            }
        }
        is VerificationResult.Invalid.Expired -> {
            println("❌ Credential expired at ${verification.expiredAt}")
        }
        is VerificationResult.Invalid.Revoked -> {
            println("❌ Credential revoked")
        }
        is VerificationResult.Invalid.InvalidProof -> {
            println("❌ Invalid proof: ${verification.reason}")
        }
        is VerificationResult.Invalid.UntrustedIssuer -> {
            println("❌ Untrusted issuer: ${verification.issuer}")
        }
        is VerificationResult.Invalid.SchemaValidationFailed -> {
            println("❌ Schema validation failed: ${verification.errors.joinToString()}")
        }
        // Compiler ensures all cases are handled
    }
} catch (error: CredentialException) {
    println("❌ Credential error: ${error.message}")
} catch (error: TrustWeaveException) {
    println("❌ TrustWeave error [${error.code}]: ${error.message}")
} catch (error: Exception) {
    println("❌ Unexpected error: ${error.message}")
}

Best Practice: Always use exhaustive when expressions to handle all VerificationResult cases. This ensures type-safe error handling and prevents missing error cases. Use try-catch blocks for exceptions thrown by the verification method itself.

See Error Handling for more details on error handling patterns.

Scenario Playbook

Ready to explore real-world workflows? Each guide below walks through an end-to-end scenario using the same APIs you just touched:

Popular Scenarios:

Troubleshooting

If you encounter issues:

Learning Path

Follow this structured path to master TrustWeave:

1. Get Started (You are here!)

  • ✅ Complete this Quick Start guide
  • ✅ Run the example code
  • ✅ Understand basic concepts

2. Learn the Fundamentals

  • Beginner Tutorial Series - Structured 5-tutorial series (2+ hours)
    • Tutorial 1: Your First DID (15-20 min)
    • Tutorial 2: Issuing Your First Credential (20-25 min)
    • Tutorial 3: Managing Credentials with Wallets (25-30 min)
    • Tutorial 4: Building a Complete Workflow (30-35 min)
    • Tutorial 5: Adding Blockchain Anchoring (25-30 min)

3. Build Real Applications

4. Deepen Your Knowledge

5. Production Deployment

What’s Next?

New to TrustWeave?

  1. Start with Beginner Tutorial Series - Tutorial 1
  2. Complete all 5 tutorials in order
  3. Move to Common Patterns for production patterns

Already familiar with DIDs/VCs?

  1. Review Common Patterns for TrustWeave-specific patterns
  2. Explore Scenarios for your use case
  3. Reference API Reference as needed

Building a specific application?

  1. Check Scenarios for similar use cases
  2. Review Common Patterns for reusable patterns
  3. Consult API Reference for details

Additional Resources