## Customize primary keys By default, DataStore models have an `id` field that is automatically populated on the client with a UUID v4, allowing DataStore to generate non-colliding globally unique identifiers in a scalable way. While UUIDs have desirable properties (they are large, non-sequential and opaque), there are times when a custom primary key, also known as custom identifier, is needed. For instance, to: - Have friendly/readable identifiers (surrogate/opaque vs. natural keys) - Define composite primary keys - Customize data partitioning to optimize for scale (especially important when planning to handle large amounts of data in short periods of time) - Selectively synchronize data to clients (e.g. by fields like `deviceId`, `userId` or similar) - Prioritize the sort order in which objects are returned by the sync queries - Make existing data consumable and syncable by DataStore A schema with the typical `id` field looks like this: ```graphql type Book @model { id: ID! title: String! description: String } ``` You can customize the primary key by adding the `@primaryKey` directive to a field: ```graphql type Book @model { isbn: ID! @primaryKey title: String! description: String } ``` You can also require multiple fields to define your primary key. When your primary key references multiple fields, it's called a **composite key**. In the example below, the primary key is defined by the `isbn` and `title` fields: ```graphql type Book @model { isbn: ID! @primaryKey(sortKeyFields: ["title"]) title: String! description: String } ``` ### Determine when the primary key field is auto-populated upon record creation When you create a record with DataStore, a UUID is automatically populated for the default `id: ID!` primary key field. When working with custom primary keys, DataStore will automatically populate the key fields in the following conditions:
Description Type Autopopulated with UUID
Without `@primaryKey` ```graphql type Customer @model { firstName: String lastName: String } ``` ✅ Yes
Without `@primaryKey`, explicit `id` field ```graphql type Customer @model { id: ID! firstName: String lastName: String } ``` ✅ Yes
`@primaryKey` on a custom field ```graphql type Customer @model { customerId: ID! @primaryKey firstName: String lastName: String } ``` ❌ No
Explicit `@primaryKey` on `id` field ```graphql type Customer@model { id: ID! @primaryKey dob: AWSDateTime! firstName: String lastName: String } ``` ✅ Yes
Explicit `@primaryKey` on `id` field with sort key ```graphql type Customer @model { id: ID! @primaryKey (sortKeyFields: ["dob"]) dob: AWSDateTime! firstName: String lastName: String } ``` ✅ Yes
Explicit `id` field in sort key ```graphql type Customer@model { country: String! @primaryKey (sortKeyFields: ["id"]) id: ID! firstName: String lastName: String } ``` ✅ Yes
@primaryKey with no `id` field ```graphql type Customer @model { zip: String! @primaryKey (sortKeyFields: ["username"]) username: String! firstName: String lastName: String } ``` ❌ No
### Querying records with custom primary keys import android0 from '/src/fragments/lib/datastore/android/advanced-workflows/custom-primary-key/query-snippet.mdx'; import flutter0 from '/src/fragments/lib/datastore/flutter/advanced-workflows/custom-primary-key/query-snippet.mdx'; import ios0 from '/src/fragments/lib/datastore/ios/advanced-workflows/custom-primary-key/query-snippet.mdx'; import js0 from '/src/fragments/lib/datastore/js/advanced-workflows/custom-primary-key/query-snippet.mdx'; import android1 from '/src/fragments/lib/datastore/android/advanced-workflows/custom-primary-key/delete-snippet.mdx'; import ios1 from '/src/fragments/lib/datastore/ios/advanced-workflows/custom-primary-key/delete-snippet.mdx'; import js1 from '/src/fragments/lib/datastore/js/advanced-workflows/custom-primary-key/delete-snippet.mdx'; import js2 from '/src/fragments/lib/datastore/js/advanced-workflows/custom-primary-key/callout-snippet.mdx';