Skip to content

Commit 25d3faf

Browse files
committed
docs: document import export hooks
1 parent 4984433 commit 25d3faf

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

adminforth/documentation/docs/tutorial/09-Plugins/08-import-export.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,93 @@ new ImportExport({
8787
This removes the import action from the resource UI and does not register the import endpoints. Export remains available in the selected `fileFormat`.
8888

8989

90+
## Choosing columns to export
91+
92+
By default every column of the resource is exported, except virtual ones (nothing stores them) and `backendOnly` ones (they never leave the server).
93+
94+
Pass `columnsToExport` to control the file precisely. The list is **exact and ordered** — only the named columns are exported, in the order they are named, so the option doubles as the file layout:
95+
96+
```typescript
97+
new ImportExport({
98+
columnsToExport: ['id', 'model', 'price'],
99+
})
100+
```
101+
102+
```csv
103+
id,model,price
104+
a1b2c3,Tesla Model 3,42000
105+
```
106+
107+
Because the list is exact, a column added to the resource later does not show up in the file until it is added here too. Names are checked at startup: an unknown name fails with a `suggestIfTypo` hint, a duplicated name fails, and an empty array fails (remove the option instead to export everything).
108+
109+
Virtual columns may be named here as well — that is the only way to get them into a file. They are not filled by anything on their own, so pair them with the hook below; if a virtual column is exported without a `hooks.export.beforeWrite`, the plugin warns at startup that the column will be empty:
110+
111+
```typescript
112+
new ImportExport({
113+
columnsToExport: ['id', 'model', 'price', 'owner_email'], // owner_email is virtual
114+
hooks: { export: { beforeWrite: fillOwnerEmail } },
115+
})
116+
```
117+
118+
Two things to keep in mind:
119+
120+
- `backendOnly` columns are rejected at startup even when named explicitly. If you really need such a column in a file, drop `backendOnly` from the column definition.
121+
- Leaving the primary key out is allowed — useful for reports that should not leak internal ids — but then an import of that file creates new records instead of updating existing ones, because there is no key to match them by.
122+
123+
`columnsToExport` affects export only. Import keeps accepting every column described in the resource, so a narrower export still imports back, and a file which still carries an exported virtual column imports cleanly too: virtual columns are dropped from every imported row, because there is no place in the database to store them.
124+
125+
## Transforming records before they are written
126+
127+
`hooks.export.beforeWrite` is called with every batch of records right before they are serialized into the file. Records are passed as a mutable array, so the hook fills virtual columns, rewrites values, or drops rows:
128+
129+
```typescript
130+
import { Filters } from 'adminforth';
131+
132+
new ImportExport({
133+
virtualColumnsToExport: ['owner_email'],
134+
hooks: {
135+
export: {
136+
beforeWrite: async ({ records, adminforth }) => {
137+
// one request per batch, not per record
138+
const owners = await adminforth.resource('users').list(
139+
Filters.IN('id', records.map((record) => record.owner_id))
140+
);
141+
const emailById = Object.fromEntries(owners.map((owner) => [owner.id, owner.email]));
142+
143+
records.forEach((record) => {
144+
record.owner_email = emailById[record.owner_id] ?? '';
145+
});
146+
},
147+
},
148+
},
149+
})
150+
```
151+
152+
The hook receives:
153+
154+
| Param | Description |
155+
| --- | --- |
156+
| `records` | Records about to be written, mutated in place by the hook |
157+
| `columns` | Columns which will be written, in the order they appear in the file |
158+
| `resource` | Resource being exported |
159+
| `adminforth` | AdminForth instance |
160+
| `adminUser` | User who started the export (absent when the export was started programmatically without one) |
161+
| `fileFormat` | `'csv'` or `'xlsx'` |
162+
| `exportMode` | `'classical'` for the REST export, `'upload'` for the background job export |
163+
| `batchOffset` | Zero-based index of the first record of the batch within the whole export |
164+
165+
Batching differs per export mode: classical export calls the hook once with the whole dataset, while upload export calls it once per `readChunkSize` chunk. Write the hook so it works for both, and batch external requests per call instead of doing one request per record.
166+
167+
Records can be dropped from or pushed into the array, and whatever the array holds when the hook returns is what gets written. Database paging is not affected by that, so upload export keeps reading `readChunkSize` records per iteration regardless.
168+
169+
Returning `{ ok: false, error }` aborts the export: classical export responds with the error, and upload export fails the background job with it.
170+
171+
An array of functions is accepted as well, and they are called in order.
172+
173+
:::info
174+
The `classicalUploadLimitMiB` check runs before the hook, on the data as it comes from the database. A hook which adds a lot of data to every record (e.g. a long virtual column) makes the response bigger than the estimate, so leave some headroom in the limit.
175+
:::
176+
90177
## Upload export
91178

92179
1) First, set up the Background Jobs plugin: go to the [Background Jobs Plugin page](/docs/tutorial/Plugins/background-jobs) and complete the setup.

0 commit comments

Comments
 (0)