Skip to content

Mapping your data

This is the point of the library: you should not have to transform your backend response before rendering it.

Say your API returns this:

json
{
  "document_id": 123,
  "document_title": "Architecture.pdf",
  "kind": "FILE",
  "folder_id": 55,
  "file_size": 2450000,
  "extra": { "owner": "Nitesh", "status": "approved" }
}

Describe the shape once:

ts
const mapper = {
  id: (d) => d.document_id,
  name: (d) => d.document_title,
  type: (d) => (d.kind === 'FOLDER' ? 'folder' : 'file'),
  parentId: (d) => d.folder_id,
  size: 'file_size',
  metadata: 'extra',
};
tsx
<DocumentExplorer data={apiResponse} mapper={mapper} />

Three ways to describe a field

Every field accepts any of these:

ts
{
  // 1. A key of the source object
  size: 'file_size',

  // 2. A dot path, for nested payloads
  owner: 'audit.created_by.name',

  // 3. A function, for anything else
  type: (d) => (d.is_directory ? 'folder' : 'file'),
}

The function form is what makes essentially any backend consumable.

Coercions done for you

  • Ids are stringified. A numeric document_id: 123 becomes '123', so SQL backends need no casting.
  • Types are normalized. 'folder', 'FOLDER', 'directory', 'dir' and true all mean folder; anything else means file.
  • Sizes are parsed. The string '2048' becomes the number 2048.
  • Extensions are derived from the name when absent — and never assigned to folders, so a folder called v1.2 is not treated as having an extension.

Nested payloads

If your rows carry children, point children at them and the tree is flattened for you:

ts
const mapper = {
  id: 'documentId',
  name: 'title',
  children: 'items',   // or (d) => d.items
};

An explicit parentId on a row wins over its position in the nesting.

Full field list

id · name · type · parentId · children · mimeType · extension · size · createdAt · updatedAt · viewUrl · downloadUrl · metadata

Only id, name and type really matter; everything else is optional and simply will not be displayed if absent.

TypeScript

The mapper is generic over your row type, so the function forms are fully typed:

ts
import type { DocumentMapper } from '@document-explorer/core';

interface ApiDoc { document_id: number; document_title: string; kind: string }

const mapper: DocumentMapper<ApiDoc> = {
  id: (d) => d.document_id,       // d is ApiDoc
  name: (d) => d.document_title,
  type: (d) => (d.kind === 'FOLDER' ? 'folder' : 'file'),
};

Released under the MIT License.