## 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:

<table>
<tbody>
<tr>
<th>Description</th>
<th>Type</th>
<th>Autopopulated with UUID</th>
</tr>
<tr>
<td>

Without `@primaryKey`

</td>
<td>

```graphql
type Customer @model {
  firstName: String
  lastName: String
}
```

</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>

Without `@primaryKey`, explicit `id` field

</td>
<td>

```graphql
type Customer @model {
  id: ID!
  firstName: String
  lastName: String
}
```

</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>

`@primaryKey` on a custom field

</td>
<td>

```graphql
type Customer @model {
  customerId: ID! @primaryKey
  firstName: String
  lastName: String
}
```

</td>
<td>❌ No</td>
</tr>
<tr>
<td>

Explicit `@primaryKey` on `id` field

</td>
<td>

```graphql
type Customer@model {
  id: ID! @primaryKey
  dob: AWSDateTime!
  firstName: String
  lastName: String
}
```

</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>

Explicit `@primaryKey` on `id` field with sort key

</td>
<td>

```graphql
type Customer @model {
  id: ID! @primaryKey
    (sortKeyFields: ["dob"])
  dob: AWSDateTime!
  firstName: String
  lastName: String
}
```

</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>

Explicit `id` field in sort key

</td>
<td>

```graphql
type Customer@model {  
  country: String! @primaryKey
    (sortKeyFields: ["id"])
  id: ID!
  firstName: String
  lastName: String
}
```

</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>

@primaryKey with no `id` field

</td>
<td>

```graphql
type Customer @model {
  zip: String! @primaryKey
    (sortKeyFields: ["username"])
  username: String!
  firstName: String
  lastName: String
}
```

</td>
<td>❌ No</td>
</tr>
</tbody>
</table>

### Querying records with custom primary keys

import flutter0 from '/src/fragments/lib-v1/datastore/flutter/advanced-workflows/custom-primary-key/query-snippet.mdx';

<Fragments fragments={{ flutter: flutter0 }} />
