Query in React Native using Parse
Introduction
In this guide, you will perform basic queries in Parse and implement a React Native component using these queries. You will learn how to set up and query realistic data using Back4App and React Native.
Prerequisites
To complete this tutorial, you will need:
- A React Native App created and connected to Back4App.
- If you want to run this guide’s example application, you should set up the
react-native-paper
library.
Goal
Query data stored on Back4App from a React Native App.
Step 1 - Understanding the Parse.Query class
Any Parse query operation uses the Parse.Query
object type, which will help you retrieve specific data from your database throughout your app. It is crucial to know that a Parse.Query
will only resolve after calling a retrieve method (like Parse.Query.find
or Parse.Query.get
), so a query can be set up and several modifiers can be chained before actually being called.
To create a new Parse.Query
, you need to pass as a parameter the desired Parse.Object
subclass, which is the one that will contain your query results. An example query can be seen below, in which a fictional Profile
subclass is being queried.
1
2
3
4
// This will create your query
let parseQuery = new Parse.Query("Profile");
// The query will resolve only after calling this method
let queryResult = await parseQuery.find();
You can read more about the Parse.Query
class here at the official documentation.
Step 2 - Save some data on Back4App
Let’s create a Profile
class, which will be the target of our queries in this guide. On Parse JS Console is possible to run JavaScript code directly, querying and updating your application database contents using the JS SDK commands. Run the code below from your JS Console and insert the data on Back4App.
Here is how the JS Console looks like in your dashboard:
Go ahead and create the user Profile
class with the following example content:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Add Profile objects and create table
// Adam Sandler
let Profile = new Parse.Object('Profile');
Profile.set('name', 'Adam Sandler');
Profile.set('birthDay', new Date('09/09/1966'));
Profile.set('friendCount', 2);
Profile.set('favoriteFoods', ['Lobster', 'Bread']);
await Profile.save();
// Adam Levine
Profile = new Parse.Object('Profile');
Profile.set('name', 'Adam Levine');
Profile.set('birthDay', new Date('03/18/1979'));
Profile.set('friendCount', 52);
Profile.set('favoriteFoods', ['Cake', 'Bread']);
await Profile.save();
// Carson Kressley
Profile = new Parse.Object('Profile');
Profile.set('name', 'Carson Kressley');
Profile.set('birthDay', new Date('11/11/1969'));
Profile.set('friendCount', 12);
Profile.set('favoriteFoods', ['Fish', 'Cookies']);
await Profile.save();
// Dan Aykroyd
Profile = new Parse.Object('Profile');
Profile.set('name', 'Dan Aykroyd');
Profile.set('birthDay', new Date('07/01/1952'));
Profile.set('friendCount', 66);
Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
await Profile.save();
// Eddie Murphy
Profile = new Parse.Object('Profile');
Profile.set('name', 'Eddie Murphy');
Profile.set('birthDay', new Date('04/03/1961'));
Profile.set('friendCount', 49);
Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
await Profile.save();
// Fergie
Profile = new Parse.Object('Profile');
Profile.set('name', 'Fergie');
Profile.set('birthDay', new Date('03/27/1975'));
Profile.set('friendCount', 55);
Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
await Profile.save();
console.log('Success!');
Step 3 - Query the data
Now that you have a populated class, we can now perform some basic queries in it. Let’s begin by filtering Profile
results by name, which is a string type field, searching for values that contain the name Adam
using the Parse.Query.contains
method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Create your query
let parseQuery = new Parse.Query('Profile');
// `contains` is a basic query method that checks if string field
// contains a specific substring
parseQuery.contains('name', 'Adam');
// The query will resolve only after calling this method, retrieving
// an array of `Parse.Objects`
let queryResults = await parseQuery.find();
// Let's show the results
for (let result of queryResults) {
// You access `Parse.Objects` attributes by using `.get`
console.log(result.get('name'));
};
Let’s now query by the number type field friendCount
by using another common query method, Parse.Query.greaterThan
. In this case, we want user Profiles
in which the friend count is greater than 20.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Create your query
let parseQuery = new Parse.Query('Profile');
// `greaterThan` is a basic query method that does what it
// says on the tin
parseQuery.greaterThan('friendCount', 20);
// The query will resolve only after calling this method, retrieving
// an array of `Parse.Objects`
let queryResults = await parseQuery.find();
// Let's show the results
for (let result of queryResults) {
// You access `Parse.Objects` attributes by using `.get`
console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}`);
};
Other recurring query methods are Parse.Query.ascending
and Parse.Query.descending
, responsible for ordering your queries. This ordering can be done in most data types, so let’s order a query by the date field birthDay
by the youngest.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Create your query
let parseQuery = new Parse.Query('Profile');
// `descending` and `ascending` can and should be chained
// with other query methods to improve your queries
parseQuery.descending('birthDay');
// The query will resolve only after calling this method, retrieving
// an array of `Parse.Objects`
let queryResults = await parseQuery.find();
// Let's show the results
for (let result of queryResults) {
// You access `Parse.Objects` attributes by using `.get`
console.log(`name: ${result.get('name')}, birthday: ${result.get('birthDay')}`);
};
As stated here before, you can and should chain query methods to achieve more refined results. Let’s then combine the previous examples in a single query request:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Create your query
let parseQuery = new Parse.Query('Profile');
parseQuery.contains('name', 'Adam');
parseQuery.greaterThan('friendCount', 20);
parseQuery.descending('birthDay');
// The query will resolve only after calling this method, retrieving
// an array of `Parse.Objects`
let queryResults = await parseQuery.find();
// Let's show the results
for (let result of queryResults) {
// You access `Parse.Objects` attributes by using `.get`
console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}, birthday: ${result.get('birthDay')}`);
};
Step 4 - Query from a React Native component
Let’s now use our example queries inside a component in React Native, with a simple interface having a list showing results and also 4 buttons for calling the queries. This is how the component code is laid out, note the doQuery
functions, containing the example code form before.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import React, {useState} from 'react';
import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
import Parse from 'parse/react-native';
import {
List,
Title,
Button as PaperButton,
Text as PaperText,
} from 'react-native-paper';
export const BookList = () => {
// State variable
const [queryResults, setQueryResults] = useState(null);
const doQueryByName = async function () {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery = new Parse.Query('Profile');
// `contains` is a basic query method that checks if string field
// contains a specific substring
parseQuery.contains('name', 'Adam');
try {
let profiles = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const doQueryByFriendCount = async function () {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery = new Parse.Query('Profile');
// `greaterThan` is a basic query method that does what it
// says on the tin
parseQuery.greaterThan('friendCount', 20);
try {
let profiles = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const doQueryByOrdering = async function () {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery = new Parse.Query('Profile');
// `descending` and `ascending` can and should be chained
// with other query methods to improve your queries
parseQuery.descending('birthDay');
try {
let profiles = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const doQueryByAll = async function () {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery = new Parse.Query('Profile');
parseQuery.contains('name', 'Adam');
parseQuery.greaterThan('friendCount', 20);
parseQuery.descending('birthDay');
try {
let profiles = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const clearQueryResults = async function () {
setQueryResults(null);
return true;
};
return (
<>
<View style={Styles.header}>
<Image
style={Styles.header_logo}
source={ {
uri:
'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
} }
/>
<PaperText style={Styles.header_text}>
<PaperText style={Styles.header_text_bold}>
{'React Native on Back4App - '}
</PaperText>
{' Basic Queries'}
</PaperText>
</View>
<ScrollView style={Styles.wrapper}>
<View>
<Title>{'Result List'}</Title>
{/* Book list */}
{queryResults !== null &&
queryResults !== undefined &&
queryResults.map((profile) => (
<List.Item
key={profile.id}
title={profile.get('name')}
description={`Friend count: ${profile.get(
'friendCount',
)}, Birthday: ${profile.get('birthDay')}`}
titleStyle={Styles.list_text}
style={Styles.list_item}
/>
))}
{queryResults === null ||
queryResults === undefined ||
(queryResults !== null &&
queryResults !== undefined &&
queryResults.length <= 0) ? (
<PaperText>{'No results here!'}</PaperText>
) : null}
</View>
<View>
<Title>{'Query buttons'}</Title>
<PaperButton
onPress={() => doQueryByName()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by name'}
</PaperButton>
<PaperButton
onPress={() => doQueryByFriendCount()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by friend count'}
</PaperButton>
<PaperButton
onPress={() => doQueryByOrdering()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by ordering'}
</PaperButton>
<PaperButton
onPress={() => doQueryByAll()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by all'}
</PaperButton>
<PaperButton
onPress={() => clearQueryResults()}
mode="contained"
icon="delete"
color={'#208AEC'}
style={Styles.list_button}>
{'Clear Results'}
</PaperButton>
</View>
</ScrollView>
</>
);
};
// These define the screen component styles
const Styles = StyleSheet.create({
header: {
alignItems: 'center',
paddingTop: 30,
paddingBottom: 50,
backgroundColor: '#208AEC',
},
header_logo: {
height: 50,
width: 220,
resizeMode: 'contain',
},
header_text: {
marginTop: 15,
color: '#f0f0f0',
fontSize: 16,
},
header_text_bold: {
color: '#fff',
fontWeight: 'bold',
},
wrapper: {
width: '90%',
alignSelf: 'center',
},
list_button: {
marginTop: 6,
marginLeft: 15,
height: 40,
},
list_item: {
borderBottomWidth: 1,
borderBottomColor: 'rgba(0, 0, 0, 0.12)',
},
list_text: {
fontSize: 15,
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import React, {FC, ReactElement, useState} from 'react';
import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
import Parse from 'parse/react-native';
import {
List,
Title,
Button as PaperButton,
Text as PaperText,
} from 'react-native-paper';
export const QueryList: FC<{}> = ({}): ReactElement => {
// State variable
const [queryResults, setQueryResults] = useState(null);
const doQueryByName = async function (): Promise<boolean> {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery: Parse.Query = new Parse.Query('Profile');
// `contains` is a basic query method that checks if string field
// contains a specific substring
parseQuery.contains('name', 'Adam');
try {
let profiles: [Parse.Object] = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const doQueryByFriendCount = async function (): Promise<boolean> {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery: Parse.Query = new Parse.Query('Profile');
// `greaterThan` is a basic query method that does what it
// says on the tin
parseQuery.greaterThan('friendCount', 20);
try {
let profiles: [Parse.Object] = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const doQueryByOrdering = async function (): Promise<boolean> {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery: Parse.Query = new Parse.Query('Profile');
// `descending` and `ascending` can and should be chained
// with other query methods to improve your queries
parseQuery.descending('birthDay');
try {
let profiles: [Parse.Object] = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const doQueryByAll = async function (): Promise<boolean> {
// Create our Parse.Query instance so methods can be chained
// Reading parse objects is done by using Parse.Query
const parseQuery: Parse.Query = new Parse.Query('Profile');
parseQuery.contains('name', 'Adam');
parseQuery.greaterThan('friendCount', 20);
parseQuery.descending('birthDay');
try {
let profiles: [Parse.Object] = await parseQuery.find();
setQueryResults(profiles);
return true;
} catch (error) {
// Error can be caused by lack of Internet connection
Alert.alert('Error!', error.message);
return false;
}
};
const clearQueryResults = async function (): Promise<boolean> {
setQueryResults(null);
return true;
};
return (
<>
<View style={Styles.header}>
<Image
style={Styles.header_logo}
source={ {
uri:
'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
} }
/>
<PaperText style={Styles.header_text}>
<PaperText style={Styles.header_text_bold}>
{'React Native on Back4App - '}
</PaperText>
{' Basic Queries'}
</PaperText>
</View>
<ScrollView style={Styles.wrapper}>
<View>
<Title>{'Result List'}</Title>
{/* Book list */}
{queryResults !== null &&
queryResults !== undefined &&
queryResults.map((profile: Parse.Object) => (
<List.Item
key={profile.id}
title={profile.get('name')}
description={`Friend count: ${profile.get(
'friendCount',
)}, Birthday: ${profile.get('birthDay')}`}
titleStyle={Styles.list_text}
style={Styles.list_item}
/>
))}
{queryResults === null ||
queryResults === undefined ||
(queryResults !== null &&
queryResults !== undefined &&
queryResults.length <= 0) ? (
<PaperText>{'No results here!'}</PaperText>
) : null}
</View>
<View>
<Title>{'Query buttons'}</Title>
<PaperButton
onPress={() => doQueryByName()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by name'}
</PaperButton>
<PaperButton
onPress={() => doQueryByFriendCount()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by friend count'}
</PaperButton>
<PaperButton
onPress={() => doQueryByOrdering()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by ordering'}
</PaperButton>
<PaperButton
onPress={() => doQueryByAll()}
mode="contained"
icon="search-web"
color={'#208AEC'}
style={Styles.list_button}>
{'Query by all'}
</PaperButton>
<PaperButton
onPress={() => clearQueryResults()}
mode="contained"
icon="delete"
color={'#208AEC'}
style={Styles.list_button}>
{'Clear Results'}
</PaperButton>
</View>
</ScrollView>
</>
);
};
// These define the screen component styles
const Styles = StyleSheet.create({
header: {
alignItems: 'center',
paddingTop: 30,
paddingBottom: 50,
backgroundColor: '#208AEC',
},
header_logo: {
height: 50,
width: 220,
resizeMode: 'contain',
},
header_text: {
marginTop: 15,
color: '#f0f0f0',
fontSize: 16,
},
header_text_bold: {
color: '#fff',
fontWeight: 'bold',
},
wrapper: {
width: '90%',
alignSelf: 'center',
},
list_button: {
marginTop: 6,
marginLeft: 15,
height: 40,
},
list_item: {
borderBottomWidth: 1,
borderBottomColor: 'rgba(0, 0, 0, 0.12)',
},
list_text: {
fontSize: 15,
},
});
This is how the component should look like after rendering and querying by all the query functions:
Conclusion
At the end of this guide, you learned how basic data queries work on Parse and how to perform them on Back4App from a React Native App. In the next guide, you will explore the Parse.Query
full potential using all the methods available on this class.