Querying Network State
Learn how to query the Convex network to read account balances, smart contract state, and execute read-only Convex Lisp expressions.
Overview
Queries are read-only operations that don't require an account or keys. They allow you to:
- Read account balances
- Query smart contract state
- Execute Convex Lisp expressions
- Inspect network data
Queries do not modify state and are not recorded as transactions.
Basic Query
The simplest way to query uses a string containing Convex Lisp code:
import { Convex } from '@convex-world/convex-ts';
const convex = new Convex('https://peer.convex.live');
// Query an account balance
const result = await convex.query('(balance #13)');
console.log('Balance:', result.value);
Query with Address Context
Some queries need an address context (the *address* special variable):
// Query using address context
const result = await convex.query({
address: '#1678',
source: '*balance*' // Uses the context address
});
console.log('Balance:', result.value);
When to Use Address Context
Use the object form with address when:
- Your query references
*address*or other address-specific variables - You need to execute code as if you were a specific account
- Querying actor-specific state
For most queries, the simple string form is sufficient.
Common Query Patterns
Account Balances
// Specific account
const balance = await convex.query('(balance #123)');
// Multiple accounts
const accounts = ['#9', '#10', '#11'];
for (const addr of accounts) {
const result = await convex.query(`(balance ${addr})`);
console.log(`${addr}:`, result.value);
}
Mathematical Expressions
// Execute Convex Lisp math
const result = await convex.query('(+ 1 2 3 4 5)');
console.log('Sum:', result.value); // 15
// More complex
const calc = await convex.query('(* (+ 10 5) (- 8 3))');
console.log('Result:', calc.value); // 75
Smart Contract Calls
// Query a smart contract
const result = await convex.query('(call #789 (get-price :BTC))');
console.log('BTC Price:', result.value);
// With parameters
const tokenInfo = await convex.query(`
(call *registry* (cns-resolve :my-token))
`);
console.log('Token:', tokenInfo.value);
Registry Lookups
// CNS (Convex Name Service) lookups
const address = await convex.query(`
(call *registry* (cns-resolve :convex.trust))
`);
// CAD (Convex Architecture Document) lookups
const cadInfo = await convex.query(`
(call *registry* (lookup :CAD001))
`);
Handling Query Results
Result Structure
Query results have this structure:
interface Result {
value: any; // The query result value
errorCode?: string; // Present if query failed
info?: any; // Additional information
}
Error Handling
try {
const result = await convex.query('(balance #123)');
if (result.errorCode) {
console.error('Query failed:', result.errorCode);
} else {
console.log('Success:', result.value);
}
} catch (error) {
console.error('Network error:', error);
}
Type Checking
const result = await convex.query('(balance #123)');
// Parse numeric values
const balance = Number(result.value);
if (isNaN(balance)) {
throw new Error('Expected numeric balance');
}
console.log('Balance in coins:', balance / 1_000_000_000);
Advanced Queries
Multi-line Queries
const result = await convex.query(`
(do
(def total-supply 1000000000)
(def circulating (* total-supply 0.3))
{:total total-supply
:circulating circulating
:locked (- total-supply circulating)})
`);
console.log('Token metrics:', result.value);
Querying Collections
// Query a map
const userData = await convex.query(`
(get user-data #1678)
`);
// Query a vector
const topScores = await convex.query(`
(get-holding #789 :top-10-scores)
`);
Conditional Queries
const result = await convex.query(`
(if (> (balance #123) 1000000)
"Sufficient balance"
"Insufficient balance")
`);
Performance Tips
Batch Queries
Run multiple independent queries in parallel:
const [balance1, balance2, balance3] = await Promise.all([
convex.query('(balance #100)'),
convex.query('(balance #200)'),
convex.query('(balance #300)')
]);
Cache Results
Cache query results when appropriate:
class ConvexCache {
private cache = new Map<string, { value: any; expires: number }>();
async query(convex: Convex, source: string, ttl = 5000) {
const cached = this.cache.get(source);
if (cached && Date.now() < cached.expires) {
return cached.value;
}
const result = await convex.query(source);
this.cache.set(source, {
value: result,
expires: Date.now() + ttl
});
return result;
}
}
Optimise Query Logic
// ❌ Bad: Multiple round trips
const balance = await convex.query('(balance #123)');
const sequence = await convex.query('(account-sequence #123)');
// ✅ Good: Single query
const result = await convex.query(`
{:balance (balance #123)
:sequence (account-sequence #123)}
`);
Next Steps
- Transactions - Learn to modify state
- Convex Lisp Guide - Master the query language
- API Reference - Complete API documentation