Folder Operations
Handlers for creating, deleting, renaming, and moving folders. Each requires a capability flag returned from getFolder in the folder's capabilities object.
Capability Flags
Return these per-folder in getFolder response:
return send({
id: folderId,
folders: [...],
assets: [...],
capabilities: {
canCreateFolder: true,
canDeleteFolder: true,
canRenameFolder: true,
canMoveFolder: true
}
})Capabilities are per-folder — some folders (root, virtual containers) may disable operations that others allow.
createFolder
Creates a new folder inside the specified parent.
Inputs
| Field | Description |
|---|---|
requestData.body.name | New folder name |
requestData.body.parentId | Parent folder ID |
Response
return send({ id: newFolderId, name: folderName })Patterns
Example - DAM that supports both library folders and collections:
createFolder: async (locals, requestData) => {
try {
const result = { id: '', name: '' }
let { name, parentId } = requestData.body
parentId = !folderIdStartsWith(parentId || '', [ 'root', 'collection:' ]) ? parentId : undefined
if (!parentId) {
throw new Error('You can not create a folder in this location')
}
if (parentId.startsWith('collections:')) {
parentId = parentId.replace('collections:', '')
const collection = await callApi(locals, {
query: queries.createCollectionMutation,
variables: { input: { name, parentId } }
})
result.id = `collection:${collection.data.createCollection.collection.id}`
result.name = collection.data.createCollection.collection.name
} else {
parentId = parentId.replace('library:', '')
const folder = await callApi(locals, {
query: queries.createFolderMutation,
variables: { input: { name, parentId } }
})
result.id = folder.data.createFolder.folder.id
result.name = folder.data.createFolder.folder.name
}
return send(result)
} catch (error) {
return sendError(error, 400)
}
},Example - DAM that supports path-based folder creation:
createFolder: async (locals, requestData) => {
try {
const { parentId, name } = requestData.body
const parentPath = parentId === 'root' ? '' : (await callApi(locals, 'POST', '/files/get_metadata', null, { path: parentId })).path_lower
const folderCreateResponse = await callApi(locals, 'POST', '/files/create_folder_v2', null, { path: `${parentPath}/${name}` })
return sendResult(null, { id: folderCreateResponse.metadata.id, name: folderCreateResponse.metadata.name })
} catch (err) {
return sendError(err, 400)
}
},UI Behavior
On success, the new folder appears in the current folder view. The plugin UI uses the returned id and name for display.
deleteFolder
Permanently removes a folder. Some platforms recursively delete contents; others require the folder to be empty.
Inputs
| Field | Description |
|---|---|
requestData.params.folderId | Folder ID to delete |
Response
return sendStatus(200)Patterns
Example - DAM that handles collection and folder types, blocks deletion of root-level folders:
deleteFolder: async (locals, requestData) => {
try {
const { folderId } = requestData.params
const id = folderIdStartsWith(folderId, [ 'root', 'collections', 'library' ]) ? undefined : folderId
if (!id) {
throw new Error('You can not delete this folder')
}
if (folderId.startsWith('collection:')) {
const collectionId = folderId.replace('collection:', '')
await callApi(locals, { query: queries.deleteCollectionMutation, variables: { input: { collectionId } } })
} else {
await callApi(locals, { query: queries.deleteFolderMutation, variables: { input: { ids: [ id ] } } })
}
return sendStatus(200)
} catch (error) {
return sendError(error, 400)
}
},Example - DAM that supports path-based deletion:
deleteFolder: async (locals, requestData) => {
try {
const { folderId } = requestData.params
await callApi(locals, 'POST', '/files/delete_v2', null, { path: folderId })
return sendResult(null)
} catch (err) {
return sendError(err, 400)
}
},Protect virtual/root folder IDs from deletion. Check the folderId against known non-deletable values before calling the platform API.
UI Behavior
On success, the folder disappears from the parent folder view.
renameFolder
Changes a folder's display name.
Inputs
| Field | Description |
|---|---|
requestData.params.folderId | Folder ID |
requestData.body.name | New name |
Response
return send({ id: folderId, name: newName })Patterns
Example - DAM that supports both collections and library folders:
renameFolder: async (locals, requestData) => {
try {
const { name } = requestData.body
const { folderId } = requestData.params
const id = folderIdStartsWith(folderId, [ 'root', 'collections', 'library' ]) ? undefined : folderId
if (!id) {
throw new Error('You can not rename this folder')
}
const result = { id: '', name: '' }
if (folderId.startsWith('collection:')) {
const collectionId = folderId.replace('collection:', '')
const collection = await callApi(locals, {
query: queries.updateCollectionMutation,
variables: { input: { id: collectionId, data: { name } } }
})
result.id = collection.data.updateCollection.collection.id ? `collection:${collection.data.updateCollection.collection.id}` : folderId
result.name = collection.data.updateCollection.collection.name || name
} else {
const folder = await callApi(locals, {
query: queries.updateFolderMutation,
variables: { input: { id, data: { name } } }
})
result.id = folder.data.updateFolder.folder.id || folderId
result.name = folder.data.updateFolder.folder.name || name
}
return send(result)
} catch (error) {
return sendError(error, 400)
}
},Example - DAM that supports JSON Patch:
renameFolder: async (locals, requestData) => {
const { folderId } = requestData.params
const { name } = requestData.body
try {
await callApi(locals, 'PATCH', `/categories/${folderId}`, null, [{ op: 'replace', path: '/tree/name', value: name }])
return send({ id: folderId, name })
} catch (err) {
return sendError(err, 400)
}
},UI Behavior
On success, the folder's name updates in the folder tree and breadcrumbs.
moveFolder
Moves a folder to a different parent folder.
Inputs
| Field | Description |
|---|---|
requestData.body.folderId | Folder ID to move |
requestData.body.targetParentId | Destination parent folder ID |
Response
return send({ id: folderId, targetParentId })Patterns
Example - DAM that supports JSON Patch to update parent:
moveFolder: async (locals, requestData) => {
try {
const { folderId, targetParentId } = requestData.body
if (typeof targetParentId !== 'string' || !targetParentId || targetParentId.startsWith('lightbox') || [ 'root', 'categories', 'lightboxes', 'home', 'mostViewed', 'recentlyUploaded' ].includes(targetParentId)) {
return sendError('Invalid folder ID', 400)
}
await callApi(locals, 'PATCH', `/categories/${folderId}`, null, [{ op: 'replace', path: '/parentId', value: targetParentId }])
return send({ id: folderId, targetParentId })
} catch (err) {
return sendError(err, 400)
}
},Validate targetParentId before calling the API. Virtual folder IDs (root, lightboxes, etc.) are not valid move destinations.
UI Behavior
On success, the folder disappears from its current location and appears under the target parent. Enable via canMoveFolder: true in the folder's capabilities.