'Apply production-ready Alchemy SDK patterns for Web3 applications.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill alchemy-sdk-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Alchemy Sdk Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-alchemy-sdk-patterns-tons-of-skills-marketplace)More formats (shields.io, HTML) on the badges page.
---
name: alchemy-sdk-patterns
description: 'Apply production-ready Alchemy SDK patterns for Web3 applications.
Use when building reusable blockchain clients, implementing caching,
multi-chain abstractions, or type-safe contract interactions.
Trigger: "alchemy SDK patterns", "alchemy best practices", "alchemy code patterns".
'
allowed-tools: Read, Write, Edit
version: 1.5.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- blockchain
- web3
- alchemy
- patterns
compatibility: Designed for Claude Code
---
# Alchemy SDK Patterns
## Overview
Production patterns for the `alchemy-sdk` package: singleton clients, multi-chain factories, response caching, and type-safe contract wrappers.
## Prerequisites
- A server-side environment with a managed provider key and a typed list of
supported chains; do not create browser-side clients with the key.
- A freshness policy for each cached response type and a test fixture for both
successful and failed provider calls.
- Input validation at the application boundary before an address, collection,
or network value reaches the client factory or query builder.
## Instructions
### Step 1: Multi-Chain Client Factory
```typescript
// src/alchemy/client-factory.ts
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
```typescript
// src/alchemy/cache.ts
interface CacheEntry<T> { data: T; expiresAt: number; }
class AlchemyCache {
private cache = new Map<string, CacheEntry<any>>();
private defaultTtlMs: number;
constructor(defaultTtlMs: number = 30000) { // 30s default
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.now() + (ttlMs || this.defaultTtlMs) });
return data;
}
invalidate(keyPrefix: string): void {
for (const key of this.cache.keys()) {
if (key.startsWith(keyPrefix)) this.cache.delete(key);
}
}
}
// Usage with Alchemy
const cache = new AlchemyCache();
async function getCachedBalance(alchemy: Alchemy, address: string): Promise<string> {
return cache.getOrFetch(
`balance:${address}`,
async () => {
const balance = await alchemy.core.getBalance(address);
return (parseInt(balance.toString()) / 1e18).toFixed(6);
},
15000 // 15s cache for balances
);
}
export { AlchemyCache, getCachedBalance };
```
### Step 3: Typed NFT Query Builder
```typescript
// src/alchemy/nft-query.ts
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; return this; }
excludeSpam(): this { this._excludeFilters.push('SPAM'); return this; }
async execute() {
if (!this._owner) throw new Error('Owner address required');
return this.alchemy.nft.getNftsForOwner(this._owner, {
contractAddresses: this._contracts.length > 0 ? this._contracts : undefined,
pageSize: this._pageSize,
excludeFilters: this._excludeFilters as any[],
});
}
}
// Usage:
// const nfts = await new NftQueryBuilder(alchemy)
// .forOwner('vitalik.eth')
// .excludeSpam()
// .pageSize(50)
// .execute();
```
### Step 4: Error Classification
```typescript
// src/alchemy/errors.ts
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 === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') return { type: 'network', retryable: true, message: 'Network error' };
if (error.message?.includes('invalid params')) return { type: 'invalid_params', retryable: false, message: error.message };
return { type: 'unknown', retryable: false, message: error.message };
}
export { classifyError, AlchemyErrorType };
```
## 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
## Examples
In a staging service, request a balance for a public test address twice through
`getCachedBalance`, assert the second request is a cache hit within the
approved TTL, then invalidate the prefix and verify the next request reaches
the provider. Use the factory only for a configured chain and reject an
unrecognized chain at the route boundary. Inject a `429` and a `401` into the
error classifier to prove that only the rate-limit case is retryable. If a
cached value breaches its freshness rule, a chain is not configured, or a key
reaches a client artifact, disable the route and repair that boundary first.
## Error Handling
| Failure | Response |
|---------|----------|
| Provider rate limit or transient server error | Use bounded retry/backoff and retain a sanitized outcome metric. |
| Authentication or authorization failure | Stop retries, verify managed-secret configuration, and rotate a suspected exposure. |
| Invalid address, contract, or chain input | Reject before creating a provider operation. |
| Cache contains stale or incompatible data | Invalidate it and refetch according to the declared freshness policy. |
## Resources
- [Alchemy SDK GitHub](https://github.com/alchemyplatform/alchemy-sdk-js)
- [Alchemy Docs](https://www.alchemy.com/docs)
## Next Steps
Apply patterns in `alchemy-core-workflow-a` for real portfolio tracking.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!