Repository: alchemyplatform/alchemy-sdk-js
Pull request #96 (2022-08-16 17:07:38)

Add tokenUriTimeoutInMs param
This PR adds support for the `tokenUriTimeoutInMs` parameter on `getNftsForOwner()`, `getNftMetadata()`, and `getNftsForContract()` methods.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1007ac93..8063f820 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 - Added support for a Wallet object. This object is identical to the Ethers `Wallet` class.
 - Added support for Arbitrum Goerli network via the `Network.ARB_GOERLI` enum.
 - Added support for the AStar Mainnet network via the `Network.ASTAR_MAINNET` enum.
+- Added support the `tokenUriTimeoutInMs` parameter on `getNftsForOwner()`, `getNftMetadata()`, and `getNftsForContract()`.
 
 ## 2.0.2
 
diff --git a/src/api/nft-namespace.ts b/src/api/nft-namespace.ts
index ccc8b1bb..32f53864 100644
--- a/src/api/nft-namespace.ts
+++ b/src/api/nft-namespace.ts
@@ -44,14 +44,25 @@ export class NftNamespace {
    * @param contractAddress - The contract address of the NFT.
    * @param tokenId - Token id of the NFT.
    * @param tokenType - Optionally specify the type of token to speed up the query.
+   * @param tokenUriTimeoutInMs - No set timeout by default - When metadata is
+   *   requested, this parameter is the timeout (in milliseconds) for the
+   *   website hosting the metadata to respond. If you want to only access the
+   *   cache and not live fetch any metadata for cache misses then set this value to 0.
    * @public
    */
   getNftMetadata(
     contractAddress: string,
     tokenId: BigNumberish,
-    tokenType?: NftTokenType
+    tokenType?: NftTokenType,
+    tokenUriTimeoutInMs?: number
   ): Promise<Nft> {
-    return getNftMetadata(this.config, contractAddress, tokenId, tokenType);
+    return getNftMetadata(
+      this.config,
+      contractAddress,
+      tokenId,
+      tokenType,
+      tokenUriTimeoutInMs
+    );
   }
 
   /**
diff --git a/src/internal/nft-api.ts b/src/internal/nft-api.ts
index 9e0334e9..d3b9f782 100644
--- a/src/internal/nft-api.ts
+++ b/src/internal/nft-api.ts
@@ -47,6 +47,7 @@ export async function getNftMetadata(
   contractAddress: string,
   tokenId: BigNumberish,
   tokenType?: NftTokenType,
+  tokenUriTimeoutInMs?: number,
   srcMethod = 'getNftMetadata'
 ): Promise<Nft> {
   const response = await requestHttpWithBackoff<GetNftMetadataParams, RawNft>(
@@ -57,7 +58,8 @@ export async function getNftMetadata(
     {
       contractAddress,
       tokenId: BigNumber.from(tokenId!).toString(),
-      tokenType: tokenType !== NftTokenType.UNKNOWN ? tokenType : undefined
+      tokenType: tokenType !== NftTokenType.UNKNOWN ? tokenType : undefined,
+      tokenUriTimeoutInMs
     }
   );
   return getNftFromRaw(response, contractAddress);
@@ -127,7 +129,8 @@ export async function getNftsForOwner(
     filters: options?.excludeFilters,
     owner,
     pageSize: options?.pageSize,
-    withMetadata
+    withMetadata,
+    tokenUriTimeoutInMs: options?.tokenUriTimeoutInMs
   });
   return {
     ownedNfts: response.ownedNfts.map(res => ({
@@ -147,13 +150,14 @@ export async function getNftsForContract(
 ): Promise<NftContractNftsResponse | NftContractBaseNftsResponse> {
   const withMetadata = omitMetadataToWithMetadata(options?.omitMetadata);
   const response = await requestHttpWithBackoff<
-    GetNftsForNftContractAlchemyParams,
+    GetNftsForContractAlchemyParams,
     RawGetBaseNftsForContractResponse | RawGetNftsForContractResponse
   >(config, AlchemyApiType.NFT, 'getNFTsForCollection', srcMethod, {
     contractAddress,
     startToken: options?.pageKey,
     withMetadata,
-    limit: options?.pageSize ?? undefined
+    limit: options?.pageSize ?? undefined,
+    tokenUriTimeoutInMs: 50
   });
 
   return {
@@ -305,6 +309,7 @@ export async function refreshNftMetadata(
     contractAddress,
     tokenIdString,
     undefined,
+    undefined,
     srcMethod
   );
   const second = await refresh(
@@ -433,11 +438,12 @@ function parseReingestionState(reingestionState: string): RefreshState {
  *
  * @internal
  */
-interface GetNftsForNftContractAlchemyParams {
+interface GetNftsForContractAlchemyParams {
   contractAddress: string;
   startToken?: string;
   withMetadata: boolean;
   limit?: number;
+  tokenUriTimeoutInMs?: number;
 }
 
 /**
@@ -454,6 +460,7 @@ interface GetNftsAlchemyParams {
   filters?: string[];
   pageSize?: number;
   withMetadata: boolean;
+  tokenUriTimeoutInMs?: number;
 }
 
 /**
@@ -466,6 +473,7 @@ interface GetNftMetadataParams {
   tokenId: string;
   tokenType?: NftTokenType;
   refreshCache?: boolean;
+  tokenUriTimeoutInMs?: number;
 }
 
 /**
diff --git a/src/types/types.ts b/src/types/types.ts
index 99625332..c7e65a12 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -264,6 +264,14 @@ export interface GetNftsForOwnerOptions {
 
   /** Optional boolean flag to omit NFT metadata. Defaults to `false`. */
   omitMetadata?: boolean;
+
+  /**
+   * No set timeout by default - When metadata is requested, this parameter is
+   * the timeout (in milliseconds) for the website hosting the metadata to
+   * respond. If you want to only access the cache and not live fetch any
+   * metadata for cache misses then set this value to 0.
+   */
+  tokenUriTimeoutInMs?: number;
 }
 
 /**
@@ -299,6 +307,14 @@ export interface GetBaseNftsForOwnerOptions {
 
   /** Optional boolean flag to include NFT metadata. Defaults to `false`. */
   omitMetadata: true;
+
+  /**
+   * No set timeout by default - When metadata is requested, this parameter is
+   * the timeout (in milliseconds) for the website hosting the metadata to
+   * respond. If you want to only access the cache and not live fetch any
+   * metadata for cache misses then set this value to 0.
+   */
+  tokenUriTimeoutInMs?: number;
 }
 
 /**
@@ -531,6 +547,14 @@ export interface GetNftsForContractOptions {
    * Maximum page size is 100.
    */
   pageSize?: number;
+
+  /**
+   * No set timeout by default - When metadata is requested, this parameter is
+   * the timeout (in milliseconds) for the website hosting the metadata to
+   * respond. If you want to only access the cache and not live fetch any
+   * metadata for cache misses then set this value to 0.
+   */
+  tokenUriTimeoutInMs?: number;
 }
 
 /**


Test patch:
diff --git a/test/unit/nft-api.test.ts b/test/unit/nft-api.test.ts
index 2cd0f30c..294266b4 100644
--- a/test/unit/nft-api.test.ts
+++ b/test/unit/nft-api.test.ts
@@ -123,6 +123,7 @@ describe('NFT module', () => {
     const contractAddress = '0xABC';
     const title = 'NFT Title';
     const tokenId = '42';
+    const timeoutInMs = 50;
     // Special case token ID as an integer string, since that's what the NFT
     // API endpoint returns.
     const rawNftResponse = createRawNft(title, tokenId.toString());
@@ -137,7 +138,8 @@ describe('NFT module', () => {
       expectedNft: Nft,
       contractAddress: string,
       tokenId: string,
-      tokenType?: NftTokenType
+      tokenType?: NftTokenType,
+      timeoutInMs?: number
     ) {
       expect(actualNft).toEqual(expectedNft);
       expect(actualNft.tokenId).toEqual(tokenId);
@@ -151,6 +153,10 @@ describe('NFT module', () => {
         'tokenType',
         tokenType ?? undefined
       );
+      expect(mock.history.get[0].params).toHaveProperty(
+        'tokenUriTimeoutInMs',
+        timeoutInMs ?? undefined
+      );
     }
 
     it('can be called with raw parameters', async () => {
@@ -158,12 +164,14 @@ describe('NFT module', () => {
         await alchemy.nft.getNftMetadata(
           contractAddress,
           tokenId,
-          NftTokenType.ERC1155
+          NftTokenType.ERC1155,
+          timeoutInMs
         ),
         expectedNft,
         contractAddress,
         tokenId,
-        NftTokenType.ERC1155
+        NftTokenType.ERC1155,
+        timeoutInMs
       );
     });
 
@@ -213,7 +221,8 @@ describe('NFT module', () => {
       pageKey,
       contractAddresses,
       excludeFilters,
-      pageSize: 3
+      pageSize: 3,
+      tokenUriTimeoutInMs: 50
     };
     const baseNftResponse: RawGetBaseNftsResponse = {
       ownedNfts: [
@@ -265,6 +274,10 @@ describe('NFT module', () => {
           'withMetadata',
           expectedWithMetadata
         );
+        expect(mock.history.get[0].params).toHaveProperty(
+          'tokenUriTimeoutInMs',
+          50
+        );
       }
     );
 
@@ -580,7 +593,8 @@ describe('NFT module', () => {
         await alchemy.nft.getNftsForContract(contractAddress, {
           pageKey,
           omitMetadata,
-          pageSize: 90
+          pageSize: 90,
+          tokenUriTimeoutInMs: 50
         });
         expect(mock.history.get.length).toEqual(1);
         expect(mock.history.get[0].params).toHaveProperty(
@@ -596,6 +610,10 @@ describe('NFT module', () => {
           expectedWithMetadata
         );
         expect(mock.history.get[0].params).toHaveProperty('limit', 90);
+        expect(mock.history.get[0].params).toHaveProperty(
+          'tokenUriTimeoutInMs',
+          50
+        );
       }
     );