Page updated Nov 9, 2023

Manipulating data

Getting started

To get started, first import the DataStore API:

import { DataStore } from 'aws-amplify';
1import { DataStore } from 'aws-amplify';

Create and update

To write data to the DataStore, pass an instance of a model to Amplify.DataStore.save():

await DataStore.save( new Post({ title: 'My First Post', rating: 10, status: PostStatus.INACTIVE }) );
1await DataStore.save(
2 new Post({
3 title: 'My First Post',
4 rating: 10,
5 status: PostStatus.INACTIVE
6 })
7);

Omitted or undefined optional fields are converted to null upon instantiation.

The save method creates a new record, or in the event that one already exists in the local store, it updates the record.

Avoid working with stale data!

Model instances which store values, such as those from the results of a DataStore.Query operation, can become stale and outdated when their properties are updated. This can be the result of a manual change or even a side effect of real time data being received by the application. In order to ensure you are performing mutations on the latest committed state to the system, either perform a query directly before the DataStore.save() operation or observe the model to keep the state updated at all times and perform mutations on that latest state referencing the model instance. The preceding example demonstrates one approach. The following example demonstrates the observeQuery approach.

// Example showing how to observe the model and keep state updated before // performing a save. This uses the useEffect React hook, but you can // substitute for a similar mechanism in your application lifecycle with // other frameworks. const App = () => { const [post, setPost] = useState(); useEffect(() => { /** * This keeps `post` fresh. */ const sub = DataStore.observeQuery(Post, (c) => c.id.eq('5a3b284c-8afc-436a-9027-c8c32bfc8ed2') ).subscribe(({ items }) => { setPost(items[0]); }); return () => { sub.unsubscribe(); }; }, []); /** * Create a new Post */ async function onCreate() { const _post = await DataStore.save( new Post({ title: `New title ${Date.now()}`, rating: Math.floor(Math.random() * (8 - 1) + 1), status: PostStatus.ACTIVE }) ); setPost(_post); } return ( <SafeAreaView> <StatusBar /> <ScrollView contentInsetAdjustmentBehavior="automatic"> <View> <Text>{post?.title}</Text> <Button onPress={onCreate} title={'New Post'} /> <TextInput disabled={!post} value={post?.name ?? ''} onChangeText={(text) => { /** * Each keypress updates the post in local React state. */ setPost( Post.copyOf(post, (draft) => { draft.title = text; }) ); }} /> <Button disabled={!post} title={'Save'} onPress={async () => { /** * This post is already up-to-date because `observeQuery` updated it. */ if (!post) { return; } const savedPost = await DataStore.save(post); console.log('Post saved: ', savedPost); }} /> </View> </ScrollView> </SafeAreaView> ); };
1// Example showing how to observe the model and keep state updated before
2// performing a save. This uses the useEffect React hook, but you can
3// substitute for a similar mechanism in your application lifecycle with
4// other frameworks.
5
6const App = () => {
7 const [post, setPost] = useState();
8
9 useEffect(() => {
10 /**
11 * This keeps `post` fresh.
12 */
13 const sub = DataStore.observeQuery(Post, (c) =>
14 c.id.eq('5a3b284c-8afc-436a-9027-c8c32bfc8ed2')
15 ).subscribe(({ items }) => {
16 setPost(items[0]);
17 });
18
19 return () => {
20 sub.unsubscribe();
21 };
22 }, []);
23
24 /**
25 * Create a new Post
26 */
27 async function onCreate() {
28 const _post = await DataStore.save(
29 new Post({
30 title: `New title ${Date.now()}`,
31 rating: Math.floor(Math.random() * (8 - 1) + 1),
32 status: PostStatus.ACTIVE
33 })
34 );
35
36 setPost(_post);
37 }
38
39 return (
40 <SafeAreaView>
41 <StatusBar />
42 <ScrollView contentInsetAdjustmentBehavior="automatic">
43 <View>
44 <Text>{post?.title}</Text>
45 <Button onPress={onCreate} title={'New Post'} />
46 <TextInput
47 disabled={!post}
48 value={post?.name ?? ''}
49 onChangeText={(text) => {
50 /**
51 * Each keypress updates the post in local React state.
52 */
53 setPost(
54 Post.copyOf(post, (draft) => {
55 draft.title = text;
56 })
57 );
58 }}
59 />
60 <Button
61 disabled={!post}
62 title={'Save'}
63 onPress={async () => {
64 /**
65 * This post is already up-to-date because `observeQuery` updated it.
66 */
67 if (!post) {
68 return;
69 }
70 const savedPost = await DataStore.save(post);
71 console.log('Post saved: ', savedPost);
72 }}
73 />
74 </View>
75 </ScrollView>
76 </SafeAreaView>
77 );
78};

Delete

To delete an item, simply pass in an instance.

const toDelete = await DataStore.query(Post, '1234567'); if (toDelete) { DataStore.delete(toDelete); }
1const toDelete = await DataStore.query(Post, '1234567');
2if (toDelete) {
3 DataStore.delete(toDelete);
4}

You can also pass predicate operators to delete multiple items. For example, the following will delete all draft posts:

await DataStore.delete(Post, (post) => post.status.eq(PostStatus.INACTIVE));
1await DataStore.delete(Post, (post) => post.status.eq(PostStatus.INACTIVE));

Additionally, you can perform a conditional delete. For instance, only delete if a post is in draft status by passing in an instance of a model:

const toDelete = await DataStore.query(Post, '123'); if (toDelete) { DataStore.delete(toDelete, (post) => post.status.eq(PostStatus.INACTIVE)); }
1const toDelete = await DataStore.query(Post, '123');
2if (toDelete) {
3 DataStore.delete(toDelete, (post) => post.status.eq(PostStatus.INACTIVE));
4}

Also, to delete all items for a model you can use Predicates.ALL:

await DataStore.delete(Post, Predicates.ALL);
1await DataStore.delete(Post, Predicates.ALL);

Query Data

Queries are performed against the local store. When cloud synchronization is enabled, the local store is updated in the background by the DataStore Sync Engine.

For more advanced filtering, such as matching arbitrary field values on an object, you can supply a query predicate.

Querying for all items

To query for all items, pass in the model name as the argument.

const posts = await DataStore.query(Post);
1const posts = await DataStore.query(Post);

Predicates

Predicates are filters that can be used to match items in the DataStore. When applied to a query(), they constrain the returned results. When applied to a save(), they act as a pre-requisite for updating the data. You can match against fields in your schema by using the following predicates:

For example if you wanted a list of all Post Models that have a rating greater than 4:

const posts = await DataStore.query(Post, (c) => c.rating.gt(4));
1const posts = await DataStore.query(Post, (c) => c.rating.gt(4));

Multiple conditions can also be used, like the ones defined in GraphQL Transform condition statements. For example, fetch all posts that have a rating greater than 4 and are ACTIVE:

When using multiple conditions, you can wrap the predicates with the and operator. For example with multiple conditions:

const posts = await DataStore.query(Post, (c) => c.and(c => [ c.rating.gt(4), c.status.eq(PostStatus.ACTIVE) ]));
1const posts = await DataStore.query(Post, (c) => c.and(c => [
2 c.rating.gt(4),
3 c.status.eq(PostStatus.ACTIVE)
4]));

Alternatively, the or logical operator can also be used:

If you wanted this to be an or statement you would wrap your combined predicates with c => c.or(...)

const posts = await DataStore.query(Post, (c) => c.or(c => [ c.rating.gt(4), c.status.eq(PostStatus.ACTIVE) ]));
1const posts = await DataStore.query(Post, (c) =>
2 c.or(c => [
3 c.rating.gt(4),
4 c.status.eq(PostStatus.ACTIVE)
5 ]));

Pagination

Query results can also be paginated by passing in a page number (starting at 0) and an optional limit (defaults to 100). This will return a list of the first 100 items:

const posts = await DataStore.query(Post, Predicates.ALL, { page: 0, limit: 100 });
1const posts = await DataStore.query(Post, Predicates.ALL, {
2 page: 0,
3 limit: 100
4});

Query, then observe changes

To both query and observe subsequent changes to a Model, consider using observeQuery.