Microsoft Graph API Design Pattern
The alternate key pattern provides the ability to query for a single, specific resource identifiable via one of an alternative set of properties that is not its primary key.
The resources exposed in Microsoft Graph are identified through a primary key, which guarantees uniqueness inside the same resource collection. Often though, that same resource can also be uniquely identified by an alternative, more convenient property that provides a better developer experience.
Take a look at the user
resource: while the id
is the typical way to get the resource details, the mail
address is also a unique property that can be used to identify it.
The resource can be accessed using the $filter
query parameter, such as
GET https://graph.microsoft.com/v1.0/users?$filter=mail eq 'bob@contoso.com'
However, in this case, the returned result is wrapped in an array that needs to be unpacked. When the uniqueness of the property within the collection implies that only zero or one results can be returned from the call this array provides a suboptimal experience for callers.
Typically resources in Graph are accessed using a simple forward-slash delimited URL pattern (this pattern is sometimes referred to as key-as-segment).
https://graph.microsoft.com/v1.0/users/0 - Retrieves the employee with ID = 0.
However, resources can also be accessed using parentheses to delimit the key, like this:
https://graph.microsoft.com/v1.0/users(0) - Also retrieves the employee with ID = 0.
Resource addressing by using an alternative key can be achieved by using this same parentheses-style convention with one difference: alternate keys MUST specify the key property name to unambiguously determine the alternate key, like this:
https://graph.microsoft.com/v1.0/users(email='bob@contoso.com') Retrieves the employee with the email matching `bob@contoso.com`.
In the same way as requesting a resource via the canonical key, if a resource cannot be located that matches the alternate key, then a 404 must be returned.
Note: When requesting a resource via alternate keys, the simple slash-delimited URL style does not work.
Note: Do not use multi-part alternate keys. Feedback has been that customers find multi-part keys confusing. Either create a composite single-part surrogate key property or fall back to logical operations in a $filter clause.
Use this pattern when your resource type has other keys than its canonical key which uniquely identify a single resource.
The same user is identified via the alternate key SSN, the canonical (primary) key ID using the non-canonical long form with a specified key property name, and the canonical short form without a key property name.
Declare mail
and ssn
as alternate keys on an entity:
<EntityType Name="user">
<Key>
<PropertyRef Name="id" />
</Key>
<Property Name="id" Type="Edm.Int32" />
<Property Name="mail" Type="Edm.String" />
<Property Name="ssn" Type="Edm.String" />
<Annotation Term="OData.Community.Keys.V1.AlternateKeys">
<Collection>
<Record Type="OData.Community.Keys.V1.AlternateKey">
<PropertyValue Property="Key">
<Collection>
<Record Type="OData.Community.Keys.V1.PropertyRef">
<PropertyValue Property="Name" PropertyPath="mail" />
</Record>
</Collection>
</PropertyValue>
</Record>
<Record Type="OData.Community.Keys.V1.AlternateKey">
<PropertyValue Property="Key">
<Collection>
<Record Type="OData.Community.Keys.V1.PropertyRef">
<PropertyValue Property="Name" PropertyPath="ssn" />
</Record>
</Collection>
</PropertyValue>
</Record>
</Collection>
</Annotation>
</EntityType>
-
Get a specific resource through
$filter
:GET https://graph.microsoft.com/v1.0/users/?$filter=ssn eq '123-45-6789'
{ "value": [ { "givenName": "Bob", "jobTitle": "Retail Manager", "mail": "bob@contoso.com", "mobilePhone": "+1 425 555 0109", "officeLocation": "18/2111", "preferredLanguage": "en-US", "ssn": "123-45-6789", "surname": "Vance", "userPrincipalName": "bob@contoso.com", "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66" } ] }
-
Get a specific resource either through its primary key or through the two alternate keys:
GET https://graph.microsoft.com/v1.0/users/1a89ade6-9f59-4fea-a139-23f84e3aef66 GET https://graph.microsoft.com/v1.0/users(1a89ade6-9f59-4fea-a139-23f84e3aef66) GET https://graph.microsoft.com/v1.0/users(ssn='123-45-6789') GET https://graph.microsoft.com/v1.0/users(mail='bob@contoso.com')
All four yield the same response:
{ "givenName": "Bob", "jobTitle": "Retail Manager", "mail": "bob@contoso.com", "mobilePhone": "+1 425 555 0109", "officeLocation": "18/2111", "preferredLanguage": "en-US", "ssn": "123-45-6789", "surname": "Vance", "userPrincipalName": "bob@contoso.com", "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66" }
-
Request a resource for an unsupported alternate key property:
GET https://graph.microsoft.com/v1.0/users(name='Bob') 400 Bad Request { "error" : { "code" : "400", "message": "'name' is not a valid alternate key for the resource type 'user'." } }
-
Request a resource where the alternate key property does not exist on any resource in the collection:
GET https://graph.microsoft.com/v1.0/users(email='unknown@contoso.com') 404 Not Found { "error" : { "code" : "404", "message": "No user with the the specified 'email' could be found." } }