Computed columns
A computed column appears after a table's own columns, with a value worked out from each row. Use one for a total from price and quantity, a customer's plan from a billing API, or a country from a phone number.
Flare passes a page of rows per call. If the column needs the network, you fetch once per page, not once per row.
Declare it
{
"permissions": { "data": "read" },
"contributes": {
"columns": [
{ "id": "total", "title": "Total", "engines": ["postgres", "mysql", "sqlite"] }
]
}
}Columns need permissions.data set to read or write. engines is optional. Leave it out to offer the column on every database.
Compute the values
The function gets the page of rows, the container they're in, and ctx. Return one value per row, in the same order:
import { definePlugin } from '@flare/plugin';
export default definePlugin({
columns: {
total: (rows) =>
rows.map(
({ values }) => Number(values.price ?? 0) * Number(values.quantity ?? 0)
),
},
});Return a plain value and Flare formats it like any other: numbers as numbers, dates as dates. Return a CellDisplay to pick the text, tone or icon yourself. Return null for a row with nothing to show.
One value per row, in order
Flare matches values to rows by position. If your array is short or out of order, values show against the wrong rows and Flare can't tell.
Fetch from another service
The function can be async and use all of ctx. This one asks a billing API about every customer on the page in a single request, using a secret key from the plugin's preferences:
import { definePlugin } from '@flare/plugin';
type Customer = { id: string; plan: string };
export default definePlugin({
columns: {
plan: async (rows, { container }, ctx) => {
const ids = rows
.map((row) => String(row.values.customer_id ?? ''))
.filter(Boolean);
if (!ids.length) return rows.map(() => null);
const response = await ctx.fetch(
`https://billing.example.com/v1/customers?ids=${encodeURIComponent(ids.join(','))}`,
{ headers: { Authorization: `Bearer ${ctx.preferences.key}` } }
);
const customers = await response.json<Customer[]>();
const plans = new Map(customers.map((customer) => [customer.id, customer.plan]));
ctx.log(`Looked up ${ids.length} customers for ${container}`);
return rows.map((row) => {
const plan = plans.get(String(row.values.customer_id));
return plan
? { text: plan, tone: plan === 'free' ? 'neutral' : 'positive' }
: null;
});
},
},
});Declare the host and the preference in the manifest:
{
"permissions": { "data": "read", "network": ["billing.example.com"] },
"preferences": [
{ "name": "key", "title": "Secret key", "type": "password", "required": true }
],
"contributes": { "columns": [{ "id": "plan", "title": "Plan" }] }
}When it runs
Flare calls the function as rows scroll into view, a page at a time, and again whenever the table reloads. Return plain data, since it crosses from your frame to Flare's window. Vue reactive objects are unwrapped for you. Errors go to the plugin's log, covered in Debugging.