imagekit-sdk-reference

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ImageKit TypeScript SDK Reference

ImageKit TypeScript SDK 参考文档

Read this skill before calling any
mcp_imagekit_api_*
tool or writing TypeScript code against the ImageKit SDK. It contains exact method signatures, parameter types, return shapes, and error handling patterns for
@imagekit/nodejs
.
Rules:
  1. Use exact parameter names — the SDK is strict about camelCase
  2. assets.list()
    returns
    (File | Folder)[]
    . Narrow with
    for...of
    +
    if (item.type === 'file')
    , never
    .filter((i): i is File => ...)
    . See the
    search-assets
    skill for the full rules on
    searchQuery
    vs a typed
    File[]
    and why the predicate fails.
  3. In
    execute
    /MCP code, do NOT try/catch single API calls — the tool reports errors for you. Only catch when you branch on a specific failure, and duck-type the error (
    'status' in err
    ) rather than
    instanceof ImageKit.APIError
    , since a value import of the SDK is not available in the sandbox.
  4. Use
    skip
    /
    limit
    for pagination (max 1000 per request)
  5. Uploads are URL-only — the
    file
    param must be a URL string; local file paths, Buffers, and streams cannot be passed. Read the
    upload-files
    skill first.
  6. Nullable properties (
    tags
    ,
    AITags
    ,
    customCoordinates
    ) require optional chaining (
    ?.
    ) or null checks
  7. .find()
    returns
    T | undefined
    — always check for
    undefined
    before accessing properties

在调用任何
mcp_imagekit_api_*
工具或针对 ImageKit SDK 编写 TypeScript 代码前,请阅读本技能文档。它包含
@imagekit/nodejs
的精确方法签名、参数类型、返回结构及错误处理模式。
规则:
  1. 使用精确的参数名称——SDK 对驼峰命名(camelCase)要求严格
  2. assets.list()
    返回
    (File | Folder)[]
    。需通过
    for...of
    +
    if (item.type === 'file')
    来收窄类型,绝不能使用
    .filter((i): i is File => ...)
    。关于
    searchQuery
    与类型化
    File[]
    的完整规则,以及断言失败的原因,请查看
    search-assets
    技能文档。
  3. execute
    /MCP 代码中,不要对单个 API 调用进行 try/catch 捕获——工具会自动为你上报错误。仅当你需要针对特定失败分支处理时才捕获,并且要通过鸭子类型判断错误(
    'status' in err
    ),而非
    instanceof ImageKit.APIError
    ,因为沙箱环境中无法导入 SDK 的值。
  4. 使用
    skip
    /
    limit
    实现分页(每次请求最多 1000 条数据)
  5. 仅支持 URL 上传——
    file
    参数必须是URL 字符串;无法传入本地文件路径、Buffer 或流。请先阅读
    upload-files
    技能文档。
  6. 可为空属性(
    tags
    AITags
    customCoordinates
    )需要使用可选链操作符(
    ?.
    )或空值检查
  7. .find()
    返回
    T | undefined
    ——访问属性前必须检查是否为
    undefined

TypeScript Gotchas

TypeScript 注意事项

Union narrowing for
assets.list()
results —
for...of
+
if
, why the
.filter((i): i is File => ...)
predicate collides with Deno's global
File
, and
searchQuery
vs a typed
File[]
— is covered in full by the
search-assets
skill
; follow it when handling list results. The gotchas below are the SDK-specific ones not covered there.
PatternProblemFix
.find(i => ...)
Returns
T | undefined
Check for
undefined
+ narrow type
file.tags
string[] | null
Use
?.
or null check
file.AITags
Array | null
Use
?.
or null check
Any member-specific prop on a
File | Folder
from
assets.list()
Only shared fields (
name
,
type
,
createdAt
,
updatedAt
,
customMetadata
) exist on the union; the rest need narrowing
Narrow once with
if
, then access freely. Branch on
type === 'folder'
(→
Folder
; else →
File
, since
File.type
is
'file' | 'file-version'
)
assets.list()
结果的联合类型收窄——使用
for...of
+
if
,以及
.filter((i): i is File => ...)
断言与 Deno 全局
File
冲突的原因、
searchQuery
与类型化
File[]
的区别——这些内容已在
search-assets
技能文档
中详细说明;处理列表结果时请遵循该文档。以下是未在该文档中覆盖的 SDK 专属注意事项。
模式问题修复方案
.find(i => ...)
返回
T | undefined
检查是否为
undefined
并收窄类型
file.tags
类型为
string[] | null
使用
?.
或空值检查
file.AITags
类型为
Array | null
使用
?.
或空值检查
assets.list()
获取的
File | Folder
中的成员专属属性
联合类型仅包含共享字段(
name
type
createdAt
updatedAt
customMetadata
);其余字段需要收窄类型
使用
if
进行一次类型收窄,之后即可自由访问属性。通过
type === 'folder'
分支判断(→
Folder
类型;否则 →
File
类型,因为
File.type
'file' | 'file-version'

.find()
returns
T | undefined

.find()
返回
T | undefined

typescript
const item = assets.find((i) => i.name === 'hero.jpg');
// Type: (File | Folder) | undefined

item.fileId; // ❌ Two errors: possibly undefined AND possibly Folder

// ✅ Fix:
if (item && item.type === 'file') {
  item.fileId; // works
}
typescript
const item = assets.find((i) => i.name === 'hero.jpg');
// 类型: (File | Folder) | undefined

item.fileId; // ❌ 两个错误:可能为 undefined,且可能是 Folder 类型

// ✅ 修复方案:
if (item && item.type === 'file') {
  item.fileId; // 可正常访问
}

Nullable properties on File

File 类型的可为空属性

typescript
const file = await client.files.get(fileId);

file.tags.length;     // ❌ tags is string[] | null
file.tags?.length;    // ✅ optional chaining

file.AITags.map(...); // ❌ AITags is Array | null
file.AITags?.map(...); // ✅

typescript
const file = await client.files.get(fileId);

file.tags.length;     // ❌ tags 类型为 string[] | null
file.tags?.length;    // ✅ 使用可选链操作符

file.AITags.map(...); // ❌ AITags 类型为 Array | null
file.AITags?.map(...); // ✅

Types

类型定义

File

File

typescript
{
  fileId: string; name: string; filePath: string; type: 'file' | 'file-version';
  url: string; thumbnail: string; isPrivateFile: boolean; isPublished: boolean;
  // Media
  fileType: string; // 'image' | 'non-image'
  mime: string; size: number; width: number; height: number; hasAlpha: boolean;
  // Video-only
  duration: number; videoCodec: string; audioCodec: string; bitRate: number;
  // Tags & metadata
  tags: string[] | null;
  AITags: Array<{ name: string; confidence: number; source: string }> | null;
  customMetadata: Record<string, unknown>; description: string;
  customCoordinates: string | null;
  embeddedMetadata: Record<string, unknown>;
  // Versioning
  versionInfo: { id: string; name: string };
  createdAt: string; updatedAt: string;
}
typescript
{
  fileId: string; name: string; filePath: string; type: 'file' | 'file-version';
  url: string; thumbnail: string; isPrivateFile: boolean; isPublished: boolean;
  // 媒体信息
  fileType: string; // 'image' | 'non-image'
  mime: string; size: number; width: number; height: number; hasAlpha: boolean;
  // 视频专属信息
  duration: number; videoCodec: string; audioCodec: string; bitRate: number;
  // 标签与元数据
  tags: string[] | null;
  AITags: Array<{ name: string; confidence: number; source: string }> | null;
  customMetadata: Record<string, unknown>; description: string;
  customCoordinates: string | null;
  embeddedMetadata: Record<string, unknown>;
  // 版本信息
  versionInfo: { id: string; name: string };
  createdAt: string; updatedAt: string;
}

Folder

Folder

typescript
{
  folderId: string; folderPath: string; name: string; type: 'folder';
  customMetadata: Record<string, unknown>;
  createdAt: string; updatedAt: string;
}
typescript
{
  folderId: string; folderPath: string; name: string; type: 'folder';
  customMetadata: Record<string, unknown>;
  createdAt: string; updatedAt: string;
}

CustomMetadataField

CustomMetadataField

typescript
{
  id: string; name: string; label: string;
  schema: { type: 'Text' | 'Number' | 'Date' | 'Boolean' | 'SingleSelect' | 'MultiSelect'; /* ... */ };
}

typescript
{
  id: string; name: string; label: string;
  schema: { type: 'Text' | 'Number' | 'Date' | 'Boolean' | 'SingleSelect' | 'MultiSelect'; /* ... */ };
}

File Operations

文件操作

Upload a file (URL only)

上传文件(仅支持URL)

typescript
// ⚠️ ONLY URL-based uploads work. Local files cannot be uploaded.
const file = await client.files.upload({
  file: 'https://example.com/img.jpg', // URL string ONLY in MCP context
  fileName: 'img.jpg',
  folder: '/uploads',
  tags: ['tag1'],
  customMetadata: { key: 'value' },
  // Key optional params:
  // useUniqueFileName: true,      // default true — appends random suffix
  // isPrivateFile: false,
  // overwriteFile: false,         // replace existing file at same path
  // overwriteTags: false,
  // overwriteCustomMetadata: false,
  // extensions: [{ name: 'google-auto-tagging', maxTags: 5 }],
  // transformation: { pre: 'w-200' },
  // webhookUrl: 'https://...',
});
// Returns: File object
typescript
// ⚠️ 仅支持基于URL的上传。无法上传本地文件。
const file = await client.files.upload({
  file: 'https://example.com/img.jpg', // MCP环境下仅支持URL字符串
  fileName: 'img.jpg',
  folder: '/uploads',
  tags: ['tag1'],
  customMetadata: { key: 'value' },
  // 可选参数:
  // useUniqueFileName: true,      // 默认值true — 追加随机后缀
  // isPrivateFile: false,
  // overwriteFile: false,         // 替换相同路径下的现有文件
  // overwriteTags: false,
  // overwriteCustomMetadata: false,
  // extensions: [{ name: 'google-auto-tagging', maxTags: 5 }],
  // transformation: { pre: 'w-200' },
  // webhookUrl: 'https://...',
});
// 返回值: File 对象

Get file details

获取文件详情

typescript
const file = await client.files.get(fileId); // Returns: File
typescript
const file = await client.files.get(fileId); // 返回值: File

List / search assets

列出/搜索资源

typescript
const result = await client.assets.list({
  searchQuery: 'name = "img.jpg"', // Lucene-like syntax
  path: '/uploads',
  fileType: 'image',           // 'image' | 'non-image' | 'all'
  type: 'file',                // 'file' | 'folder' | 'file-version' | 'all'
  sort: 'ASC_NAME',
  skip: 0,
  limit: 100,
});
// Returns: (File | Folder)[] — a flat array, NOT { files, folders }
Type narrowing depends on whether you use
searchQuery
— see the
search-assets
skill for the full rules (a top-level
type
gives a typed
File[]
; a
searchQuery
returns the
(File | Folder)[]
union, which you narrow with
for...of
+
if
).
Shared properties (safe on both File and Folder):
name
,
type
,
customMetadata
,
createdAt
,
updatedAt
File-only properties (require narrowing):
fileId
,
filePath
,
fileType
,
mime
,
size
,
width
,
height
,
url
,
thumbnail
,
tags
,
AITags
,
description
,
isPrivateFile
,
isPublished
,
customCoordinates
,
embeddedMetadata
,
versionInfo
,
duration
,
videoCodec
,
audioCodec
,
bitRate
,
hasAlpha
Folder-only properties:
folderId
,
folderPath
typescript
const result = await client.assets.list({
  searchQuery: 'name = "img.jpg"', // 类Lucene语法
  path: '/uploads',
  fileType: 'image',           // 'image' | 'non-image' | 'all'
  type: 'file',                // 'file' | 'folder' | 'file-version' | 'all'
  sort: 'ASC_NAME',
  skip: 0,
  limit: 100,
});
// 返回值: (File | Folder)[] — 扁平数组,而非 { files, folders }
类型收窄取决于是否使用
searchQuery
——完整规则请查看
search-assets
技能文档(顶级
type
参数会返回类型化的
File[]
searchQuery
返回
(File | Folder)[]
联合类型,需通过
for...of
+
if
收窄类型)。
共享属性(File和Folder均可安全访问):
name
type
customMetadata
createdAt
updatedAt
File专属属性(需要类型收窄):
fileId
filePath
fileType
mime
size
width
height
url
thumbnail
tags
AITags
description
isPrivateFile
isPublished
customCoordinates
embeddedMetadata
versionInfo
duration
videoCodec
audioCodec
bitRate
hasAlpha
Folder专属属性
folderId
folderPath

Update / delete file

更新/删除文件

typescript
await client.files.update(fileId, { tags: ['newTag'], customMetadata: { key: 'value' } }); // Returns: File
await client.files.delete(fileId); // Returns: void
typescript
await client.files.update(fileId, { tags: ['newTag'], customMetadata: { key: 'value' } }); // 返回值: File
await client.files.delete(fileId); // 返回值: void

Copy / move / rename file

复制/移动/重命名文件

typescript
await client.files.copy({ sourceFilePath: '/a/img.jpg', destinationPath: '/b/', includeFileVersions: false });
await client.files.move({ sourceFilePath: '/a/img.jpg', destinationPath: '/b/' });
await client.files.rename({ filePath: '/a/img.jpg', newFileName: 'new.jpg', purgeCache: false });

typescript
await client.files.copy({ sourceFilePath: '/a/img.jpg', destinationPath: '/b/', includeFileVersions: false });
await client.files.move({ sourceFilePath: '/a/img.jpg', destinationPath: '/b/' });
await client.files.rename({ filePath: '/a/img.jpg', newFileName: 'new.jpg', purgeCache: false });

Bulk Operations

批量操作

typescript
await client.files.bulk.delete({ fileIds: ['id1', 'id2'] }); // { successfullyDeletedFileIds }
await client.files.bulk.addTags({ fileIds: ['id1'], tags: ['promo'] });    // max 50 files
await client.files.bulk.removeTags({ fileIds: ['id1'], tags: ['old'] });
await client.files.bulk.removeAITags({ fileIds: ['id1'], AITags: ['cat'] });

typescript
await client.files.bulk.delete({ fileIds: ['id1', 'id2'] }); // { successfullyDeletedFileIds }
await client.files.bulk.addTags({ fileIds: ['id1'], tags: ['promo'] });    // 最多支持50个文件
await client.files.bulk.removeTags({ fileIds: ['id1'], tags: ['old'] });
await client.files.bulk.removeAITags({ fileIds: ['id1'], AITags: ['cat'] });

Folder Operations

文件夹操作

typescript
await client.folders.create({ folderName: 'myfolder', parentFolderPath: '/' });
await client.folders.delete({ folderPath: '/myfolder' });

// Copy / move / rename — async operations, return { jobId }
const { jobId } = await client.folders.copy({ sourceFolderPath: '/a', destinationPath: '/b/' });
const { jobId } = await client.folders.move({ sourceFolderPath: '/a', destinationPath: '/b/' });
const { jobId } = await client.folders.rename({ folderPath: '/a', newFolderName: 'renamed' });

// Check job status
const job = await client.folders.job.get(jobId);
// job.status: 'Pending' | 'Completed'

typescript
await client.folders.create({ folderName: 'myfolder', parentFolderPath: '/' });
await client.folders.delete({ folderPath: '/myfolder' });

// 复制/移动/重命名——异步操作,返回 { jobId }
const { jobId } = await client.folders.copy({ sourceFolderPath: '/a', destinationPath: '/b/' });
const { jobId } = await client.folders.move({ sourceFolderPath: '/a', destinationPath: '/b/' });
const { jobId } = await client.folders.rename({ folderPath: '/a', newFolderName: 'renamed' });

// 检查任务状态
const job = await client.folders.job.get(jobId);
// job.status: 'Pending' | 'Completed'

File Versions

文件版本管理

typescript
const versions = await client.files.versions.list(fileId);      // Returns: File[]
const version = await client.files.versions.get(versionId, { fileId }); // Returns: File
await client.files.versions.restore(versionId, { fileId });     // Returns: File
await client.files.versions.delete(versionId, { fileId });      // Returns: void

typescript
const versions = await client.files.versions.list(fileId);      // 返回值: File[]
const version = await client.files.versions.get(versionId, { fileId }); // 返回值: File
await client.files.versions.restore(versionId, { fileId });     // 返回值: File
await client.files.versions.delete(versionId, { fileId });      // 返回值: void

File Metadata

文件元数据

typescript
const metadata = await client.files.metadata.getFromURL({ url: 'https://ik.imagekit.io/x/img.jpg' });
// Returns EXIF/IPTC metadata: { height, width, exif, iptc, xmp, ... }

typescript
const metadata = await client.files.metadata.getFromURL({ url: 'https://ik.imagekit.io/x/img.jpg' });
// 返回EXIF/IPTC元数据: { height, width, exif, iptc, xmp, ... }

Cache Invalidation

缓存失效

typescript
const purge = await client.cache.invalidation.create({ url: 'https://ik.imagekit.io/x/img.jpg' });
const status = await client.cache.invalidation.get(purge.requestId);
// status.status: 'Pending' | 'Completed'

typescript
const purge = await client.cache.invalidation.create({ url: 'https://ik.imagekit.io/x/img.jpg' });
const status = await client.cache.invalidation.get(purge.requestId);
// status.status: 'Pending' | 'Completed'

URL Building

URL 构建

typescript
const url = client.helper.buildSrc({
  urlEndpoint: 'https://ik.imagekit.io/your_id',
  src: '/path/img.jpg',
  transformation: [{ width: 400, height: 300, format: 'webp', quality: 80 }],
  signed: true,
  expiresIn: 3600,
});
// Returns: string

typescript
const url = client.helper.buildSrc({
  urlEndpoint: 'https://ik.imagekit.io/your_id',
  src: '/path/img.jpg',
  transformation: [{ width: 400, height: 300, format: 'webp', quality: 80 }],
  signed: true,
  expiresIn: 3600,
});
// 返回值: string

Custom Metadata Fields

自定义元数据字段

typescript
// Define schema fields for your media library
const field = await client.customMetadataFields.create({
  name: 'brand', label: 'Brand Name',
  schema: { type: 'Text', defaultValue: '', isValueRequired: false },
});
const fields = await client.customMetadataFields.list(); // Returns: CustomMetadataField[]
await client.customMetadataFields.update(field.id, { label: 'Updated Label' });
await client.customMetadataFields.delete(field.id);

typescript
// 为媒体库定义 schema 字段
const field = await client.customMetadataFields.create({
  name: 'brand', label: 'Brand Name',
  schema: { type: 'Text', defaultValue: '', isValueRequired: false },
});
const fields = await client.customMetadataFields.list(); // 返回值: CustomMetadataField[]
await client.customMetadataFields.update(field.id, { label: 'Updated Label' });
await client.customMetadataFields.delete(field.id);

Account Usage

账户使用情况

typescript
const usage = await client.accounts.usage.get({ startDate: '2025-01-01', endDate: '2025-01-31' });
// { bandwidthBytes, mediaLibraryStorageBytes, extensionUnitsCount, videoProcessingUnitsCount }

typescript
const usage = await client.accounts.usage.get({ startDate: '2025-01-01', endDate: '2025-01-31' });
// { bandwidthBytes, mediaLibraryStorageBytes, extensionUnitsCount, videoProcessingUnitsCount }

Pagination & Error Handling

分页与错误处理

typescript
// Offset-based pagination — skip / limit (max 1000). No cursor support.
for (let skip = 0; ; skip += 100) {
  const page = await client.assets.list({ skip, limit: 100 });
  if (!page.length) break;
  for (const item of page) {
    if (item.type === 'file') {
      // item is narrowed to File here
    }
  }
}

// Error handling — in execute/MCP code you normally DON'T need try/catch: let the
// error propagate and the tool reports it for you. The ONLY reason to catch is to
// branch on a specific failure (e.g. treat 404 as "not found") — and then you must
// re-throw everything you don't handle. Duck-type the error: a value import of the
// SDK (import ImageKit from '@imagekit/nodejs') is NOT available in the Deno sandbox
// and throws at runtime, so don't rely on `instanceof ImageKit.APIError`.
try {
  return await client.files.get(fileId);
} catch (err) {
  if (err && typeof err === 'object' && 'status' in err) {
    const e = err as { status?: number; message?: string };
    if (e.status === 404) return null;  // the one case we handle
  }
  throw err;  // re-throw anything else — don't swallow it
}
// Auto-retries: connection errors, 408/409/429/5xx — up to 2× with exponential backoff
typescript
// 基于偏移量的分页 — skip / limit(最多1000条)。不支持游标分页。
for (let skip = 0; ; skip += 100) {
  const page = await client.assets.list({ skip, limit: 100 });
  if (!page.length) break;
  for (const item of page) {
    if (item.type === 'file') {
      // 此处 item 已收窄为 File 类型
    }
  }
}

// 错误处理 — 在execute/MCP代码中通常不需要try/catch:让错误向上传播,工具会自动上报。唯一需要捕获的场景是针对特定失败分支处理(例如将404视为“未找到”)——此时必须重新抛出所有未处理的错误。使用鸭子类型判断错误:在Deno沙箱环境中无法导入SDK(import ImageKit from '@imagekit/nodejs'),运行时会抛出错误,因此不要依赖 `instanceof ImageKit.APIError`。
try {
  return await client.files.get(fileId);
} catch (err) {
  if (err && typeof err === 'object' && 'status' in err) {
    const e = err as { status?: number; message?: string };
    if (e.status === 404) return null;  // 唯一需要处理的情况
  }
  throw err;  // 重新抛出其他错误——不要吞掉错误
}
// 自动重试:连接错误、408/409/429/5xx状态码——最多重试2次,使用指数退避策略

Parallel Execution for Bulk File Operations

批量文件操作的并行执行

When you have a list of files to operate on, never await in a loop. Chunk into batches of 100 and run each batch concurrently with Promise.allSettled().
typescript
// ✅ files is any array of { fileId, name } — from assets.list(), a prior search, etc.
const CHUNK = 100;
const chunks = [];
for (let i = 0; i < files.length; i += CHUNK) chunks.push(files.slice(i, i + CHUNK));

for (const chunk of chunks) {
  await Promise.allSettled(
    chunk.map(({ fileId, name }) =>
      client.files.update(fileId, { tags: ['promo'] })
        .then(() => ({ fileId, name, status: 'ok' }))
        .catch((err: unknown) => ({ fileId, name, status: 'error', error: String(err) }))
    )
  );
}
Same pattern applies for files.delete(fileId), files.copy({...}), and files.move({...}).
For delete / addTags / removeTags, prefer the bulk endpoints (max 50 IDs each) — chunk IDs and run chunks in parallel:
typescript
const CHUNK = 50;
const chunks = [];
for (let i = 0; i < allFileIds.length; i += CHUNK) chunks.push(allFileIds.slice(i, i + CHUNK));
await Promise.allSettled(chunks.map(ids => client.files.bulk.delete({ fileIds: ids })));
当你需要对一批文件执行操作时,绝不要在循环中使用await。将文件分成100个一组的批次,使用Promise.allSettled()并行执行每个批次。
typescript
// ✅ files 是任意包含 { fileId, name } 的数组——来自 assets.list()、之前的搜索结果等
const CHUNK = 100;
const chunks = [];
for (let i = 0; i < files.length; i += CHUNK) chunks.push(files.slice(i, i + CHUNK));

for (const chunk of chunks) {
  await Promise.allSettled(
    chunk.map(({ fileId, name }) =>
      client.files.update(fileId, { tags: ['promo'] })
        .then(() => ({ fileId, name, status: 'ok' }))
        .catch((err: unknown) => ({ fileId, name, status: 'error', error: String(err) }))
    )
  );
}
该模式同样适用于 files.delete(fileId)、files.copy({...}) 和 files.move({...})。
对于删除/添加标签/移除标签操作,优先使用批量接口(最多支持50个ID)——将ID分成批次,并行执行批次:
typescript
const CHUNK = 50;
const chunks = [];
for (let i = 0; i < allFileIds.length; i += CHUNK) chunks.push(allFileIds.slice(i, i + CHUNK));
await Promise.allSettled(chunks.map(ids => client.files.bulk.delete({ fileIds: ids })));