Strapi 5 plugin for managing Media Library folders and files through the Content API.
This project is a fork of SamirHodzic/strapi-plugin-media-library-handler.
It adds an endpoint that returns the complete nested folder tree with files attached to every folder.
- Manage folders and files through API.
- Retrieve the complete nested folder tree with files in every folder.
- Easy integration with Strapi projects.
npm i @powoftwo/strapi-plugin-media-library-handlerPlugin is compatible with Strapi 5.0.0 and above.
No additional configuration needed, the plugin is automatically enabled after installation.
Generate API Token (with proper permissions for Media section) which would be provided as Authorization header to each API call.
Responses from all folder and file GET endpoints are cached. When
@strapi-community/plugin-redis
is installed and its default connection is ready, Redis is used. Otherwise,
the plugin falls back to an in-memory cache local to the current Strapi process.
Cache keys include path IDs and normalized query parameters, so filtered and
sorted requests are stored separately.
Set MEDIA_LIBRARY_HANDLER_REDIS_CONNECTION to use another named connection.
Set MEDIA_LIBRARY_HANDLER_MEMORY_CACHE_MAX_ENTRIES to a positive integer to
change the in-memory cache limit (500 entries by default). In-memory caches are
not shared between multiple Strapi processes or containers, so Redis is
recommended for horizontally scaled deployments.
The cache expires after 60 seconds by default. Set
MEDIA_LIBRARY_HANDLER_CACHE_TTL_SECONDS to a positive integer to change the
TTL. Upload file and folder create, update, and delete events clear the local
in-memory cache and switch the shared Redis cache version. Superseded Redis
entries expire through their TTL.
Create a new folder.
POST {STRAPI_BASE_URL}/api/media/folders
Content-Type: application/json
Request Body
{
"name": "My New Folder",
"parentId": 123
}Response (201)
{
"id": 456,
"name": "My New Folder",
"parent": null,
"createdAt": "2025-05-05T12:34:56.789Z",
...
}Errors
400 Bad Request– validation failed.500 Internal Server Error– unexpected error.
Retrieve folders, with optional search and sorting.
GET {STRAPI_BASE_URL}/api/media/folders?parentId=123&_q=term&sort=name:asc&sort=createdAt:desc
Query Parameters
parentId(integer) — optional, filter by parent folder (omit or null for root)_q(string) — optional, full-text search stringsort(string) — optional, repeatable. Format:field:asc|desc.
Response (200)
[
{
"id": 123,
"name": "Root Folder",
"children": { "count": 2 },
"files": { "count": 5 },
...
}
]Get a specific folder by its ID, including nested parents up to 5 levels.
GET {STRAPI_BASE_URL}/api/media/folders/:id
Path Parameter
id(integer) — required, positive folder ID.
Response (200)
{
"id": 123,
"name": "Nested Folder",
"parent": {
"id": 50,
"name": "Parent 1",
"parent": {
"id": 10,
"name": "Parent 2"
...
}
},
"children": { "count": 0 },
"files": { "count": 3 },
...
}Errors
400 Bad Request– invalid or missing ID.404 Not Found– folder not found.
Update an existing folder’s name and/or parent.
PUT {STRAPI_BASE_URL}/api/media/folders/:id
Content-Type: application/json
Path Parameter
id(integer) — required.
Request Body
{
"name": "Renamed Folder",
"parentId": 999
}Response (200)
{
"id": 123,
"name": "Renamed Folder",
"parent": 999,
...
}Errors
400 Bad Request– validation failure.404 Not Found– folder doesn’t exist.
Delete a single folder.
DELETE {STRAPI_BASE_URL}/api/media/folders/:id
Path Parameter
id(integer) — required.
Response (200)
[
{
"id": 123,
"name": "Deleted Folder",
...
}
]Delete multiple files and folders in one call.
POST {STRAPI_BASE_URL}/api/media/bulk-delete
Content-Type: application/json
Request Body
{
"fileIds": [1, 2, 3],
"folderIds": [10, 11, 12]
}Response (200)
{
"deletedFiles": [ { ... } ],
"deletedFolders": [ { ... }]
}Move multiple files and folders to a target folder.
POST {STRAPI_BASE_URL}/api/media/bulk-move
Content-Type: application/json
Request Body
{
"fileIds": [1, 2, 3],
"folderIds": [10, 11, 12],
"targetFolderId": 99
}Response (200)
{
"movedFiles": [ { ... } ],
"movedFolders": [ { ... } ]
}Retrieve the entire nested folder tree.
GET {STRAPI_BASE_URL}/api/media/folders-structure
Response (200)
[
{
"id": 1,
"name": "Root",
"children": [
{ "id": 2, "name": "Child A", "children": [ { ... } ] },
...
]
}
]Retrieve the complete recursive folder tree as a single virtual Root object.
Files from the media library root are returned in its files array, and actual
root folders with all nested folders are returned in its children array.
GET {STRAPI_BASE_URL}/api/media/folders-structure-with-files
Response (200)
{
"id": null,
"name": "Root",
"files": [
{
"id": 41,
"name": "root-image.png",
"url": "/uploads/root-image.png",
"folder": null
}
],
"children": [
{
"id": 1,
"name": "Folder",
"files": [
{
"id": 42,
"name": "image.png",
"url": "/uploads/image.png",
"folder": {
"id": 1,
"name": "Folder"
}
}
],
"children": [
{
"id": 2,
"name": "Child",
"files": [],
"children": []
}
]
}
]
}Upload a file to a folder.
POST {STRAPI_BASE_URL}/api/media/files
Content-Type: multipart/form-data
Form Data
file(file) — required.folderId(integer) — optional.alternativeText(string) — optional.caption(string) — optional.skipIfExist(string) — optional, will skip if there is already file existing with same filename, caption and alternativeText (no matter the folderId).
Response (200)
{
"data": [
{
"id": 321,
"name": "image.png",
"url": "/uploads/image.png",
"folder": 99,
...
}
]
}Update file metadata (name, alt text, caption, or folder).
POST {STRAPI_BASE_URL}/api/media/files/:id
Content-Type: application/json
Path Parameter
id(integer) — required.
Request Body
{
"name": "newname.png",
"alternativeText": "An example",
"caption": "Caption text",
"folderId": 99
}Response (200)
{
"data": {
"id": 321,
"name": "newname.png",
"alternativeText": "An example",
"caption": "Caption text",
"folder": 99,
...
}
}Retrieve files, with optional search by name, caption or alternativeText, and sorting.
GET {STRAPI_BASE_URL}/api/media/files?name=some_term&sort=createdAt:desc
Query Parameters
name(string) — optional, filter by filenamecaption(string) — optional, filter by captionalternativeText(string) — optional, filter by alternativeTextsort(string) — optional, repeatable. Format:field:asc|desc.
Response (200)
[
{
"id": 42,
"name": "filename.png",
"alternativeText": "marketing_contributors_other_internal",
"caption": "some random caption",
"folder": {
"id": 159,
"name": "Base dir"
},
"createdAt": "2025-04-20T11:22:33.444Z",
...
},
]Get file metadata (name, alt text, caption, folder, etc).
GET {STRAPI_BASE_URL}/api/media/files/:id
Path Parameter
id(integer) — required.
Response (200)
{
"data": {
"id": 321,
"name": "newname.png",
"alternativeText": "An example",
"caption": "Caption text",
"folder": 99,
"formats": {
...
}
...
}
}Delete a single media file.
DELETE {STRAPI_BASE_URL}/api/media/files/:id
Path Parameter
id(integer) — required.
Response (200)
[
{
"id": 123,
"name": "Deleted File",
...
}
]- 400 Bad Request: validation errors, missing/invalid params.
- 404 Not Found: resource not found.
- 500 Internal Server Error: unexpected server error.
This project is licensed under the MIT.