Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Apr 29, 2024

Customize primary keys

Amplify Flutter v0 is now in Maintenance Mode until July 19th, 2024. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v0.

Please use the latest version (v1) of Amplify Flutter to get started.

If you are currently using v0, follow these instructions to upgrade to v1.

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:

1type Book @model {
2 id: ID!
3 title: String!
4 description: String
5}

You can customize the primary key by adding the @primaryKey directive to a field:

1type Book @model {
2 isbn: ID! @primaryKey
3 title: String!
4 description: String
5}

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:

1type Book @model {
2 isbn: ID! @primaryKey(sortKeyFields: ["title"])
3 title: String!
4 description: String
5}

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:

DescriptionTypeAutopopulated with UUID

Without @primaryKey

1type Customer @model {
2 firstName: String
3 lastName: String
4}
✅ Yes

Without @primaryKey, explicit id field

1type Customer @model {
2 id: ID!
3 firstName: String
4 lastName: String
5}
✅ Yes

@primaryKey on a custom field

1type Customer @model {
2 customerId: ID! @primaryKey
3 firstName: String
4 lastName: String
5}
❌ No

Explicit @primaryKey on id field

1type Customer @model {
2 id: ID! @primaryKey
3 dob: AWSDateTime!
4 firstName: String
5 lastName: String
6}
✅ Yes

Explicit @primaryKey on id field with sort key

1type Customer @model {
2 id: ID! @primaryKey(sortKeyFields: ["dob"])
3 dob: AWSDateTime!
4 firstName: String
5 lastName: String
6}
✅ Yes

Explicit id field in sort key

1type Customer @model {
2 country: String! @primaryKey(sortKeyFields: ["id"])
3 id: ID!
4 firstName: String
5 lastName: String
6}
✅ Yes

@primaryKey with no id field

1type Customer @model {
2 zip: String! @primaryKey(sortKeyFields: ["username"])
3 username: String!
4 firstName: String
5 lastName: String
6}
❌ No

Querying records with custom primary keys

A record of a model with custom primary key can be queried with query predicate in the following ways:

1final book = (
2 await Amplify.DataStore.query(
3 Book.classType,
4 where: Book.ISBN.eq('12345'),
5 ),
6)[0];

You should always query by model identifier if the model has a composite primary key.

If you know the value of each field of the composite primary key, you can create a instance of the model identifier by using codegen generated class to create a query predicate:

1final book = (
2 await Amplify.DataStore.query(
3 Book.classType,
4 where: Book.MODEL_IDENTIFIER.eq(
5 // BookModelIdentifier is a codegen generated class
6 // that is exported from ModelProvider.dart
7 BookModelIdentifier(
8 isbn: '12345',
9 ),
10 ),
11 ),
12)[0];

If you have the reference of a model instance, you can use the getter modelIdentifier to get the identifier of this instance, and to create a query predicate with it.

1final reQueriedBook = (
2 await Amplify.DataStore.query(
3 Book.classType,
4 where: Book.MODEL_IDENTIFIER.eq(
5 book.modelIdentifier,
6 ),
7 ),
8)[0];