Skip to main content

Virtual fields

Declare any fields you want to process in typescript as virtual fields.

const virtualFieldsLoader = makeQueryLoader({
// ...
virtualFields: {
fullName: {
dependencies: ["first_name", "last_name"],
async resolve(row) {
// async code supported
return Promise.resolve(row.first_name + row.last_name);
},
},
}
});

The virtual fields can then be selected exactly like normal fields. Their return type will be inferred if possible.

const data = await virtualFieldsLoader.load({
select: ["id", "fullName"]
}));
danger

Virtual fields are only supported when using load/loadPagination currently. If you're loading results manually using getQuery you won't get the virtual fields.

Using ctx when resolving​

The query arguments are passed as the 2nd argument to each virtual field resolver. This can be used to return a field based on the context, e.g.

const virtualFieldsLoader = makeQueryLoader({
// ...
virtualFields: {
content: {
dependencies: ["content"],
resolve(row, args) {
if (!args.ctx.isLoggedIn) {
// Return null if user isn't logged in.
return null;
}
return row.content;
},
},
}
});
Overriding

A virtual field can override a real field by using the same name. This can also be used to access control certain fields based on user context.

Remote joins​

If you need to load remote data in your virtual fields, it's often more efficient to use a batch loader, rather than returning a promise in the resolve function of each row. Use load for this purpose.

❌ ⚠️ Avoid This!​

virtualFields: {
posts: {
dependencies: ["id"],
// 📉 This can lead to performance issues.
resolve: async (row) => {
const posts = await fetchPostsForAuthor(row.id);
return posts;
}
}
},

✅ Do This Instead!​

virtualFields: {
posts: {
dependencies: ["id"],
// 🚀 Use the load function to fetch data in batches
async load(rows) {
const allPosts = await fetchPostsForAuthors(rows.map(row => row.id));
return allPosts;
},
// ✔️ Use the resolve function to handle synchronous operations.
resolve: (row, args, posts) => {
// Only return the posts of each user.
return posts.filter(post => post.authorId = row.id)
}
}
},