TypeScript Engineering

Fetching Azure DevOps Project Users via REST API with TypeScript

My console was returning empty arrays where I expected member objects, and the documentation felt like a maze. Here is how I finally isolated the correct REST endpoints to pull user lists reliably.

Need the supporting files, visual references, or downloadable resources that normally sit behind this kind of workflow?

Open on 3DCGHub

1. Stumbling Over Missing Documentation

I started this task assuming a straightforward 'get-project-users' endpoint existed in the official Azure DevOps REST reference. I spent hours scanning the documentation only to realize that specific projects don't expose a simple, direct list of members in the way I initially expected.

The primary symptom was a series of 404s and empty result sets when querying endpoints that looked promising but were meant for global accounts rather than scoped project containers.

  • Verified scope mismatch between collection-level and project-level queries.
  • Identified that the Graph API is the intended path for identity retrieval.
  • Caught the failure to properly resolve project descriptors.

2. Leveraging the Graph API

Once I stopped looking for a project-user list and started looking for membership descriptors, the path forward became clearer. The key lies in the Graph Membership API, which requires the project descriptor rather than the project ID or name.

I needed to first resolve the project to a descriptor before I could request the users associated with that specific security container.

  • Use the Project Discovery API to fetch the descriptor.
  • Construct a request to the Graph Membership REST endpoint.
  • Handle the pagination fields required by the API.

3. Writing the TypeScript Implementation

I opted for a clean axios wrapper to handle the HTTP requests. Using TypeScript interfaces helped me enforce the shape of the user objects returned by the API, which are often deeply nested.

The logic requires two distinct phases: resolving the project descriptor, then querying the user graph based on that descriptor.

  • Define an interface for the UserDescriptor result set.
  • Implement an async function to map project names to descriptors.
  • Add authorization headers using Personal Access Tokens (PAT).

4. Validation and Error Handling

After the initial implementation, I had to ensure the code handled rate limiting and missing permissions. If your PAT lacks the 'Member Read' scope, the API will fail silently or return an empty result set.

I added a status check for every request to differentiate between empty projects and genuine authorization failures.

  • Validate scope requirements for the PAT.
  • Include try-catch blocks around every network-dependent operation.
  • Test with projects of varying sizes to confirm pagination stability.

FAQ

Why does the API return an empty list even if the project has users?

This is almost always a permission issue. Ensure your PAT has the correct scopes, specifically 'Member Read', and verify that you are using the correct project descriptor.

Do I need the Project ID or the Project Name for these requests?

The Graph API requires the project descriptor, not just the name or ID. You must perform an initial lookup to resolve your project name into the required descriptor format first.