imagekit-sdk-reference
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImageKit TypeScript SDK Reference
ImageKit TypeScript SDK 参考文档
Read this skill before calling any tool or writing TypeScript code against the ImageKit SDK. It contains exact method signatures, parameter types, return shapes, and error handling patterns for .
mcp_imagekit_api_*@imagekit/nodejsRules:
- Use exact parameter names — the SDK is strict about camelCase
- returns
assets.list(). Narrow with(File | Folder)[]+for...of, neverif (item.type === 'file'). See the.filter((i): i is File => ...)skill for the full rules onsearch-assetsvs a typedsearchQueryand why the predicate fails.File[] - In /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 (
execute) rather than'status' in err, since a value import of the SDK is not available in the sandbox.instanceof ImageKit.APIError - Use /
skipfor pagination (max 1000 per request)limit - Uploads are URL-only — the param must be a URL string; local file paths, Buffers, and streams cannot be passed. Read the
fileskill first.upload-files - Nullable properties (,
tags,AITags) require optional chaining (customCoordinates) or null checks?. - returns
.find()— always check forT | undefinedbefore accessing propertiesundefined
在调用任何 工具或针对 ImageKit SDK 编写 TypeScript 代码前,请阅读本技能文档。它包含 的精确方法签名、参数类型、返回结构及错误处理模式。
mcp_imagekit_api_*@imagekit/nodejs规则:
- 使用精确的参数名称——SDK 对驼峰命名(camelCase)要求严格
- 返回
assets.list()。需通过(File | Folder)[]+for...of来收窄类型,绝不能使用if (item.type === 'file')。关于.filter((i): i is File => ...)与类型化searchQuery的完整规则,以及断言失败的原因,请查看File[]技能文档。search-assets - 在 /MCP 代码中,不要对单个 API 调用进行 try/catch 捕获——工具会自动为你上报错误。仅当你需要针对特定失败分支处理时才捕获,并且要通过鸭子类型判断错误(
execute),而非'status' in err,因为沙箱环境中无法导入 SDK 的值。instanceof ImageKit.APIError - 使用 /
skip实现分页(每次请求最多 1000 条数据)limit - 仅支持 URL 上传——参数必须是URL 字符串;无法传入本地文件路径、Buffer 或流。请先阅读
file技能文档。upload-files - 可为空属性(、
tags、AITags)需要使用可选链操作符(customCoordinates)或空值检查?. - 返回
.find()——访问属性前必须检查是否为T | undefinedundefined
TypeScript Gotchas
TypeScript 注意事项
Union narrowing for results — + , why the predicate collides with Deno's global , and vs a typed — is covered in full by the skill; follow it when handling list results. The gotchas below are the SDK-specific ones not covered there.
assets.list()for...ofif.filter((i): i is File => ...)FilesearchQueryFile[]search-assets| Pattern | Problem | Fix |
|---|---|---|
| Returns | Check for |
| | Use |
| | Use |
Any member-specific prop on a | Only shared fields ( | Narrow once with |
assets.list()for...ofif.filter((i): i is File => ...)FilesearchQueryFile[]search-assets| 模式 | 问题 | 修复方案 |
|---|---|---|
| 返回 | 检查是否为 |
| 类型为 | 使用 |
| 类型为 | 使用 |
从 | 联合类型仅包含共享字段( | 使用 |
.find()
returns T | undefined
.find()T | undefined.find()
返回 T | undefined
.find()T | undefinedtypescript
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 objecttypescript
// ⚠️ 仅支持基于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: Filetypescript
const file = await client.files.get(fileId); // 返回值: FileList / 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 — see the skill for the full rules (a top-level gives a typed ; a returns the union, which you narrow with + ).
searchQuerysearch-assetstypeFile[]searchQuery(File | Folder)[]for...ofifShared properties (safe on both File and Folder): , , , ,
nametypecustomMetadatacreatedAtupdatedAtFile-only properties (require narrowing): , , , , , , , , , , , , , , , , , , , , ,
fileIdfilePathfileTypemimesizewidthheighturlthumbnailtagsAITagsdescriptionisPrivateFileisPublishedcustomCoordinatesembeddedMetadataversionInfodurationvideoCodecaudioCodecbitRatehasAlphaFolder-only properties: ,
folderIdfolderPathtypescript
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 }类型收窄取决于是否使用 ——完整规则请查看 技能文档(顶级 参数会返回类型化的 ; 返回 联合类型,需通过 + 收窄类型)。
searchQuerysearch-assetstypeFile[]searchQuery(File | Folder)[]for...ofif共享属性(File和Folder均可安全访问):、、、、
nametypecustomMetadatacreatedAtupdatedAtFile专属属性(需要类型收窄):、、、、、、、、、、、、、、、、、、、、、
fileIdfilePathfileTypemimesizewidthheighturlthumbnailtagsAITagsdescriptionisPrivateFileisPublishedcustomCoordinatesembeddedMetadataversionInfodurationvideoCodecaudioCodecbitRatehasAlphaFolder专属属性:、
folderIdfolderPathUpdate / delete file
更新/删除文件
typescript
await client.files.update(fileId, { tags: ['newTag'], customMetadata: { key: 'value' } }); // Returns: File
await client.files.delete(fileId); // Returns: voidtypescript
await client.files.update(fileId, { tags: ['newTag'], customMetadata: { key: 'value' } }); // 返回值: File
await client.files.delete(fileId); // 返回值: voidCopy / 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: voidtypescript
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 }); // 返回值: voidFile 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: stringtypescript
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,
});
// 返回值: stringCustom 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 backofftypescript
// 基于偏移量的分页 — 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 })));