Alchemy SDK Patterns
Overview
Production patterns for the alchemy-sdk package: singleton clients, multi-chain factories, response caching, and type-safe contract wrappers.
Instructions
Step 1: Multi-Chain Client Factory
import { Alchemy, Network } from 'alchemy-sdk';
type ChainName = 'ethereum' | 'polygon' | 'arbitrum' | 'optimism' | 'base';
const NETWORK_MAP: Record<ChainName, Network> = {
ethereum: Network.ETH_MAINNET,
polygon: Network.MATIC_MAINNET,
arbitrum: Network.ARB_MAINNET,
optimism: Network.OPT_MAINNET,
base: Network.BASE_MAINNET,
};
class AlchemyClientFactory {
private static clients = new Map<string, Alchemy>();
static getClient(chain: ChainName): Alchemy {
if (!this.clients.has(chain)) {
this.clients.set(chain, new Alchemy({
apiKey: process.env.ALCHEMY_API_KEY,
network: NETWORK_MAP[chain],
maxRetries: 3,
}));
}
return this.clients.get(chain)!;
}
static getAllClients(): Map<ChainName, Alchemy> {
for (const chain of Object.keys(NETWORK_MAP) as ChainName[]) {
this.getClient(chain);
}
return this.clients as Map<ChainName, Alchemy>;
}
}
export { AlchemyClientFactory, ChainName };
Step 2: Response Caching Layer
interface CacheEntry<T> { data: T; expiresAt: number; }
class AlchemyCache {
private cache = new Map<string, CacheEntry<any>>();
private defaultTtlMs: number;
constructor(defaultTtlMs: number = 30000) {
this.defaultTtlMs = defaultTtlMs;
}
async getOrFetch<T>(key: string, fetcher: () => Promise<T>, ttlMs?: number): Promise<T> {
const cached = this.cache.get(key);
if (cached && cached.expiresAt > Date.now()) return cached.data;
const data = await fetcher();
this.cache.set(key, { data, expiresAt: Date.() + (ttlMs || .) });
data;
}
(: ): {
( key ..()) {
(key.(keyPrefix)) ..(key);
}
}
}
cache = ();
(): <> {
cache.(
,
() => {
balance = alchemy..(address);
((balance.()) / ).();
},
);
}
{ , getCachedBalance };
Step 3: Typed NFT Query Builder
import { Alchemy, NftOrdering } from 'alchemy-sdk';
class NftQueryBuilder {
private alchemy: Alchemy;
private _owner?: string;
private _contracts: string[] = [];
private _pageSize = 20;
private _excludeFilters: string[] = [];
constructor(alchemy: Alchemy) { this.alchemy = alchemy; }
forOwner(address: string): this { this._owner = address; return this; }
inCollection(contractAddress: string): this { this._contracts.push(contractAddress); return this; }
pageSize(size: number): this { this._pageSize = size; ; }
(): { ..(); ; }
() {
(!.) ();
...(., {
: .. > ? . : ,
: .,
: . [],
});
}
}
Step 4: Error Classification
type AlchemyErrorType = 'rate_limit' | 'auth' | 'network' | 'invalid_params' | 'server' | 'unknown';
function classifyError(error: any): { type: AlchemyErrorType; retryable: boolean; message: string } {
const status = error.response?.status || error.code;
if (status === 429) return { type: 'rate_limit', retryable: true, message: 'Rate limit exceeded' };
if (status === 401 || status === 403) return { type: 'auth', retryable: false, message: 'Invalid API key' };
if (status >= 500) return { type: 'server', retryable: true, message: 'Alchemy server error' };
if (error.code === || error. === ) { : , : , : };
(error.?.()) { : , : , : error. };
{ : , : , : error. };
}
{ classifyError, };
Output
- Multi-chain client factory with lazy initialization
- Response cache with configurable TTL
- Type-safe NFT query builder pattern
- Structured error classification for retry decisions
Resources
Next Steps
Apply patterns in alchemy-core-workflow-a for real portfolio tracking.