Wondering how to tell if a CustomObject__c record is manually shared for the current user? You query the CustomObject__Share table for relevant entries, which reveals explicit shares and access levels. This approach aligns with Salesforce’s sharing model and is a reliable way to confirm manual sharing.

Multiple Choice

How would a developer determine if a CustomObject__c record has been manually shared with the current user in Apex?

To determine if a CustomObject__c record has been manually shared with the current user in Apex, querying the CustomObject__Share table is the appropriate method. In Salesforce, when a record is manually shared, it creates an entry within the corresponding share table for that object. The share table includes entries for explicitly sharing records with specific users or groups. By querying the CustomObject__Share object, the developer can check for any existing share entries for the specified record that include the current user's ID. This approach allows the developer to ascertain whether or not that particular record has been shared with the user in question, based on the entries present in the share table. This method is directly aligned with Salesforce's sharing model, where share records determine the access level for records outside of the standard role hierarchy and default sharing settings. Thus, it provides a reliable way to check for manual sharing specifically.

Sharing a record by hand: how to check it in Apex with the share table

If you’ve ever wrestled with Salesforce record visibility, you know the sharing model can feel like a galaxy of little rules. You’ve got the role hierarchy doing its thing, org-wide defaults, permission sets, and then… manual sharing. Manually shared records are a special case: they bypass the normal channels and hand direct access to a user or group. For developers, the question often comes up: “Did the current user get access to this particular CustomObject__c record through manual sharing?” The honest answer is simple in theory, a bit more nuanced in practice, and it centers on one star player—the share table.

In Salesforce, each custom object gets its own corresponding Share object. For a custom object named CustomObject__c, that table is CustomObject__Share. Every manually shared record creates one (or more) entries in that share table, detailing who has access, what kind of access, and which record is involved. It’s the Salesforce representation of a permission slip, a log that says, in plain terms, “User X can view/edit this record.”

Let’s break down how you can determine whether the current user has been granted manual access to a given CustomObject__c record, using Apex. We’ll keep the focus tight, but along the way I’ll drop in practical gotchas and a few patterns you’ll find handy in real-world apps.

The core idea: query the share table for the record and the user

Think of it this way: you know the record you’re interested in (its Id), and you know who the current user is (via UserInfo.getUserId()). If there’s a share entry for that record with that user, then the record has been manually shared with the user. The most direct, reliable way to confirm this is to query CustomObject__Share with two conditions: the parent Id of the share entry must match the CustomObject__c record’s Id, and the UserOrGroupId should match the current user’s Id (or you could filter for a group, if needed). The important part is that you’re looking at the share table itself, not the role hierarchy or profile settings.

A practical pattern you can use

  • Start with the basics: you have a CustomObject__c record, say rec, and you want to know if the current user has access via manual sharing.

  • The query can be as lean as:

  • SELECT Id, UserOrGroupId, AccessLevel FROM CustomObject__Share WHERE ParentId = :rec.Id AND UserOrGroupId = :UserInfo.getUserId()

  • If that query returns a row, the current user has an explicit share entry for that record. If it returns no rows, there isn’t a manual share entry for that user on that specific record.

Let me explain the pieces a bit more so the pattern feels natural:

  • ParentId in CustomObject__Share is the Id of the CustomObject__c record being shared. It’s the “which record” pointer.

  • UserOrGroupId is the Id of the User or Group that the share entry targets. This is the “who has access” part of the puzzle.

  • AccessLevel tells you what kind of access the share gives: Read, Edit, or other granular permission if your org uses advanced sharing. You don’t always need the exact level for a quick check, but it can matter if you’re deciding whether to read vs. write or to drive UI decisions.

  • You’ll usually run this in a context where you already know the user’s intent or need to reflect sharing status in the UI. It’s common to couple this kind of check with a isAccessible flag used to render buttons, fields, or components accordingly.

A small code sketch

Here’s a concise Apex snippet that demonstrates the core idea. It’s intentionally small, because clarity matters when you’re dealing with access logic.

  • Id recId = someRecordId;

  • Id currentUserId = UserInfo.getUserId();

List shares = [

SELECT Id, UserOrGroupId, AccessLevel

FROM CustomObject__Share

WHERE ParentId = :recId AND UserOrGroupId = :currentUserId

];

Boolean isManuallyShared = shares.size() > 0;

System.debug('Is manually shared with current user? ' + isManuallyShared);

If you want to be a tad more defensive, you might also consider whether the share entry is active. There are scenarios where sharing entries can be “deactivated” or conditions that require checking other fields, but the above covers the standard manual sharing check.

A few practical notes and best practices

  • Respect security-first thinking: even if a share row exists, the current user must still pass the normal CRUD checks to actually perform actions. Salesforce’s sharing model works as a layered system. Share table entries grant access, but CRUD permissions come from object-level and field-level security. In code, you often combine isManuallyShared with allowed checks like Schema.sObjectType.CustomObject__c.isAccessible(), isCreateable(), isUpdateable(), and so on.

  • Be mindful of sharing recalculation: if a user’s access changes—through a change in a group, a new manual share, or an attachment of a role’s changes—the Shares cache can update. In a live system, you can see the user’s effective access fluctuate as permissions shift, so tests and UI should reflect the possibility of changes happening outside the current transaction.

  • Bulk considerations: in production, you’ll likely be dealing with lists of records. If you need to know which of a batch of CustomObject__c records are shared with the current user, you can bulk-query CustomObject__Share for all the ParentId values in one go, then map results back to each record. It’s more efficient and saves you from firing a separate query per record.

  • Don’t forget to expose a graceful UX: a user interface might show a “View” button only if the user has read access, or it might hide a certain edit action unless the user has Edit-level sharing or object-level permission. Build your UI decisions around a small, clear set of boolean flags derived from both sharing and FLS checks.

  • Consider the quiet but real edge cases: what about manual shares granted to a role or public groups that include the current user indirectly? The UserOrGroupId field supports both direct user Ids and group Ids. If you want to be thorough, you can query for shares where UserOrGroupId IN (:queryableGroupIds) to capture these indirect access paths as well. Sometimes, you’ll see a share row that references a public group; the user is a member of that group, so you might want to resolve group membership to ensure you’re truly counting access in your UI.

  • Keep sharing in mind when testing: test coverage for sharing behavior should reflect both the presence and absence of manual shares. Create a couple of test records, set up a test user, and craft a minimal share scenario to confirm the logic behaves as expected. Tests help ensure you don’t accidentally rely on organization defaults or role hierarchy alone when you actually need a manual share signal.

A few practical tips to keep things smooth

  • Use selective fields: in production code, pull only the fields you actually need from CustomObject__Share. It keeps the query lean and reduces the risk of hitting governor limits in busy orgs.

  • Consider a helper method: if you end up checking manual sharing in multiple places, a tiny helper that takes a record Id and returns a boolean (and possibly the AccessLevel) can keep your code DRY and readable.

  • Don’t ignore nulls: if there’s no share record for a given ParentId and UserOrGroupId, your query returns an empty list. Treat that as “not manually shared” rather than throwing an error or giving a false positive.

  • Documentation matters: although this topic is a common pattern, it’s worth leaving a quick comment in code about the rationale. “Checking CustomObject__Share for manual access” helps future developers understand why this path exists.

A quick tangent that ties it all together

Sharing frameworks aren’t just about access; they’re about collaboration. When you see someone’s name attached to a record in that share table, you’re peeking into a collaboration boundary. A project manager might share a spec doc with a senior engineer; a sales rep might be granted access to an account note. In code, those same mechanics show up as fairly straightforward data checks, but the impact is real: who can see what, when, and why. It’s the human layer of a technically precise system.

From theory to practice—the essence

To determine whether a CustomObject__c record has been manually shared with the current user, querying the CustomObject__Share table is the straightforward, Salesforce-native approach. It’s the direct evidence of manual sharing, the audit trail that tells you exactly who has access. And while the mechanics are simple—ParentId points to the record, UserOrGroupId points to the recipient—the implications ripple across your UI, data integrity, and user experience.

If you’re building apps that rely on precise access logic, this pattern becomes a reliable compass. It keeps you aligned with Salesforce’s sharing model while giving you the control to reflect that model in a clean, user-friendly way. And yes, it’s perfectly acceptable to blend this check with other security layers you’ve implemented—because in real-world apps, fewer silos mean fewer surprises for users and developers alike.

A closing note: when in doubt, test with intention

As you implement this check in your codebase, give yourself permission to test in a sandbox that mirrors your production sharing rules. Try a couple of edge cases: a direct user share, a group-based share, a deactivated share, a scenario where a record is shared but the user lost access through a permission change. These scenarios aren’t just “nice to have” considerations; they’re the kind of realities that keep your apps robust and trustworthy in the long run.

In the end, the share table isn’t just a database detail. It’s the formal record of who can see what, and the Apex pattern to read it is refreshingly straightforward. When you query CustomObject__Share with a focused filter on ParentId and UserOrGroupId, you’re effectively asking Salesforce to reveal the truth about manual sharing for that particular user and record. That clarity—that moment of truth—can be the difference between a confusing user experience and a smooth, intuitive one.