Field Selection
Field selection allows you to control exactly which fields are returned from your queries. This is one of the key benefits of GraphQL, and Cobalt's SDK makes it type-safe and intuitive.
Basic Selection
When a query returns an object type, you can select fields using a selector function:
// Select all fields (default)
const user = await sdk.query.user({ id: "123" })();
// Select specific fields
const user = await sdk.query.user({ id: "123" })(
({ id, name, email }) => ({ id, name, email })
);How It Works
The selector function receives a "selections" object with all available fields. You destructure what you need and return them:
const user = await sdk.query.user({ id: "123" })(
// This function receives all available fields
({ id, name, email, createdAt, profile }) => {
// Return only what you need
return { id, name };
}
);
// Result: { id: "123", name: "John" }Nested Selection
For nested objects, chain the selection:
const user = await sdk.query.user({ id: "123" })(
({ id, name, profile }) => ({
id,
name,
profile: profile(({ avatar, bio }) => ({
avatar,
bio
}))
})
);
// Result: { id: "123", name: "John", profile: { avatar: "...", bio: "..." } }Deep Nesting
Handle deeply nested structures:
const user = await sdk.query.user({ id: "123" })(
({ id, profile }) => ({
id,
profile: profile(({ settings }) => ({
settings: settings(({ theme, notifications }) => ({
theme,
notifications: notifications(({ email, push }) => ({
email,
push
}))
}))
}))
})
);Deep Nesting With Partial Field Selection
You can also use the $all selector (omit the selector function) to select all fields in a nested object.
const user = await sdk.query.user({ id: "123" })(
({ id, profile }) => ({
id,
profile: profile(({ settings }) => ({
settings: settings(({ theme, notifications }) => ({
theme,
notifications: notifications() // Select all notifications fields
}))
}))
})
);Lists
When a field returns a list, the selector applies to each item:
const users = await sdk.query.users()(
({ id, name }) => ({ id, name })
);
// Result: [{ id: "1", name: "John" }, { id: "2", name: "Jane" }]Nested Lists
const teams = await sdk.query.teams()(
({ id, name, members }) => ({
id,
name,
members: members() // Select all members fields
})
);
// Result: [
// { id: "1", name: "Engineering", members: [{ id: "...", name: "...", role: "..." }] }
// ]The $all Selector
You can use the magic $all selector to select all fields in an object or list. This recursively selects all fields in the object or list and excludes cyclic references by default, when you simply omit the selector function.
const user = await sdk.query.user({ id: "123" })();
// Returns all scalar fields (strings, numbers, booleans) and nested objects or lists automaticallyYou can also use the magic $all selector explicitly to select all fields in an object or list and exclude some paths.
const user = await sdk.query.user({ id: "123" })(
(s) => s.$all({ exclude: ["profile.settings.notifications.email"] })
);
// Returns all scalar fields (strings, numbers, booleans) and nested objects or lists automatically, but excludes the email field in the notifications object in the profile settings objectYou can also use the magic $all selector explicitly to select all fields in an object or list even when there are cyclic references.
With cyclic you can choose from "select cyclic levels: 1 | 2 | 3 | 4 | 5" to select the depth of the cyclic references you want to include.
const user = await sdk.query.user({ id: "123" })(
(s) => s.$all({
cyclic: {
"teams.members.user": "select cyclic levels: 2" // this is a type-safe string literal, you can choose from "select cyclic levels: 1 | 2 | 3 | 4 | 5"
}
})
);
// Returns all scalar fields (strings, numbers, booleans) and nested objects or lists automatically, but includes the user object in the members object in the teams object up to a depth of 2Aliasing
Give fields custom names in the result.
This will also be translated to the GraphQL query sent to the server.
const result = await sdk.query.user({ id: "123" })(
({ id, name, email }) => ({
id,
userName: name, // Aliased
userEmail: email // Aliased
})
);
// Result: { id: "123", userName: "John", userEmail: "[email protected]" }Conditional Selection
Select different fields based on conditions:
const getUser = (includeProfile: boolean) => {
return sdk.query.user({ id: "123" })(
({ id, name, profile }) => ({
id,
name,
profile: includeProfile ? profile(({ avatar }) => ({ avatar })) : undefined
})
);
};Type Safety
Field selection is fully type-safe:
const user = await sdk.query.user({ id: "123" })(
({ id, name }) => ({ id, name })
);
// ✅ These work
console.log(user.id);
console.log(user.name);
// ❌ Type error - email wasn't selected
console.log(user.email); // Property 'email' does not existUnion Types
For union types, use the $on selector:
const result = await sdk.query.searchResult()(
({ $on }) => ({
...$on.User(({ id, name }) => ({ id, name })),
...$on.Post(({ id, title }) => ({ id, title })),
})
);See the Union Types guide for more details.
Performance Benefits
Field selection reduces network payload:
// ❌ Fetches all 20 fields
const user = await sdk.query.user({ id: "123" })();
// ✅ Fetches only 2 fields
const user = await sdk.query.user({ id: "123" })(
({ id, name }) => ({ id, name })
);Best Practices
1. Select What You Need
// ✅ Good - only fetch what's displayed
const listUsers = sdk.query.users()(
({ id, name, avatar }) => ({ id, name, avatar })
).$lazy;
// ❌ Avoid - fetching everything
const listUsers = sdk.query.users()().$lazy;2. Avoid Over-Nesting
// ✅ Good - reasonable depth
const result = await sdk.query.user({ id: "123" })(
({ id, profile }) => ({
id,
profile: profile(({ avatar }) => ({ avatar }))
})
);
// ⚠️ Consider if you really need this depth
const result = await sdk.query.user({ id: "123" })(
({ organization }) => ({
organization: organization(({ settings }) => ({
settings: settings(({ features }) => ({
features: features(({ config }) => ({
config: config(/* ... */)
}))
}))
}))
})
);Next Steps
- The $lazy Pattern — Reusable operations
- SDK Usage — Full SDK documentation
- Union Types — Handling union selections