How to filter list items based on a Person type field?

There could be scenarios where we have to filter or query for SharePoint list items based on a Person field. For example, we might want to show the list items created by the current logged in user. This can be done using the below REST call.

https://<site-url>/_api/web/lists/getbytitle('Personal Links')/items?$filter=Author eq 6

In this REST call, the current user's ID is used to filter the items. This ID is specific to a site collection, based on the User Information List of that particular site.

Within an SPFx webpart

In the classic pages one can get the current user's id using the _spPageContextInfo.userId. This property is also exposed in the SPFx webpart through this.context.pageContext.legacyPageContext.userId.

But, it is not recommended to use the legacyPageContext object, as it could change in the future releases. And it is provided only to migrate the old code and webparts.

The next approach would be, to make a rest call to find the current user's ID. Something like below

    sp.web.currentUser.get().then(user => {
      // Query for the list items using user.Id
      // sp.web.lists.......
    });

The only drawback with this approach is, we are making an additional REST call just to find the current user's id. So, we make two REST calls to query for list items filtered for the current user. Can we avoid the REST call to get the user id? Here are few approaches

Other approaches

The REST API also supports filtering for person type fields using EMail and User Token. Here are REST calls to filter for items created by the current user

https://<site-url>/_api/web/lists/getbytitle('Personal Links')/items?
$filter=Author/EMail eq 'r@tenant-name.onmicrosoft.com'

https://<site-url>/_api/web/lists/getbytitle('Personal Links')/items?
$filter=Author/Name eq 'i:0#.f|membership|r@tenant-name.onmicrosoft.com'

To use these filters within an SPFx webpart, we can use the pageContext property of the WebpartContext object, which has the user's email and loginName populated. If the current user's email or user token are used to filter the items, the additional REST call to find the user's ID can be avoided.

Below is the method which shows how we can use these variables and PnPjs to query for list items created by current user.

  private _getPersonalLinks = (filterByEmail:boolean):Promise<Array<any>> => {

    // This legacyPageContext is not recommended to be used.
    console.log(this.context.pageContext.legacyPageContext.userId);

    if(filterByEmail)
    {
      // Filter by EMail
      return sp.web.lists.getByTitle('Personal Links').items
      .filter(`Author/EMail eq '${encodeURIComponent(this.context.pageContext.user.email)}'`)
      .select('Title')
      .get();
    }
    else
    {
      //Filter by LoginName (i:0#.f|membership|r@tenant-name.onmicrosoft.com)
      let userToken = `i:0#.f|membership|${this.context.pageContext.user.loginName}`;
      return sp.web.lists.getByTitle('Personal Links').items
      .filter(`Author/Name eq '${encodeURIComponent(userToken)}'`)
      .select('Title')
      .get();
    }
  }

  private _links:Array<any>;

  protected async onInit(): Promise<void> {
    sp.setup({spfxContext:this.context});
    this._links = await this._getPersonalLinks(false);
    return super.onInit();
  }

I would recommend using the loginName instead of the email for filtering, because there could be some users in an Office 365 tenant without an email address.

For external users, this approach will not work, because the user token has a different format (like i:0#.f|membership|ramprasad_em_demo.com#ext#@example.onmicrosoft.com). So, in such cases use the context.pageContext.user.isAnonymousGuestUser and context.pageContext.user.isExternalGuestUser properties to find if the user is an external user and query based on the user's ID.

You can find the full sample on Github - https://github.com/RamPrasadMeenavalli/myPersonalLinks