[title] Untitled
[path] React Native/Parse SDK (REST)/


[title] Start your React Native Expo project using a pre-built template
[path] React Native/Parse SDK (REST)/

## Introduction

In this section, you will learn how to get started with **React Native** using an **Expo** template and how to connect it to Back4App in 4 easy steps.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:


- A Backend Back4App App.
  - Note: Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- <a href="https://www.npmjs.com/get-npm?utm_source=house&utm_medium=homepage&utm_campaign=free%20orgs&utm_term=Install%20npm" target="_blank">NPM </a>or <a href="https://classic.yarnpkg.com/en/docs/install" target="_blank">YARN </a>installed on your system.
- Expo CLI installed following <a href="https://docs.expo.io/get-started/installation/?redirected" target="_blank">this guide</a>.
:::

## 1 - Get the template

To get the template project, download and unzip the source code at our <a href="https://github.com/templates-back4app/react-native-template" target="_blank">GitHub repository</a> into your machine or clone the project with the git command line.

Run the following command to download and extract the template using CURL:

> curl -LOk https://github.com/templates-back4app/react-native-template/archive/master.zip && unzip master.zip

OR

Run the following command to clone the template using GIT:

> git clone https://github.com/templates-back4app/react-native-template

## 2 - Download the app’s dependencies

1. Make sure that you have installed npm or yarn in your system.

:::hint{type="info"}
&#x20;Look at the <a href="https://docs.npmjs.com/getting-started" target="_blank">get npm</a> or <a href="https://classic.yarnpkg.com/en/docs/install#windows-stable" target="_blank">get yarn</a> guides for more info.
:::

&#x20;    2\. On your terminal, run cd react-native-template-master and open the project's root directory.

&#x20;    3\. Run npm install to install dependencies.

In this tutorial, we are using *npm* to manage dependencies but you are free to use *yarn* instead.

## 3 - Set up the app’s credentials

To allow the App to securely connect to Back4App servers, you must provide Parse JavaScript SDK with the app’s credentials.

1. Locate your App ID and Javascript Key credentials by navigating to your app Dashboard > App Settings > Security & Keys.

On the project’s root directory, open the file at: app/(tabs)/index.tsx.

The file should look like this:

:::CodeblockTabs
index.tsx

```typescript
Parse.initialize(
  'YOUR APPLICATION ID HERE',     // Replace with your Parse Application ID
  'YOUR JAVASCRIPT KEY HERE'      // Replace with your Parse JavaScript key
);
Parse.serverURL = 'https://parseapi.back4app.com/'; 
```
:::

Copy and paste your App Id and Javascript Key on it.

## 4 - Test your connection

Inside the App.js of your template project, there is a function that creates a Person object and another for fetching People of your App to your Back4App Database.

:::CodeblockTabs
index.tsx

```typescript
  // Function to create a new Person 
  async function createPerson() {
    setLoading(true);
    setError(null);
    
    try {
      const PersonObject = Parse.Object.extend("Person");
      const personObject = new PersonObject();
      
      personObject.set("name", "Back4App User");
      
      const result = await personObject.save();
      setResult(`Object created with ID: ${result.id}`);
      
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  }

  async function fetchPeople() {
    setLoading(true);
    setError(null);
    
    try {
      const PersonObject = Parse.Object.extend("Person");
      const query = new Parse.Query(PersonObject);
  
      const results = await query.find();
      const names = results.map(result => ({
        objectId: result.id,
        name: result.get("name"),
      }));
      
      setResult(`Fetched names: ${JSON.stringify(names, null, 2)}`);
      
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  }
```
:::

To run and test your App connection:

1. Open your project’s terminal.
2. Runnpm run android or npm run ios or npm run start to open the application on your target platform



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ua5liIXkXvYN1gKWfmaPe_screenshot-2024-10-31-at-134200.png)

## It’s done!

At this point, you have learned how to get up and run a React Native application connected to Back4app.

[title] User Registration
[path] React Native/Parse SDK (REST)/Users/

# User Registration for React Native

## Introduction

At the core of many apps, user accounts have a notion that lets users securely access their information. Parse provides a specialized user class called Parse.User that automatically handles much of the functionality required for user account management.

In this guide, you’ll learn how Parse.User class works by creating a user registration feature for your React Native App using Parse JS SDK.

::embed[]{url="https://www.youtube.com/embed/5cGCJGnRtVk"}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
:::

## Goal

To build a User Registration feature using Parse for a React Native App.

## 1 - Understanding the SignUp method

Parse User management uses the Parse.User object type, which extends the default ParseObject type while containing unique helper methods, such as current and getUsername, that will help you retrieve user data throughout your app. You can read more about the Parse.User object [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/2.18.0/Parse.User.html).

In this guide, you will learn how to use the signUp method that creates a new valid and unique Parse.User object both locally and on the server, taking as arguments valid username and password values.

## 2 - Create the user registration component

Let’s now build the functional component, which will call the signUp method in our App. First, create a new file in your root directory called UserRegistration.js (UserRegistration.tsx if you are using TypeScript) and also add the needed input elements (username and password inputs), using state hooks via useState to manage their data:

:::CodeblockTabs
UserRegistration.js

```javascript
1	import React, { FC, ReactElement, useState } from "react";
2	import { Button, StyleSheet, TextInput } from "react-native";
3	import Parse from "parse/react-native";
4	
5	export const UserRegistration = () => {
6	  const [username, setUsername] = useState("");
7	  const [password, setPassword] = useState("");
8	
9	  return (
10	    <>
11	      <TextInput
12	        style={styles.input}
13	        value={username}
14	        placeholder={"Username"}
15	        onChangeText={(text) => setUsername(text)}
16	        autoCapitalize={"none"}
17	      />
18	      <TextInput
19	        style={styles.input}
20	        value={password}
21	        placeholder={"Password"}
22	        secureTextEntry
23	        onChangeText={(text) => setPassword(text)}
24	      />
25	      <Button title={"Sign Up"} onPress={() => {}} />
26	    </>
27	  );
28	};
29	
30	const styles = StyleSheet.create({
31	  input: {
32	    height: 40,
33	    marginBottom: 10,
34	    backgroundColor: '#fff',
35	  },
36	});
```

UserRegistration.tsx

```typescript
1	import React, { FC, ReactElement, useState } from "react";
2	import { Button, StyleSheet, TextInput } from "react-native";
3	import Parse from "parse/react-native";
4	
5	export const UserRegistration: FC<{}> = ({}): ReactElement => {
6	  const [username, setUsername] = useState("");
7	  const [password, setPassword] = useState("");
8	
9	  return (
10	    <>
11	      <TextInput
12	        style={styles.input}
13	        value={username}
14	        placeholder={"Username"}
15	        onChangeText={(text) => setUsername(text)}
16	        autoCapitalize={"none"}
17	      />
18	      <TextInput
19	        style={styles.input}
20	        value={password}
21	        placeholder={"Password"}
22	        secureTextEntry
23	        onChangeText={(text) => setPassword(text)}
24	      />
25	      <Button title={"Sign Up"} onPress={() => {}} />
26	    </>
27	  );
28	};
29	
30	const styles = StyleSheet.create({
31	  input: {
32	    height: 40,
33	    marginBottom: 10,
34	    backgroundColor: '#fff',
35	  },
36	});
```
:::

## 3 - Create a Sign Up function

You can now create the sign-up function that will call the signUp method:

:::CodeblockTabs
JavaScript

```javascript
1	const doUserRegistration = async function () {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue = username;
4	  const passwordValue = password;
5	  // Since the signUp method returns a Promise, we need to call it using await
6	  return await Parse.User.signUp(usernameValue, passwordValue)
7	    .then((createdUser) => {
8	      // Parse.User.signUp returns the already created ParseUser object if successful
9	      Alert.alert(
10	        'Success!',
11	        `User ${createdUser.getUsername()} was successfully created!`,
12	      );
13	      return true;
14	    })
15	    .catch((error) => {
16	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
17	      Alert.alert('Error!', error.message);
18	      return false;
19	    });
20	};
```

```typescript
1	const doUserRegistration = async function (): Promise<boolean> {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue: string = username;
4	  const passwordValue: string = password;
5	  // Since the signUp method returns a Promise, we need to call it using await
6	  return await Parse.User.signUp(usernameValue, passwordValue)
7	    .then((createdUser: Parse.User) => {
8	      // Parse.User.signUp returns the already created ParseUser object if successful
9	      Alert.alert(
10	        'Success!',
11	        `User ${createdUser.getUsername()} was successfully created!`,
12	      );
13	      return true;
14	    })
15	    .catch((error: object) => {
16	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
17	      Alert.alert('Error!', error.message);
18	      return false;
19	    });
20	};
```
:::

:::hint{type="info"}
**Note:** Creating a new user using signUp also makes it the currently logged-in user, so there is no need for your user to log in again to continue using your App.
:::

Insert this function inside the UserRegistration component, just before the return call, to be called and tested. Remember to update the form’s sign up button onPress action to () => doUserRegistration() and to import Alert from react-native. Your component should now look like this:

:::CodeblockTabs
UserRegistration.js

```javascript
1	import React, { FC, ReactElement, useState } from "react";
2	import { Alert, Button, StyleSheet, TextInput } from "react-native";
3	import Parse from "parse/react-native";
4	
5	export const UserRegistration = () => {
6	  const [username, setUsername] = useState("");
7	  const [password, setPassword] = useState("");
8	
9	  const doUserRegistration = async function () {
10	    // Note that these values come from state variables that we've declared before
11	    const usernameValue = username;
12	    const passwordValue = password;
13	    // Since the signUp method returns a Promise, we need to call it using await
14	    return await Parse.User.signUp(usernameValue, passwordValue)
15	      .then((createdUser) => {
16	        // Parse.User.signUp returns the already created ParseUser object if successful
17	        Alert.alert(
18	          "Success!",
19	          `User ${createdUser.get("username")} was successfully created!`
20	        );
21	        return true;
22	      })
23	      .catch((error) => {
24	        // signUp can fail if any parameter is blank or failed an uniqueness check on the server
25	        Alert.alert("Error!", error.message);
26	        return false;
27	      });
28	  };
29	
30	  return (
31	    <>
32	      <TextInput
33	        style={styles.input}
34	        value={username}
35	        placeholder={"Username"}
36	        onChangeText={(text) => setUsername(text)}
37	        autoCapitalize={"none"}
38	      />
39	      <TextInput
40	        style={styles.input}
41	        value={password}
42	        placeholder={"Password"}
43	        secureTextEntry
44	        onChangeText={(text) => setPassword(text)}
45	      />
46	      <Button title={"Sign Up"} onPress={() => doUserRegistration()} />
47	    </>
48	  );
49	};
50	
51	const styles = StyleSheet.create({
52	  input: {
53	    height: 40,
54	    marginBottom: 10,
55	    backgroundColor: "#fff",
56	  },
57	});
```

UserRegistration.tsx

```typescript
1	import React, { FC, ReactElement, useState } from "react";
2	import { Alert, Button, StyleSheet, TextInput } from "react-native";
3	import Parse from "parse/react-native";
4	
5	export const UserRegistration: FC<{}> = ({}): ReactElement => {
6	  const [username, setUsername] = useState("");
7	  const [password, setPassword] = useState("");
8	
9	  const doUserRegistration = async function (): Promise<boolean> {
10	    // Note that these values come from state variables that we've declared before
11	    const usernameValue: string = username;
12	    const passwordValue: string = password;
13	    // Since the signUp method returns a Promise, we need to call it using await
14	    return await Parse.User.signUp(usernameValue, passwordValue)
15	      .then((createdUser: Parse.User) => {
16	        // Parse.User.signUp returns the already created ParseUser object if successful
17	        Alert.alert(
18	          "Success!",
19	          `User ${createdUser.get("username")} was successfully created!`
20	        );
21	        return true;
22	      })
23	      .catch((error: object) => {
24	        // signUp can fail if any parameter is blank or failed an uniqueness check on the server
25	        Alert.alert("Error!", error.message);
26	        return false;
27	      });
28	  };
29	
30	  return (
31	    <>
32	      <TextInput
33	        style={styles.input}
34	        value={username}
35	        placeholder={"Username"}
36	        onChangeText={(text) => setUsername(text)}
37	        autoCapitalize={"none"}
38	      />
39	      <TextInput
40	        style={styles.input}
41	        value={password}
42	        placeholder={"Password"}
43	        secureTextEntry
44	        onChangeText={(text) => setPassword(text)}
45	      />
46	      <Button title={"Sign Up"} onPress={() => doUserRegistration()} />
47	    </>
48	  );
49	};
50	
51	const styles = StyleSheet.create({
52	  input: {
53	    height: 40,
54	    marginBottom: 10,
55	    backgroundColor: "#fff",
56	  },
57	});
```
:::

## 4 - Testing the component

The final step is to use our new component inside your React Native Application App.js file (or App.tsx if using TypeScript):

:::CodeblockTabs
App.js

```javascript
1	import { UserRegistration } from "./UserRegistration";
2	/*
3	Your functions here
4	*/
5	return (
6	  <>
7	    <StatusBar />
8	    <SafeAreaView style={styles.container}>
9	      <>
10	        <Text style={styles.title}>React Native on Back4App</Text>
11	        <Text>User registration tutorial</Text>
12	        <UserRegistration />
13	      </>
14	    </SafeAreaView>
15	  </>
16	);
17	
18	// Remember to add some styles at the end of your file
19	const styles = StyleSheet.create({
20	  container: {
21	    flex: 1,
22	    backgroundColor: "#fff",
23	    alignItems: "center",
24	    justifyContent: "center",
25	  },
26	  title: {
27	    fontSize: 20,
28	    fontWeight: "bold",
29	  },
30	});
```

App.tsx

```typescript
1	import { UserRegistration } from "./UserRegistration";
2	/*
3	Your functions here
4	*/
5	return (
6	  <>
7	    <StatusBar />
8	    <SafeAreaView style={styles.container}>
9	      <>
10	        <Text style={styles.title}>React Native on Back4App</Text>
11	        <Text>User registration tutorial</Text>
12	        <UserRegistration />
13	      </>
14	    </SafeAreaView>
15	  </>
16	);
17	
18	// Remember to add some styles at the end of your file
19	const styles = StyleSheet.create({
20	  container: {
21	    flex: 1,
22	    backgroundColor: "#fff",
23	    alignItems: "center",
24	    justifyContent: "center",
25	  },
26	  title: {
27	    fontSize: 20,
28	    fontWeight: "bold",
29	  },
30	});
```
:::

Your app now should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/iPbehNRY59rROxZSszBfD_image.png" signedSrc size="50" width="358" height="750" position="center" caption}

After providing the desired user credentials, you will see this message after pressing on Sign Up if everything was successful:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_xSJ2aMIPg4gOxCkazhNF_image.png" signedSrc size="50" width="363" height="751" position="center" caption}

Error handling can be tested if you try to register a user with the same username as before:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EpTs9m2bvVYMFgJ0PZ9Zw_image.png" signedSrc size="50" width="368" height="751" position="center" caption}

You will get another error if you try to sign up with no password:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZIv5M1Rf7GFMUZYPfVvKA_image.png" signedSrc size="50" width="370" height="750" position="center" caption}

## Conclusion

At the end of this guide, you learned how to register new Parse users on React Native. In the next guide, we will show you how to log in and out users.

[title] Verification emails
[path] Platform/

# Setup Parse email verification

## Introduction

In this guide, you will learn how to use Verification emails.

## Goal

- Change the Parse Server version.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read this page, however, to edit it you should be the owner.**
:::

## Verification emails

Enabling email verification in an application’s settings allows the application to reserve part of its experience for users with confirmed email addresses.

Email verification adds the emailVerified key to the Parse.User object. When a Parse User’s email is set or modified, then emailVerified is set to false. Parse then emails the user a link that will automatically set emailVerified to true.

This block looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tkAfOXlJZrkP9Eik1x_WR_image.png" signedSrc size="50" width="240" height="303" position="center" caption}

To edit and enable the Email Verification, please take a look at the following steps:

1. Go to your App at [Back4App Website ](https://www.back4app.com/)and click on Server Settings.
2. Find the “Verification emails” block and click on Settings.
3. Click on the Verify User Email box.
4. Fill in the empty fields and modify the ones that have already been filled based on your preferences.
5. Click on the SAVE button.

:::hint{type="danger"}
Note that you need to reach us in our live chat if you wish to edit the email body.
:::

## Would you like to use your email verification tool?

If you want to use an alternative service, take a look at one of which guides below:

- <a href="https://help.back4app.com/hc/en-us/articles/360032214231-How-can-I-use-my-own-verification-email-tool-SENDGRID-" target="_blank">SendGrid</a>
- <a href="https://help.back4app.com/hc/en-us/articles/360028152251-How-can-I-use-my-own-verification-email-tool-MAILGUN-" target="_blank">Mailgun</a>

## Conclusion

At this point, you have learned how to enable your email verification and customize it.

[title] Basic Operations
[path] React Native/Parse SDK (REST)/Data objects/

# React Native CRUD tutorial

## Introduction

Storing data on Parse is built around Parse.Object class. Each Parse.Object contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each Parse.Object. You can simply set whatever key-value pairs you want, and our backend will store it.

You can also specify the datatypes according to your application needs and persist types such as number, boolean, string, DateTime, list, GeoPointers, and Object, encoding them to JSON before saving. Parse also supports store and query relational data by using the types Pointers and Relations.

In this guide, you will learn how to perform basic data operations through a CRUD example app, which will show you how to create, read, update and delete data from your Parse server database in React Native. You will first create your component functions for each CRUD operation, using them later in a complete screen layout, resulting in a to-do list app.

::embed[]{url="https://www.youtube.com/embed/79t0A3sEdfA"}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

To build a basic CRUD application in React Native using Parse.

## 1 - Creating data objects

The first step to manage your data in your Parse database is to have some on it. Let’s now make a createTodo function that will create a new instance of Parse.Object with the “Todo” subclass. The Todo will have a title(string) describing the task and a done(boolean) field indicating if the task is completed.

:::CodeblockTabs
JavaScript

```javascript
1	const createTodo = async function () {
2	  // This value comes from a state variable
3	  const newTodoTitleValue = newTodoTitle;
4	  // Creates a new Todo parse object instance
5	  let Todo = new Parse.Object('Todo');
6	  Todo.set('title', newTodoTitleValue);
7	  Todo.set('done', false);
8	  // After setting the todo values, save it on the server
9	  try {
10	    await Todo.save();
11	    // Success
12	    Alert.alert('Success!', 'Todo created!');
13	    // Refresh todos list to show the new one (you will create this function later)
14	    readTodos();
15	    return true;
16	  } catch (error) {
17	    // Error can be caused by lack of Internet connection
18	    Alert.alert('Error!', error.message);
19	    return false;
20	  };
21	};
```

TodoList.tsx

```typescript
1	const createTodo = async function (): Promise<boolean> {
2	  // This value comes from a state variable
3	  const newTodoTitleValue: string = newTodoTitle;
4	  // Creates a new Todo parse object instance
5	  let Todo: Parse.Object = new Parse.Object('Todo');
6	  Todo.set('title', newTodoTitleValue);
7	  Todo.set('done', false);
8	  // After setting the todo values, save it on the server
9	  try {
10	    await Todo.save();
11	    // Success
12	    Alert.alert('Success!', 'Todo created!');
13	    // Refresh todos list to show the new one (you will create this function later)
14	    readTodos();
15	    return true;
16	  } catch (error) {
17	    // Error can be caused by lack of Internet connection
18	    Alert.alert('Error!', error.message);
19	    return false;
20	  };
21	};
```
:::

Notice that if your database does not have a Todo table (or subclass) in it yet, Parse will create it automatically and also add any columns set inside the Parse.Object instance using the Parse.Object.set() method, which takes two arguments: the field name and the value to be set.

## 2 - Reading data objects

After creating some data in your database, your application can now be able to read it from the server and show it to your user. Go ahead and create a readTodos function, which will perform a Parse.Query, storing the result inside a state variable.

:::CodeblockTabs
JavaScript

```javascript
1	const readTodos = async function () {
2	  // Reading parse objects is done by using Parse.Query
3	  const parseQuery = new Parse.Query('Todo');
4	  try {
5	    let todos = await parseQuery.find();
6	    // Be aware that empty or invalid queries return as an empty array
7	    // Set results to state variable
8	    setReadResults(todos);
9	    return true;
10	  } catch (error) {
11	    // Error can be caused by lack of Internet connection
12	    Alert.alert('Error!', error.message);
13	    return false;
14	  };
15	};
```

```typescript
1	const readTodos = async function (): Promise<boolean> {
2	  // Reading parse objects is done by using Parse.Query
3	  const parseQuery: Parse.Query = new Parse.Query('Todo');
4	  try {
5	    let todos: Parse.Object[] = await parseQuery.find();
6	    // Be aware that empty or invalid queries return as an empty array
7	    // Set results to state variable
8	    setReadResults(todos);
9	    return true;
10	  } catch (error) {
11	    // Error can be caused by lack of Internet connection
12	    Alert.alert('Error!', error.message);
13	    return false;
14	  };
15	};
```
:::

The Parse.Query class is very powefull, many constraints and orderings can be applied to your queries. For now, we will stick to this simple query, which will retrieve every saved Todo object.

## 3 - Updating data objects

Updating a Parse.Object instance is very similar to creating a new one, except that in this case, you need to assign the previously created objectId to it and then save, after setting your new values.

:::CodeblockTabs
JavaScript

```javascript
1	const updateTodo = async function (todoId, done) {
2	  // Create a new todo parse object instance and set todo id
3	  let Todo = new Parse.Object('Todo');
4	  Todo.set('objectId', todoId);
5	  // Set new done value and save Parse Object changes
6	  Todo.set('done', done);
7	  try {
8	    await Todo.save();
9	    // Success
10	    Alert.alert('Success!', 'Todo updated!');
11	    // Refresh todos list
12	    readTodos();
13	    return true;
14	  } catch (error) {
15	    // Error can be caused by lack of Internet connection
16	    Alert.alert('Error!', error.message);
17	    return false;
18	  };
19	};
```

```typescript
1	const updateTodo = async function (
2	  todoId: string,
3	  done: boolean,
4	): Promise<boolean> {
5	  // Create a new todo parse object instance and set todo id
6	  let Todo: Parse.Object = new Parse.Object('Todo');
7	  Todo.set('objectId', todoId);
8	  // Set new done value and save Parse Object changes
9	  Todo.set('done', done);
10	  try {
11	    await Todo.save();
12	    // Success
13	    Alert.alert('Success!', 'Todo updated!');
14	    // Refresh todos list
15	    readTodos();
16	    return true;
17	  } catch (error) {
18	    // Error can be caused by lack of Internet connection
19	    Alert.alert('Error!', error.message);
20	    return false;
21	  };
22	};
```
:::

Since this example app represents a to-do list, your update function takes an additional argument, the done value, which will represent if the specific task is completed or not.

## 4 - Deleting data objects

To delete a data object, you need to call the .destroy() method in the Parse.Object instance representing it. Please be careful because this operation is not reversible.

:::CodeblockTabs
JavaScript

```javascript
1	const deleteTodo = async function (todoId) {
2	  // Create a new todo parse object instance and set todo id
3	  const Todo = new Parse.Object('Todo');
4	  Todo.set('objectId', todoId);
5	  // .destroy should be called to delete a parse object
6	  try {
7	    await Todo.destroy();
8	    Alert.alert('Success!', 'Todo deleted!');
9	    // Refresh todos list to remove this one
10	    readTodos();
11	    return true;
12	  } catch (error) {
13	    // Error can be caused by lack of Internet connection
14	    Alert.alert('Error!', error.message);
15	    return false;
16	  };
17	};
```

```typescript
1	const deleteTodo = async function (todoId: string): Promise<boolean> {
2	  // Create a new todo parse object instance and set todo id
3	  let Todo: Parse.Object = new Parse.Object('Todo');
4	  Todo.set('objectId', todoId);
5	  // .destroy should be called to delete a parse object
6	  try {
7	    await Todo.destroy();
8	    Alert.alert('Success!', 'Todo deleted!');
9	    // Refresh todos list to remove this one
10	    readTodos();
11	    return true;
12	  } catch (error) {
13	    // Error can be caused by lack of Internet connection
14	    Alert.alert('Error!', error.message);
15	    return false;
16	  };
17	};
```
:::

Let´s now use these four functions in a complete component, so you can test it and make sure that every CRUD operation is working properly.

## 5 - Using CRUD in a React Native component

Here is the complete component code, including styled user interface elements, state variables, and calls to your CRUD functions. You can create a separate component in a file called TodoList.js/TodoList.tsx containing the following code or add it to your main application file (App.js/App.tsx or index.js):

:::CodeblockTabs
TodoList.js

```javascript
1	import React, {useState} from 'react';
2	import {
3	  Alert,
4	  View,
5	  SafeAreaView,
6	  Image,
7	  ScrollView,
8	  StatusBar,
9	  StyleSheet,
10	  TouchableOpacity,
11	} from 'react-native';
12	import Parse from 'parse/react-native';
13	import {
14	  List,
15	  Title,
16	  IconButton,
17	  Text as PaperText,
18	  Button as PaperButton,
19	  TextInput as PaperTextInput,
20	} from 'react-native-paper';
21	
22	export const TodoList = () => {
23	  // State variables
24	  const [readResults, setReadResults] = useState([]);
25	  const [newTodoTitle, setNewTodoTitle] = useState('');
26	
27	  // Functions used by the screen components
28	  const createTodo = async function () {
29	    // This value comes from a state variable
30	    const newTodoTitleValue = newTodoTitle;
31	    // Creates a new Todo parse object instance
32	    let Todo = new Parse.Object('Todo');
33	    Todo.set('title', newTodoTitleValue);
34	    Todo.set('done', false);
35	    // After setting the todo values, save it on the server
36	    try {
37	      await Todo.save();
38	      // Success
39	      Alert.alert('Success!', 'Todo created!');
40	      // Refresh todos list to show the new one (you will create this function later)
41	      readTodos();
42	      return true;
43	    } catch (error) {
44	      // Error can be caused by lack of Internet connection
45	      Alert.alert('Error!', error.message);
46	      return false;
47	    }
48	  };
49	
50	  const readTodos = async function () {
51	    // Reading parse objects is done by using Parse.Query
52	    const parseQuery = new Parse.Query('Todo');
53	    try {
54	      let todos = await parseQuery.find();
55	      // Be aware that empty or invalid queries return as an empty array
56	      // Set results to state variable
57	      setReadResults(todos);
58	      return true;
59	    } catch (error) {
60	      // Error can be caused by lack of Internet connection
61	      Alert.alert('Error!', error.message);
62	      return false;
63	    }
64	  };
65	
66	  const updateTodo = async function (todoId, done) {
67	    // Create a new todo parse object instance and set todo id
68	    let Todo = new Parse.Object('Todo');
69	    Todo.set('objectId', todoId);
70	    // Set new done value and save Parse Object changes
71	    Todo.set('done', done);
72	    try {
73	      await Todo.save();
74	      // Success
75	      Alert.alert('Success!', 'Todo updated!');
76	      // Refresh todos list
77	      readTodos();
78	      return true;
79	    } catch (error) {
80	      // Error can be caused by lack of Internet connection
81	      Alert.alert('Error!', error.message);
82	      return false;
83	    };
84	  };
85	
86	  const deleteTodo = async function (todoId) {
87	    // Create a new todo parse object instance and set todo id
88	    let Todo = new Parse.Object('Todo');
89	    Todo.set('objectId', todoId);
90	    // .destroy should be called to delete a parse object
91	    try {
92	      await Todo.destroy();
93	      Alert.alert('Success!', 'Todo deleted!');
94	      // Refresh todos list to remove this one
95	      readTodos();
96	      return true;
97	    } catch (error) {
98	      // Error can be caused by lack of Internet connection
99	      Alert.alert('Error!', error.message);
100	      return false;
101	    }
102	  };
103	
104	  return (
105	    <>
106	      <StatusBar backgroundColor="#208AEC" />
107	      <SafeAreaView style={Styles.container}>
108	        <View style={Styles.header}>
109	          <Image
110	            style={Styles.header_logo}
111	            source={ {
112	              uri:
113	                'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
114	            } }
115	          />
116	          <PaperText style={Styles.header_text_bold}>
117	            {'React Native on Back4App'}
118	          </PaperText>
119	          <PaperText style={Styles.header_text}>{'Product Creation'}</PaperText>
120	        </View>
121	        <View style={Styles.wrapper}>
122	          <View style={Styles.flex_between}>
123	            <Title>Todo List</Title>
124	            {/* Todo read (refresh) button */}
125	            <IconButton
126	              icon="refresh"
127	              color={'#208AEC'}
128	              size={24}
129	              onPress={() => readTodos()}
130	            />
131	          </View>
132	          <View style={Styles.create_todo_container}>
133	            {/* Todo create text input */}
134	            <PaperTextInput
135	              value={newTodoTitle}
136	              onChangeText={text => setNewTodoTitle(text)}
137	              label="New Todo"
138	              mode="outlined"
139	              style={Styles.create_todo_input}
140	            />
141	            {/* Todo create button */}
142	            <PaperButton
143	              onPress={() => createTodo()}
144	              mode="contained"
145	              icon="plus"
146	              color={'#208AEC'}
147	              style={Styles.create_todo_button}>
148	              {'Add'}
149	            </PaperButton>
150	          </View>
151	          <ScrollView>
152	            {/* Todo read results list */}
153	            {readResults !== null &&
154	              readResults !== undefined &&
155	              readResults.map((todo) => (
156	                <List.Item
157	                  key={todo.id}
158	                  title={todo.get('title')}
159	                  titleStyle={
160	                    todo.get('done') === true
161	                      ? Styles.todo_text_done
162	                      : Styles.todo_text
163	                  }
164	                  style={Styles.todo_item}
165	                  right={props => (
166	                    <>
167	                      {/* Todo update button */}
168	                      {todo.get('done') !== true && (
169	                        <TouchableOpacity
170	                          onPress={() => updateTodo(todo.id, true)}>
171	                          <List.Icon
172	                            {...props}
173	                            icon="check"
174	                            color={'#4CAF50'}
175	                          />
176	                        </TouchableOpacity>
177	                      )}
178	                      {/* Todo delete button */}
179	                      <TouchableOpacity onPress={() => deleteTodo(todo.id)}>
180	                        <List.Icon {...props} icon="close" color={'#ef5350'} />
181	                      </TouchableOpacity>
182	                    </>
183	                  )}
184	                />
185	              ))}
186	          </ScrollView>
187	        </View>
188	      </SafeAreaView>
189	    </>
190	  );
191	};
192	
193	// These define the screen component styles
194	const Styles = StyleSheet.create({
195	  container: {
196	    flex: 1,
197	    backgroundColor: '#FFF',
198	  },
199	  wrapper: {
200	    width: '90%',
201	    alignSelf: 'center',
202	  },
203	  header: {
204	    alignItems: 'center',
205	    paddingTop: 10,
206	    paddingBottom: 20,
207	    backgroundColor: '#208AEC',
208	  },
209	  header_logo: {
210	    width: 170,
211	    height: 40,
212	    marginBottom: 10,
213	    resizeMode: 'contain',
214	  },
215	  header_text_bold: {
216	    color: '#fff',
217	    fontSize: 14,
218	    fontWeight: 'bold',
219	  },
220	  header_text: {
221	    marginTop: 3,
222	    color: '#fff',
223	    fontSize: 14,
224	  },
225	  flex_between: {
226	    flexDirection: 'row',
227	    alignItems: 'center',
228	    justifyContent: 'space-between',
229	  },
230	  create_todo_container: {
231	    flexDirection: 'row',
232	  },
233	  create_todo_input: {
234	    flex: 1,
235	    height: 38,
236	    marginBottom: 16,
237	    backgroundColor: '#FFF',
238	    fontSize: 14,
239	  },
240	  create_todo_button: {
241	    marginTop: 6,
242	    marginLeft: 15,
243	    height: 40,
244	  },
245	  todo_item: {
246	    borderBottomWidth: 1,
247	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
248	  },
249	  todo_text: {
250	    fontSize: 15,
251	  },
252	  todo_text_done: {
253	    color: 'rgba(0, 0, 0, 0.3)',
254	    fontSize: 15,
255	    textDecorationLine: 'line-through',
256	  },
257	})
```

TodoList.tsx

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  View,
5	  SafeAreaView,
6	  Image,
7	  ScrollView,
8	  StatusBar,
9	  StyleSheet,
10	  TouchableOpacity,
11	} from 'react-native';
12	import Parse from 'parse/react-native';
13	import {
14	  List,
15	  Title,
16	  IconButton,
17	  Text as PaperText,
18	  Button as PaperButton,
19	  TextInput as PaperTextInput,
20	} from 'react-native-paper';
21	
22	export const TodoList: FC<{}> = ({}): ReactElement => {
23	  // State variables
24	  const [readResults, setReadResults] = useState<[Parse.Object]>();
25	  const [newTodoTitle, setNewTodoTitle] = useState('');
26	
27	  // Functions used by the screen components
28	  const createTodo = async function (): Promise<boolean> {
29	    // This value comes from a state variable
30	    const newTodoTitleValue: string = newTodoTitle;
31	    // Creates a new Todo parse object instance
32	    let Todo: Parse.Object = new Parse.Object('Todo');
33	    Todo.set('title', newTodoTitleValue);
34	    Todo.set('done', false);
35	    // After setting the todo values, save it on the server
36	    try {
37	      await Todo.save();
38	      // Success
39	      Alert.alert('Success!', 'Todo created!');
40	      // Refresh todos list to show the new one (you will create this function later)
41	      readTodos();
42	      return true;
43	    } catch (error) {
44	      // Error can be caused by lack of Internet connection
45	      Alert.alert('Error!', error.message);
46	      return false;
47	    }
48	  };
49	
50	  const readTodos = async function (): Promise<boolean> {
51	    // Reading parse objects is done by using Parse.Query
52	    const parseQuery: Parse.Query = new Parse.Query('Todo');
53	    try {
54	      let todos: Parse.Object[] = await parseQuery.find();
55	      // Be aware that empty or invalid queries return as an empty array
56	      // Set results to state variable
57	      setReadResults(todos);
58	      return true;
59	    } catch (error) {
60	      // Error can be caused by lack of Internet connection
61	      Alert.alert('Error!', error.message);
62	      return false;
63	    }
64	  };
65	
66	  const updateTodo = async function (
67	    todoId: string,
68	    done: boolean,
69	  ): Promise<boolean> {
70	    // Create a new todo parse object instance and set todo id
71	    let Todo: Parse.Object = new Parse.Object('Todo');
72	    Todo.set('objectId', todoId);
73	    // Set new done value and save Parse Object changes
74	    Todo.set('done', done);
75	    try {
76	      await Todo.save();
77	      // Success
78	      Alert.alert('Success!', 'Todo updated!');
79	      // Refresh todos list
80	      readTodos();
81	      return true;
82	    } catch (error) {
83	      // Error can be caused by lack of Internet connection
84	      Alert.alert('Error!', error.message);
85	      return false;
86	    }
87	  };
88	
89	  const deleteTodo = async function (todoId: string): Promise<boolean> {
90	    // Create a new todo parse object instance and set todo id
91	    let Todo: Parse.Object = new Parse.Object('Todo');
92	    Todo.set('objectId', todoId);
93	    // .destroy should be called to delete a parse object
94	    try {
95	      await Todo.destroy();
96	      Alert.alert('Success!', 'Todo deleted!');
97	      // Refresh todos list to remove this one
98	      readTodos();
99	      return true;
100	    } catch (error) {
101	      // Error can be caused by lack of Internet connection
102	      Alert.alert('Error!', error.message);
103	      return false;
104	    }
105	  };
106	
107	  return (
108	    <>
109	      <StatusBar backgroundColor="#208AEC" />
110	      <SafeAreaView style={Styles.container}>
111	        <View style={Styles.header}>
112	          <Image
113	            style={Styles.header_logo}
114	            source={ {
115	              uri:
116	                'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
117	            } }
118	          />
119	          <PaperText style={Styles.header_text_bold}>
120	            {'React Native on Back4App'}
121	          </PaperText>
122	          <PaperText style={Styles.header_text}>{'Product Creation'}</PaperText>
123	        </View>
124	        <View style={Styles.wrapper}>
125	          <View style={Styles.flex_between}>
126	            <Title>Todo List</Title>
127	            {/* Todo read (refresh) button */}
128	            <IconButton
129	              icon="refresh"
130	              color={'#208AEC'}
131	              size={24}
132	              onPress={() => readTodos()}
133	            />
134	          </View>
135	          <View style={Styles.create_todo_container}>
136	            {/* Todo create text input */}
137	            <PaperTextInput
138	              value={newTodoTitle}
139	              onChangeText={text => setNewTodoTitle(text)}
140	              label="New Todo"
141	              mode="outlined"
142	              style={Styles.create_todo_input}
143	            />
144	            {/* Todo create button */}
145	            <PaperButton
146	              onPress={() => createTodo()}
147	              mode="contained"
148	              icon="plus"
149	              color={'#208AEC'}
150	              style={Styles.create_todo_button}>
151	              {'Add'}
152	            </PaperButton>
153	          </View>
154	          <ScrollView>
155	            {/* Todo read results list */}
156	            {readResults !== null &&
157	              readResults !== undefined &&
158	              readResults.map((todo: Parse.Object) => (
159	                <List.Item
160	                  key={todo.id}
161	                  title={todo.get('title')}
162	                  titleStyle={
163	                    todo.get('done') === true
164	                      ? Styles.todo_text_done
165	                      : Styles.todo_text
166	                  }
167	                  style={Styles.todo_item}
168	                  right={props => (
169	                    <>
170	                      {/* Todo update button */}
171	                      {todo.get('done') !== true && (
172	                        <TouchableOpacity
173	                          onPress={() => updateTodo(todo.id, true)}>
174	                          <List.Icon
175	                            {...props}
176	                            icon="check"
177	                            color={'#4CAF50'}
178	                          />
179	                        </TouchableOpacity>
180	                      )}
181	                      {/* Todo delete button */}
182	                      <TouchableOpacity onPress={() => deleteTodo(todo.id)}>
183	                        <List.Icon {...props} icon="close" color={'#ef5350'} />
184	                      </TouchableOpacity>
185	                    </>
186	                  )}
187	                />
188	              ))}
189	          </ScrollView>
190	        </View>
191	      </SafeAreaView>
192	    </>
193	  );
194	};
195	
196	// These define the screen component styles
197	const Styles = StyleSheet.create({
198	  container: {
199	    flex: 1,
200	    backgroundColor: '#FFF',
201	  },
202	  wrapper: {
203	    width: '90%',
204	    alignSelf: 'center',
205	  },
206	  header: {
207	    alignItems: 'center',
208	    paddingTop: 10,
209	    paddingBottom: 20,
210	    backgroundColor: '#208AEC',
211	  },
212	  header_logo: {
213	    width: 170,
214	    height: 40,
215	    marginBottom: 10,
216	    resizeMode: 'contain',
217	  },
218	  header_text_bold: {
219	    color: '#fff',
220	    fontSize: 14,
221	    fontWeight: 'bold',
222	  },
223	  header_text: {
224	    marginTop: 3,
225	    color: '#fff',
226	    fontSize: 14,
227	  },
228	  flex_between: {
229	    flexDirection: 'row',
230	    alignItems: 'center',
231	    justifyContent: 'space-between',
232	  },
233	  create_todo_container: {
234	    flexDirection: 'row',
235	  },
236	  create_todo_input: {
237	    flex: 1,
238	    height: 38,
239	    marginBottom: 16,
240	    backgroundColor: '#FFF',
241	    fontSize: 14,
242	  },
243	  create_todo_button: {
244	    marginTop: 6,
245	    marginLeft: 15,
246	    height: 40,
247	  },
248	  todo_item: {
249	    borderBottomWidth: 1,
250	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
251	  },
252	  todo_text: {
253	    fontSize: 15,
254	  },
255	  todo_text_done: {
256	    color: 'rgba(0, 0, 0, 0.3)',
257	    fontSize: 15,
258	    textDecorationLine: 'line-through',
```
:::

If your component is properly set up, you should see something like this after building and running the app:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/51-3FbSNWXp3gY__jZoBZ_image.png" signedSrc size="50" width="348" height="731" position="center" caption}

Go ahead and add some to-do’s by typing its titles in the input box one at a time and pressing on the Add button. Note that after every successful creation, the createTodo function triggers the readTodos one, refreshing your task list automatically. You should now have a sizeable to-do list like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HWcos8Q-b6m_tnv3S574C_image.png" signedSrc size="50" width="349" height="736" position="center" caption}

You can now mark your tasks as done by clicking in the checkmark beside it, causing their done value to be updated to true and changing its icon status on the left.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bmIa9FjvqA_eGDRbKnfhY_image.png" signedSrc size="50" width="343" height="729" position="center" caption}

The only remaining data operation is now the delete one, which can be done by pressing on the trash can icon at the far right of your to-do list object. After successfully deleting an object, you should get an alert message like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FmDMU0AzdKiS_cN8b3sTu_image.png" signedSrc size="50" width="343" height="735" position="center" caption}

## Conclusion

At the end of this guide, you learned how to perform basic data operations (CRUD) in Parse on React Native. In the next guide, we will show you which data types are supported in Parse and how to use them.

[title] Untitled
[path] React Native/Parse SDK (REST)/


[title] Logs
[path] Platform/

# Parse Server Logs

## Introduction

In this guide, you will learn about the logs that you can find at the Back4App dashboard.

## Goal

- Understand the logs:
- System
- Info
- Error
- Access

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read this page.**
:::

## Logs

Searching for logs? You’re in the right place :)

Logs are essential for development or production apps, it is an important way to understand what is happening with an aggregate output of the running processes. You must be able to track the app’s behaviors.

Application event logging is critical for understanding the requests and identifying possible loops or bugs in your code.

You can check them at the left menu, on Cloud Code > Logs. It looks like the image below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EbMZJzpBLUz4XsysY8K5i_image.png" signedSrc size="50" width="296" height="339" position="center" caption}

### **System**

The *System Log* shows the logs of console.log() and console.error() and all general logs of everything that happens with your app.

### **Info**

Here will appear the warnings about Cloud Code functions and triggers, as well as Live Query status.

### **Error**

Errors related to your Cloud Code functions or the database.

### **Access**

This block contains information about the <a href="https://help.back4app.com/hc/en-us/articles/115001377071-What-is-an-API-request-" target="_blank">requests</a> that are coming into the server. The information is essential to understand where the data is being accessed from, as well as how it’s being accessed, by whom, and the status of requests.

## Sample Logs

Here you can find some examples of Back4App Logs:

:::ExpandableHeading
### System

> 1   (node:19) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
> 2   (node:19) DeprecationWarning: The option `reconnectInterval` is incompatible with the unified topology, please read more by visiting http://bit.ly/2D8WfT6
> 3   (node:18) DeprecationWarning: The option `reconnectTries` is incompatible with the unified topology, please read more by visiting http://bit.ly/2D8WfT6

At Back4App, the parse-cache module is set by default at versions higher than 2.8.4. Therefore, it’s also possible to see some warnings from Parse Server initialization:

> 1   Using redis cache for query.cache() methods
> 2   Using memory cache for query.cache() methods
> 3   The server is listening on port 3000

Cloud code syntax errors:

> 1   Warning: main.js not found: to run any cloud code functions you need first to create a main.js file
> 2   Error loading your cloud code:
> 3    /usr/src/app/data/cloud/main.js:186

And also console.log from Cloud Code triggers:

> 1   This is the log from beforeSave trigger
:::

:::ExpandableHeading
### Info

When the Server URL and Live Query are enabled for a class, a message will appear in this section:

> 1   Parse LiveQuery Server starts running

Calling a Cloud Code function will also be logged here:

> 1   Ran cloud function helloWorld for user undefined with:
> 2   Input: {}

Running a Cloud Code function without a return statement:

> 1   Ran cloud function helloWorld for user undefined with:
> 2   Input: {}
> 3   Result: undefined
:::

:::ExpandableHeading
### Error

Timeout errors:

> 1   Uncaught internal server error. timeout of 1000ms exceeded

Cloud Code triggers errors (see the example below):

```markdown
1  beforeSave failed for myClass for user undefined:
2    Input: {"Name":"Person","createdAt":"2021-06-16T17:12:54.863Z","updatedAt":"2021-06-16T17:17:14.717Z","objectId":"AsWn26ns4Q"}
3    Error: {"message":"You cannot save a person with age under 18!","code":141}
```
:::

:::ExpandableHeading
### Access

```markdown
1   2974:431:c7dc:5bb0:51ec:6258:6a16:e12b - - [2021-06-16T16:48:32.352Z] "POST /serverInfo"  200  1 ms  217 bytes_in  732 bytes_out
2   2974:431:c7dc:5bb0:51ec:6258:6a16:e12b - - [2021-06-16T16:47:26.879Z] "GET /classes/MyFirstClass"  200  50 ms  248 bytes_in  24 bytes_out
3   2974:431:c7dc:5bb0:51ec:6258:6a16:e12b - - [2021-06-16T16:46:57.068Z] "PUT /classes/MyFirstClass/Ao2ezFUQrS"  200  9 ms  240 bytes_in  40 bytes_out

```

After the endpoints, we can verify the response status code. Check some examples below:

```markdown
1   200 = The request has succeeded.
2   201 = The request has succeeded and a new resource has been created as a result. This is typically the response sent after POST or PUT requests.
3   204 = No content
4   400 = Bad request
5   401 = Unauthorized
6   404 = Not found
7   408 = Request timeout
8   500 = Internal server error
9   502 = Bad gateway
```

Calling Cloud Code functions will appear here too:

```markdown
1   2804:431:c7dc:5bb0:51ec:6258:6a16:e12b - - [2021-06-16T17:10:16.245Z] "POST /functions/hello"  200  2 ms  2 bytes_in  32 bytes_out
```
:::


[title] Relational Schema
[path] Get started/

This guide explains how to work with relational schemas on Back4app, including creating related classes and performing efficient queries using Parse Server. You will learn how to use `Pointers` and `Relations` effectively, along with practical examples.

## **What is a Relational Schema?**

A relational schema organizes data into different classes connected to each other. In Parse Server, these relationships are managed through:

- **Pointers**: Refer directly to a single object.
- **Relations**: Manage multiple connections (many-to-many relationships).

These tools allow you to perform complex queries efficiently and consistently.

## Goals

By the end of this guide, you will be able to:

- Create relationships between classes using *Pointers* and *Relations*.
- Perform relational queries to retrieve connected data.
- Optimize your schema for better performance.

## Prerequisites

:::hint{type="info"}
- A Back4app application. [Create an App Guide](https://www.back4app.com/docs/get-started/new-parse-app).
- Parse SDK installed. [Installation Guide](https://www.back4app.com/docs/get-started/parse-sdk).
:::

## 1 - Creating Related Classes

### **Practical Example**: States and Cities

Imagine you want to model a system where cities are associated with states:

- Class State with the field state\_name.
- Class City with the field city\_name and a *Pointer* to State.

### **Creating Classes and Relationships**

:::CodeblockTabs
JavaScript

```javascript
async function createStateAndCity() {
  try {
    // Create a State
    const State = Parse.Object.extend('State');
    const california = new State();
    california.set('state_name', 'California');
    const savedState = await california.save();

    console.log(`State created with objectId: ${savedState.id}`);

    // Create a City with a pointer to State
    const City = Parse.Object.extend('City');
    const losAngeles = new City();
    losAngeles.set('city_name', 'Los Angeles');
    losAngeles.set('state', savedState);

    const savedCity = await losAngeles.save();
    console.log(`City created with objectId: ${savedCity.id}`);
  } catch (error) {
    console.error('Error creating state and city:', error.message);
  }
}

createStateAndCity();
```

Flutter

```dart
Future<void> createClasses() async {
  await Parse().initialize('YOUR_APP_ID', 'https://parseapi.back4app.com/',
      clientKey: 'YOUR_CLIENT_KEY', autoSendSessionId: true);

  // Create a State
  final state = ParseObject('State')
    ..set('state_name', 'California');
  final stateResult = await state.save();

  if (stateResult.success) {
    // Create a City with a pointer to State
    final city = ParseObject('City')
      ..set('city_name', 'Los Angeles')
      ..set('state', state);
    final cityResult = await city.save();

    if (cityResult.success) {
      print('State and City created successfully.');
    }
  }
}
```

Android

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize Parse
        Parse.initialize(new Parse.Configuration.Builder(this)
                .applicationId("YOUR_APP_ID")
                .clientKey("YOUR_CLIENT_KEY")
                .server("https://parseapi.back4app.com/")
                .build()
        );

        // Create State and City
        createClasses();
    }

    private void createClasses() {
        // Create a State
        ParseObject state = new ParseObject("State");
        state.put("state_name", "California");

        state.saveInBackground(e -> {
            if (e == null) {
                Log.d("Parse", "State created successfully with objectId: " + state.getObjectId());

                // Save City with a pointer to State
                ParseObject city = new ParseObject("City");
                city.put("city_name", "Los Angeles");
                city.put("state", state);

                city.saveInBackground(ex -> {
                    if (ex == null) {
                        Log.d("Parse", "City created successfully with objectId: " + city.getObjectId());
                    } else {
                        Log.e("Parse", "Failed to create City: " + ex.getMessage());
                    }
                });
            } else {
                Log.e("Parse", "Failed to create State: " + e.getMessage());
            }
        });
    }
}
```

iOS

```swift
struct State: ParseObject {
    var objectId: String?
    var state_name: String?
    var createdAt: Date?
    var updatedAt: Date?
}

struct City: ParseObject {
    var objectId: String?
    var city_name: String?
    var state: State?
    var createdAt: Date?
    var updatedAt: Date?
}

func createClasses() async throws {
    try await ParseSwift.initialize(applicationId: "YOUR_APP_ID",
                                    clientKey: "YOUR_CLIENT_KEY",
                                    serverURL: URL(string: "https://parseapi.back4app.com")!)

    let state = State(state_name: "California")
    let savedState = try await state.save()

    var city = City(city_name: "Los Angeles", state: savedState)
    city = try await city.save()

    print("State and City created successfully.")
}
```

PHP

```php
require 'vendor/autoload.php';

use Parse\ParseClient;
use Parse\ParseObject;

ParseClient::initialize('YOUR_APP_ID', 'YOUR_CLIENT_KEY', 'https://parseapi.back4app.com/');

try {
    // Create a State
    $state = new ParseObject("State");
    $state->set("state_name", "California");
    $state->save();

    // Create a City with a pointer to State
    $city = new ParseObject("City");
    $city->set("city_name", "Los Angeles");
    $city->set("state", $state);
    $city->save();

    echo "State and City created successfully.";
} catch (Exception $ex) {
    echo "Error: " . $ex->getMessage();
}
```

.NET

```csharp
namespace ParseExampleApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // Initialize Parse
            ParseClient.Initialize(new ParseClient.Configuration
            {
                ApplicationId = "YOUR_APP_ID",
                Server = "https://parseapi.back4app.com/",
                Key = "YOUR_CLIENT_KEY"
            });

            // Call the method to create State and City
            await CreateClassesAsync();
        }

        static async Task CreateClassesAsync()
        {
            try
            {
                // Create a State
                var state = new ParseObject("State");
                state["state_name"] = "California";
                await state.SaveAsync();
                Console.WriteLine($"State created with objectId: {state.ObjectId}");

                // Create a City with a Pointer to the State
                var city = new ParseObject("City");
                city["city_name"] = "Los Angeles";
                city["state"] = state; // Set the pointer to the State
                await city.SaveAsync();
                Console.WriteLine($"City created with objectId: {city.ObjectId}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
}
```

REST API

```curl
# Create State
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"state_name": "California"}' \
https://parseapi.back4app.com/classes/State

# Use the objectId of the created state to create a city
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"city_name": "Los Angeles", "state": {"__type": "Pointer", "className": "State", "objectId": "STATE_OBJECT_ID"}}' \
https://parseapi.back4app.com/classes/City
```
:::

## **2 - Querying Related Data**

Now that the data is related, you can perform queries to retrieve it.

### **Example 1**: Fetch Cities in a Specific State

:::CodeblockTabs
JavaScript

```javascript
const stateQuery = new Parse.Query("State");
stateQuery.equalTo("state_name", "California");

stateQuery.first().then(state => {
  const cityQuery = new Parse.Query("City");
  cityQuery.equalTo("state", state);
  return cityQuery.find();
}).then(cities => {
  cities.forEach(city => {
    console.log(`City: ${city.get("city_name")}`);
  });
}).catch(error => {
  console.error("Error fetching cities:", error.message);
});
```

Flutter

```dart
final stateQuery = QueryBuilder(ParseObject('State'))..whereEqualTo('state_name', 'California');

final stateResult = await stateQuery.query();
if (stateResult.success && stateResult.results != null) {
  final state = stateResult.results!.first;
  final cityQuery = QueryBuilder(ParseObject('City'))..whereEqualTo('state', state);
  final cityResult = await cityQuery.query();

  if (cityResult.success && cityResult.results != null) {
    for (var city in cityResult.results!) {
      print('City: ${city.get<String>('city_name')}');
    }
  }
}
```

Android

```java
ParseQuery<ParseObject> stateQuery = ParseQuery.getQuery("State");
stateQuery.whereEqualTo("state_name", "California");

stateQuery.getFirstInBackground((state, e) -> {
    if (e == null) {
        ParseQuery<ParseObject> cityQuery = ParseQuery.getQuery("City");
        cityQuery.whereEqualTo("state", state);
        cityQuery.findInBackground((cities, ex) -> {
            if (ex == null) {
                for (ParseObject city : cities) {
                    Log.d("Parse", "City: " + city.getString("city_name"));
                }
            } else {
                Log.e("Parse", "Error fetching cities: " + ex.getMessage());
            }
        });
    } else {
        Log.e("Parse", "Error fetching state: " + e.getMessage());
    }
});
```

iOS

```swift
let stateQuery = State.query("state_name" == "California")

stateQuery.first { result in
    switch result {
    case .success(let state):
        let cityQuery = City.query("state" == state)
        cityQuery.find { cityResult in
            switch cityResult {
            case .success(let cities):
                cities.forEach { city in
                    print("City: \(city.city_name ?? "Unknown")")
                }
            case .failure(let error):
                print("Error fetching cities: \(error.localizedDescription)")
            }
        }
    case .failure(let error):
        print("Error fetching state: \(error.localizedDescription)")
    }
}
```

PHP

```php
use Parse\ParseQuery;

// Query State
$stateQuery = new ParseQuery("State");
$stateQuery->equalTo("state_name", "California");
$state = $stateQuery->first();

if ($state) {
    // Query Cities
    $cityQuery = new ParseQuery("City");
    $cityQuery->equalTo("state", $state);
    $cities = $cityQuery->find();

    foreach ($cities as $city) {
        echo "City: " . $city->get("city_name") . "\n";
    }
}
```

.NET

```csharp
var stateQuery = new ParseQuery<ParseObject>("State").WhereEqualTo("state_name", "California");
var state = await stateQuery.FirstAsync();

if (state != null)
{
    var cityQuery = new ParseQuery<ParseObject>("City").WhereEqualTo("state", state);
    var cities = await cityQuery.FindAsync();

    foreach (var city in cities)
    {
        Console.WriteLine($"City: {city["city_name"]}");
    }
}
```

REST API

```curl
# Query State:
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/State?where={\"state_name\":\"California\"}"

# Query Cities:
# Replace STATE_OBJECT_ID with the objectId from the state query.
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/City?where={\"state\":{\"__type\":\"Pointer\",\"className\":\"State\",\"objectId\":\"STATE_OBJECT_ID\"}}"
```
:::

### **Example 2**: Query States with Related Cities

Create a query that returns states connected to any city:

:::CodeblockTabs
JavaScript

```javascript
const stateQuery = new Parse.Query("State");
const cityQuery = new Parse.Query("City");
cityQuery.matchesQuery("state", stateQuery);
cityQuery.include("state");

cityQuery.find().then(cities => {
  cities.forEach(city => {
    const state = city.get("state");
    console.log(`City: ${city.get("city_name")} belongs to state: ${state.get("state_name")}`);
  });
}).catch(error => {
  console.error("Error fetching data:", error.message);
});
```

Flutter

```dart
final stateQuery = QueryBuilder(ParseObject('State'));
final cityQuery = QueryBuilder(ParseObject('City'))
  ..whereMatchesQuery('state', stateQuery)
  ..includeObject(['state']);

final cityResult = await cityQuery.query();

if (cityResult.success && cityResult.results != null) {
  for (var city in cityResult.results!) {
    final state = city.get<ParseObject>('state');
    print('City: ${city.get<String>('city_name')} belongs to state: ${state?.get<String>('state_name')}');
  }
}
```

Android

```java
ParseQuery<ParseObject> stateQuery = ParseQuery.getQuery("State");
ParseQuery<ParseObject> cityQuery = ParseQuery.getQuery("City");
cityQuery.whereMatchesQuery("state", stateQuery);
cityQuery.include("state");

cityQuery.findInBackground((cities, e) -> {
    if (e == null) {
        for (ParseObject city : cities) {
            ParseObject state = city.getParseObject("state");
            Log.d("Parse", "City: " + city.getString("city_name") + " belongs to state: " + state.getString("state_name"));
        }
    } else {
        Log.e("Parse", "Error: " + e.getMessage());
    }
});
```

iOS

```swift
let stateQuery = State.query()
let cityQuery = City.query(matchesQuery(key: "state", query: stateQuery))
cityQuery.include("state")

cityQuery.find { result in
    switch result {
    case .success(let cities):
        cities.forEach { city in
            if let state = city.state {
                print("City: \(city.city_name ?? "Unknown") belongs to state: \(state.state_name ?? "Unknown")")
            }
        }
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}
```

PHP

```php
use Parse\ParseQuery;

$stateQuery = new ParseQuery("State");
$cityQuery = new ParseQuery("City");
$cityQuery->matchesQuery("state", $stateQuery);
$cityQuery->includeKey("state");

$cities = $cityQuery->find();
foreach ($cities as $city) {
    $state = $city->get("state");
    echo "City: " . $city->get("city_name") . " belongs to state: " . $state->get("state_name") . "\n";
}
```

.NET

```csharp
var stateQuery = new ParseQuery<ParseObject>("State");
var cityQuery = new ParseQuery<ParseObject>("City").WhereMatchesQuery("state", stateQuery).Include("state");

var cities = await cityQuery.FindAsync();
foreach (var city in cities)
{
    var state = city.Get<ParseObject>("state");
    Console.WriteLine($"City: {city["city_name"]} belongs to state: {state["state_name"]}");
}
```

REST API

```curl
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/City?where={\"state\":{\"$inQuery\":{\"className\":\"State\"}}}&include=state"
```
:::

## **Best Practices**

To effectively work with relational schemas using Parse Server in Back4App, follow these best practices to ensure performance, maintainability, and scalability:

### **Choose the Right Relationship Type**

- **Use Pointers** for **one-to-one relationships**, such as linking a user to their profile.
- **Use Relations** for **many-to-many relationships**, such as linking a project to multiple tasks.

### **Efficient Querying**

- **Use **`.include()` to load related data in the same query, reducing the need for multiple requests.
- **Limit results** using `limit()` and `skip()` to avoid fetching large datasets at once.
- **Index frequently queried fields** to speed up searches.

### **Avoid Excessive Nesting**

- **Keep queries flat** to reduce complexity and improve performance. Use nested queries sparingly and only when necessary.

### **Offload Complex Queries to Cloud Code**

- For **complex queries involving multiple relations** or large datasets, offload these to **Cloud Code** to keep the client lightweight and responsive.

## **Conclusion**

In this guide, you learned how to create relationships between classes and query-related objects on Back4app. Continue exploring the [specific SDK documentation](https://docs.parseplatform.org) to dive even deeper!

[title] Android Setup
[path] React Native/Parse SDK (REST)/Push Notifications/

# Push Notifications for React Native on Android

## Introduction

This guide will teach you how to use Parse to send push notifications to your React Native application on Android. You will set up Firebase and configure your Back4App app to send them via dashboard and Cloud Code functions.

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/react-native-js-push-notifications" target="_blank">JavaScript Example Repository</a>
- <a href="https://github.com/templates-back4app/react-native-ts-push-notifications" target="_blank">TypeScript Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Understand how to <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">deploy a cloud function</a> on Back4App.
- An active account at Google, so you can use Google Firebase.
:::

## Goal

Send push notifications from Parse to a React Native application on Android, using Back4App’s dashboard and Cloud Code functions.

## 1 - Setting up Firebase

Firebase is the most used service provider for sending and receiving push notifications on Android. It’s used by a wide range of companies nowadays, together with the other various tools. Let’s start by creating a Firebase project for your app following steps 1 and 2 from [this documentation](https://firebase.google.com/docs/android/setup#console).

After creating your app and before exiting Firebase’s console, make sure to download and save the file google-services.json and also go to the project settings, over the “Cloud Messaging” tab and retrieve the following keys:

- **Server key;**
- **Sender ID.**

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/UX0EeTvU80dzhrLVfRPJW_image.png)

## 2 - Enabling Push Notifications on Back4App

To link your Firebase Project with Back4App and enable sending push notifications through your Dashboard and Cloud Code, follow these steps:

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on Server Settings.
2. Find the “Android Push notification” block and click on SETTINGS > EDIT. The “Android Push notification” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uytiokeM6FEsn1egTKByX_image.png)

&#x20;    3\. Add the FirebaseServer Keyto theAPI Keyfield and theSender IDto theGCM Sender IDfield.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tUTrVpr6XQcBG2k4G-muV_image.png)

&#x20;    4\. Save it and you are done.

## 3 - Installing and Setting Up Dependencies

On your React Native project, let’s install the react-native-push-notification library, which will integrate the application with Firebase and enable you to handle any received notification. Run the following command on your project root directory:

> yarn add react-native-push-notification

After installing it, you still need to add some configurations to the android/app/src/main/AndroidManifest.xml, android/build.gradle and android/app/build.gradle following the library [docs](https://github.com/zo0r/react-native-push-notification).

Make sure to also add in the AndroidManifest.xml file a meta-data containing the default notification channel id for your application(let’s use guideChannel as our example) . You can add the name directly to the directive or include it in the strings.xml file. Also, place your project’s google-services.json file at the android/app directory.

```xml
1	 <meta-data
2	      android:name="com.google.firebase.messaging.default_notification_channel_id"
3	      android:value="guideChannel" />
4	  <!-- OR -->
5	  <meta-data
6	      android:name="com.google.firebase.messaging.default_notification_channel_id"
7	      android:value="@string/default_notification_channel_id" />
```

In the next step, we will learn how to use this library and combine it with Parse.

## 4 - Handling Push Notifications

Let’s create a file with new methods related to push notifications called PushNotificationMethods.js (or PushNotificationMethods.tsx) and add the following function, that is responsible for initializing the react-native-push-notification, creating a notification channel, and also setting up Parse to recognize our device’s push configuration:

:::CodeblockTabs
JavaScript

```javascript
1	import PushNotification from 'react-native-push-notification';
2	import Parse from 'parse/react-native';
3	
4	const channelId = 'guideChannel';
5	
6	export async function configurePushNotifications() {
7	  // Initialize PushNotification
8	  await PushNotification.configure({
9	    // (required) Called when a remote is received or opened, or local notification is opened
10	    onNotification: function (notification) {
11	      // process the notification
12	      console.log('NOTIFICATION:', notification);
13	      // If notification is remote and has data, trigger local notification to show popup.
14	      // This is needed for Parse sent notifications because Firebase doesn't trigger popup
15	      // notifications with data by itself
16	      if (
17	        notification.data !== undefined &&
18	        notification.data.data !== undefined
19	      ) {
20	        try {
21	          // Notification data comes as a stringified JSON, so parsing is needed
22	          const notificationData = JSON.parse(notification.data.data);
23	          // JSON Parse notifications from the dashboard and Cloud Code
24	          // should contain the default `title` and `message` parameters
25	          let title = 'Notification Title';
26	          if (notificationData.title !== undefined) {
27	            title = notificationData.title;
28	          }
29	          let message = 'Noticiation Message';
30	          if (notificationData.message !== undefined) {
31	            message = notificationData.message;
32	          }
33	          // Text Parse notifications from the dashboard only have an `alert` parameter
34	          if (notificationData.alert !== undefined) {
35	            message = notificationData.alert;
36	          }
37	          PushNotification.localNotification({
38	            channelId: channelId,
39	            title: title,
40	            message: message,
41	          });
42	        } catch (error) {
43	          console.log(`Error triggering local notification ${error}`);
44	        }
45	      }
46	    },
47	
48	    onRegister: async function (token) {
49	      console.log(`Registered with device token ${token.token}`);
50	      let deviceToken = token.token;
51	
52	      // Create the notification channel, required for Android notifications
53	      await PushNotification.createChannel({
54	        channelId: channelId,
55	        channelName: 'Guide channel',
56	      });
57	      console.log('Notification channel created!');
58	
59	      // Create a Parse Installation, that will link our application's push notification
60	      // to the Parse server
61	      try {
62	        const installationId = await Parse._getInstallationId();
63	        const Installation = new Parse.Installation();
64	        // Make sure to change any needed value from the following
65	        Installation.set('deviceType', 'android');
66	        Installation.set('GCMSenderId', 'YOUR_GCM_SENDER_ID');
67	        Installation.set('pushType', 'gcm');
68	        Installation.set('appIdentifier', 'YOUR_APP_IDENTIFIER(PACKAGE_NAME)');
69	        Installation.set('parseVersion', '3.2.0');
70	        Installation.set('appName', 'Back4AppGuidePushNotifications');
71	        Installation.set('appVersion', '1.0');
72	        Installation.set('localeIdentifier', 'pt-BR');
73	        Installation.set('badge', 0); // Set initial notification badge number
74	        Installation.set('timeZone', 'America/Sao_Paulo');
75	        Installation.set('installationId', installationId);
76	        Installation.set('channels', [channelId]);
77	        Installation.set('deviceToken', deviceToken);
78	        await Installation.save();
79	        console.log(`Created new Parse Installation ${Installation}`);
80	      } catch (error) {
81	        console.log(error.message);
82	      }
83	    },
84	    popInitialNotification: true,
85	    requestPermissions: true,
86	  });
87	}
```

```typescript
1	import PushNotification from 'react-native-push-notification';
2	import Parse from 'parse/react-native';
3	
4	const channelId: string = 'guideChannel';
5	
6	export async function configurePushNotifications() {
7	  // Initialize PushNotification
8	  await PushNotification.configure({
9	    // (required) Called when a remote is received or opened, or local notification is opened
10	    onNotification: function (notification: object) {
11	      // process the notification
12	      console.log('NOTIFICATION:', notification);
13	      // If notification is remote and has data, trigger local notification to show popup.
14	      // This is needed for Parse sent notifications because Firebase doesn't trigger popup
15	      // notifications with data by itself
16	      if (
17	        notification.data !== undefined &&
18	        notification.data.data !== undefined
19	      ) {
20	        try {
21	          // Notification data comes as a stringified JSON, so parsing is needed
22	          const notificationData = JSON.parse(notification.data.data);
23	          // JSON Parse notifications from the dashboard and Cloud Code
24	          // should contain the default `title` and `message` parameters
25	          let title: string = 'Notification Title';
26	          if (notificationData.title !== undefined) {
27	            title = notificationData.title;
28	          }
29	          let message: string = 'Noticiation Message';
30	          if (notificationData.message !== undefined) {
31	            message = notificationData.message;
32	          }
33	          // Text Parse notifications from the dashboard only have an `alert` parameter
34	          if (notificationData.alert !== undefined) {
35	            message = notificationData.alert;
36	          }
37	          PushNotification.localNotification({
38	            channelId: channelId,
39	            title: title,
40	            message: message,
41	          });
42	        } catch (error: any) {
43	          console.log(`Error triggering local notification ${error}`);
44	        }
45	      }
46	    },
47	
48	    onRegister: async function (token: {os: string; token: string}) {
49	      console.log(`Registered with device token ${token.token}`);
50	      let deviceToken: string = token.token;
51	
52	      // Create the notification channel, required for Android notifications
53	      await PushNotification.createChannel({
54	        channelId: channelId,
55	        channelName: 'Guide channel',
56	      });
57	      console.log('Notification channel created!');
58	
59	      // Create a Parse Installation, that will link our application's push notification
60	      // to the Parse server
61	      try {
62	        const installationId = await Parse._getInstallationId();
63	        const Installation = new Parse.Installation();
64	        // Make sure to change any needed value from the following
65	        Installation.set('deviceType', 'android');
66	        Installation.set('GCMSenderId', 'YOUR_GCM_SENDER_ID');
67	        Installation.set('pushType', 'gcm');
68	        Installation.set('appIdentifier', 'YOUR_APP_IDENTIFIER(PACKAGE_NAME)');
69	        Installation.set('parseVersion', '3.2.0');
70	        Installation.set('appName', 'Back4AppGuidePushNotifications');
71	        Installation.set('appVersion', '1.0');
72	        Installation.set('localeIdentifier', 'pt-BR');
73	        Installation.set('badge', 0); // Set initial notification badge number
74	        Installation.set('timeZone', 'America/Sao_Paulo');
75	        Installation.set('installationId', installationId);
76	        Installation.set('channels', [channelId]);
77	        Installation.set('deviceToken', deviceToken);
78	        await Installation.save();
79	        console.log(`Created new Parse Installation ${Installation}`);
80	      } catch (error) {
81	        console.log(error.message);
82	      }
83	    },
84	    popInitialNotification: true,
85	    requestPermissions: true,
86	  });
87	}
```
:::

:::hint{type="info"}
**Open Source Documentation**
More information about the Parse.Installation class can be found [here](https://docs.parseplatform.org/js/guide/#push-notifications).
:::

Note that the event handler onNotification method has a code that triggers a local notification in the app after receiving a remote one. Parse requires this code because Firebase doesn’t trigger popup notifications with data by itself. More on that and Android notification types can be read <a href="https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages" target="_blank">here</a>.

Call this configurePushNotifications method in your App.js (or App.tsx) file after initializing Parse and before declaring your App content.

:::CodeblockTabs
App.js

```javascript
1	// Other imports
2	// ...
3	import {configurePushNotifications} from './src/PushNotificationMethods';
4	
5	// Your Parse initialization configuration goes here
6	// ...
7	
8	// Initialize PushNotifications
9	configurePushNotifications();
```
:::

Build and run the application. You can now see an Installation table in your app’s Back4App dashboard with an entry corresponding to your app.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZC2-pd0RZQlBAl3DW7c9d_image.png)

## 5 - Sending Push Notifications via Dashboard

We are now ready to send the first push notification to our app. Follow the steps below to send a push message via Back4App’s Dashboard:

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on Dashboard.
2. Click on Push > Send New Push and create an audience for your push notification.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ily4bGb7Ftx038Bgvkmmc_image.png)

&#x20;    3\. Write your message and look at the preview by clicking on the Android option.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0Pl7dwnpiZZgUlYcrwsD3_image.png)

&#x20;    4\. If you have already reviewed the push notification and you want to send it, click on Send push.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/iekjy_NEACTLvOjNUR-Yl_image.png)

:::hint{type="info"}
You may explore the other options for Push Notification at Parse Dashboard.
There, it’s also possible to look at Past Pushes you sent and the Audiences you created for them.
:::

## 6 - Sending Push Notifications via Cloud Code

Using [Cloud functions starter guide](https://www.back4app.com/docs/get-started/cloud-functions), you can detach reusable methods from your front-end and get complete control of all your backend resources via the master key.

Let’s use cloud code functions to send a push message. First, create a cloud function called sendPush which calls the Parse.Push.send method. There are two ways to select which users will receive the notification: querying the Parse.Installation class or via notification channel names. It’s more common in Android Apps to use channels to distinguish between the users, so let’s use it. Make sure to deploy this function on Back4App before go ahead.

:::hint{type="info"}
**Open Source Documentation**
Please check the [open source documentation](https://docs.parseplatform.org/js/guide/#sending-options) for more details about the send method.
:::

```javascript
1	Parse.Cloud.define('sendPush', async (request) => {
2	  try {
3	    await Parse.Push.send({
4	      channels: [ 'guideChannel' ],
5	      data: {
6	        message: 'Test message',
7	        title: 'Back4App Guide Notification'
8	      }
9	    }, { useMasterKey: true });
10	    return true;
11	  } catch (error) {
12	    return `Error: ${error}`
13	  }
14	});
```

Let’s now call the function from the React Native app. Add this function to the PushNotificationMethods.js (or PushNotificationMethods.tsx) file and call it in a button inside your application’s main screen:

```javascript
1	export async function sendNotification() {
2	  try {
3	    await Parse.Cloud.run('sendPush');
4	    console.log('Success!');
5	  } catch (error: any) {
6	    console.log(`Error: ${error}`);
7	  }
8	}
```

:::hint{type="info"}
Note that this case of allowing a user to trigger a push notification by himself is not common, we used it here just to show how simple it is to integrate the push notification sending with Parse.
:::

## Conclusion

At the end of this guide, you learned how to send Push Notifications using Parse to your React Native application on Android. In the next guide, you will learn how to send them on iOS.

[title] App templates
[path] Parse Dashboard/

# Mobile App Templates

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read this page.**
:::

## Introduction

Templates are helpful to reduce the time for app creation, using them you can use a pre-existing structure.

With the app templates, you will reduce the backend creation time significantly, and you will be able to engage more users to use your app.

Along with Back4app, the application code templates help you accelerate the app development cycle, saving months of development.

The current deployment process on Back4App works as follows:

- A customer purchases the app on Codecanyon
- The customer needs to [create an account ](https://www.back4app.com/docs/get-started/new-parse-app)on Back4App
- The customer needs to copy the App IDand Client Key and paste into Config files
- Upload main.js file to the cloud code
- Edit records on dashboard (time-consuming process)
- Create class
- Create columns
- Create rows
- Enter data

Are you interested to contribute? You can [publish an app on Hub](https://www.back4app.com/docs/database-hub/publish) to help other users :)

[title] Sign Up With GitHub
[path] Platform/

# Sign In with Github Tutorial

## Introduction

Sign In with Github enables users to sign in to Apps using their Github accounts.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create an subdomain in Back4App.
- An <a href="https://github.com/" target="_blank">Github account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a new Github App

Create a new Github Application by going to [Applications/New](https://github.com/settings/applications/new)
Fill up the Application name, your Homepage URL, a quick Description and your Authorization callback URL\`

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ld1eSVaG7slk55nMfTb39_image.png" signedSrc size="80" width="1178" height="1124" position="center" caption}

Then click Register Application. You should then see your App Secret and Client Secret

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Y-ycoPAFfoOZqBF0fG-u2_image.png" signedSrc size="80" width="1516" height="1584" position="center" caption}

## 3 - Retrieve your Code

Visit the following URL, changing the values for CLIENT\_ID for the one you created.

> https://github.com/login/oauth/authorize scope=user:email&client_id=CLIENT_ID

Log in with your GitHub account:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/k7oreeJEhLcTKa53B201E_image.png" signedSrc size="90" width="1130" height="1268" position="center" caption}

and the redirected website will have your code in the URL:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/s-OO81f3ZtNAzMDW9Zpo4_image.png)

Copy the Code part of the URL only and run the following CURL command replacing the values YOUR\_CODE, YOUR\_CLIENT\_ID, and YOUR\_CLIENT\_SECRET for the values of your application

```curl
1   curl -X POST \
2       -F \'client_id=YOUR_CLIENT_ID' 
3       -F 'client_secret=YOUR_CLIENT_SECRET' 
4       -F 'code=YOUR_CODE' 
5       -F 'accept=json' 
6       https://github.com/login/oauth/access_token
```

Run it and you should retrieve your access token:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7pxb4bu-YmtkcXDt1kUlg_image.png)

**REMEMBER**: the code can be used only once. If you get an error or don’t use your token, you must re-generate your Code to be able to run it again.

## 4 - Start the development

Now that the Sign In with Github is configured, you can start the development process.
The format for AUTHDATA is:

```json
1   {
2     "github": {
3       "id": "user's Github id (string)",
4       "access_token": "an authorized Github access token for the user"
5     }
6   }
```

Here is the method for the iOS SDK:

```swift
1   PFUser.logInWithAuthType(inBackground: "github", authData: ["access_token":tokenString, "id": user]).continueWith { task -> Any? in
2    
3   }
```

And here for the Android SDK:

```java
1   Map<string, string, bool> authData = new HashMap<string, string, bool>(); 
2   authData.put("access_token", tokenString);
3   authData.put("id", user);
4   ParseUser.logInWithInBackground("github", authData){
5
6   }
```


[title] User LogIn
[path] React Native/Parse SDK (REST)/Users/

# User LogIn and LogOut for React Native

## Introduction

After implementing a component that handles User Registration in Parse in the last guide, you will now learn how to log in and log out users using the same Parse.User class. You will also learn to install and configure react-navigation so you can navigate the user through your new screens and components.
The Parse.User.logIn method stores in your local storage a valid user session, so future calls to methods like currentAsync will successfully retrieve your User data. On the other hand, logOut will clear this session from disk and log out of any linked services in your Parse server.

::embed[]{url="https://www.youtube.com/embed/NKhzloPrIQw"}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## Goal

To build a User LogIn and LogOut feature using Parse for a React Native App.

## 1 - Installing dependencies

At some point, every application in React Native will need screen navigation. You will now learn how to install and configure the most used library in React Native for this, react-navigation.

Go to your project root directory and install the following dependencies:

> cd yourApp
>
> yarn add @react-navigation/native react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

If you are developing for iOS, you need to install the pods to complete your app auto-linking:

> cd ios
>
> npx pod-install

:::hint{type="info"}
**Note:** Linking is automatic for React native 0.60+ to all platforms, so if you are still using an older version of RN, take a look at React Native docs [here](https://reactnative.dev/docs/linking-libraries-ios).&#x20;
:::

In your app entry file (App.tsx or App.js), add this import line at the very top of the file. Now you need to move your components inside the react-navigation container, which will encapsulate your app inside a NavigationContainer:

:::CodeblockTabs
App.tsx/App.js

```typescript
1	import "react-native-gesture-handler";
2	// Your other imports go here
3	import { NavigationContainer } from "@react-navigation/native";
4	
5	const App = () => {
6	  return <NavigationContainer>{/* Your app code */}</NavigationContainer>;
7	}
8	
9	export default App;
```
:::

The core navigation library has different additional navigation modules like a stack, tabs, drawer, others. Stack navigator is the most straightforward one and the one that you will use in this guide. Proceed with the installation:

> \# This is the navigator that you will use
> yarn add @react-navigation/stack

## 2 - Creating a StackNavigator

Let’s now create and set up a StackNavigator. This module will manage and handle screen navigation on your app. Since it’s not a goal here to give you a react-navigation deep understanding, please go to the [official docs](https://reactnavigation.org/docs/hello-react-navigation/) to know more.

In your App.js (App.tsx if you are using TypeScript) file, import and create a StackNavigator, create a function containing your user registration screen and insert the navigator inside your app NavigationContainer. Your main file should look like this:

:::CodeblockTabs
App.tsx/App.js

```typescript
1	import 'react-native-gesture-handler';
2	import React from 'react';
3	import {Image, SafeAreaView, StatusBar, Text, View} from 'react-native';
4	
5	import AsyncStorage from '@react-native-async-storage/async-storage';
6	import Parse from 'parse/react-native';
7	import {NavigationContainer} from '@react-navigation/native';
8	import {createStackNavigator} from '@react-navigation/stack';
9	import {UserRegistration} from './UserRegistration';
10	import Styles from './Styles';
11	
12	// Your Parse initialization configuration goes here
13	Parse.setAsyncStorage(AsyncStorage);
14	const PARSE_APPLICATION_ID: string = 'APPLICATION_ID';
15	const PARSE_HOST_URL: string = 'HOST_URL';
16	const PARSE_JAVASCRIPT_ID: string = 'JAVASCRIPT_ID';
17	Parse.initialize(PARSE_APPLICATION_ID, PARSE_JAVASCRIPT_ID);
18	Parse.serverURL = PARSE_HOST_URL;
19	
20	// Wrap your old app screen in a separate function, so you can create a screen
21	// inside the navigator; you can also declare your screens in a separate file, 
22	// export and import here to reduce some clutter
23	function UserRegistrationScreen() {
24	  return (
25	    <>
26	      <StatusBar />
27	      <SafeAreaView style={Styles.login_container}>
28	        <View style={Styles.login_header}>
29	          <Image
30	            style={Styles.login_header_logo}
31	            source={require('./assets/logo-back4app.png')}
32	          />
33	          <Text style={Styles.login_header_text}>
34	            <Text style={Styles.login_header_text_bold}>
35	              {'React Native on Back4App - '}
36	            </Text>
37	            {' User registration'}
38	          </Text>
39	        </View>
40	        <UserRegistration />
41	      </SafeAreaView>
42	    </>
43	  );
44	}
45	
46	// Create your main navigator here
47	const Stack = createStackNavigator();
48	
49	// Add the stack navigator to your NavigationContainer
50	// and in it you can add all your app screens in the order you need
51	// them on your stack
52	const App = () => {
53	  return (
54	    <NavigationContainer>
55	      <Stack.Navigator>
56	        <Stack.Screen name="Sign Up" component={UserRegistrationScreen} />
57	      </Stack.Navigator>
58	    </NavigationContainer>
59	  );
60	};
61	
62	export default App;
```
:::

If you run your app now, you will notice the inclusion of a header at the top of the screen containing your screen name:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Axy6jQGtw5aTsMPqRiOAG_image.png" signedSrc size="50" width="358" height="732" position="center" caption}

## 3 - Creating a login component

Let’s now build the UserLogIn functional component in your App. Create a new file in your root directory called UserLogIn.js (UserLogIn.tsx if you are using TypeScript) and add the input elements using state hooks to manage their data:

:::CodeblockTabs
UserLogIn.js

```javascript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Image,
4	  Text,
5	  TextInput,
6	  TouchableOpacity,
7	  View,
8	} from 'react-native';
9	import Parse from 'parse/react-native';
10	import Styles from './Styles';
11	
12	export const UserLogIn = () => {
13	  const [username, setUsername] = useState("");
14	  const [password, setPassword] = useState("");
15	
16	  return (
17	    <View style={Styles.login_wrapper}>
18	      <View style={Styles.form}>
19	        <TextInput
20	          style={Styles.form_input}
21	          value={username}
22	          placeholder={'Username'}
23	          onChangeText={(text) => setUsername(text)}
24	          autoCapitalize={'none'}
25	          keyboardType={'email-address'}
26	        />
27	        <TextInput
28	          style={Styles.form_input}
29	          value={password}
30	          placeholder={'Password'}
31	          secureTextEntry
32	          onChangeText={(text) => setPassword(text)}
33	        />
34	        <TouchableOpacity onPress={() => {}}>
35	          <View style={Styles.button}>
36	            <Text style={Styles.button_label}>{'Sign in'}</Text>
37	          </View>
38	        </TouchableOpacity>
39	      </View>
40	      <View style={Styles.login_social}>
41	        <View style={Styles.login_social_separator}>
42	          <View style={Styles.login_social_separator_line} />
43	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
44	          <View style={Styles.login_social_separator_line} />
45	        </View>
46	        <View style={Styles.login_social_buttons}>
47	          <TouchableOpacity>
48	            <View
49	              style={[
50	                Styles.login_social_button,
51	                Styles.login_social_facebook,
52	              ]}>
53	              <Image
54	                style={Styles.login_social_icon}
55	                source={require('./assets/icon-facebook.png')}
56	              />
57	            </View>
58	          </TouchableOpacity>
59	          <TouchableOpacity>
60	            <View style={Styles.login_social_button}>
61	              <Image
62	                style={Styles.login_social_icon}
63	                source={require('./assets/icon-google.png')}
64	              />
65	            </View>
66	          </TouchableOpacity>
67	          <TouchableOpacity>
68	            <View style={Styles.login_social_button}>
69	              <Image
70	                style={Styles.login_social_icon}
71	                source={require('./assets/icon-apple.png')}
72	              />
73	            </View>
74	          </TouchableOpacity>
75	        </View>
76	      </View>
77	    </View>
78	  );
79	};
```

UserLogIn.tsx

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Image,
4	  Text,
5	  TextInput,
6	  TouchableOpacity,
7	  View,
8	} from 'react-native';
9	import Parse from 'parse/react-native';
10	import Styles from './Styles';
11	
12	export const UserLogIn: FC<{}> = ({}): ReactElement => {
13	  const [username, setUsername] = useState("");
14	  const [password, setPassword] = useState("");
15	
16	  return (
17	    <View style={Styles.login_wrapper}>
18	      <View style={Styles.form}>
19	        <TextInput
20	          style={Styles.form_input}
21	          value={username}
22	          placeholder={'Username'}
23	          onChangeText={(text) => setUsername(text)}
24	          autoCapitalize={'none'}
25	          keyboardType={'email-address'}
26	        />
27	        <TextInput
28	          style={Styles.form_input}
29	          value={password}
30	          placeholder={'Password'}
31	          secureTextEntry
32	          onChangeText={(text) => setPassword(text)}
33	        />
34	        <TouchableOpacity onPress={() => {}}>
35	          <View style={Styles.button}>
36	            <Text style={Styles.button_label}>{'Sign in'}</Text>
37	          </View>
38	        </TouchableOpacity>
39	      </View>
40	      <View style={Styles.login_social}>
41	        <View style={Styles.login_social_separator}>
42	          <View style={Styles.login_social_separator_line} />
43	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
44	          <View style={Styles.login_social_separator_line} />
45	        </View>
46	        <View style={Styles.login_social_buttons}>
47	          <TouchableOpacity>
48	            <View
49	              style={[
50	                Styles.login_social_button,
51	                Styles.login_social_facebook,
52	              ]}>
53	              <Image
54	                style={Styles.login_social_icon}
55	                source={require('./assets/icon-facebook.png')}
56	              />
57	            </View>
58	          </TouchableOpacity>
59	          <TouchableOpacity>
60	            <View style={Styles.login_social_button}>
61	              <Image
62	                style={Styles.login_social_icon}
63	                source={require('./assets/icon-google.png')}
64	              />
65	            </View>
66	          </TouchableOpacity>
67	          <TouchableOpacity>
68	            <View style={Styles.login_social_button}>
69	              <Image
70	                style={Styles.login_social_icon}
71	                source={require('./assets/icon-apple.png')}
72	              />
73	            </View>
74	          </TouchableOpacity>
75	        </View>
76	      </View>
77	    </View>
78	  );
79	};
```
:::

You can now implement the function that will call theParse.User.logInmethod, using state variables:

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogIn = async function () {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue = username;
4	  const passwordValue = password;
5	  return await Parse.User.logIn(usernameValue, passwordValue)
6	    .then(async (loggedInUser) => {
7	      // logIn returns the corresponding ParseUser object
8	      Alert.alert(
9	        'Success!',
10	        `User ${loggedInUser.get('username')} has successfully signed in!`,
11	      );
12	      // To verify that this is in fact the current user, currentAsync can be used
13	      const currentUser = await Parse.User.currentAsync();
14	      console.log(loggedInUser === currentUser);
15	      return true;
16	    })
17	    .catch((error) => {
18	      // Error can be caused by wrong parameters or lack of Internet connection
19	      Alert.alert('Error!', error.message);
20	      return false;
21	    });
22	};
```

```typescript
1	const doUserLogIn = async function (): Promise<boolean> {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue: string = username;
4	  const passwordValue: string = password;
5	  return await Parse.User.logIn(usernameValue, passwordValue)
6	    .then(async (loggedInUser: Parse.User) => {
7	      // logIn returns the corresponding ParseUser object
8	      Alert.alert(
9	        'Success!',
10	        `User ${loggedInUser.get('username')} has successfully signed in!`,
11	      );
12	      // To verify that this is in fact the current user, currentAsync can be used
13	      const currentUser: Parse.User = await Parse.User.currentAsync();
14	      console.log(loggedInUser === currentUser);
15	      return true;
16	    })
17	    .catch((error: object) => {
18	      // Error can be caused by wrong parameters or lack of Internet connection
19	      Alert.alert('Error!', error.message);
20	      return false;
21	    });
22	};
```
:::

Insert this function inside the UserLogIn component, just before the return call, to be called and tested. Remember to update the form’s login button onPress action to () => doUserLogIn() and to import Alert from react-native. Your component should now look like this:

:::CodeblockTabs
UserLogIn.js

```javascript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  Text,
6	  TextInput,
7	  TouchableOpacity,
8	  View,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import Styles from './Styles';
12	
13	export const UserLogIn = () => {
14	  const [username, setUsername] = useState('');
15	  const [password, setPassword] = useState('');
16	
17	  const doUserLogIn = async function () {
18	    // Note that these values come from state variables that we've declared before
19	    const usernameValue = username;
20	    const passwordValue = password;
21	    return await Parse.User.logIn(usernameValue, passwordValue)
22	      .then(async (loggedInUser) => {
23	        // logIn returns the corresponding ParseUser object
24	        Alert.alert(
25	          'Success!',
26	          `User ${loggedInUser.get('username')} has successfully signed in!`,
27	        );
28	        // To verify that this is in fact the current user, currentAsync can be used
29	        const currentUser = await Parse.User.currentAsync();
30	        console.log(loggedInUser === currentUser);
31	        return true;
32	      })
33	      .catch((error) => {
34	        // Error can be caused by wrong parameters or lack of Internet connection
35	        Alert.alert('Error!', error.message);
36	        return false;
37	      });
38	  };
39	
40	  return (
41	    <View style={Styles.login_wrapper}>
42	      <View style={Styles.form}>
43	        <TextInput
44	          style={Styles.form_input}
45	          value={username}
46	          placeholder={'Username'}
47	          onChangeText={(text) => setUsername(text)}
48	          autoCapitalize={'none'}
49	          keyboardType={'email-address'}
50	        />
51	        <TextInput
52	          style={Styles.form_input}
53	          value={password}
54	          placeholder={'Password'}
55	          secureTextEntry
56	          onChangeText={(text) => setPassword(text)}
57	        />
58	        <TouchableOpacity onPress={() => doUserLogIn()}>
59	          <View style={Styles.button}>
60	            <Text style={Styles.button_label}>{'Sign in'}</Text>
61	          </View>
62	        </TouchableOpacity>
63	      </View>
64	      <View style={Styles.login_social}>
65	        <View style={Styles.login_social_separator}>
66	          <View style={Styles.login_social_separator_line} />
67	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
68	          <View style={Styles.login_social_separator_line} />
69	        </View>
70	        <View style={Styles.login_social_buttons}>
71	          <TouchableOpacity>
72	            <View
73	              style={[
74	                Styles.login_social_button,
75	                Styles.login_social_facebook,
76	              ]}>
77	              <Image
78	                style={Styles.login_social_icon}
79	                source={require('./assets/icon-facebook.png')}
80	              />
81	            </View>
82	          </TouchableOpacity>
83	          <TouchableOpacity>
84	            <View style={Styles.login_social_button}>
85	              <Image
86	                style={Styles.login_social_icon}
87	                source={require('./assets/icon-google.png')}
88	              />
89	            </View>
90	          </TouchableOpacity>
91	          <TouchableOpacity>
92	            <View style={Styles.login_social_button}>
93	              <Image
94	                style={Styles.login_social_icon}
95	                source={require('./assets/icon-apple.png')}
96	              />
97	            </View>
98	          </TouchableOpacity>
99	        </View>
100	      </View>
101	    </View>
102	  );
103	};
```

UserLogIn.tsx

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  Text,
6	  TextInput,
7	  TouchableOpacity,
8	  View,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import Styles from './Styles';
12	
13	export const UserLogIn: FC<{}> = ({}): ReactElement => {
14	  const [username, setUsername] = useState('');
15	  const [password, setPassword] = useState('');
16	
17	  const doUserLogIn = async function (): Promise<boolean> {
18	    // Note that these values come from state variables that we've declared before
19	    const usernameValue: string = username;
20	    const passwordValue: string = password;
21	    return await Parse.User.logIn(usernameValue, passwordValue)
22	      .then(async (loggedInUser: Parse.User) => {
23	        // logIn returns the corresponding ParseUser object
24	        Alert.alert(
25	          'Success!',
26	          `User ${loggedInUser.get('username')} has successfully signed in!`,
27	        );
28	        // To verify that this is in fact the current user, currentAsync can be used
29	        const currentUser: Parse.User = await Parse.User.currentAsync();
30	        console.log(loggedInUser === currentUser);
31	        return true;
32	      })
33	      .catch((error: object) => {
34	        // Error can be caused by wrong parameters or lack of Internet connection
35	        Alert.alert('Error!', error.message);
36	        return false;
37	      });
38	  };
39	
40	  return (
41	    <View style={Styles.login_wrapper}>
42	      <View style={Styles.form}>
43	        <TextInput
44	          style={Styles.form_input}
45	          value={username}
46	          placeholder={'Username'}
47	          onChangeText={(text) => setUsername(text)}
48	          autoCapitalize={'none'}
49	          keyboardType={'email-address'}
50	        />
51	        <TextInput
52	          style={Styles.form_input}
53	          value={password}
54	          placeholder={'Password'}
55	          secureTextEntry
56	          onChangeText={(text) => setPassword(text)}
57	        />
58	        <TouchableOpacity onPress={() => doUserLogIn()}>
59	          <View style={Styles.button}>
60	            <Text style={Styles.button_label}>{'Sign in'}</Text>
61	          </View>
62	        </TouchableOpacity>
63	      </View>
64	      <View style={Styles.login_social}>
65	        <View style={Styles.login_social_separator}>
66	          <View style={Styles.login_social_separator_line} />
67	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
68	          <View style={Styles.login_social_separator_line} />
69	        </View>
70	        <View style={Styles.login_social_buttons}>
71	          <TouchableOpacity>
72	            <View
73	              style={[
74	                Styles.login_social_button,
75	                Styles.login_social_facebook,
76	              ]}>
77	              <Image
78	                style={Styles.login_social_icon}
79	                source={require('./assets/icon-facebook.png')}
80	              />
81	            </View>
82	          </TouchableOpacity>
83	          <TouchableOpacity>
84	            <View style={Styles.login_social_button}>
85	              <Image
86	                style={Styles.login_social_icon}
87	                source={require('./assets/icon-google.png')}
88	              />
89	            </View>
90	          </TouchableOpacity>
91	          <TouchableOpacity>
92	            <View style={Styles.login_social_button}>
93	              <Image
94	                style={Styles.login_social_icon}
95	                source={require('./assets/icon-apple.png')}
96	              />
97	            </View>
98	          </TouchableOpacity>
99	        </View>
100	      </View>
101	    </View>
102	  );
103	};
```
:::

## 4 - Creating a login screen

Let’s now create a new screen for your app that will use your UserLogIn component. Add the new screen as the first (or top) screen of your StackNavigator as well:

:::CodeblockTabs
App.tsx/App.js

```typescript
1	// ...
2	
3	// Add this new screen function below the UserRegistrationScreen
4	function UserLogInScreen() {
5	  return (
6	    <>
7	      <StatusBar />
8	      <SafeAreaView style={Styles.login_container}>
9	        <View style={Styles.login_header}>
10	          <Image
11	            style={Styles.login_header_logo}
12	            source={require('./assets/logo-back4app.png')}
13	          />
14	          <Text style={Styles.login_header_text}>
15	            <Text style={Styles.login_header_text_bold}>
16	              {'React Native on Back4App - '}
17	            </Text>
18	            {' User login'}
19	          </Text>
20	        </View>
21	        <UserLogIn />
22	      </SafeAreaView>
23	    </>
24	  );
25	}
26	
27	// ...
28	
29	// Add the screen as the top one at your StackNavigator
30	const App = () => {
31	  return (
32	    <NavigationContainer>
33	      <Stack.Navigator>
34	        <Stack.Screen name="Login" component={UserLogInScreen} />
35	        <Stack.Screen name="Sign Up" component={UserRegistrationScreen} />
36	      </Stack.Navigator>
37	    </NavigationContainer>
38	  );
39	};
40	
41	// ...
```
:::

Go ahead and test your screen and component. Notice that your app will now show the user login screen instead of the user registration one, due to their placement on the StackNavigator screen list. You will see a message like this after signing in:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cXG_2oVHycVlL5VmR3BjT_image.png" signedSrc size="50" width="355" height="730" position="center" caption}

## 5 - Creating a Home Screen and handling navigation

After logging in, you will generally want to take the user to your app home screen. Create this screen in your app’s main file:

:::CodeblockTabs
App.tsx/App.js

```typescript
1	// ...
2	
3	// Add this new screen function below the UserRegistrationScreen
4	function UserLogInScreen() {
5	  return (
6	    <>
7	      <StatusBar />
8	      <SafeAreaView style={Styles.login_container}>
9	        <View style={Styles.login_header}>
10	          <Image
11	            style={Styles.login_header_logo}
12	            source={require('./assets/logo-back4app.png')}
13	          />
14	          <Text style={Styles.login_header_text}>
15	            <Text style={Styles.login_header_text_bold}>
16	              {'React Native on Back4App - '}
17	            </Text>
18	            {' User login'}
19	          </Text>
20	        </View>
21	        <UserLogIn />
22	      </SafeAreaView>
23	    </>
24	  );
25	}
26	
27	// ...
28	
29	// Add the screen as the top one at your StackNavigator
30	const App = () => {
31	  return (
32	    <NavigationContainer>
33	      <Stack.Navigator>
34	        <Stack.Screen name="Login" component={UserLogInScreen} />
35	        <Stack.Screen name="Sign Up" component={UserRegistrationScreen} />
36	      </Stack.Navigator>
37	    </NavigationContainer>
38	  );
39	};
40	
41	// ...
```
:::

Now you need to use ‘react-navigation’ to navigate the user after he logs in, adding this new code inside theUserLogIncomponent:

:::CodeblockTabs
UserLogIn.js

```javascript
1	// ...
2	// Add this import
3	import {useNavigation} from '@react-navigation/native';
4	
5	export const UserLogIn = () => {
6	  // Add this to use useNavigation hook
7	  const navigation = useNavigation();
8	  
9	  //...
10	
11	  const doUserLogIn = async function () {
12	    const usernameValue = username;
13	    const passwordValue = password;
14	    return await Parse.User.logIn(usernameValue, passwordValue)
15	      .then(async (loggedInUser) => {
16	        Alert.alert(
17	          'Success!',
18	          `User ${loggedInUser.get('username')} has successfully signed in!`,
19	        );
20	        const currentUser = await Parse.User.currentAsync();
21	        console.log(loggedInUser === currentUser);
22	        // Add this to navigate your home screen; Navigation.navigate takes
23	        // the user to the screen named after the one passed as parameter
24	        navigation.navigate('Home');
25	        return true;
26	      })
27	      .catch((error) => {
28	        Alert.alert('Error!', error.message);
29	        return false;
30	      });
31	  };
32	// ...
```

UserLogIn.tsx

```typescript
1	// ...
2	// Add this import
3	import {useNavigation} from '@react-navigation/native';
4	
5	export const UserLogIn: FC<{}> = ({}): ReactElement => {
6	  // Add this to use useNavigation hook
7	  const navigation = useNavigation();
8	
9	  // ...
10	  
11	  const doUserLogIn = async function (): Promise<boolean> {
12	    const usernameValue: string = username;
13	    const passwordValue: string = password;
14	    return await Parse.User.logIn(usernameValue, passwordValue)
15	      .then(async (loggedInUser: Parse.User) => {
16	        Alert.alert(
17	          'Success!',
18	          `User ${loggedInUser.get('username')} has successfully signed in!`,
19	        );
20	        const currentUser: Parse.User = await Parse.User.currentAsync();
21	        console.log(loggedInUser === currentUser);
22	        // Add this to navigate your home screen; Navigation.navigate takes
23	        // the user to the screen named after the one passed as parameter
24	        navigation.navigate('Home');
25	        return true;
26	      })
27	      .catch((error: object) => {
28	        Alert.alert('Error!', error.message);
29	        return false;
30	      });
31	  };
32	
33	// ...
```
:::

You will be redirected to your new HomeScreen after logging in. You can upgrade this screen by adding this HelloUser component that shows the current user username:

:::CodeblockTabs
HelloUser.js

```javascript
1	import React, {FC, ReactElement, useEffect, useState} from 'react';
2	import {Text} from 'react-native';
3	import Parse from 'parse/react-native';
4	import Styles from './Styles';
5	
6	export const HelloUser = () => {
7	  // State variable that will hold username value
8	  const [username, setUsername] = useState('');
9	
10	  // useEffect is called after the component is initially rendered and
11	  // after every other render
12	  useEffect(() => {
13	    // Since the async method Parse.User.currentAsync is needed to
14	    // retrieve the current user data, you need to declare an async
15	    // function here and call it afterwards
16	    async function getCurrentUser() {
17	      // This condition ensures that username is updated only if needed
18	      if (username === '') {
19	        const currentUser = await Parse.User.currentAsync();
20	        if (currentUser !== null) {
21	          setUsername(currentUser.getUsername());
22	        }
23	      }
24	    }
25	    getCurrentUser();
26	  }, [username]);
27	
28	  // Note the condition operator here, so the "Hello" text is only
29	  // rendered if there is an username value
30	  return (
31	    <View style={Styles.login_wrapper}>
32	      <View style={Styles.form}>
33	        {username !== '' && <Text>{`Hello ${username}!`}</Text>}
34	      </View>
35	    </View>
36	  );
37	};
```

HelloUser.tsx

```typescript
1	import React, {FC, ReactElement, useEffect, useState} from 'react';
2	import {Text} from 'react-native';
3	import Parse from 'parse/react-native';
4	import Styles from './Styles';
5	
6	export const HelloUser: FC<{}> = ({}): ReactElement => {
7	  // State variable that will hold username value
8	  const [username, setUsername] = useState('');
9	
10	  // useEffect is called after the component is initially rendered and
11	  // after every other render
12	  useEffect(() => {
13	    // Since the async method Parse.User.currentAsync is needed to
14	    // retrieve the current user data, you need to declare an async
15	    // function here and call it afterwards
16	    async function getCurrentUser() {
17	      // This condition ensures that username is updated only if needed
18	      if (username === '') {
19	        const currentUser = await Parse.User.currentAsync();
20	        if (currentUser !== null) {
21	          setUsername(currentUser.getUsername());
22	        }
23	      }
24	    }
25	    getCurrentUser();
26	  }, [username]);
27	
28	  // Note the condition operator here, so the "Hello" text is only
29	  // rendered if there is an username value
30	  return (
31	    <View style={Styles.login_wrapper}>
32	      <View style={Styles.form}>
33	        {username !== '' && <Text>{`Hello ${username}!`}</Text>}
34	      </View>
35	    </View>
36	  );
37	};
```
:::

:::hint{type="info"}
**Note:** You can take a better look at React’s useEffect hooks [here](https://reactjs.org/docs/hooks-effect.html).
:::

After calling this component inside your HomeScreen, your app should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/F1JR7J-5HVh7MjAAdPtgt_image.png" signedSrc size="50" width="361" height="731" position="center" caption}

## 6 - Creating a logout component

The UserLogOut component is simpler than the login since the Parse.User.logOut method takes no arguments and clears the currentUser data stored locally automatically. Create this component in your app root directory:

:::CodeblockTabs
UserLogOut.js

```javascript
1	import React, {FC, ReactElement} from 'react';
2	import {Alert, Text, TouchableOpacity, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {useNavigation} from '@react-navigation/native';
5	import {StackActions} from '@react-navigation/native';
6	import Styles from './Styles';
7	
8	export const UserLogOut = () => {
9	  const navigation = useNavigation();
10	
11	  const doUserLogOut = async function () {
12	    return await Parse.User.logOut()
13	      .then(async () => {
14	        // To verify that current user is now empty, currentAsync can be used
15	        const currentUser = await Parse.User.currentAsync();
16	        if (currentUser === null) {
17	          Alert.alert('Success!', 'No user is logged in anymore!');
18	        }
19	        // Navigation dispatch calls a navigation action, and popToTop will take
20	        // the user back to the very first screen of the stack
21	        navigation.dispatch(StackActions.popToTop());
22	        return true;
23	      })
24	      .catch((error) => {
25	        Alert.alert('Error!', error.message);
26	        return false;
27	      });
28	  };
29	
30	  return (
31	    <View style={Styles.login_wrapper}>
32	      <View style={Styles.form}>
33	        <TouchableOpacity onPress={() => doUserLogOut()}>
34	          <View style={Styles.button}>
35	            <Text style={Styles.button_label}>{'Logout'}</Text>
36	          </View>
37	        </TouchableOpacity>
38	      </View>
39	    </View>
40	  );
41	};
```

UserLogOut.tsx

```typescript
1	import React, {FC, ReactElement} from 'react';
2	import {Alert, Text, TouchableOpacity, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {useNavigation} from '@react-navigation/native';
5	import {StackActions} from '@react-navigation/native';
6	import Styles from './Styles';
7	
8	export const UserLogOut: FC<{}> = ({}): ReactElement => {
9	  const navigation = useNavigation();
10	
11	  const doUserLogOut = async function (): Promise<boolean> {
12	    return await Parse.User.logOut()
13	      .then(async () => {
14	        // To verify that current user is now empty, currentAsync can be used
15	        const currentUser: Parse.User = await Parse.User.currentAsync();
16	        if (currentUser === null) {
17	          Alert.alert('Success!', 'No user is logged in anymore!');
18	        }
19	        // Navigation dispatch calls a navigation action, and popToTop will take
20	        // the user back to the very first screen of the stack
21	        navigation.dispatch(StackActions.popToTop());
22	        return true;
23	      })
24	      .catch((error: object) => {
25	        Alert.alert('Error!', error.message);
26	        return false;
27	      });
28	  };
29	
30	  return (
31	    <View style={Styles.login_wrapper}>
32	      <View style={Styles.form}>
33	        <TouchableOpacity onPress={() => doUserLogOut()}>
34	          <View style={Styles.button}>
35	            <Text style={Styles.button_label}>{'Logout'}</Text>
36	          </View>
37	        </TouchableOpacity>
38	      </View>
39	    </View>
40	  );
41	};
```
:::

Append this new component to HomeScreen so you can test it after signing in. Your app home screen will look like this now:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/onWiS-UCALVmfhSazjE7L_image.png" signedSrc size="50" width="351" height="728" position="center" caption}

If you perform a successful logout, you will see a message like this while being redirected to the UserLogIn screen, which is the first one in the navigation stack:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4ncdQ-pdCOArfxivJ-IZF_image.png" signedSrc size="50" width="374" height="736" position="center" caption}

Connect your UserRegistration to a registration button inside the UserLogIn screen and repeat the same redirection pattern to your app HomeScreen. Remember to read through react-navigation’s docs to discover several customization options and control every aspect of your app navigation.

:::CodeblockTabs
UserLogIn.js

```javascript
1	// ...
2	
3	export const UserLogIn = () => {
4	  const navigation = useNavigation();
5	
6	  const [username, setUsername] = useState('');
7	  const [password, setPassword] = useState('');
8	
9	  const doUserLogIn = async function () {
10	    // Note that these values come from state variables that we've declared before
11	    const usernameValue = username;
12	    const passwordValue = password;
13	    return await Parse.User.logIn(usernameValue, passwordValue)
14	      .then(async (loggedInUser) => {
15	        // logIn returns the corresponding ParseUser object
16	        Alert.alert(
17	          'Success!',
18	          `User ${loggedInUser.get('username')} has successfully signed in!`,
19	        );
20	        // To verify that this is in fact the current user, currentAsync can be used
21	        const currentUser: Parse.User = await Parse.User.currentAsync();
22	        console.log(loggedInUser === currentUser);
23	        // Navigation.navigate takes the user to the screen named after the one
24	        // passed as parameter
25	        navigation.navigate('Home');
26	        return true;
27	      })
28	      .catch((error) => {
29	        // Error can be caused by wrong parameters or lack of Internet connection
30	        Alert.alert('Error!', error.message);
31	        return false;
32	      });
33	  };
34	
35	  return (
36	    <View style={Styles.login_wrapper}>
37	      <View style={Styles.form}>
38	        <TextInput
39	          style={Styles.form_input}
40	          value={username}
41	          placeholder={'Username'}
42	          onChangeText={(text) => setUsername(text)}
43	          autoCapitalize={'none'}
44	          keyboardType={'email-address'}
45	        />
46	        <TextInput
47	          style={Styles.form_input}
48	          value={password}
49	          placeholder={'Password'}
50	          secureTextEntry
51	          onChangeText={(text) => setPassword(text)}
52	        />
53	        <TouchableOpacity onPress={() => doUserLogIn()}>
54	          <View style={Styles.button}>
55	            <Text style={Styles.button_label}>{'Sign in'}</Text>
56	          </View>
57	        </TouchableOpacity>
58	      </View>
59	      <View style={Styles.login_social}>
60	        <View style={Styles.login_social_separator}>
61	          <View style={Styles.login_social_separator_line} />
62	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
63	          <View style={Styles.login_social_separator_line} />
64	        </View>
65	        <View style={Styles.login_social_buttons}>
66	          <TouchableOpacity>
67	            <View
68	              style={[
69	                Styles.login_social_button,
70	                Styles.login_social_facebook,
71	              ]}>
72	              <Image
73	                style={Styles.login_social_icon}
74	                source={require('./assets/icon-facebook.png')}
75	              />
76	            </View>
77	          </TouchableOpacity>
78	          <TouchableOpacity>
79	            <View style={Styles.login_social_button}>
80	              <Image
81	                style={Styles.login_social_icon}
82	                source={require('./assets/icon-google.png')}
83	              />
84	            </View>
85	          </TouchableOpacity>
86	          <TouchableOpacity>
87	            <View style={Styles.login_social_button}>
88	              <Image
89	                style={Styles.login_social_icon}
90	                source={require('./assets/icon-apple.png')}
91	              />
92	            </View>
93	          </TouchableOpacity>
94	        </View>
95	      </View>
96	      <>
97	        <TouchableOpacity onPress={() => navigation.navigate('Sign Up')}>
98	          <Text style={Styles.login_footer_text}>
99	            {"Don't have an account? "}
100	            <Text style={Styles.login_footer_link}>{'Sign up'}</Text>
101	          </Text>
102	        </TouchableOpacity>
103	      </>
104	    </View>
105	  );
106	};
107	
108	// ...
```

UserLogIn.tsx

```typescript
1	// ...
2	
3	export const UserLogIn: FC<{}> = ({}): ReactElement => {
4	  const navigation = useNavigation();
5	
6	  const [username, setUsername] = useState('');
7	  const [password, setPassword] = useState('');
8	
9	  const doUserLogIn = async function (): Promise<boolean> {
10	    // Note that these values come from state variables that we've declared before
11	    const usernameValue: string = username;
12	    const passwordValue: string = password;
13	    return await Parse.User.logIn(usernameValue, passwordValue)
14	      .then(async (loggedInUser: Parse.User) => {
15	        // logIn returns the corresponding ParseUser object
16	        Alert.alert(
17	          'Success!',
18	          `User ${loggedInUser.get('username')} has successfully signed in!`,
19	        );
20	        // To verify that this is in fact the current user, currentAsync can be used
21	        const currentUser: Parse.User = await Parse.User.currentAsync();
22	        console.log(loggedInUser === currentUser);
23	        // Navigation.navigate takes the user to the screen named after the one
24	        // passed as parameter
25	        navigation.navigate('Home');
26	        return true;
27	      })
28	      .catch((error: object) => {
29	        // Error can be caused by wrong parameters or lack of Internet connection
30	        Alert.alert('Error!', error.message);
31	        return false;
32	      });
33	  };
34	
35	  return (
36	    <View style={Styles.login_wrapper}>
37	      <View style={Styles.form}>
38	        <TextInput
39	          style={Styles.form_input}
40	          value={username}
41	          placeholder={'Username'}
42	          onChangeText={(text) => setUsername(text)}
43	          autoCapitalize={'none'}
44	          keyboardType={'email-address'}
45	        />
46	        <TextInput
47	          style={Styles.form_input}
48	          value={password}
49	          placeholder={'Password'}
50	          secureTextEntry
51	          onChangeText={(text) => setPassword(text)}
52	        />
53	        <TouchableOpacity onPress={() => doUserLogIn()}>
54	          <View style={Styles.button}>
55	            <Text style={Styles.button_label}>{'Sign in'}</Text>
56	          </View>
57	        </TouchableOpacity>
58	      </View>
59	      <View style={Styles.login_social}>
60	        <View style={Styles.login_social_separator}>
61	          <View style={Styles.login_social_separator_line} />
62	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
63	          <View style={Styles.login_social_separator_line} />
64	        </View>
65	        <View style={Styles.login_social_buttons}>
66	          <TouchableOpacity>
67	            <View
68	              style={[
69	                Styles.login_social_button,
70	                Styles.login_social_facebook,
71	              ]}>
72	              <Image
73	                style={Styles.login_social_icon}
74	                source={require('./assets/icon-facebook.png')}
75	              />
76	            </View>
77	          </TouchableOpacity>
78	          <TouchableOpacity>
79	            <View style={Styles.login_social_button}>
80	              <Image
81	                style={Styles.login_social_icon}
82	                source={require('./assets/icon-google.png')}
83	              />
84	            </View>
85	          </TouchableOpacity>
86	          <TouchableOpacity>
87	            <View style={Styles.login_social_button}>
88	              <Image
89	                style={Styles.login_social_icon}
90	                source={require('./assets/icon-apple.png')}
91	              />
92	            </View>
93	          </TouchableOpacity>
94	        </View>
95	      </View>
96	      <>
97	        <TouchableOpacity onPress={() => navigation.navigate('Sign Up')}>
98	          <Text style={Styles.login_footer_text}>
99	            {"Don't have an account? "}
100	            <Text style={Styles.login_footer_link}>{'Sign up'}</Text>
101	          </Text>
102	        </TouchableOpacity>
103	      </>
104	    </View>
105	  );
106	};
107	
108	// ...
```
:::

:::CodeblockTabs
UserRegistration.js

```javascript
1	export const UserRegistration = () => {
2	  const navigation = useNavigation();
3	
4	  const [username, setUsername] = useState('');
5	  const [password, setPassword] = useState('');
6	
7	  const doUserSignUp = async function () {
8	    // Note that these values come from state variables that we've declared before
9	    const usernameValue = username;
10	    const passwordValue = password;
11	    // Since the signUp method returns a Promise, we need to call it using await
12	    return await Parse.User.signUp(usernameValue, passwordValue)
13	      .then((createdUser) => {
14	        // Parse.User.signUp returns the already created ParseUser object if successful
15	        Alert.alert(
16	          'Success!',
17	          `User ${createdUser.get('username')} was successfully created!`,
18	        );
19	        // Navigation.navigate takes the user to the screen named after the one
20	        // passed as parameter
21	        navigation.navigate('Home');
22	        return true;
23	      })
24	      .catch((error) => {
25	        // signUp can fail if any parameter is blank or failed an uniqueness check on the server
26	        Alert.alert('Error!', error.message);
27	        return false;
28	      });
29	  };
30	
31	  return (
32	    <View style={Styles.login_wrapper}>
33	      <View style={Styles.form}>
34	        <TextInput
35	          style={Styles.form_input}
36	          value={username}
37	          placeholder={'Username'}
38	          onChangeText={(text) => setUsername(text)}
39	          autoCapitalize={'none'}
40	          keyboardType={'email-address'}
41	        />
42	        <TextInput
43	          style={Styles.form_input}
44	          value={password}
45	          placeholder={'Password'}
46	          secureTextEntry
47	          onChangeText={(text) => setPassword(text)}
48	        />
49	        <TouchableOpacity onPress={() => doUserSignUp()}>
50	          <View style={Styles.button}>
51	            <Text style={Styles.button_label}>{'Sign Up'}</Text>
52	          </View>
53	        </TouchableOpacity>
54	      </View>
55	      <View style={Styles.login_social}>
56	        <View style={Styles.login_social_separator}>
57	          <View style={Styles.login_social_separator_line} />
58	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
59	          <View style={Styles.login_social_separator_line} />
60	        </View>
61	        <View style={Styles.login_social_buttons}>
62	          <TouchableOpacity>
63	            <View
64	              style={[
65	                Styles.login_social_button,
66	                Styles.login_social_facebook,
67	              ]}>
68	              <Image
69	                style={Styles.login_social_icon}
70	                source={require('./assets/icon-facebook.png')}
71	              />
72	            </View>
73	          </TouchableOpacity>
74	          <TouchableOpacity>
75	            <View style={Styles.login_social_button}>
76	              <Image
77	                style={Styles.login_social_icon}
78	                source={require('./assets/icon-google.png')}
79	              />
80	            </View>
81	          </TouchableOpacity>
82	          <TouchableOpacity>
83	            <View style={Styles.login_social_button}>
84	              <Image
85	                style={Styles.login_social_icon}
86	                source={require('./assets/icon-apple.png')}
87	              />
88	            </View>
89	          </TouchableOpacity>
90	        </View>
91	      </View>
92	      <>
93	        <TouchableOpacity onPress={() => navigation.navigate('Login')}>
94	          <Text style={Styles.login_footer_text}>
95	            {'Already have an account? '}
96	            <Text style={Styles.login_footer_link}>{'Log In'}</Text>
97	          </Text>
98	        </TouchableOpacity>
99	      </>
100	    </View>
101	  );
102	};
103	
104	// ...
```

UserRegistration.tsx

```typescript
1	// ...
2	
3	export const UserRegistration: FC<{}> = ({}): ReactElement => {
4	  const navigation = useNavigation();
5	
6	  const [username, setUsername] = useState('');
7	  const [password, setPassword] = useState('');
8	
9	  const doUserSignUp = async function (): Promise<boolean> {
10	    // Note that these values come from state variables that we've declared before
11	    const usernameValue: string = username;
12	    const passwordValue: string = password;
13	    // Since the signUp method returns a Promise, we need to call it using await
14	    return await Parse.User.signUp(usernameValue, passwordValue)
15	      .then((createdUser: Parse.User) => {
16	        // Parse.User.signUp returns the already created ParseUser object if successful
17	        Alert.alert(
18	          'Success!',
19	          `User ${createdUser.get('username')} was successfully created!`,
20	        );
21	        // Navigation.navigate takes the user to the screen named after the one
22	        // passed as parameter
23	        navigation.navigate('Home');
24	        return true;
25	      })
26	      .catch((error: object) => {
27	        // signUp can fail if any parameter is blank or failed an uniqueness check on the server
28	        Alert.alert('Error!', error.message);
29	        return false;
30	      });
31	  };
32	
33	  return (
34	    <View style={Styles.login_wrapper}>
35	      <View style={Styles.form}>
36	        <TextInput
37	          style={Styles.form_input}
38	          value={username}
39	          placeholder={'Username'}
40	          onChangeText={(text) => setUsername(text)}
41	          autoCapitalize={'none'}
42	          keyboardType={'email-address'}
43	        />
44	        <TextInput
45	          style={Styles.form_input}
46	          value={password}
47	          placeholder={'Password'}
48	          secureTextEntry
49	          onChangeText={(text) => setPassword(text)}
50	        />
51	        <TouchableOpacity onPress={() => doUserSignUp()}>
52	          <View style={Styles.button}>
53	            <Text style={Styles.button_label}>{'Sign Up'}</Text>
54	          </View>
55	        </TouchableOpacity>
56	      </View>
57	      <View style={Styles.login_social}>
58	        <View style={Styles.login_social_separator}>
59	          <View style={Styles.login_social_separator_line} />
60	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
61	          <View style={Styles.login_social_separator_line} />
62	        </View>
63	        <View style={Styles.login_social_buttons}>
64	          <TouchableOpacity>
65	            <View
66	              style={[
67	                Styles.login_social_button,
68	                Styles.login_social_facebook,
69	              ]}>
70	              <Image
71	                style={Styles.login_social_icon}
72	                source={require('./assets/icon-facebook.png')}
73	              />
74	            </View>
75	          </TouchableOpacity>
76	          <TouchableOpacity>
77	            <View style={Styles.login_social_button}>
78	              <Image
79	                style={Styles.login_social_icon}
80	                source={require('./assets/icon-google.png')}
81	              />
82	            </View>
83	          </TouchableOpacity>
84	          <TouchableOpacity>
85	            <View style={Styles.login_social_button}>
86	              <Image
87	                style={Styles.login_social_icon}
88	                source={require('./assets/icon-apple.png')}
89	              />
90	            </View>
91	          </TouchableOpacity>
92	        </View>
93	      </View>
94	      <>
95	        <TouchableOpacity onPress={() => navigation.navigate('Login')}>
96	          <Text style={Styles.login_footer_text}>
97	            {'Already have an account? '}
98	            <Text style={Styles.login_footer_link}>{'Log In'}</Text>
99	          </Text>
100	        </TouchableOpacity>
101	      </>
102	    </View>
103	  );
104	};
105	
106	// ...
```
:::

Your login and registration screens now should look like these:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EGxclVQlaT1CgqtgpSYoH_image.png" signedSrc size="50" width="362" height="732" position="center" caption}

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/E_IJmJBmL96wTGsmwh5aa_image.png" signedSrc size="50" width="364" height="730" position="center" caption}

## Conclusion

At the end of this guide, you learned how to log in and log out Parse users on React Native and to navigate users through your application using react-navigation. In the next guide, we will show you how to perform useful user queries.

[title] Database Browser
[path] Parse Dashboard/Core/

# Database Browser

## Introduction

In this tutorial, you will learn how to explore the features available at Database Browser.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read or edit this page.**
:::



## Database Browser

Back4App is a BaaS (*Backend-as-a-service*) platform powered by Parse Open Source which you can use to build your app faster, host it with no hassles and keep full control over your Backend.

Once your Back4App is created, the *Database Browser* allows you to manage your app’s data with creating, reading, updating or removing your objects and classes.

The Dashboard looks similar to a spreadsheet and lets you create classes and its properties in a row/column structure, that’s easy to understand and maintain over time. Also, importing/exporting/filtering data makes data management a breeze.

At this point, we are going to show you the main features available at this section:

## Add a row

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gZQC0c9y798AutPJRRn7j_image.png" signedSrc size="40" width="94" height="46" position="flex-start" caption}
:::

:::VerticalSplitItem
To add a new object in the class, you need to click in the Add a row blue button or in the icon, after you’ve created a new row, just double-click into the cell to set the new values.
:::
::::



## Add a column

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ueUa3yKi_ZjNmuXa25a7D_image.png" signedSrc size="40" width="91" height="43" position="flex-start" caption}
:::

:::VerticalSplitItem
To add a new column in the class, you need to click over the item and a modal will be opened:
:::
::::

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_UDv5XsEP_DgQ4E6a7e8F_image.png)
:::

:::VerticalSplitItem
Now, you should add the column type and choose its name. Learn more about Data Types <a href="https://docs.parseplatform.org/js/guide/#data-types" target="_blank">here</a>.&#x20;
:::
::::



## Import CSV

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pDWTQBxd-mWgwZszjrLGP_image.png)
:::

:::VerticalSplitItem
import data into Parse tables, see the link below for how it works and examples of how this tool works: <a href="https://www.back4app.com/docs/parse-dashboard/core/import-csv" target="_blank">Importing CSV files</a>&#x20;
:::
::::



## Manage Columns

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SPj2ZGYbG1hnrToCXpJP__image.png" signedSrc size="40" width="47" height="42" position="flex-start" caption}
:::

:::VerticalSplitItem
By clicking on this icon, you can hide or sort the columns.
:::
::::



## Refresh Browser

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BQQSxvXBmJL973Uv14bMO_image.png" signedSrc size="40" width="47" height="46" position="flex-start" caption}
:::

:::VerticalSplitItem
Clicking on this icon, you will hold reload the page to update the data.
:::
::::



## Filter

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vStVbJvCCOFtNV6XWO8sR_image.png" signedSrc size="40" width="49" height="46" position="flex-start" caption}
:::

:::VerticalSplitItem
The easiest way to filter your data from the Dashboard is using this icon which will allow a quick analysis through the conditions you can select.
:::
::::



## **Security**

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YsFAyIRqkn5Gde99LA0Dd_image.png)
:::

:::VerticalSplitItem
By clicking on this icon, you can configure the <a href="https://docs.parseplatform.org/rest/guide/#class-level-permissions" target="_blank">class-level Permissions</a> that apply security measures at that level, restricting how and when client applications can access and create data in Parse.&#x20;
:::
::::



## Edit

::::VerticalSplit{layout="left"}
:::VerticalSplitItem
::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bllBaE8PWY4mSvWrM8R-4_image.png" signedSrc size="40" width="55" height="48" position="flex-start" caption}
:::

:::VerticalSplitItem
Briefly, the *Edit* button allows manipulations of data like add/remove row, column or class, configure the <a href="https://docs.parseplatform.org/rest/guide/#class-level-permissions" target="_blank">class-level Permissions</a> that apply security measures at that level, restricting how and when client applications can access and create data in Parse, and manipulate and create indexes to your database that will improve the database performance. Also, inside this option, you will be able to <a href="https://help.back4app.com/hc/en-us/articles/115003438951-How-can-I-import-to-my-Database-" target="_blank">import</a> or <a href="https://help.back4app.com/hc/en-us/articles/115003457092-How-can-I-export-my-Database-" target="_blank">export</a> your data.
:::
::::

## It’s done

In this guide, you’ve learned about the main features inside Database Browser.

[title] Instagram Basic Display
[path] Platform/

# Instagram Basic Display API Tutorial

## Introduction

The Instagram Basic Display API is an HTTP-based API that apps can use to get an Instagram user’s profile, images, videos, and albums.
Since October 15, 2019, new client registration and permission review on Instagram API platform are discontinued in favor of the Instagram Basic Display API and you should use this method from now on.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create a subdomain in Back4App.
- An <a href="https://developers.facebook.com/docs/instagram" target="_blank">Instagram Developer account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Present the Authorization Window

The Authorization Window allows app users to grant your app permissions and short-lived Instagram User Access Tokens. After a user logs in and chooses which data to allow your app to access, we will redirect the user to your app and include an Authorization Code, which you can then exchange for a short-lived access token.

To begin the process, get the Authorization Window and present it to the user:

> 1   https://api.instagram.com/oauth/authorize
> 2     ?client_id={instagram-app-id}
> 3     &redirect_uri={redirect-uri}
> 4     &scope={scope}
> 5     &response_type=code
> 6     &state={state}        //Optional

All parameters except state are required.

If authorization is successful, we will redirect the user to your redirect\_uri and pass you an Authorization Code through the code query string parameter. Capture the code so your app can exchange if for a short-lived Instagram User Access Token.

Authorization Codes are valid for 1 hour and can only be used once.

A sample Authorization Code would be:

> **https://myapp.back4app.io/auth/?code=AQBx-hBsH3...#_**

Note that #\_ will be appended to the end of the redirect URI, but it is not part of the code itself, so strip it out.

## 3 - Retrieve your Token

Once you receive a code, exchange it for a short-lived access token by sending a POST request to the following endpoint:

> 1   POST https://api.instagram.com/oauth/access_token

A sample request would be like this:

```curl
1   curl -X POST \
2     https://api.instagram.com/oauth/access_token \
3     -F client_id=990602627938098 \
4     -F client_secret=eb8c7... \
5     -F grant_type=authorization_code \
6     -F redirect_uri=https://socialsizzle.herokuapp.com/auth/ \
7     -F code=AQBx-hBsH3...
```

and a successful response will look similar to this:

```json
1   {
2     "access_token": "IGQVJ...",
3     "user_id": 17841405793187218
4   }
```

## 4 - Start the development

Now that the Sign In with Instagram is configured, you can start the development process passing the Access Token you retrieved for authentication.
The format for AUTHDATA is:

```json
1   {
2     "instagram": {
3       "id": "user's Instagram id (string)",
4       "access_token": "an authorized Instagram access token for the user"
5     }
6   }
```

Here is the method for the iOS SDK:

```swift
1   PFUser.logInWithAuthType(inBackground: "instagram", authData: ["access_token":tokenString, "id": user]).continueWith { task -> Any? in
2    
3   }
```

And here for the Android SDK:

```java
1   Map<string, string> authData = new HashMap<string, string>(); 
2   authData.put("access_token", tokenString);
3   authData.put("id", user);
4   ParseUser.logInWithInBackground("instagram", authData){
5
6   }
```


[title] File Storage
[path] React Native/Parse SDK (REST)/Files/

# Save Files from a React Native App

## Introduction

In this guide, you will learn how to store and retrieve files in your React Native Application using Parse Javascript SDK to manage Back4app cloud storage.

In the Parse world, we use the typeParse.File to manage files. After creating the Parse.File, you will store it on the Back4App Cloud using the save() method. You should always associate the file with another data object so you can retrieve this file path when querying the object. If you do not associate, the file will be stored, but you will not find them on the Cloud.

Another important tip is to give a name to the file that has a file extension. This extension lets Parse figure out the file type and handle it accordingly. We should also mention that Each upload gets a unique identifier, so there’s no problem with uploading multiple files using the same name.

React Native App’s most common use case is storing images. In this guide, you will build a demo gallery app to store and display pictures.

The full sample code for the created App in this tutorial is [here](https://github.com/templates-back4app/react-native-demo-gallery-app). Feel free to follow along step by step or jump straight to the code.
playing images.

:::hint{type="danger"}
If you do not associate your file to a data object the file will become an orphan file and you wont be able to find it on Back4App Cloud.
:::

## Goal

To create a React Native gallery App that uploads and displays images using Parse Javascript and Back4app.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- Complete the <a href="https://www.back4app.com/docs/parse-sdk/react-native-sdk" target="_blank">Install Parse SDK</a> tutorial.
:::

## 1 - Installing dependencies

Working with files (i.e., uploading photos) on React Native apps is one of the most common features. In this tutorial, you will build a simple gallery App that uploads and displays images.

Once you have a [React Native project successfully connected with Back4app](https://www.back4app.com/docs/parse-sdk/react-native-sdk), go to its root directory and install the following dependency:

> cd startWithBack4app
>
> \# To select images on devices
> yarn add react-native-image-picker 

For iOS, install pods:

> cd ios && npx pod-install

Note that auto-linking is available for React native v0.60+, but for information on installing react-native-image-picker older versions, [check the official documentation here](https://github.com/react-native-image-picker/react-native-image-picker).

After installing, you will need to add the NSPhotoLibraryUsageDescription key to your info.plist for allowing the user to select image/video from photos on iOS.

> \<dict>
> //other keys ...
> \<key>NSPhotoLibraryUsageDescription</key>
> \<string>APP_NAME_HERE would like access to your photo</string> 
> \</dict>

On android no permissions are required to select photos for gallery.

## 2 - Selecting an Image from Gallery

Next, you will build a component that wraps the UI and logic for selecting an image from the gallery and uploading it.

In your root directory, create a UploadingImage.js file with the following content:

```javascript
1	import  React, {useState} from  'react';
2	import {View, Button, Image, StyleSheet} from  'react-native';
3	
4	import {launchImageLibrary} from  'react-native-image-picker';
5	import Parse from 'parse/react-native.js';
6	
7	const  UploadImage = () => {
8	const [image, setImage] = useState(null);
9	
10	async function upload() {
11	        // TODO: implement this method
12	}
13	// This will open phone image library
14	function pickImage() {
15	  launchImageLibrary(
16	        {
17	          mediaType:  'photo',
18	          includeBase64:  true,
19	          maxHeight:  200,
20	          maxWidth:  200,
21	        },
22	        (response) => {
23	          // Add selected image to the state
24	          setImage(response);
25	        },
26	  );
27	}
28	
29	return (
30	  <View>
31	        <Button
32	          onPress={pickImage}
33	          title="Pick an image from gallery"
34	          color="#841584" />
35	          {image && <Image source={ {uri: image.uri} } style={styles.currentImage}/>}
36	
37	          {image && <Button title="Upload" color="green" onPress={upload}  />}
38	  </View>
39	);
40	
41	};
42	
43	const styles = StyleSheet.create({
44	  container: {
45	    height:  400,
46	    justifyContent:  'center',
47	    alignItems:  'center',
48	  },
49	  currentImage: {
50	        width:  250,
51	        height:  250,
52	        resizeMode:  'cover',
53	        alignSelf:  'center',
54	  },
55	});
56	
57	export  default  UploadImage;
```

The above component renders:

1. A button which opens the image library when a user clicks
2. The selected image along with an upload button

As you can see, the upload method does not do anything. Next, you will implement its behavior and see how to actually upload images to Back4app cloud.

## 3 - Uploading an Image

Back4app storage is built upon Parse.File and lets you store any files such as documents, images, videos, music, and any other binary data. Parse.File is a utility class that Parse Javascript SDK provides to abstract the file storage process and make it easy for you.

Therefore, to upload an image, you will only need to create a Parse.File instance and then call the save method. By doing this, Parse will automatically do the rest for you. You can read the [full documentation about Parse Files here](https://docs.parseplatform.org/js/guide/#files).

Let’s do that in our upload function:

```javascript
1	async function upload() {
2	  // 1. Create a file
3	  const {base64, fileName} = image;
4	  const  parseFile = new  Parse.File(fileName, {base64});
5	
6	  // 2. Save the file
7	  try {
8	        const responseFile = await  parseFile.save();
9	        const Gallery = Parse.Object.extend('Gallery');
10	        const gallery = new  Gallery();
11	        gallery.set('picture', responseFile);
12	
13	        await gallery.save();
14	        Alert.alert('The file has been saved to Back4app.');
15	  } catch (error) {
16	          console.log(
17	            'The file either could not be read, or could not be saved to Back4app.',
18	          );
19	        }
20	}
```

In short, the above snippet creates and saves the selected image, and after the save completes, we associate it with a Parse.Object called Gallery.

Now you need to import and use the UploadImage component in your App.js:

```javascript
1	import React from 'react';
2	import {SafeAreaView, StyleSheet} from 'react-native';
3	// In a React Native application
4	import Parse from 'parse/react-native.js';
5	import AsyncStorage from '@react-native-community/async-storage';
6	import keys from './constants/Keys';
7	//Before using the SDK...
8	Parse.setAsyncStorage(AsyncStorage);
9	Parse.initialize(keys.applicationId, keys.javascriptKey);
10	Parse.serverURL = keys.serverURL;
11	
12	import UploadImage from './UploadImage';
13	
14	const App = () => {
15	  return (
16	        <SafeAreaView style={styles.container}>
17	                <UploadImage/>
18	        </SafeAreaView>
19	  )
20	};
21	
22	const styles = StyleSheet.create({
23	  container: {
24	        backgroundColor: '#f5f5f5',
25	        flex: 1,
26	  },
27	  title: {
28	        fontSize: 18,
29	        textAlign: 'center',
30	        fontWeight: 'bold',
31	  },
32	});
33	
34	export default App;
```

Once you do that, you should be able to pick images from the gallery:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wj-9xLb7nlPZOFo-Z3n_J_image.png" signedSrc size="50" width="380" height="796" position="center" caption}

And successfully upload images hitting the upload button:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kosbv459hnKX83Q1F8Uo7_image.png" signedSrc size="50" width="377" height="797" position="center" caption}

## 4 - Displaying Images

We need to get the image’s URL to retrieve the image’s contents and display it to our users. Next, you will build a component for querying images from our Gallery Object and displaying them on a FlatList.

In your root directory, create a Gallery.js file with the following content:

```javascript
1	import React, {useState, useEffect} from  'react';
2	import {Text, Image, FlatList, StyleSheet} from 'react-native';
3	
4	import Parse from 'parse/react-native.js';
5	
6	const Gallery = () => {
7	const [images, setImages] = useState([]);
8	
9	useEffect(() => {
10	  const fetchImages = async () => {
11	        let query = new Parse.Query('Gallery');
12	        const results = await query.find();
13	        setImages(results);
14	  };
15	  
16	  fetchImages();
17	}, []);
18	
19	return (
20	  <FlatList
21	    style={styles.container}
22	        contentContainerStyle={styles.listContent}
23	        data={images}
24	        horizontal={false}
25	        numColumns={3}
26	        ListEmptyComponent={() =>  <Text>No images uploaded.</Text>}
27	        renderItem={({item}) => 
28	          <Image source={ {uri: item.get('picture').url()} } style={styles.imageItem}/>
29	        )}
30	        keyExtractor={(item) => item.id}
31	  />);
32	};
33	
34	const  styles = StyleSheet.create({
35	  container: {
36	        flex: 1,
37	        backgroundColor: '#f5f5f5',
38	  },
39	  listContent: {
40	        justifyContent: 'center',
41	        alignItems: 'center',
42	  },
43	  imageItem: {
44	        width: 100,
45	        height: 100,
46	        resizeMode: 'cover',
47	        marginHorizontal: 5,
48	        marginVertical: 5,
49	  },
50	});
51	
52	export default Gallery;
```

The above component uses the useEffect hook to query images uploaded to the Gallery Object once it finishes rendering. Next, you will need to add the component to your App.js:

```javascript
1	// ...other imports
2	import UploadImage from './UploadImage';
3	import Gallery from './Gallery';
4	
5	const App = () => {
6	  return (
7	        <SafeAreaView style={styles.container}>
8	          <UploadImage/>
9	          <Gallery/>
10	        </SafeAreaView>
11	        );
12	}
```

When you run your App, you should be able to see the list of uploaded images like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qWi5YHa87nR0gLTJ4Rsdh_image.png" signedSrc size="50" width="377" height="799" position="center" caption}

## 5 - It’s Done!

At this point, you have uploaded your first image on Back4App and displayed it in a React Native application.

[title] 1:N Relationship
[path] React Native/Parse SDK (REST)/Data objects/

# One to many Relationship

## Introduction

At the core of many backends, you will find the ability to store data. Using Parse, you can store data objects establishing relations between them. Data relations standardize how each data object is related or associated with other ones. That can give you extra power when building and running complex queries. There are three main relation types:

- one-to-many, where one object can be related to many other objects;
- one-to-one, establishing direct relations between two objects and only them;
- many-to-many, which can create many complex relations between many objects.

In this guide, we will focus on the most common relation, the one-to-many. Data storage backends usually will demand explicit declarations of these associations and even require that you create, on your own, relational tables for managing their joining. Parse makes it automatically for you, removing any possibility of errors while building your data relations and speeding up your application modeling process.

There are two ways to create a one-to-many relation in Parse. The first is using the Parse.Object Pointers, which is the fastest in creation and query time. The second is using arrays which can lead to slow query times depending on their size. Because of this performance issue, we will use only pointers examples from now on.

In this guide, you will implement a React Native book registration application that contains the three main kinds of data associations. You will learn how to create and query one-to-many data relations using your Back4App and React Native.

::embed[]{url="https://www.youtube.com/embed/MUebHAqJXdA"}

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">JavaScript Example Repository</a>
- <a href="https://github.com/templates-back4app/react-native-ts-associations" target="_blank">TypeScript Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

Use the Parse one-to-many relation to model a Book Store React Native App

## 1 - Understanding the Book class

Since we will be using a book registration application example in this guide, you need first to understand how the object relations are laid out in this database. The main object class you’ll be using is the Book class, storing each book entry in the registration. Also, these are the other four object classes:

- Publisher: book publisher name, one-to-many relation withBook
- Genre: book genre, one-to-many relation withBook. Note that for this example we will consider that a book can only have one genre;
- Author: book author, many-to-many relation withBook, since a book can have more than one author and an author can have more than one book as well;
- ISDB: book ISDB identifying number, one-to-one relation withBook, since this number is unique for each book.

Here is a visual representation of these database tables:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wfFSmbYqKoR49nmlOkZ2__image.png)

For simplicity, we will assume that each object class has only a string type name attribute (title for the Book), apart from any additional relational attribute.

## 2 - Creating one-to-many relations

Before going into this step we recommend you to clone and run the React Native Library app exmaple (<a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">JavaScript Example Repository</a>, <a href="https://github.com/templates-back4app/react-native-ts-associations" target="_blank">TypeScript Example Repository</a>). This application has two main screens: one responsible for listing the registered books and the other for creating new books. In the book registration form, there are direct links to the other related objects, so when a user picks a publisher or genre option, the form fields will hold the complete Parse.Object instance.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HFle1L6_yx8-Vg1IAQ20I_image.png" signedSrc size="50" width="346" height="730" position="center" caption}

Let’s take a look at the book creation method that is called when submitting this form:

:::CodeblockTabs
JavaScript

```javascript
1	const createBook = async function () {
2	  try {
3	    // These values come from state variables linked to
4	    // the screen form fields, retrieving the user choices
5	    // as a complete Parse.Object, when applicable;
6	    const bookTitleValue = bookTitle;
7	    const bookISBDValue = bookISBD;
8	    // For example, bookPublisher holds the value from
9	    // RadioButton.Group field with its options being every
10	    // Publisher parse object instance saved on server, which is
11	    // queried on screen load via useEffect
12	    const bookPublisherObject = bookPublisher;
13	    const bookGenreObject = bookGenre;
14	    // bookAuthors can be an array of Parse.Objects, since the book
15	    // may have more than one Author
16	    const bookAuthorsObjects = bookAuthors;
17	
18	    // Creates a new parse object instance
19	    let Book = new Parse.Object('Book');
20	
21	    // Set data to parse object
22	    // Simple title field
23	    Book.set('title', bookTitleValue);
24	
25	    // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
26	    let isbdQuery = new Parse.Query('ISBD');
27	    isbdQuery.equalTo('name', bookISBDValue);
28	    let isbdQueryResult = await isbdQuery.first();
29	    if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
30	      // If first returns a valid object instance, it means that there
31	      // is at least one instance of ISBD with the informed value
32	      Alert.alert(
33	        'Error!',
34	        'There is already an ISBD instance with this value!',
35	      );
36	      return false;
37	    } else {
38	      // Create a new ISBD object instance to create a one-to-one relation on saving
39	      let ISBD = new Parse.Object('ISBD');
40	      ISBD.set('name', bookISBDValue);
41	      ISBD = await ISBD.save();
42	      // Set the new object to the new book object ISBD field
43	      Book.set('isbd', ISBD);
44	    }
45	
46	    // One-to-many relations can be set in two ways:
47	    // add direct object to field (Parse will convert to pointer on save)
48	    Book.set('publisher', bookPublisherObject);
49	    // or add pointer to field
50	    Book.set('genre', bookGenreObject.toPointer());
51	
52	    // many-to-many relation
53	    // Create a new relation so data can be added
54	    let authorsRelation = Book.relation('authors');
55	    // bookAuthorsObjects is an array of Parse.Objects,
56	    // you can add to relation by adding the whole array or object by object
57	    authorsRelation.add(bookAuthorsObjects);
58	
59	    // After setting the values, save it on the server
60	    try {
61	      await Book.save();
62	      // Success
63	      Alert.alert('Success!');
64	      navigation.goBack();
65	      return true;
66	    } catch (error) {
67	      // Error can be caused by lack of Internet connection
68	      Alert.alert('Error!', error.message);
69	      return false;
70	    }
71	  } catch (error) {
72	    // Error can be caused by lack of value selection
73	    Alert.alert(
74	      'Error!',
75	      'Make sure to select valid choices in Publisher, Genre and Author fields!',
76	    );
77	    return false;
78	  }
79	};
```

```typescript
1	const createBook = async function (): Promise<boolean> {
2	  try {
3	    // These values come from state variables linked to
4	    // the screen form fields, retrieving the user choices
5	    // as a complete Parse.Object, when applicable;
6	    const bookTitleValue: string = bookTitle;
7	    const bookISBDValue: string = bookISBD;
8	    // For example, bookPublisher holds the value from
9	    // RadioButton.Group field with its options being every
10	    // Publisher parse object instance saved on server, which is
11	    // queried on screen load via useEffect
12	    const bookPublisherObject: Parse.Object = bookPublisher;
13	    const bookGenreObject: Parse.Object = bookGenre;
14	    // bookAuthors can be an array of Parse.Objects, since the book
15	    // may have more than one Author
16	    const bookAuthorsObjects: [Parse.Object] = bookAuthors;
17	
18	    // Creates a new parse object instance
19	    let Book: Parse.Object = new Parse.Object('Book');
20	
21	    // Set data to parse object
22	    // Simple title field
23	    Book.set('title', bookTitleValue);
24	
25	    // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
26	    let isbdQuery: Parse.Query = new Parse.Query('ISBD');
27	    isbdQuery.equalTo('name', bookISBDValue);
28	    let isbdQueryResult: Parse.Object = await isbdQuery.first();
29	    if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
30	      // If first returns a valid object instance, it means that there
31	      // is at least one instance of ISBD with the informed value
32	      Alert.alert(
33	        'Error!',
34	        'There is already an ISBD instance with this value!',
35	      );
36	      return false;
37	    } else {
38	      // Create a new ISBD object instance to create a one-to-one relation on saving
39	      let ISBD: Parse.Object = new Parse.Object('ISBD');
40	      ISBD.set('name', bookISBDValue);
41	      ISBD = await ISBD.save();
42	      // Set the new object to the new book object ISBD field
43	      Book.set('isbd', ISBD);
44	    }
45	
46	    // One-to-many relations can be set in two ways:
47	    // add direct object to field (Parse will convert to pointer on save)
48	    Book.set('publisher', bookPublisherObject);
49	    // or add pointer to field
50	    Book.set('genre', bookGenreObject.toPointer());
51	
52	    // many-to-many relation
53	    // Create a new relation so data can be added
54	    let authorsRelation = Book.relation('authors');
55	    // bookAuthorsObjects is an array of Parse.Objects,
56	    // you can add to relation by adding the whole array or object by object
57	    authorsRelation.add(bookAuthorsObjects);
58	
59	    // After setting the values, save it on the server
60	    try {
61	      await Book.save();
62	      // Success
63	      Alert.alert('Success!');
64	      navigation.goBack();
65	      return true;
66	    } catch (error) {
67	      // Error can be caused by lack of Internet connection
68	      Alert.alert('Error!', error.message);
69	      return false;
70	    }
71	  } catch (error) {
72	    // Error can be caused by lack of value selection
73	    Alert.alert(
74	      'Error!',
75	      'Make sure to select valid choices in Publisher, Genre and Author fields!',
76	    );
77	    return false;
78	  }
79	};
```
:::

Look how the bookPublisherObject and bookGenreObject are set to the new book Parse.Object instance. See how simple it is in Parse to create a one-to-many relation: you could either assign the target object instance or a pointer to it using the Parse.Object.set method, which takes two arguments: the field name and the value to be set. Parse will create a pointer data type column and a direct link on your dashboard for quick access under the hood.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VET2v9-ZUrlxO_yLPDU3a_image.png)

Clicking on the object pointer value in your dashboard will take you to the referenced object entry. It may seem like a harmless feature, but this makes debugging and error tracing much quicker than searching for it manually.

## 3 - Querying one-to-many relations

Querying one-to-many related objects is pretty straightforward as much of it is handled by Parse. Take a look at the query function in the book registers list screen:

:::CodeblockTabs
JavaScript

```javascript
1	const queryBooks = async function () {
2	  // These values come from state variables linked to
3	  // the screen query RadioButton.Group fields, with its options being every
4	  // parse object instance saved on server from the referred class, which is
5	  // queried on screen load via useEffect; these variables retrievie the user choices
6	  // as a complete Parse.Object;
7	  const queryPublisherValue = queryPublisher;
8	  const queryGenreValue = queryGenre;
9	  const queryAuthorValue = queryAuthor;
10	  const queryIsbdValue = queryIsbd;
11	
12	  // Reading parse objects is done by using Parse.Query
13	  const parseQuery = new Parse.Query('Book');
14	
15	  // One-to-many queries
16	  if (queryPublisherValue !== '') {
17	    parseQuery.equalTo('publisher', queryPublisherValue);
18	  }
19	  if (queryGenreValue !== '') {
20	    parseQuery.equalTo('genre', queryGenreValue);
21	  }
22	
23	  // One-to-one query
24	  if (queryIsbdValue !== '') {
25	    parseQuery.equalTo('isbd', queryIsbdValue);
26	  }
27	
28	  // Many-to-many query
29	  // In this case, we need to retrieve books related to the chosen author
30	  if (queryAuthorValue !== '') {
31	    parseQuery.equalTo('authors', queryAuthorValue);
32	  }
33	
34	  try {
35	    let books = await parseQuery.find();
36	    // Many-to-many objects retrieval
37	    // In this example we need to get every related author Parse.Object
38	    // and add it to our query result objects
39	    for (let book of books) {
40	      // This query is done by creating a relation and querying it
41	      let bookAuthorsRelation = book.relation('authors');
42	      book.authorsObjects = await bookAuthorsRelation.query().find();
43	    }
44	    setQueriedBooks(books);
45	    return true;
46	  } catch (error) {
47	    // Error can be caused by lack of Internet connection
48	    Alert.alert('Error!', error.message);
49	    return false;
50	  }
51	};
```

```typescript
1	const queryBooks = async function (): Promise<boolean> {
2	  // These values come from state variables linked to
3	  // the screen query RadioButton.Group fields, with its options being every
4	  // parse object instance saved on server from the referred class, which is
5	  // queried on screen load via useEffect; these variables retrievie the user choices
6	  // as a complete Parse.Object;
7	  const queryPublisherValue: Parse.Object = queryPublisher;
8	  const queryGenreValue: Parse.Object = queryGenre;
9	  const queryAuthorValue: Parse.Object = queryAuthor;
10	  const queryIsbdValue: Parse.Object = queryIsbd;
11	
12	  // Reading parse objects is done by using Parse.Query
13	  const parseQuery: Parse.Query = new Parse.Query('Book');
14	
15	  // One-to-many queries
16	  if (queryPublisherValue !== '') {
17	    parseQuery.equalTo('publisher', queryPublisherValue);
18	  }
19	  if (queryGenreValue !== '') {
20	    parseQuery.equalTo('genre', queryGenreValue);
21	  }
22	
23	  // One-to-one query
24	  if (queryIsbdValue !== '') {
25	    parseQuery.equalTo('isbd', queryIsbdValue);
26	  }
27	
28	  // Many-to-many query
29	  // In this case, we need to retrieve books related to the chosen author
30	  if (queryAuthorValue !== '') {
31	    parseQuery.equalTo('authors', queryAuthorValue);
32	  }
33	
34	  try {
35	    let books: [Parse.Object] = await parseQuery.find();
36	    // Many-to-many objects retrieval
37	    // In this example we need to get every related author Parse.Object
38	    // and add it to our query result objects
39	    for (let book of books) {
40	      // This query is done by creating a relation and querying it
41	      let bookAuthorsRelation = book.relation('authors');
42	      book.authorsObjects = await bookAuthorsRelation.query().find();
43	    }
44	    setQueriedBooks(books);
45	    return true;
46	  } catch (error) {
47	    // Error can be caused by lack of Internet connection
48	    Alert.alert('Error!', error.message);
49	    return false;
50	  }
51	};
```
:::

In this case, to query any books related to a specific publisher or genre, you need only to perform a Parse.Query.equalTo method passing the Parse.Object instance as the parameter. After querying, Parse will store inside the resulting objects the complete instances of any one-to-many relational fields. To retrieve and show data from these object instances, you can chain the Parse.Object.get method like this: bookParseObject.get(('publisher').get('name'). Here is how the list screen looks like by using these getters to retrieve the publisher and genre names from the list items:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8ogG07fbntUTVQq5VT90E_image.png" signedSrc size="50" width="352" height="733" position="center" caption}

## Conclusion

At the end of this guide, you learned how to create and query one-to-many relations in Parse on React Native. In the next guide, we will show you how to do many-to-many and one-to-one queries and relations.

[title] Getting started
[path] GraphQL Cookbook/

# Getting started with Parse GraphQL API

## Problem

You want to start using a GraphQL API for your Parse app running at Back4App.

## Solution

1. If you have already created an app at Back4App, go to the next step. Otherwise, you need to [create a new app](https://www.back4app.com/docs/get-started/new-parse-app).
2. The Parse GraphQL API is available at Back4App since version 3.6.0. Therefore, you need to [manage your Parse Server version](https://help.back4app.com/hc/en-us/articles/360016081032-Manage-Parse-Server-Version) and make sure it is above or equal to 3.6.0.
3. Visit your [Parse Dashboard](https://parse-dashboard.back4app.com/) (refresh the page in case it was already open), and click on your app -> Core -> API Console -> GraphQL Console.
4. You are now ready to play around with your Parse GraphQL API on Back4App!

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cvP49fY9HdSvPq1ErWrs9_image.png)


[title] Querying Users
[path] React Native/Parse SDK (REST)/Users/

# Querying users in Parse on React Native

## Introduction

Some React Native apps need to directly manage your application users or at least be able to list a specific subset of them. Parse has powerful querying tools and they can also be used for your users in your social media app, for example.
In this guide, you’ll learn how to use Parse.Query to perform realistic user querying in your React Native App using Parse JS SDK.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
:::

## Goal

To build a user querying feature using Parse for a React Native App.

## 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 for this guide use-case (Parse.User) can be seen below.

:::CodeblockTabs
JavaScript

```javascript
1   // This will create your query
2   const parseQuery = new Parse.Query(Parse.User);
3   // The query will resolve only after calling this method
5   const queryResult = await parseQuery.find();
```

```typescript
1   // This will create your query
2   const parseQuery: Parse.Query = new Parse.Query(Parse.User);
3   // The query will resolve only after calling this method
4   const queryResult: [Parse.User] = await parseQuery.find();
```
:::

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Performing relevant user queries

Let’s now take a look at some relevant queries that you may need to perform when managing or displaying users in your app. First of all, let’s perform a text search query, searching for users whose usernames contain the search value.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserQuery = async function () {
2	  // This value comes from a state variable
3	  const usernameSearchValue = usernameSearch;
4	  // This will create your user query
5	  const parseQuery = new Parse.Query(Parse.User);
6	  // Several query functions can be set to your Parse,Query, they will
7	  // only resolve when calling "find", for example
8	  if (usernameSearchValue !== '') {
9	    // "contains" will retrieve users whose username contain the searched value, case-sensitive
10	    parseQuery.contains('username', usernameSearchValue);
11	    // 
12	    // or 
13	    // 
14	    // for case-insensitive string search, use "matches", that will take into account
15	    // an regexp for matching, in this case use only "i", which is the regexp modifier
16	    // for case-insensitive
17	    parseQuery.matches('username', usernameSearchValue, 'i');
18	  }
19	  // Only after calling "find" all query conditions will resolve
20	  return await parseQuery
21	    .find()
22	    .then(async (queriedUsers => {
23	      // Set the query results to an state variable to retrieve it on your JSX
24	      // Be aware that empty or invalid queries return as an empty array
25	      setQueryResults(queriedUsers);
26	      return true;
27	    })
28	    .catch((error) => {
29	      // Error can be caused by lack of Internet connection, but in most
30	      // cases "find" will return as an empty array on "then"
31	      Alert.alert('Error!', error.message);
32	      setQueryResults([]);
33	      return false;
34	    });
35	};
```

```typescript
1	const doUserQuery = async function (): Promise<Boolean> {
2	  // This value comes from a state variable
3	  const usernameSearchValue: string = usernameSearch;
4	  // This will create your user query
5	  const parseQuery: Parse.Query = new Parse.Query(Parse.User);
6	  // Several query functions can be set to your Parse,Query, they will
7	  // only resolve when calling "find", for example
8	  if (usernameSearchValue !== '') {
9	    // "contains" will retrieve users whose username contain the searched value, case-sensitive
10	    parseQuery.contains('username', usernameSearchValue);
11	    // 
12	    // or 
13	    // 
14	    // for case-insensitive string search, use "matches", that will take into account
15	    // an regexp for matching, in this case use only "i", which is the regexp modifier
16	    // for case-insensitive
17	    parseQuery.matches('username', usernameSearchValue, 'i');
18	  }
19	  // Only after calling "find" all query conditions will resolve
20	  return await parseQuery
21	    .find()
22	    .then(async (queriedUsers: [Parse.User]) => {
23	      // Set the query results to an state variable to retrieve it on your JSX
24	      // Be aware that empty or invalid queries return as an empty array
25	      setQueryResults(queriedUsers);
26	      return true;
27	    })
28	    .catch((error: object) => {
29	      // Error can be caused by lack of Internet connection, but in most
30	      // cases "find" will return as an empty array on "then"
31	      Alert.alert('Error!', error.message);
32	      setQueryResults([]);
33	      return false;
34	    });
35	};
```
:::

Note that that are at least two different ways to search for a string, each one having its specific applications and advantages. In most cases, you will want to use Parse.Query.matches to ensure case-insensitive results and avoid unexpected behavior in your code.

After performing this query, your user list on your app should be showing something like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4vywdrYv0rn3auip-iLeS_image.png" signedSrc size="50" width="349" height="726" position="center" caption}

In addition to string querying, you can also perform “exact” queries, when you want to retrieve objects that contain an exact value, just as with boolean fields. The next example will show how to retrieve users that are verified by email, through the emailVerified field.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserQuery = async function () {
2	  // This value comes from a state variable
3	  const showOnlyVerifiedValue: boolean = showOnlyVerified;
4	  // This will create your user query
5	  const parseQuery = new Parse.Query(Parse.User);
6	  // Several query functions can be set to your Parse,Query, they will
7	  // only resolve when calling "find", for example
8	  if (showOnlyVerifiedValue === true) {
9	    // "equalTo" will retrieve users whose "emailVerified" value is exactly "true"
10	    parseQuery.equalTo('emailVerified', true);
11	  }
12	  // Only after calling "find" all query conditions will resolve
13	  return await parseQuery
14	    .find()
15	    .then(async (queriedUsers) => {
16	      // Set the query results to an state variable to retrieve it on your JSX
17	      // Be aware that empty or invalid queries return as an empty array
18	      setQueryResults(queriedUsers);
19	      return true;
20	    })
21	    .catch((error) => {
22	      // Error can be caused by lack of Internet connection, but in most
23	      // cases "find" will return as an empty array on "then"
24	      Alert.alert('Error!', error.message);
25	      setQueryResults([]);
26	      return false;
27	    });
28	};
```

```typescript
1	const doUserQuery = async function (): Promise<Boolean> {
2	  // This value comes from a state variable
3	  const showOnlyVerifiedValue: boolean = showOnlyVerified;
4	  // This will create your user query
5	  const parseQuery: Parse.Query = new Parse.Query(Parse.User);
6	  // Several query functions can be set to your Parse,Query, they will
7	  // only resolve when calling "find", for example
8	  if (showOnlyVerifiedValue === true) {
9	    // "equalTo" will retrieve users whose "emailVerified" value is exactly "true"
10	    parseQuery.equalTo('emailVerified', true);
11	  }
12	  // Only after calling "find" all query conditions will resolve
13	  return await parseQuery
14	    .find()
15	    .then(async (queriedUsers: [Parse.User]) => {
16	      // Set the query results to an state variable to retrieve it on your JSX
17	      // Be aware that empty or invalid queries return as an empty array
18	      setQueryResults(queriedUsers);
19	      return true;
20	    })
21	    .catch((error: object) => {
22	      // Error can be caused by lack of Internet connection, but in most
23	      // cases "find" will return as an empty array on "then"
24	      Alert.alert('Error!', error.message);
25	      setQueryResults([]);
26	      return false;
27	    });
28	};
```
:::

Your app should now be updating your user list like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7NvaTct3a8HdR40C1Urec_image.png" signedSrc size="50" width="353" height="731" position="center" caption}

Another common example would be to apply orderings to your query. This can be done in two ways, either by using Parse.Query.ascending/Parse.Query.descending or Parse.Query.addAscending/Parse.Query.addDescending. The first case will override any other ordering and will be the only one that the query will take and the latter will concatenate with existing orderings, making multiple orderings possible.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserQuery = async function () {
2	  // This value comes from a state variable
3	  const orderByValue = orderBy;
4	  // This will create your user query
5	  const parseQuery = new Parse.Query(Parse.User);
6	  // Several query functions can be set to your Parse,Query, they will
7	  // only resolve when calling "find", for example
8	  // For list ordering, you can use "addAscending" or "addDescending", passing as parameter
9	  // which object field should be the one to order by
10	  // Note that "usernameAsc", "usernameDesc" and so on are made up string values applied to a filter in
11	  // our example app, so change it by what is suitable to you
12	  if (orderByValue === 'usernameAsc') {
13	    parseQuery.ascending('username');
14	    //
15	    // or
16	    //
17	    parseQuery.addAscending('username');
18	  } else if (orderByValue === 'usernameDesc') {
19	    parseQuery.descending('username');
20	    //
21	    // or
22	    //
23	    parseQuery.addDescending('username');
24	  } else if (orderByValue === 'dateAsc') {
25	    parseQuery.ascending('createdAt');
26	    //
27	    // or
28	    //
29	    parseQuery.addAscending('createdAt');
30	  } else if (orderByValue === 'dateDesc') {
31	    parseQuery.descending('createdAt');
32	    //
33	    // or
34	    //
35	    parseQuery.addDescending('createdAt');
36	  }
37	  // Only after calling "find" all query conditions will resolve
38	  return await parseQuery
39	    .find()
40	    .then(async (queriedUsers) => {
41	      // Set the query results to an state variable to retrieve it on your JSX
42	      // Be aware that empty or invalid queries return as an empty array
43	      setQueryResults(queriedUsers);
44	      return true;
45	    })
46	    .catch((error) => {
47	      // Error can be caused by lack of Internet connection, but in most
48	      // cases "find" will return as an empty array on "then"
49	      Alert.alert('Error!', error.message);
50	      setQueryResults([]);
51	      return false;
52	    });
53	};
```

```typescript
1	const doUserQuery = async function (): Promise<Boolean> {
2	  // This value comes from a state variable
3	  const orderByValue: string = orderBy;
4	  // This will create your user query
5	  const parseQuery: Parse.Query = new Parse.Query(Parse.User);
6	  // Several query functions can be set to your Parse,Query, they will
7	  // only resolve when calling "find", for example
8	  // For list ordering, you can use "addAscending" or "addDescending", passing as parameter
9	  // which object field should be the one to order by
10	  // Note that "usernameAsc", "usernameDesc" and so on are made up string values applied to a filter in
11	  // our example app, so change it by what is suitable to you
12	  if (orderByValue === 'usernameAsc') {
13	    parseQuery.ascending('username');
14	    //
15	    // or
16	    //
17	    parseQuery.addAscending('username');
18	  } else if (orderByValue === 'usernameDesc') {
19	    parseQuery.descending('username');
20	    //
21	    // or
22	    //
23	    parseQuery.addDescending('username');
24	  } else if (orderByValue === 'dateAsc') {
25	    parseQuery.ascending('createdAt');
26	    //
27	    // or
28	    //
29	    parseQuery.addAscending('createdAt');
30	  } else if (orderByValue === 'dateDesc') {
31	    parseQuery.descending('createdAt');
32	    //
33	    // or
34	    //
35	    parseQuery.addDescending('createdAt');
36	  }
37	  // Only after calling "find" all query conditions will resolve
38	  return await parseQuery
39	    .find()
40	    .then(async (queriedUsers: [Parse.User]) => {
41	      // Set the query results to an state variable to retrieve it on your JSX
42	      // Be aware that empty or invalid queries return as an empty array
43	      setQueryResults(queriedUsers);
44	      return true;
45	    })
46	    .catch((error: object) => {
47	      // Error can be caused by lack of Internet connection, but in most
48	      // cases "find" will return as an empty array on "then"
49	      Alert.alert('Error!', error.message);
50	      setQueryResults([]);
51	      return false;
52	    });
53	};
```
:::

Your app must now be ordering your queries like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/einf4n5_dzjeGfLJC65vj_image.png" signedSrc size="50" width="358" height="735" position="center" caption}

Remember that all of the query constraints mentioned above can be chained and performed in a single query, improving your app usability for creating different filters and orderings that will work altogether. Here is the full code presenting all the query methods used in this guide:

:::CodeblockTabs
JavaScript

```javascript
1	const doUserQuery = async function () {
2	  // This value comes from a state variable
3	  const usernameSearchValue = usernameSearch;
4	  const showOnlyVerifiedValue = showOnlyVerified;
5	  const orderByValue = orderBy;
6	  // This will create your user query
7	  const parseQuery = new Parse.Query(Parse.User);
8	  // Several query functions can be set to your Parse,Query, they will
9	  // only resolve when calling "find", for example
10	  if (usernameSearchValue !== '') {
11	    // "contains" will retrieve users whose username contain the searched value, case-sensitive
12	    parseQuery.contains('username', usernameSearchValue);
13	    //
14	    // or
15	    //
16	    // for case-insensitive string search, use "matches", that will take into account
17	    // an regexp for matching, in this case use only "i", which is the regexp modifier
18	    // for case-insensitive
19	    parseQuery.matches('username', usernameSearchValue, 'i');
20	  }
21	  if (showOnlyVerifiedValue === true) {
22	    // "equalTo" will retrieve users whose "emailVerified" value is exactly "true"
23	    parseQuery.equalTo('emailVerified', true);
24	  }
25	  // For list ordering, you can use "addAscending" or "addDescending", passing as parameter
26	  // which object field should be the one to order by
27	  if (orderByValue === 'usernameAsc') {
28	    parseQuery.ascending('username');
29	    //
30	    // or
31	    //
32	    parseQuery.addAscending('username');
33	  } else if (orderByValue === 'usernameDesc') {
34	    parseQuery.descending('username');
35	    //
36	    // or
37	    //
38	    parseQuery.addDescending('username');
39	  } else if (orderByValue === 'dateAsc') {
40	    parseQuery.ascending('createdAt');
41	    //
42	    // or
43	    //
44	    parseQuery.addAscending('createdAt');
45	  } else if (orderByValue === 'dateDesc') {
46	    parseQuery.descending('createdAt');
47	    //
48	    // or
49	    //
50	    parseQuery.addDescending('createdAt');
51	  }
52	  // Only after calling "find" all query conditions will resolve
53	  return await parseQuery
54	    .find()
55	    .then(async (queriedUsers) => {
56	      // Set the query results to an state variable to retrieve it on your JSX
57	      // Be aware that empty or invalid queries return as an empty array
58	      setQueryResults(queriedUsers);
59	      return true;
60	    })
61	    .catch((error) => {
62	      // Error can be caused by lack of Internet connection, but in most
63	      // cases "find" will return as an empty array on "then"
64	      Alert.alert('Error!', error.message);
65	      setQueryResults([]);
66	      return false;
67	    });
68	};
```

```typescript
1	const doUserQuery = async function (): Promise<Boolean> {
2	  // This value comes from a state variable
3	  const usernameSearchValue: string = usernameSearch;
4	  const showOnlyVerifiedValue: boolean = showOnlyVerified;
5	  const orderByValue: string = orderBy;
6	  // This will create your user query
7	  const parseQuery: Parse.Query = new Parse.Query(Parse.User);
8	  // Several query functions can be set to your Parse,Query, they will
9	  // only resolve when calling "find", for example
10	  if (usernameSearchValue !== '') {
11	    // "contains" will retrieve users whose username contain the searched value, case-sensitive
12	    parseQuery.contains('username', usernameSearchValue);
13	    //
14	    // or
15	    //
16	    // for case-insensitive string search, use "matches", that will take into account
17	    // an regexp for matching, in this case use only "i", which is the regexp modifier
18	    // for case-insensitive
19	    parseQuery.matches('username', usernameSearchValue, 'i');
20	  }
21	  if (showOnlyVerifiedValue === true) {
22	    // "equalTo" will retrieve users whose "emailVerified" value is exactly "true"
23	    parseQuery.equalTo('emailVerified', true);
24	  }
25	  // For list ordering, you can use "addAscending" or "addDescending", passing as parameter
26	  // which object field should be the one to order by
27	  if (orderByValue === 'usernameAsc') {
28	    parseQuery.ascending('username');
29	    //
30	    // or
31	    //
32	    parseQuery.addAscending('username');
33	  } else if (orderByValue === 'usernameDesc') {
34	    parseQuery.descending('username');
35	    //
36	    // or
37	    //
38	    parseQuery.addDescending('username');
39	  } else if (orderByValue === 'dateAsc') {
40	    parseQuery.ascending('createdAt');
41	    //
42	    // or
43	    //
44	    parseQuery.addAscending('createdAt');
45	  } else if (orderByValue === 'dateDesc') {
46	    parseQuery.descending('createdAt');
47	    //
48	    // or
49	    //
50	    parseQuery.addDescending('createdAt');
51	  }
52	  // Only after calling "find" all query conditions will resolve
53	  return await parseQuery
54	    .find()
55	    .then(async (queriedUsers: [Parse.User]) => {
56	      // Set the query results to an state variable to retrieve it on your JSX
57	      // Be aware that empty or invalid queries return as an empty array
58	      setQueryResults(queriedUsers);
59	      return true;
60	    })
61	    .catch((error: object) => {
62	      // Error can be caused by lack of Internet connection, but in most
63	      // cases "find" will return as an empty array on "then"
64	      Alert.alert('Error!', error.message);
65	      setQueryResults([]);
66	      return false;
67	    });
68	};
```
:::

## Conclusion

At the end of this guide, you learned how to perform queries on Parse users on React Native. In the next guide, we will show you how to save and read data on Parse.

[title] Signing up
[path] GraphQL Cookbook/

# Signing up a user through the Parse GraphQL API

## Problem

You want to sign up a new user in your backend through the Parse GraphQL API.

## Solution

Using the Parse GraphQL API, you can sign up a new user just by sending the user’s data through the signUp mutation. The username and password fields are mandatory. The mutation will return back not only the objectId and createdAt fields (that are returned by default when [creating an object](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object)), but also the sessionToken.

After signing up a new user, you can use the [authenticating a user](https://www.back4app.com/docs/parse-graphql/graphql-user-authentication) recipe to send the sessionToken in the following operations so they will be executed in the behavior of this user. You can also use the [logging in](https://www.back4app.com/docs/parse-graphql/graphql-login) recipe to log in the user by using the defined credentials and the [logging out](https://www.back4app.com/docs/parse-graphql/graphql-logout-mutation) recipe to destroy the sessionToken.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.

Please choose the correct example along with the Parse version you are running.

## &#x20;**Parse Server 4.2.0 and later**

:::CodeblockTabs
Request

```graphql
1   mutation SignUp{
2     signUp(input: {
3       fields: {
4         username: "somefolk"
5         password: "somepassword"
6       }
7     }){
8       viewer{
9         user{
10          id
11          createdAt
12        }
13        sessionToken
14      }
15    }
16  }
```

Response

```graphql
1   {
2     "data": {
3       "signUp": {
4         "viewer": {
5           "user": {
6             "id": "X1VzZXI6ckZWbDR3YlJucA==",
7             "createdAt": "2020-02-06T13:38:04.517Z"
8           },
9           "sessionToken": "r:3233bc3b6801a15bcda39ff250416143"
10        }
11      }
12    }
13  }
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse Server 3.10.0 and later**

:::CodeblockTabs
Request

```graphql
1   mutation SignUp{
2     signUp(input: {
3       userFields: {
4         username: "somefolk"
5         password: "somepassword"
6       }
7     }){
8       viewer{
9         user{
10          id
11          createdAt
12        }
13        sessionToken
14      }
15    }
16  }
```

Response

```graphql
1   {
2     "data": {
3       "signUp": {
4         "viewer": {
5           "user": {
6             "id": "X1VzZXI6UHNOUkJ3Y1YyRQ==",
7             "createdAt": "2020-02-06T13:38:04.517Z"
8           },
9           "sessionToken": "r:c7abf06d951e8087c00fa66d546d1fea"
10        }
11      }
12    }
13  }
```
:::


::::

::::ExpandableHeading
**Parse Server 3.9.0**

:::CodeblockTabs
Request

```graphql
1   mutation SignUp{
2     signUp(fields:{
3       username: "somefolk"
4       password: "somepassword"
5     }){
6       id
7       createdAt
8       sessionToken
9     }
10  }
```

Response

```graphql
1   {
2     "data": {
3       "signUp": {
4         "id": "Gx2zW7yEnY",
5         "createdAt": "2019-11-04T14:24:21.333Z",
6         "sessionToken": "r:6d5f75f0f2d9ee16077b0a0ff1e20eb2"
7       }
8     }
9   }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.8.0**

:::CodeblockTabs
Request

```graphql
1   mutation SignUp{
2     signUp(fields:{
3       username: "somefolk"
4       password: "somepassword"
5     }){
6       objectId
7       createdAt
8     }
9   }
```

Response

```graphql
1   {
2     "data": {
3       "signUp": {
4         "objectId": "KTznKVzto2",
5         "createdAt": "2019-11-04T14:23:46.014Z",
6         "sessionToken": "r:2ca6914312ed16803cf3769a25934cdc"
7       }
8     }
9   }
```
:::


::::

::::ExpandableHeading
**Parse Server 3.7.2**

:::CodeblockTabs
Request

```graphql
1   mutation SignUp {
2     users {
3       signUp(fields: { username: "somefolk", password: "somepassword" }) {
4         objectId,
5         createdAt,
6         sessionToken
7       }
8     }
9   }
```

Response

```graphql
1   {
2     "data": {
3       "users": {
4         "signUp": {
5           "objectId": "NyU1lNlhPd",
6           "createdAt": "2019-07-29T09:09:58.222Z",
7           "sessionToken": "r:a86665f0b63d9d8f945e4b0f302a1655"
8         }
9       }
10    }
11  }
```
:::
::::




[title] Deleting an object
[path] GraphQL Cookbook/

# Deleting an object through the Parse GraphQL API

## Problem

You want to delete an existing object in your database through the Parse GraphQL API.

## Solution

Using the parse GraphQL, there are two different ways to delete an existing object in your database:

- [Using generic mutation ](https://www.back4app.com/docs/parse-graphql/graphql-mutation-delete-object#mutation-generic)- this is the mutation that you can use to delete an object of any class.
- [Using class mutation ](https://www.back4app.com/docs/parse-graphql/graphql-mutation-delete-object#mutation-class)- this is the recommended mutation that you should use to delete an object of a specific class.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

### **Using generic mutation**

When you use the delete generic mutation, you send the object’s className and objectId, and Parse Server will delete this object.

Therefore, the objects’ delete generic mutation is the one that you can use for deleting an existing object of any class. If you want to delete an existing object of a specific class, we recommend using the [class mutation](https://www.back4app.com/docs/parse-graphql/graphql-mutation-delete-object#mutation-class).

:::hint{type="info"}
This example will only work if you use a className and an objectId of an existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse 3.8.0**

:::CodeblockTabs
Request

```graphql
1   mutation DeleteObject {
2     delete(className: "Hero", objectId: "rR8jmFRnkS")
3   }
```

Response

```graphql
1   {
2     "data": {
3       "delete": true
4     }
5   }
```
:::

### **Example Parse 3.9.0 and later:**

Parse 3.9.0 and later does not have the generic method DELETE. You must use the specific methods below to delete objects.

### **Using class mutation**

Once you have already created your object’s class in your application’s schema (for instance, using the [creating an object](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic) recipe), Parse Server instantly adds to your GraphQL API a new delete\<ClassName> mutation to delete an existing object of this class.

Therefore, the object’s class mutation is the recommended method for deleting an existing object of a specific class.

:::hint{type="info"}
This example will only work if you use a objectId or id of an existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse 3.10.0 and later**

:::CodeblockTabs
Request

```graphql
1   mutation DeleteObject {
2     deleteHero(input:{
3       id: "SGVybzpVRm5TVDM1YnBp"
4     }){
5       hero{
6         id
7       }
8     }
9   }
```

Response

```graphql
1   {
2     "data": {
3       "deleteHero": {
4         "hero": {
5           "id": "SGVybzpVRm5TVDM1YnBp"
6         }
7       }
8     }
9   }
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse 3.9.0**

**Class mutation:**

:::CodeblockTabs
Request

```graphql
1   mutation DeleteObject {
2     deleteHero(id: "CkhurmMjZW"){
3       id
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "deleteHero": {
4         "id": "CkhurmMjZW"
5       }
6     }
7   }
```
:::


::::

::::ExpandableHeading
**Parse 3.8.0**

**Class mutation:**

:::CodeblockTabs
Request

```graphql
1   mutation DeleteObject {
2     deleteHero(objectId: "rR8jmFRnkS"){
3       objectId
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "deleteHero": {
4         "objectId": "rR8jmFRnkS"
5       }
6     }
7   }
```
:::
::::

::::ExpandableHeading
**Parse 3.7.2**

**Generic mutation:**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   mutation DeleteObject {
2     objects {
3       delete(className: "Hero", objectId: "ffyOBOTk85")
4     }
5   }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "delete": true
5       }
6     }
7   }
```
:::

**Class mutation:**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   mutation DeleteHero {
2     objects {
3       deleteHero(objectId: "jJH0aQQjfs")
4     }
5   }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "delete": true
5       }
6     }
7   }
```
:::
::::




[title] Admin App
[path] Parse Dashboard/

# Admin App

## Introduction

The Admin App is a real-time administration panel without touching the code, it is readable of developers and even non-technical users at all levels.

By using this feature, you are allowing a non-tech user to perform an operation on a database resource and also manage your data.

In this guide, we are going to learn to enable and add some customizations to the Admin interface.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read or edit this page.**
:::

## **How to enable it?**

This video will efficiently walk you through the process to enable this interface:

::embed[]{url="https://www.youtube.com/watch?embeds_referring_euri=https%3A%2F%2Fwww.back4app.com%2F&source_ve_path=Mjg2NjQsMTY0NTAz&feature=emb_share&v=7CHdIniAACE"}

## Frequently Asked Question

Below you’ll find answer to the questions we get asked the most about Admin App.

::::ExpandableHeading
### **How to customize the Admin App's layout?**

Go to Dashboard > Core > Click at B4aSetting class and you might see something like below:

![](https://www.back4app.com/docs/assets/images/png/parse-dashboard/admin-classes.png)

:::hint{type="warning"}
Is one of these keys missing? Don’t worry, you can add a new row and insert the key and value.
:::

In order to prevent any typo error, you can copy and paste them below:

- isHomeHidden

This property will hide the “Home” section of your Admin app.

Type: Boolean Value: *true* or *false*.

- isFeedbackHidden

This property will hide the “Feedback button”.

Type: Boolean Value: *true* or *false*.

- isSupportButtonHidden

This property will hide the “Support button”.

Type: Boolean Value: *true* or *false*.

- customizeDefaultFields

This property will allow you to hide default fields as “Id”, “Created At” and “Updated At” columns.

Type: Boolean Value: *true* or *false*.

- logo

This property will allow you to change the default logo from Admin.

Type: String Value: Insert a URL of your logo.

- brandColor

This property will allow you to the admin’s column by using hexadecimal colors or its name.

Type: String Value: Type “#800080” or write “purple”.

- appName

This property will change the name of the app on Admin only.

Type: String
::::

:::ExpandableHeading
### **How to change the columns' order?**

Go to the Admin App > Custom Field at Admin App Setting.

You’ll see a populated class, but, you don’t need to worry, these registers help the admin’s ordenation, let’s keep focused to our original need.

Click to add a new register (+Add), it’s the green button at the top. The following window will appear:

::Image[]{src="https://www.back4app.com/docs/assets/images/png/parse-dashboard/admin-custom-field-object.png" signedSrc="https://www.back4app.com/docs/assets/images/png/parse-dashboard/admin-custom-field-object.png" size="90" width="1165" height="762" position="center" caption}

In order to move the column, please fill the following fields:

- ***Class**** *- Select the class that you would like to change the ordination.
- ***Field ***- Select the column
- ***Title ***- Type the column name (To change the column name in the admin app, write a different value)
- ***Relevance**** *- You need to know that it’s set as ascending order, so, fieldset with the higher relevance number will be on the left.

Now, you need to click on the Save button.
:::

:::ExpandableHeading
### **How to hide a column?**

Go to the Admin App > Custom Field at Admin App Setting.

Click to add a new register (+Add), it’s the green button at the top. The following window will appear:

::Image[]{src="https://www.back4app.com/docs/assets/images/png/parse-dashboard/admin-custom-field-object.png" signedSrc="https://www.back4app.com/docs/assets/images/png/parse-dashboard/admin-custom-field-object.png" size="90" width="1165" height="762" position="center" caption}

In order to move the column, please fill the following fields:

- ***Class**** *- Select the class that you would like to hide the column.
- ***Field  ***- Select the column that you would like to hide.
- ***Title ***- Retype the column name
- ***Is Table Hidden ****-  Select *YES* option.*
- ***Relevance ***- Set as 0.

Now, you need to click on the Save button.
:::

:::ExpandableHeading
### **How to add more than one user?**

Go to Dashboard > Core > Click at Role class. You’ll see a Role called B4aAdminUser, which was automatically created when the Admin app was enabled.

Click on the users relation and you’ll see the following buttons:

- *Create a \_User and attach*

By choosing this option, you need to put the username and set a password.

- *Attach existing rows from \_User*

In this option, you can allow an existing user to access your Admin too. By clicking on this button, you must choose the user (object Id) that you want to add on the Admin.
:::

:::ExpandableHeading
### **How to reset the admin user password?**

Go to Dashboard > Core > Click at Role class, search for the Role “B4aAdminUser”, and click at View relation on its relation to the User class. Go to the password field and click on it to insert a new password to your admin user.


:::


[title] Getting an object
[path] GraphQL Cookbook/

# Getting an object through the Parse GraphQL API

## Problem

You want to get an existing object from your database through the Parse GraphQL API.

## Solution

Using the parse GraphQL, there are two different ways to get an existing object from your database:

- [Using generic query ](https://www.back4app.com/docs/parse-graphql/graphql-query-get-object#query-generic)- this is the query that you can use to get an object of any class.
- [Using class query ](https://www.back4app.com/docs/parse-graphql/graphql-query-get-object#query-class)- this is the recommended query that you should use to get an object of a specific class.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

### **Using generic query**

When you use the get generic query, Parse Server behaves like a schemaless database. It means that you do not need to specify which object’s fields you want to get. You just need to send the object’s className and objectId, and Parse Server will return all fields of this object.

Therefore, the objects’ get generic query is the query that you can use for getting an existing object of any class. If you want to get an existing object of a specific class, we recommend using the [class query](https://www.back4app.com/docs/parse-graphql/graphql-query-get-object#query-class).

:::hint{type="info"}
This example will only work if you use a className with existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse Server 3.8.0**

:::CodeblockTabs
Request

```graphql
1   query GetObject {
2     get(className: "Hero", objectId: "rR8jmFRnkS")
3   }
```

Response

```graphql
1   {
2     "data": {
3       "get": {
4         "objectId": "rR8jmFRnkS",
5         "name": "Luke Skywalker",
6         "createdAt": "2019-11-04T12:42:40.723Z",
7         "updatedAt": "2019-11-04T12:42:40.723Z"
8       }
9     }
10  }
```
:::

## **Example Parse 3.9.0 and later:**

Parse 3.9 and later do not have the generic methods GET and FIND. You must use the specific methods below to retrieve objects.

### **Using class query**

Once you have already created your object’s class in your application’s schema (for instance, using the [creating an object](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic) recipe), Parse Server instantly adds to your GraphQL API a new get\<ClassName> query to get an existing object of this class.

Therefore, the object’s class query is the recommended method for getting an existing object of a specific class. Since this query knows your class’ data, it will automatically make available for you additional features like code auto-complete and validation.

:::hint{type="info"}
This example will only work if you use a className and an objectId of an existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse Server 3.10.0 and later**

:::CodeblockTabs
Request

```graphql
1   query GetHero {
2     hero(id: "SGVybzpVRm5TVDM1YnBp") {
3       id,
4       name,
5       createdAt,
6       updatedAt
7     }
8   }
```

Response

```graphql
1   {
2     "data": {
3       "hero": {
4         "id": "SGVybzpVRm5TVDM1YnBp",
5         "name": "R2-D2",
6         "createdAt": "2020-02-06T13:13:26.678Z",
7         "updatedAt": "2020-02-06T13:13:26.678Z"
8       }
9     }
10  }
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse 3.9.0**

**Class query**

:::CodeblockTabs
Request

```graphql
1   query GetHero {
2     hero(id: "CkhurmMjZW") {
3       id,
4       name,
5       createdAt,
6       updatedAt
7     }
8   }
```

Response

```graphql
1   {
2     "data": {
3       "hero": {
4         "id": "CkhurmMjZW",
5         "name": "Luke Skywalker",
6         "createdAt": "2019-11-04T12:37:22.462Z",
7         "updatedAt": "2019-11-04T12:37:22.462Z"
8       }
9     }
10  }
```
:::


::::

:::ExpandableHeading
**Parse Server 3.8.0**

**Class queryParse Server 3.7.2**
:::

::::ExpandableHeading
**Parse Server 3.7.2**

**Generic query**

:::CodeblockTabs
Request

```graphql
1   query GetObject {
2     objects {
3       get(className: "Hero", objectId: "ffyOBOTk85")
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "objects": {
4         "get": {
5           "objectId": "ffyOBOTk85",
6           "name": "Luke Skywalker",
7           "createdAt": "2019-07-15T01:25:20.875Z",
8           "updatedAt": "2019-07-15T01:25:20.875Z"
9         }
10      }
11    }
12  }
```
:::

**Class query**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   query GetHero {
2     objects {
3       getHero(objectId: "ffyOBOTk85") {
4         objectId,
5         name,
6         createdAt,
7         updatedAt
8       }
9     }
10  }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "getHero": {
5           "objectId": "ffyOBOTk85",
6           "name": "Luke Skywalker",
7           "createdAt": "2019-07-15T01:25:20.875Z",
8           "updatedAt": "2019-07-15T01:25:20.875Z"
9         }
10      }
11    }
12  }
```
:::
::::




[title] Local environment
[path] Local Development/

The Back4app local development environment allows you to build and test apps locally using its core features, such as Database, Cloud Functions, Files, Authentication, and Web Hosting. This setup consists of the open-source Parse Server, Parse Dashboard, and a locally installed database, enabling you to prototype and iterate quickly.

This setup is ideal for prototyping, development, and continuous integration workflows.

## Goal

Set up a local development environment with Back4app.

For a more detailed explanation, you can watch the tutorial video:
[Back4app Local Setup Guide.](https://www.youtube.com/watch?v=Zy8k-B6FLzY)

## **1 - Connect Your Local Machine to Back4app Servers Using the CLI**

Follow the steps to connect an existing app to your local machine or create a new one.
See the CLI guide: [Parse CLI Documentation](https://www.back4app.com/docs/local-development/parse-cli)** **

## **2 - Install and Run the Parse Server**

The easiest way to install Parse Server locally is by using NPM. Run the following command in your terminal:

> npm install -g parse-server mongodb-runner

After installation, start the database so Parse Server can store data:

> mongodb-runner start

Go to your Back4app dashboard and copy the APPLICATION\_ID, CLIENT\_KEY, and MASTER\_KEY from **Dashboard > App Settings > Security & Keys**. Use these values to start your Parse Server:

> parse-server --appId APPLICATION_ID --clientKey CLIENT_KEY --masterKey MASTER_KEY --databaseURI mongodb://localhost/test

## **3- Install Parse Dashboard**

To improve your development experience, install the Parse Dashboard for a user-friendly interface:

> npm install -g parse-dashboard

Use the same credentials from the previous step and run the following command to start the dashboard:

> parse-dashboard --dev --appId APPLICATION_ID --masterKey MASTER_KEY --serverURL http://localhost:1337/parse --appName MY_APP

If the Parse Dashboard fails to load, replace the URL `http://0.0.0.0:4040/` with `http://localhost:4040/`.

## 4 - Updating the Server URL

By default, the main server URL for Back4app is `https://parseapi.back4app.com`. However, for your local instance, you need to update your app's code to point to `http://localhost:1337/parse`.

## Troubleshooting

- **Common Dashboard Errors**: If you cannot access the Parse Dashboard locally, ensure you are using the correct localhost URL.
- **Database Issues**: Ensure the `mongodb-runner` service is running properly before starting the Parse Server.


Conclusion
----------

With your local Parse Server and Parse Dashboard set up, you now have a fully functional environment for developing and testing your Back4app applications. This local setup allows you to prototype new features, debug issues, and create apps faster without relying on a live environment.

[title] Password Reset
[path] React Native/Parse SDK (REST)/Users/

# User Password Reset for React Native

## Introduction

It’s a fact that as soon as you introduce passwords into a system, users will forget them. In such cases, Parse library provides a way to let them securely reset their password.
As with email verification, Parse already has an implementation ready for this, Parse.User.requestPasswordEmail. By using this method, Parse will handle all aspects of password resetting for you seamlessly.

::embed[]{url="https://www.youtube.com/embed/dmlPFO1zo80"}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## Goal

To add a user password reset feature to a React Native App using Parse.

## 1 - Customizing password reset emails

Before calling the Parse.User.requestPasswordEmail method, you can customize the message that your user will get after requesting a password reset. Log in to your App dashboard, go to Settings->Verification Emails and change your password reset email subject or message. Ensure that your user will receive an email containing clear instructions and indicating that it is indeed from your application.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vWUUq02YQjyLFsQRtm5aN_image.png)

## 2 - Using requestPasswordEmail

Calling the Parse.User.requestPasswordEmail method only requires your user account email as a parameter, so go ahead and add the following function to your password reset screen. Remember to add a text input for your user email to your screen.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserPasswordReset = async function () {
2	  // Note that this value come from state variables linked to your text input
3	  const emailValue = email;
4	  return await Parse.User.requestPasswordReset(emailValue)
5	    .then(() => {
6	      // logIn returns the corresponding ParseUser object
7	      Alert.alert(
8	        'Success!',
9	        `Please check ${email} to proceed with password reset.`,
10	      );
11	      return true;
12	    })
13	    .catch((error) => {
14	      // Error can be caused by lack of Internet connection
15	      Alert.alert('Error!', error.message);
16	      return false;
17	    });
18	};
```

```typescript
1	const doUserPasswordReset = async function (): Promise<boolean> {
2	  // Note that this value come from state variables linked to your text input
3	  const emailValue: string = email;
4	  return await Parse.User.requestPasswordReset(emailValue)
5	    .then(() => {
6	      // logIn returns the corresponding ParseUser object
7	      Alert.alert(
8	        'Success!',
9	        `Please check ${email} to proceed with password reset.`,
10	      );
11	      return true;
12	    })
13	    .catch((error: object) => {
14	      // Error can be caused by lack of Internet connection
15	      Alert.alert('Error!', error.message);
16	      return false;
17	    });
18	};
```
:::

Go ahead and test your screen and component. You will see a message like this after requesting a password reset email:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XfbIUhm89_x4I7CJI23-H_image.png" signedSrc size="50" width="355" height="740" position="center" caption}

You should have received the email, so go ahead and check your inbox. Note that the message will contain any changes you had set up before in your Parse dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/28ePpcP3tau-T1R_1Lj77_image.png)

The password reset form will look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7xjVKTUDVHZkKmgZxTUVL_image.png)

That’s it, after changing the password in this form, your user will be able to log in again to your application.

## 3 - Creating a password request component

As said before, you should create a component containing the function shown on Step 2 and also a text input field for your user account email to enable password reset in your app. Here is a complete example of this component. You can plug it in in our previous guides user login project if you like.

:::CodeblockTabs
UserResetPassword.js

```javascript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Text, TextInput, TouchableOpacity, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {useNavigation} from '@react-navigation/native';
5	import Styles from './Styles';
6	
7	export const UserResetPassword = () => {
8	  const navigation = useNavigation();
9	
10	  // Your state variable
11	  const [email, setEmail] = useState('');
12	
13	  const doUserPasswordReset = async function (): Promise<boolean> {
14	    // Note that this value come from state variables linked to your text input
15	    const emailValue = email;
16	    return await Parse.User.requestPasswordReset(emailValue)
17	      .then(() => {
18	        // logIn returns the corresponding ParseUser object
19	        Alert.alert(
20	          'Success!',
21	          `Please check ${email} to proceed with password reset.`,
22	        );
23	        // Redirect user to your login screen
24	        navigation.navigate('Login');
25	        return true;
26	      })
27	      .catch((error) => {
28	        // Error can be caused by lack of Internet connection
29	        Alert.alert('Error!', error.message);
30	        return false;
31	      });
32	  };
33	
34	  return (
35	    <View style={Styles.login_wrapper}>
36	      <View style={Styles.form}>
37	        <Text>{'Please enter your account email to reset your password:'}</Text>
38	        <TextInput
39	          style={Styles.form_input}
40	          value={email}
41	          placeholder={'Your account email'}
42	          onChangeText={(text) => setEmail(text)}
43	          autoCapitalize={'none'}
44	          keyboardType={'email-address'}
45	        />
46	        <TouchableOpacity onPress={() => doUserPasswordReset()}>
47	          <View style={Styles.button}>
48	            <Text style={Styles.button_label}>{'Request password reset'}</Text>
49	          </View>
50	        </TouchableOpacity>
51	      </View>
52	    </View>
53	  );
54	};
```

UserResetPassword.tsx

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Text, TextInput, TouchableOpacity, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {useNavigation} from '@react-navigation/native';
5	import Styles from './Styles';
6	
7	export const UserResetPassword: FC<{}> = ({}): ReactElement => {
8	  const navigation = useNavigation();
9	
10	  // Your state variable
11	  const [email, setEmail] = useState('');
12	
13	  const doUserPasswordReset = async function (): Promise<boolean> {
14	    // Note that this value come from state variables linked to your text input
15	    const emailValue: string = email;
16	    return await Parse.User.requestPasswordReset(emailValue)
17	      .then(() => {
18	        // logIn returns the corresponding ParseUser object
19	        Alert.alert(
20	          'Success!',
21	          `Please check ${email} to proceed with password reset.`,
22	        );
23	        // Redirect user to your login screen
24	        navigation.navigate('Login');
25	        return true;
26	      })
27	      .catch((error: object) => {
28	        // Error can be caused by lack of Internet connection
29	        Alert.alert('Error!', error.message);
30	        return false;
31	      });
32	  };
33	
34	  return (
35	    <View style={Styles.login_wrapper}>
36	      <View style={Styles.form}>
37	        <Text>{'Please enter your account email to reset your password:'}</Text>
38	        <TextInput
39	          style={Styles.form_input}
40	          value={email}
41	          placeholder={'Your account email'}
42	          onChangeText={(text) => setEmail(text)}
43	          autoCapitalize={'none'}
44	          keyboardType={'email-address'}
45	        />
46	        <TouchableOpacity onPress={() => doUserPasswordReset()}>
47	          <View style={Styles.button}>
48	            <Text style={Styles.button_label}>{'Request password reset'}</Text>
49	          </View>
50	        </TouchableOpacity>
51	      </View>
52	    </View>
53	  );
54	};
```
:::

This component should render in a screen like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NzfeuOTZiQ8jQ_0jivdJ6_image.png" signedSrc size="50" width="347" height="741" position="center" caption}

### Conclusion

At the end of this guide, you learned how to allow your Parse users to reset their password on React Native. In the next guide, we will show you how to perform useful user queries.

[title] Logging in
[path] GraphQL Cookbook/

# Logging in an existing user through the Parse GraphQL API

# Problem

You want to log in an existing user in your backend through the Parse GraphQL API.

## Solution

Using the Parse GraphQL API, you can log in an existing user just by sending the user’s credentials through the logIn mutation. The username and password arguments are mandatory. The mutation will return back all users’ fields, including the sessionToken.

After logging in an existing user, you can use the [authenticating an user](https://www.back4app.com/docs/parse-graphql/graphql-user-authentication) recipe to send the sessionToken in the following operations so they will be executed in the behavior of this user. You can also use the [logging out](https://www.back4app.com/docs/parse-graphql/graphql-logout-mutation) recipe to destroy the sessionToken.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

## **Parse Server 3.10.0 and later**

:::CodeblockTabs
Request

```graphql
1   mutation LogIn{
2     logIn(input: {
3       username: "somefolk"
4       password: "somepassword"
5     }){
6       viewer{
7         user{
8           id
9           createdAt
10          updatedAt
11          username
12        }
13        sessionToken
14      }
15    }
16  }
```

Response

```graphql
1   {
2     "data": {
3       "logIn": {
4         "viewer": {
5           "user": {
6             "id": "X1VzZXI6UHNOUkJ3Y1YyRQ==",
7             "createdAt": "2020-02-06T13:38:04.517Z",
8             "updatedAt": "2020-02-06T13:38:04.517Z",
9             "username": "somefolk"
10          },
11          "sessionToken": "r:a5318d28821a78069f5b618de35b57bb"
12        }
13      }
14    }
15  }
```
:::

::::ExpandableHeading
**Parse Server 3.9.0**

:::CodeblockTabs
Request

```graphql
1   mutation LogIn{
2     logIn(fields:{
3       username: "somefolk"
4       password: "somepassword"
5     }){
6       id,
7       createdAt,
8       updatedAt,
9       username,
10      sessionToken
11    }
12  }
```

Response

```graphql
1   {
2     "data": {
3       "viewer": {
4         "sessionToken": "r:1450d329038f876835fb7aac16742380",
5         "username": "somefolk"
6       }
7     }
8   }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.8.0**

:::CodeblockTabs
Request

```graphql
1   mutation LogIn{
2     logIn(fields:{
3       username: "somefolk"
4       password: "somepassword"
5     }){
6       objectId,
7       createdAt,
8       updatedAt,
9       username,
10      sessionToken
11    }
12  }
```

Response

```graphql
1   {
2     "data": {
3       "logIn": {
4         "objectId": "KTznKVzto2",
5         "createdAt": "2019-11-04T14:23:46.014Z",
6         "updatedAt": "2019-11-04T14:23:46.014Z",
7         "username": "somefolk",
8         "sessionToken": "r:fe39d9de406d53d13e9af1efbbe967a8"
9       }
10    }
11  }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.7.2**

:::CodeblockTabs
Request

```graphql
1   mutation LogIn {
2     users {
3       logIn(username: "somefolk", password: "somepassword") {
4         objectId,
5         createdAt,
6         updatedAt,
7         username,
8         sessionToken
9       }
10    }
11  }
```

Response

```graphql
1   {
2     "data": {
3       "users": {
4         "logIn": {
5           "objectId": "NyU1lNlhPd",
6           "createdAt": "2019-07-29T09:09:58.222Z",
7           "updatedAt": "2019-07-29T09:09:58.222Z",
8           "username": "somefolk",
9           "sessionToken": "r:cbca71d29d7601761b48ed01bbe9638d"
10        }
11      }
12    }
13  }
```
:::


::::


[title] Parse Dashboard
[path] /


[title] Core
[path] Parse Dashboard/


[title] Cron Job
[path] Platform/

# How to create your Parse Cron Job

## Introduction

This section explains how you can schedule a cron job using <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

For this tutorial, as an example, you will build a cron job that removes users of your Parse Dashboard that haven’t verified their emails some time after they have signed up.

:::hint{type="info"}
**At any time, you can access the complete Project built for this tutorial at our **<a href="https://github.com/back4app/android-background-jobs" target="_blank">**GitHub repository**</a>**.**
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app created on Back4App.
  - Note: Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App
- Knowledge about Cloud Code.
  - Note: Follow the <a href="https://www.back4app.com/docs/android/parse-cloud-code" target="_blank">Cloud Code for Android Tutorial</a> or the <a href="https://www.back4app.com/docs" target="_blank">Cloud Code for iOS Tutorial</a> for more information.
  - A device (or <a href="https://developer.android.com/studio/run/managing-avds?hl=pt-br" target="_blank">virtual device</a>) running Android 4.0 (Ice Cream Sandwich)or newer.
:::

## 1 - Create your cron job code

1. Create a .js file to put your cron job code into. In this example, a main.js file is created in a cloud\_code directory.
2. Define a job function using Parse.Cloud.job. In this example, the following code verifies every user in your Parse Dashboard, then query the ones that still have their email unverified after some time and destroy them:

:::CodeblockTabs
Parse Server 3.X main.js

```javascript
1   Parse.Cloud.job("removeInvalidLogin", async (request) => {
2     let date = new Date();
3     let timeNow = date.getTime();
4     let intervalOfTime = 3*60*1000;  // the time set is 3 minutes in milliseconds
5     let timeThen = timeNow - intervalOfTime;
6
7     // Limit date
8     let queryDate = new Date();
9     queryDate.setTime(timeThen);
10
11    // The query object
12    let query = new Parse.Query(Parse.User);
13
14    // Query the users that still unverified after 3 minutes
15    query.equalTo("emailVerified", false);
16    query.lessThanOrEqualTo("createdAt", queryDate);
17
18    const results = await query.find({useMasterKey:true});
19
20    results.forEach(object => {
21        object.destroy({useMasterKey: true}).then(destroyed => {
22            console.log("Successfully destroyed object" + JSON.stringify(destroyed));
23        }).catch(error => {
24            console.log("Error: " + error.code + " - " + error.message);
25        })
26    });
27    
28    return ("Successfully retrieved " + results.length + " invalid logins."); 
29  });
```

Parse Server 2.X main.js

```javascript
1   Parse.Cloud.job("removeInvalidLogin", function (request, response) {
2     var date = new Date();
3     var timeNow = date.getTime();
4     var intervalOfTime = 3*60*1000;  // the time set is 3 minutes in milliseconds
5     var timeThen = timeNow - intervalOfTime;
6
7     // Limit date
8     var queryDate = new Date();
9     queryDate.setTime(timeThen);
10
11    // The query object
12    var query = new Parse.Query(Parse.User);
13
14    // Query the users that still unverified after 3 minutes
15    query.equalTo("emailVerified", false);
16    query.lessThanOrEqualTo("createdAt", queryDate);
17
18    query.find({
19        success: function (results) {
20        console.log("Successfully retrieved " + results.length + " invalid logins.");
21
22        // Destroying the invalid users
23        query.each(function (object, err) {
24          object.destroy({
25            success: function (object) {
26              response.success("Successfully destroyed object " + object.objectId);
27            },
28            error: function (error) {
29              response.error("Error: " + error.code + " - " + error.message);
30            },
31            useMasterKey: true  // VERY IMPORTANT!!
32          })
33        })
34      },
35      error: function (error) {
36        response.error("Error: " + error.code + " - " + error.message);
37      }
38    });
39  });
```
:::

:::hint{type="danger"}
It is required to use the **master key **in this operation.
:::

:::hint{type="info"}
You can modify the intervalOfTime content with the amount of time you think an unverified user can still have his account active without verifying it. Just don’t forget that to test your application, small time intervals are better. So, it’s suggested that you set the intervalOfTime content to three minutes to test if the cron job is working and then change the JavaScript code with the amount of time you actually want intervalOfTime to be.
:::

:::hint{type="danger"}
Don’t forget that changes to the JavaScript file are only computed in your application if you upload the file again on Back4app Cloud Code block. To do this, delete the .js file with the unwanted intervalOfTime content and follow Step 2 to upload the file with the correct intervalOfTime content.
:::

## 2 - Upload cron job to Cloud Code

:::hint{type="info"}
To know more about how to get started with Cloud Code look at <a href="https://www.back4app.com/docs/android/parse-cloud-code" target="_blank">Cloud Code for Android Tutorial</a> or <a href="https://www.back4app.com/docs" target="_blank">Cloud Code for iOS Tutorial</a>.
:::

1. Go to your App at the <a href="https://www.back4app.com/" target="_blank">Back4App</a> website and click on Dashboard.
2. Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gAqCZlMJvj1u1_2A82SEg_image.png" signedSrc size="80" width="810" height="578" position="center" caption}

&#x20;    3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click on Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cEJDTAvCqpLgcEwst1IYa_image.png)

## 3 - Schedule cron job on Back4App

1. Go to your app at the <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and click on Server Settings.
2. Find the “Background Jobs” block and click on SETTINGS. The “Background Jobs” block looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/X18vSRO1Tvs7KSC0u6elV_image.png" signedSrc size="50" width="331" height="339" position="center" caption}

&#x20;    3\. A Background Jobs page will appear and two options will be displayed: Browser a job or Schedule a job. Click on Schedule a job, as shown below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/t5AO9omCAxyHaiVXvcKUK_image.png" signedSrc size="70" width="786" height="551" position="center" caption}

:::hint{type="info"}
If you want to Edit, Run now, or Delete an existing cron job, click on the Browser a job button.
:::

&#x20;     4\. A Schedule a job page will appear and you have to fill in the Description field of your job with its description and also the field Cloud Job with the name you set to your cron job in the first line of its JavaScript code. In this example, the name of the cron job created is removeInvalidLogin.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MJWhjahzgsKOjL0wJ_Apk_image.png" signedSrc size="70" width="672" height="755" position="center" caption}

&#x20;5\. You can also set other options for your cron job as what time it should start running, if it should repeat, and how often. After filling in these options with your preferences, click on the SAVE button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/lbntJNDOU5GfmZr02WH6p_image.png)

## 4 - Test your app

1. Create some users with emailVerified column set as False at your Parse Dashboard, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pGu1s06Aow8ReNU_HLnuF_image.png)

&#x20; 2\. Run your application and refresh your Parse Dashboard. It should have destroyed the unverified users. For the Parse Dashboard shown above, this is the result:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/JAjqPNXa7wD-QLmm7VAbe_image.png)

:::hint{type="info"}
**You can also see if the cron job is working by accessing Back4App Logs. To do that, follow these steps:**

1. Go to your app at the <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and click on Server Settings.
2. Find the “Logs” block and click on SETTINGS. The “Logs” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/hWuI1nQOsjx9FfSr0qjbF_image.png)

&#x20;      3\. Scroll the page until you see the Server System Log. There you should find information about the cron job that is running, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/c8KgYmpE5D3TtfKNQKXb3_image.png)
:::

## It’s done!

At this stage, you can schedule cron jobs in your application using Parse Server Core features through Back4App!

[title] React Native
[path] /


[title] Updating an object
[path] GraphQL Cookbook/

# Updating an object through the Parse GraphQL API

## Problem

You want to update an existing object in your database through the Parse GraphQL API.

## Solution

Using the parse GraphQL, there are two different ways to update an existing object in your database:

- [Using generic mutation ](https://www.back4app.com/docs/parse-graphql/graphql-mutation-update-object#mutation-generic)- this is the mutation that you must use if you want to set fields that do not belong to your object’s class yet.
- [Using class mutation ](https://www.back4app.com/docs/parse-graphql/graphql-mutation-update-object#mutation-class)- this is the recommended mutation if your object’s class already has all fields that you want to update.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

### **Using generic mutation**

When you use the update generic mutation, Parse Server behaves like a schemaless database. It means that you do not need to define all fields of your object beforehand. You just need to send the fields that you want to update, and Parse Server will not only store it, but also learn from it, and automatically create any new field in this object’s class.

Therefore, the objects’ update generic mutation is the method that you must use for updating an existing object if you want to set fields that do not belong to your object’s class yet. You can actually use this mutation for updating any existing object, but we recommend using the [class mutation](https://www.back4app.com/docs/parse-graphql/graphql-mutation-update-object#mutation-class) if your object’s class already has all fields that you want to update.

:::hint{type="info"}
This example will only work if you use a className and an objectId of an existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse 3.8.0**

:::CodeblockTabs
Request

```graphql
1   mutation UpdateObject {
2     update(className: "Hero", objectId: "rR8jmFRnkS", fields: { height: 5.6 }) {
3       updatedAt
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "updateHero": {
4        "updatedAt": "2019-11-04T13:28:44.150Z"
5       }
6     }
7   }
```
:::

### **Example Parse 3.9.0 and later:**

Parse 3.9.0 and later does not have the generic method UPDATE. You must use the specific methods below to update objects.

### **Using class mutation**

Once you have already created your object’s class in your application’s schema (for instance, using the [creating an object](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic) recipe), Parse Server instantly adds to your GraphQL API a new update\<ClassName> mutation to update an existing object of this class.

Therefore, the object’s class mutation is the recommended method for updating an existing object if your object’s class already has all fields that you want to update. Since this mutation knows your class’ data, it will automatically make available for you additional features like code auto-complete and validation. You also don’t need to specify the data types when sending dates, pointers, relations, files, geo points, polygons, or bytes through the class update mutation.

:::hint{type="info"}
This example will only work if you use a class’ mutation and objectId or id of an existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe. The object’s class must have all fields that you are trying to update. You can create new fields using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-update-object#mutation-generic" target="_blank">generic mutation</a>.
:::

## **Parse 3.10.0 and later**

:::CodeblockTabs
Request

```graphql
1   mutation UpdateObject {
2     updateHero(input: {
3       id: "SGVybzpVRm5TVDM1YnBp"
4       fields: {
5         height: 5.6
6       }
7     }){
8       hero{
9         updatedAt
10      }
11    }
12  }
```

Response

```graphql
1   {
2     "data": {
3       "updateHero": {
4         "hero": {
5           "updatedAt": "2020-02-06T13:31:49.866Z"
6         }
7       }
8     }
9   }
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse 3.9.0**

**Class mutation:**

:::CodeblockTabs
Parse Server 3.9.0

```graphql
1   mutation UpdateObject {
2     updateHero(id: "CkhurmMjZW" fields:{
3       height: 5.6
4     }){
5       updatedAt
6     }
7   }
```

Result Parse 3.9.0

```graphql
1   {
2     "data": {
3       "updateHero": {
4         "updatedAt": "2019-11-04T13:30:20.457Z"
5       }
6     }
7   }
```
:::
::::

::::ExpandableHeading
**Parse 3.8.0**

**Class mutation:**

:::CodeblockTabs
Request

```graphql
1   mutation UpdateObject {
2     updateHero(objectId: "rR8jmFRnkS" fields:{
3       height: 5.6
4     }){
5       updatedAt
6     }
7   }
```

Response

```graphql
1   {
2     "data": {
3       "updateHero": {
4         "updatedAt": "2019-11-04T13:38:46.343Z"
5       }
6     }
7   }
```
:::


::::

::::ExpandableHeading
**Parse 3.7.2**

**Generic mutation:**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   mutation UpdateObject {
2     objects {
3       update(className: "Hero", objectId: "ffyOBOTk85", fields: { height: 5.6 }) {
4         updatedAt
5       }
6     }
7   }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "update": {
5           "updatedAt": "2019-07-15T05:57:14.416Z"
6         }
7       }
8     }
9   }
```
:::

**Class mutation:**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   mutation UpdateHero {
2     objects {
3       updateHero(objectId: "jJH0aQQjfs", fields: { height: 3.6 }) {
4         updatedAt
5       }
6     }
7   }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "updateHero": {
5           "updatedAt": "2019-07-15T05:51:25.572Z"
6         }
7       }
8     }
9   }
```
:::
::::


[title] Relational Queries
[path] React Native/Parse SDK (REST)/Data objects/

# Relational Query in React Native using Parse

## Introduction

In this guide, you will perform relational 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

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

Query relational data stored on Back4App from a React Native App.

## 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.first), 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 Book subclass is being queried.

```javascript
1	// This will create your query
2	let parseQuery = new Parse.Query("Book");
3	// The query will resolve only after calling this method
4	let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Save some data on Back4App

Let’s create an assortment of classes, which will be the target of our queries in this guide. The classes are: Author, Book, Publisher and BookStore, in which Book has a 1\:N relation with Publisher and N\:N with Author, and BookStore has an N\:N relation with Book.

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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/adpU1LhbeKubr97OeagbI_image.png)

Go ahead and create the classes with the following example content:

```javascript
1	// Add objects and create tables
2	// Authors
3	const AuthorA = new Parse.Object('Author');
4	AuthorA.set('name', 'Aaron Writer');
5	await AuthorA.save();
6	
7	const AuthorB = new Parse.Object('Author');
8	AuthorB.set('name', 'Beatrice Novelist');
9	await AuthorB.save();
10	
11	const AuthorC = new Parse.Object('Author');
12	AuthorC.set('name', 'Casey Columnist');
13	await AuthorC.save();
14	
15	// Publishers
16	const PublisherA = new Parse.Object('Publisher');
17	PublisherA.set('name', 'Acacia Publishings');
18	await PublisherA.save();
19	
20	const PublisherB = new Parse.Object('Publisher');
21	PublisherB.set('name', 'Birch Distributions');
22	await PublisherB.save();
23	
24	// Books
25	const BookA = new Parse.Object('Book');
26	BookA.set('title', 'A Love Story');
27	BookA.set('publisher', PublisherA);
28	BookA.set('publishingDate', new Date('05/07/1998'));
29	const BookARelation = BookA.relation("authors");
30	BookARelation.add(AuthorA);
31	await BookA.save();
32	
33	const BookB = new Parse.Object('Book');
34	BookB.set('title', 'Benevolent Elves');
35	BookB.set('publisher', PublisherB);
36	BookB.set('publishingDate', new Date('11/31/2008'));
37	const BookBRelation = BookB.relation("authors");
38	BookBRelation.add(AuthorB);
39	await BookB.save();
40	
41	const BookC = new Parse.Object('Book');
42	BookC.set('title', 'Can You Believe It?');
43	BookC.set('publisher', PublisherB);
44	BookC.set('publishingDate', new Date('08/21/2018'));
45	const BookCRelation = BookC.relation("authors");
46	BookCRelation.add(AuthorA);
47	BookCRelation.add(AuthorC);
48	await BookC.save();
49	
50	// BookStore
51	const BookStoreA = new Parse.Object('BookStore');
52	BookStoreA.set('name', 'Books of Love');
53	const BookStoreARelation = BookStoreA.relation("books");
54	BookStoreARelation.add(BookA);
55	await BookStoreA.save();
56	
57	const BookStoreB = new Parse.Object('BookStore');
58	BookStoreB.set('name', 'Fantasy Books');
59	const BookStoreBRelation = BookStoreB.relation("books");
60	BookStoreBRelation.add(BookB);
61	await BookStoreB.save();
62	
63	const BookStoreC = new Parse.Object('BookStore');
64	BookStoreC.set('name', 'General Books');
65	const BookStoreCRelation = BookStoreC.relation("books");
66	BookStoreCRelation.add(BookA);
67	BookStoreCRelation.add(BookC);
68	await BookStoreC.save();
69	
70	console.log('Success');
```

## 3 - Query the data

Now that you have populated all the classes, we can now perform some relational queries in it. Let’s begin by filtering Book results by the publisher, searching for the ones that belong to the Publisher “Acacia Publishings” (or “Publisher A”) using the basic Parse.Query.equalTo method:

```javascript
1	// Get PublisherA object
2	const PublisherAQuery = new Parse.Query('Publisher');
3	PublisherAQuery.equalTo('name', 'Acacia Publishings');
4	const PublisherA = await PublisherAQuery.first();
5	
6	// Query Books with PublisherA
7	const bookQuery = new Parse.Query('Book');
8	bookQuery.equalTo('publisher', PublisherA);
9	let queryResults = await bookQuery.find();
10	
11	// Let's show the results
12	for (let result of queryResults) {
13	  // You access `Parse.Objects` attributes by using `.get`
14	  console.log(result.get('title'));
15	};
```

Let’s now query which BookStore objects contain Book objects with publishing date greater than 01/01/2010, using an inner query with the Parse.Query.greaterThan method and then the Parse.Query.matchesQuery method:

```javascript
1	// Create inner Book query
2	const bookQuery = new Parse.Query('Book');
3	bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
4	
5	// Query BookStore using inner Book query
6	const bookStoreQuery = new Parse.Query('BookStore');
7	bookStoreQuery.matchesQuery('books', bookQuery);
8	let queryResults = await bookStoreQuery.find();
9	
10	// Let's show the results
11	for (let result of queryResults) {
12	  // You access `Parse.Objects` attributes by using `.get`
13	  console.log(result.get('name'));
14	};
```

Let’s now create a more complex relational query, looking for BookStore objects that have at least one Book written by Author “Aaron Writer” (or “AuthorA”), using equalTo and matchesQuery:

```javascript
1	// Get AuthorA object
2	const AuthorAQuery = new Parse.Query('Author');
3	AuthorAQuery.equalTo('name', 'Aaron Writer');
4	const AuthorA = await AuthorAQuery.first();
5	
6	// Create inner Book query
7	const bookQuery = new Parse.Query('Book');
8	bookQuery.equalTo('authors', AuthorA);
9	
10	// Query BookStore using inner Book query
11	const bookStoreQuery = new Parse.Query('BookStore');
12	bookStoreQuery.matchesQuery('books', bookQuery);
13	let queryResults = await bookStoreQuery.find();
14	
15	// Let's show the results
16	for (let result of queryResults) {
17	  // You access `Parse.Objects` attributes by using `.get`
18	  console.log(result.get('name'));
19	};
```

## 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 buttons for calling the queries. This is how the component code is laid out, note the doQuery functions, containing the example code from before.

:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  Button as PaperButton,
8	  Text as PaperText,
9	} from 'react-native-paper';
10	
11	export const QueryList = () => {
12	  // State variable
13	  const [queryResults, setQueryResults] = useState(null);
14	
15	  const doQueryA = async function () {
16	    // Get PublisherA object
17	    const PublisherAQuery = new Parse.Query('Publisher');
18	    PublisherAQuery.equalTo('name', 'Acacia Publishings');
19	    const PublisherA = await PublisherAQuery.first();
20	
21	    // Query Books with PublisherA
22	    const bookQuery = new Parse.Query('Book');
23	    bookQuery.equalTo('publisher', PublisherA);
24	
25	    try {
26	      let results = await bookQuery.find();
27	      setQueryResults(results);
28	      return true;
29	    } catch (error) {
30	      // Error can be caused by lack of Internet connection
31	      Alert.alert('Error!', error.message);
32	      return false;
33	    }
34	  };
35	
36	  const doQueryB = async function () {
37	    // Create inner Book query
38	    const bookQuery = new Parse.Query('Book');
39	    bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
40	
41	    // Query BookStore using inner Book query
42	    const bookStoreQuery = new Parse.Query('BookStore');
43	    bookStoreQuery.matchesQuery('books', bookQuery);
44	
45	    try {
46	      let results = await bookStoreQuery.find();
47	      setQueryResults(results);
48	      return true;
49	    } catch (error) {
50	      // Error can be caused by lack of Internet connection
51	      Alert.alert('Error!', error.message);
52	      return false;
53	    }
54	  };
55	
56	  const doQueryC = async function () {
57	    // Get AuthorA object
58	    const AuthorAQuery = new Parse.Query('Author');
59	    AuthorAQuery.equalTo('name', 'Aaron Writer');
60	    const AuthorA = await AuthorAQuery.first();
61	
62	    // Create inner Book query
63	    const bookQuery = new Parse.Query('Book');
64	    bookQuery.equalTo('authors', AuthorA);
65	
66	    // Query BookStore using inner Book query
67	    const bookStoreQuery = new Parse.Query('BookStore');
68	    bookStoreQuery.matchesQuery('books', bookQuery);
69	
70	    try {
71	      let results = await bookStoreQuery.find();
72	      setQueryResults(results);
73	      return true;
74	    } catch (error) {
75	      // Error can be caused by lack of Internet connection
76	      Alert.alert('Error!', error.message);
77	      return false;
78	    }
79	  };
80	
81	  const clearQueryResults = async function () {
82	    setQueryResults(null);
83	    return true;
84	  };
85	
86	  return (
87	    <>
88	      <View style={Styles.header}>
89	        <Image
90	          style={Styles.header_logo}
91	          source={ {
92	            uri:
93	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
94	          } }
95	        />
96	        <PaperText style={Styles.header_text}>
97	          <PaperText style={Styles.header_text_bold}>
98	            {'React Native on Back4App - '}
99	          </PaperText>
100	          {' Relational Queries'}
101	        </PaperText>
102	      </View>
103	      <ScrollView style={Styles.wrapper}>
104	        <View>
105	          <Title>{'Result List'}</Title>
106	          {/* Query list */}
107	          {queryResults !== null &&
108	            queryResults !== undefined &&
109	            queryResults.map((result) => (
110	              <List.Item
111	                key={result.id}
112	                title={
113	                  result.get('name') !== undefined
114	                    ? result.get('name')
115	                    : result.get('title')
116	                }
117	                titleStyle={Styles.list_text}
118	                style={Styles.list_item}
119	              />
120	            ))}
121	          {queryResults === null ||
122	          queryResults === undefined ||
123	          (queryResults !== null &&
124	            queryResults !== undefined &&
125	            queryResults.length <= 0) ? (
126	            <PaperText>{'No results here!'}</PaperText>
127	          ) : null}
128	        </View>
129	        <View>
130	          <Title>{'Query buttons'}</Title>
131	          <PaperButton
132	            onPress={() => doQueryA()}
133	            mode="contained"
134	            icon="search-web"
135	            color={'#208AEC'}
136	            style={Styles.list_button}>
137	            {'Query A'}
138	          </PaperButton>
139	          <PaperButton
140	            onPress={() => doQueryB()}
141	            mode="contained"
142	            icon="search-web"
143	            color={'#208AEC'}
144	            style={Styles.list_button}>
145	            {'Query B'}
146	          </PaperButton>
147	          <PaperButton
148	            onPress={() => doQueryC()}
149	            mode="contained"
150	            icon="search-web"
151	            color={'#208AEC'}
152	            style={Styles.list_button}>
153	            {'Query C'}
154	          </PaperButton>
155	          <PaperButton
156	            onPress={() => clearQueryResults()}
157	            mode="contained"
158	            icon="delete"
159	            color={'#208AEC'}
160	            style={Styles.list_button}>
161	            {'Clear Results'}
162	          </PaperButton>
163	        </View>
164	      </ScrollView>
165	    </>
166	  );
167	};
168	
169	// These define the screen component styles
170	const Styles = StyleSheet.create({
171	  header: {
172	    alignItems: 'center',
173	    paddingTop: 30,
174	    paddingBottom: 50,
175	    backgroundColor: '#208AEC',
176	  },
177	  header_logo: {
178	    height: 50,
179	    width: 220,
180	    resizeMode: 'contain',
181	  },
182	  header_text: {
183	    marginTop: 15,
184	    color: '#f0f0f0',
185	    fontSize: 16,
186	  },
187	  header_text_bold: {
188	    color: '#fff',
189	    fontWeight: 'bold',
190	  },
191	  wrapper: {
192	    width: '90%',
193	    alignSelf: 'center',
194	  },
195	  list_button: {
196	    marginTop: 6,
197	    marginLeft: 15,
198	    height: 40,
199	  },
200	  list_item: {
201	    borderBottomWidth: 1,
202	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
203	  },
204	  list_text: {
205	    fontSize: 15,
206	  },
207	});
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  Button as PaperButton,
8	  Text as PaperText,
9	} from 'react-native-paper';
10	
11	export const QueryList: FC<{}> = ({}): ReactElement => {
12	  // State variable
13	  const [queryResults, setQueryResults] = useState(null);
14	
15	  const doQueryA = async function (): Promise<boolean> {
16	    // Get PublisherA object
17	    const PublisherAQuery: Parse.Query = new Parse.Query('Publisher');
18	    PublisherAQuery.equalTo('name', 'Acacia Publishings');
19	    const PublisherA: Parse.Object = await PublisherAQuery.first();
20	
21	    // Query Books with PublisherA
22	    const bookQuery: Parse.Query = new Parse.Query('Book');
23	    bookQuery.equalTo('publisher', PublisherA);
24	
25	    try {
26	      let results: [Parse.Object] = await bookQuery.find();
27	      setQueryResults(results);
28	      return true;
29	    } catch (error) {
30	      // Error can be caused by lack of Internet connection
31	      Alert.alert('Error!', error.message);
32	      return false;
33	    }
34	  };
35	
36	  const doQueryB = async function (): Promise<boolean> {
37	    // Create inner Book query
38	    const bookQuery: Parse.Query = new Parse.Query('Book');
39	    bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
40	
41	    // Query BookStore using inner Book query
42	    const bookStoreQuery: Parse.Query = new Parse.Query('BookStore');
43	    bookStoreQuery.matchesQuery('books', bookQuery);
44	
45	    try {
46	      let results: [Parse.Object] = await bookStoreQuery.find();
47	      setQueryResults(results);
48	      return true;
49	    } catch (error) {
50	      // Error can be caused by lack of Internet connection
51	      Alert.alert('Error!', error.message);
52	      return false;
53	    }
54	  };
55	
56	  const doQueryC = async function (): Promise<boolean> {
57	    // Get AuthorA object
58	    const AuthorAQuery: Parse.Query = new Parse.Query('Author');
59	    AuthorAQuery.equalTo('name', 'Aaron Writer');
60	    const AuthorA: Parse.Object = await AuthorAQuery.first();
61	
62	    // Create inner Book query
63	    const bookQuery: Parse.Query = new Parse.Query('Book');
64	    bookQuery.equalTo('authors', AuthorA);
65	
66	    // Query BookStore using inner Book query
67	    const bookStoreQuery: Parse.Query = new Parse.Query('BookStore');
68	    bookStoreQuery.matchesQuery('books', bookQuery);
69	
70	    try {
71	      let results: [Parse.Object] = await bookStoreQuery.find();
72	      setQueryResults(results);
73	      return true;
74	    } catch (error) {
75	      // Error can be caused by lack of Internet connection
76	      Alert.alert('Error!', error.message);
77	      return false;
78	    }
79	  };
80	
81	  const clearQueryResults = async function (): Promise<boolean> {
82	    setQueryResults(null);
83	    return true;
84	  };
85	
86	  return (
87	    <>
88	      <View style={Styles.header}>
89	        <Image
90	          style={Styles.header_logo}
91	          source={ {
92	            uri:
93	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
94	          } }
95	        />
96	        <PaperText style={Styles.header_text}>
97	          <PaperText style={Styles.header_text_bold}>
98	            {'React Native on Back4App - '}
99	          </PaperText>
100	          {' Relational Queries'}
101	        </PaperText>
102	      </View>
103	      <ScrollView style={Styles.wrapper}>
104	        <View>
105	          <Title>{'Result List'}</Title>
106	          {/* Query list */}
107	          {queryResults !== null &&
108	            queryResults !== undefined &&
109	            queryResults.map((result: Parse.Object) => (
110	              <List.Item
111	                key={result.id}
112	                title={
113	                  result.get('name') !== undefined
114	                    ? result.get('name')
115	                    : result.get('title')
116	                }
117	                titleStyle={Styles.list_text}
118	                style={Styles.list_item}
119	              />
120	            ))}
121	          {queryResults === null ||
122	          queryResults === undefined ||
123	          (queryResults !== null &&
124	            queryResults !== undefined &&
125	            queryResults.length <= 0) ? (
126	            <PaperText>{'No results here!'}</PaperText>
127	          ) : null}
128	        </View>
129	        <View>
130	          <Title>{'Query buttons'}</Title>
131	          <PaperButton
132	            onPress={() => doQueryA()}
133	            mode="contained"
134	            icon="search-web"
135	            color={'#208AEC'}
136	            style={Styles.list_button}>
137	            {'Query A'}
138	          </PaperButton>
139	          <PaperButton
140	            onPress={() => doQueryB()}
141	            mode="contained"
142	            icon="search-web"
143	            color={'#208AEC'}
144	            style={Styles.list_button}>
145	            {'Query B'}
146	          </PaperButton>
147	          <PaperButton
148	            onPress={() => doQueryC()}
149	            mode="contained"
150	            icon="search-web"
151	            color={'#208AEC'}
152	            style={Styles.list_button}>
153	            {'Query C'}
154	          </PaperButton>
155	          <PaperButton
156	            onPress={() => clearQueryResults()}
157	            mode="contained"
158	            icon="delete"
159	            color={'#208AEC'}
160	            style={Styles.list_button}>
161	            {'Clear Results'}
162	          </PaperButton>
163	        </View>
164	      </ScrollView>
165	    </>
166	  );
167	};
168	
169	// These define the screen component styles
170	const Styles = StyleSheet.create({
171	  header: {
172	    alignItems: 'center',
173	    paddingTop: 30,
174	    paddingBottom: 50,
175	    backgroundColor: '#208AEC',
176	  },
177	  header_logo: {
178	    height: 50,
179	    width: 220,
180	    resizeMode: 'contain',
181	  },
182	  header_text: {
183	    marginTop: 15,
184	    color: '#f0f0f0',
185	    fontSize: 16,
186	  },
187	  header_text_bold: {
188	    color: '#fff',
189	    fontWeight: 'bold',
190	  },
191	  wrapper: {
192	    width: '90%',
193	    alignSelf: 'center',
194	  },
195	  list_button: {
196	    marginTop: 6,
197	    marginLeft: 15,
198	    height: 40,
199	  },
200	  list_item: {
201	    borderBottomWidth: 1,
202	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
203	  },
204	  list_text: {
205	    fontSize: 15,
206	  },
207	});
```
:::

This is how the component should look like after rendering and querying by one of the query functions:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/k7yWIsaqOMaKj0ym3QuoF_image.png" signedSrc size="50" width="356" height="747" position="center" caption}

## Conclusion

At the end of this guide, you learned how relational queries work on Parse and how to perform them on Back4App from a React Native App. In the next guide, you will learn how to work with Users in Parse.

[title] User Sign Up
[path] React Native/Relay (GraphQL)/Users/

# User Sign up with Relay

## Introduction

The first thing your app will do is probably ask the user to sign up. Back4App/Parse already provides by default a class User, which already has a ready-to-use GraphQL Mutation to sign up new users when it is necessary for your app.

In this guide, you will create a Sign-Up feature for a React Native app using Relay to persist our data on Back4App.

The flow is very similar to create a Query Renderer. After implementation, the Relay Compiler will check Frontend(fragments) and Backend(data model) and return if everything matches. If so, the types and the application is already to communicate with the backend.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## Goal

At the end of this guide, you will have a React Native application with the user sign up feature implemented as show below.

## Prerequisites

- **An app created at Back4App using the **<a href="https://www.back4app.com/docs/platform/parse-server-version" target="_blank">**Parse Server Version 3.10**</a>** or above.**
- **You have to conclude the **<a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">**Relay Environment setup tutorial**</a>**:**
- **For this tutorial we are going to use the Expo as a React Native framework;**
- **For this tutorial we are going to use Javascript as our default implementation language;**
- **For this tutorial we are going to use our Style css sample;**

## 1 - Creating Sign Up Form

If the application already has a Form component, go to step 2. Otherwise, feel free to follow our boilerplate.

We will use an Expo app having with which has a Form with the username and password. To make our life easier, we are going to use some third-party libraries to help build the SignUp feature. Our form component will use the formik library. It is important to note that it doesn’t infer the final result.

- Install formik

> yarn add formik

- Create a new component and name it FormSignUp.js
- Paste the following code inside it:

```javascript
1	import React, { useState } from 'react';
2	import { Button, Text, TextInput, View, TouchableOpacity } from "react-native";
3	import { useFormik, FormikProvider } from 'formik';
4	import Styles from "../../Style"
5	import environment from '../../relay/environment';
6	
7	const SignUp = () => {
8	  const [userCreated, setUserCreated] = useState();
9	
10	  const onSubmit = (values) => {
11	    // @todo the mutation will be implemented here
12	  };
13	
14	  const formikbag = useFormik({
15	    initialValues: {
16	      username: '',
17	      password: '',
18	    },
19	    onSubmit,
20	  });
21	
22	  const { handleSubmit, setFieldValue } = formikbag;
23	  if (userCreated?.id) {
24	    return (
25	      <View style={ {marginTop: 15, alignItems: 'center'} }>
26	        <Text>User {userCreated.name} created</Text>
27	      </View>
28	    );
29	  }
30	
31	  return (
32	    <FormikProvider value={formikbag}>
33	      <View style={Styles.login_wrapper}>
34	        <View style={Styles.form}>
35	          <Text>Username</Text>
36	          <TextInput
37	            name={"username"}
38	            style={Styles.form_input}
39	            autoCapitalize="none"
40	            onChangeText={(text) => setFieldValue("username", text)}
41	          />
42	          <Text>Password</Text>
43	          <TextInput
44	            style={Styles.form_input}
45	            name={"password"}
46	            autoCapitalize="none"
47	            secureTextEntry
48	            onChangeText={(text) => setFieldValue("password", text)}
49	          />
50	          <TouchableOpacity onPress={() => handleSubmit()}>
51	            <View style={Styles.button}>
52	              <Text style={Styles.button_label}>{"Sign in"}</Text>
53	            </View>
54	          </TouchableOpacity>
55	        </View>
56	      </View>
57	    </FormikProvider>
58	  );
59	};
60	
61	export default SignUp;
```

The application should run ok until here. The form should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/p7wZa2TV6QilONY0iUHTN_image.png" signedSrc size="70" width="828" height="1792" position="center" caption}

Let’s situate our component explaining some points:

- **We are using formik to control our form values. You can also use a form with HTML, CSS, and JS.**
- **styled-components will be used to give simple CSS styles for the component**
- **There is a use state to control if our user was registered or not.**

Please, look at the onSubmit function. Note that the Relay Mutation will be inside of this function. Again, it is not a problem if the application is not using Formik. Once we are implementing a Form Component, the Relay Mutation only needs to be called inside the submit function.

## 2 - Creating the Mutation

Using the Colocation principle, let’s create a new folder the most closely to the Form Component. Name it as mutations. To remember about colocation you can go to our doc <a href="https://www.back4app.com/docs/react-native/graphql/get-started-relay-graphql" target="_blank">Getting Started</a>, where we give a brief about it.

Exemplifying how handles the colocation, in the image below the component SignUp is wrapped by a folder. Inside of this folder is where we will create the folder mutations. And, inside of mutations folder, we will create the Relay Mutation. This works perfectly on big projects. Everything related to the component will be placed close to it and will be more easily work, find, etc.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OLpnCu7Lsfj0TdMoCgF44_image.png" signedSrc size="80" width="291" height="105" position="center" caption}

:::hint{type="success"}
use this approach for every new mutation of the application. Every time put it close to the component that will use it.
:::

Inside this folder, you will create a new file called SignUpMutation.js. According to our last guide where we explained the Relay Mutations, you will create a function inside and call it commit. You can use the code below:

```javascript
1	function commit({ environment, input, onCompleted, onError }) {
2	  const variables = { input };
3	
4	  commitMutation(environment, {
5	    mutation,
6	    variables,
7	    onCompleted,
8	    onError,
9	  });
10	}
11	
12	export default {
13	  commit,
14	};
```

Before going back to the form component, let’s create our variable that will receive the GraphQL Fragment, representing the Mutation. The GraphQL Fragment is what the Relay Compiler will read and match with schema.graphql.

Before the commitMutation, copy and paste the following code:

```javascript
1	const mutation = graphql`
2	  mutation SignUpMutation($input: SignUpInput!) {
3	    signUp(input: $input) {
4	      viewer {
5	        user {
6	          id
7	          username
8	          createdAt
9	        }
10	        sessionToken
11	      }
12	    }
13	  }
14	`;
```

Final file:

```javascript
1	import {commitMutation, graphql} from 'react-relay';
2	
3	const mutation = graphql`
4	  mutation SignUpMutation($input: SignUpInput!) {
5	    signUp(input: $input) {
6	      viewer {
7	        user {
8	          id
9	          username
10	          createdAt
11	        }
12	        sessionToken
13	      }
14	    }
15	  }
16	`;
17	
18	function commit({environment, input, onCompleted, onError}) {
19	  const variables = {input};
20	
21	  commitMutation(environment, {
22	    mutation,
23	    variables,
24	    onCompleted,
25	    onError,
26	  });
27	}
28	
29	export default {
30	  commit,
31	};
```

:::hint{type="success"}
Since the GraphQL Fragment represents the backend, to get the code of Relay Mutation, you can go to the <a href="https://www.back4app.com/docs/react-native/graphql/users/back4app.com/docs/parse-graphql/graphql-sign-up" target="_blank">Back4App GraphQL Cookbook</a> and find the Fragment.
:::

Run yarn relay to generate the new mutation and update the files. If everything is okay the types of mutation it will be generated and you can go forward.

## 3 - Implement On Submit Function

The submit step is the most important. Here is where the Relay Mutation magic happens.

:::hint{type="success"}
this step gets the values of the form from the formik. If the application is not using formik, the values need to be available here independent of the way they get it.
:::

Back to Form Component, let’s start the implementation of the Relay Mutation.

- **Import the mutation**

```javascript
1	import SignUpMutation from './mutations/SignUpMutation';
```

- **Inside of OnSubmit function, stars creating the input variables:**

```javascript
1	const onSubmit = (values) => {
2	    const {username, password} = values;
3	    
4	    const input = {
5	      userFields: {
6	        username,
7	        password,
8	      },
9	    };
10	}
```

:::hint{type="info"}
The values are injected by Formik. Here, if you are not using formik, the values will likely come via the form’s native oSubmit or as you prefer.
:::

- **At last, call the Mutation passing all props (remember to import them).**

```javascript
1	    SignUpMutation.commit({
2	      environment,
3	      input,
4	      onCompleted: ({signUp}) => {
5	        const { viewer } = signUp;
6	        const { sessionToken, user } = viewer;
7	
8	        if (sessionToken !== null) {
9	          alert(`user ${user.username} successfully created`);
10	          setUserCreated(user);
11	          return;
12	        }
13	      },
14	      onError: (errors) => {
15	        alert(errors[0].message);
16	      },
17	    });
```

- **Final result of onSubmit**

```javascript
1	const onSubmit = (values) => {
2	    const { username, password } = values;
3	    
4	    const input = {
5	      userFields: {
6	        username,
7	        password,
8	      },
9	    };
10	
11	    SignUpMutation.commit({
12	      environment,
13	      input,
14	      onCompleted: ({signUp}) => {
15	        const { viewer } = signUp;
16	        const { sessionToken, user } = viewer;
17	    
18	        if (sessionToken !== null) {
19	          alert(`user ${user.username} successfully created`);
20	          setUserCreated(user);
21	          return;
22	        }
23	      },
24	      onError: (errors) => {
25	        alert(errors[0].message);
26	      },
27	    });
28	};
```

Run your project, register your User and then check it on Back4App Dashboard. The Mutation will return the values from the server. Once the session token is returned, the application can start to manage it.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/D3FfkncQ0Xg1nFqMylUgI_image.png" signedSrc size="50" width="828" height="1792" position="center" caption}

## Handling Errors

On commit mutation, the application can handle errors on onError. Always will receive an array of errors. The most common is this array has only one object containing the error message. See the example below:

> 1	{
> 2	  "errors": [
> 3	    {
> 4	      "message": "Account already exists for this username.",
> 5	      "locations": [
> 6	        {
> 7	          "line": 2,
> 8	          "column": 3
> 9	        }
> 10	      ],
> 11	      "path": [
> 12	        "signUp"
> 13	      ],
> 14	      "extensions": {
> 15	        "code": 202
> 16	      }
> 17	    }
> 18	  ],
> 19	  "data": {
> 20	    "signUp": null
> 21	  }
> 22	}

Based on this example feel free to create your our error handle. By now, if some error is returned we just show it by an alert:

```javascript
1   onError: (errors) => {
2     alert(errors[0].message);
3   },
```

## Conclusion

You now have an application with a sign-up feature fully working. In the next guide, you will understand how to authenticate a user(login) and log out him using the same approach. You will also use Relay Mutations to call our backend.

[title] Untitled
[path] React Native/


[title] Untitled
[path] React Native/Parse SDK (REST)/


[title] Getting a logged user
[path] GraphQL Cookbook/

# Getting a logged user through the Parse GraphQL API

## Problem

You want to get a logged user’s data from your backend through the Parse GraphQL API.

## Solution

Using the Parse GraphQL API, you can get a logged user’s data just by sending the user’s sessionToken through the X-Parse-Session-Token header (as described in the [authenticating a user](https://www.back4app.com/docs/parse-graphql/graphql-user-authentication) recipe) and calling the me query.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

## **Parse Server 4.4.0 and later**

:::CodeblockTabs
Request

```graphql
//The headers for this query are X-Parse-Application-Id, X-Parse-Client-Key and X-Parse-Session-Token
1   query GetCurrentUser {
2     viewer {
3       sessionToken
4       user {
5         id
6         objectId
7       }
8     }
9   }
```

Response

```graphql
1   {
2     "data": {
3       "viewer": {
4         "sessionToken": "r:07dbfe8425d47d57c973bddce0df2ec9",
5         "user": {
6           "id": "X1VzZXI6OXZjM05sallIUA==",
7           "objectId": "9vc3NljYHP"
8         }
9       }
10    }
11  }
```
:::

::::ExpandableHeading
**Parse Server 3.10.0 and 4.2.0**

:::CodeblockTabs
Request

```graphql
1   query Me {
2     viewer {
3       user{
4         id
5         createdAt
6         updatedAt
7         username
8       }
9       sessionToken
10    }
11  }
```

Response

```graphql
1   {
2     "data": {
3       "viewer": {
4         "user": {
5           "id": "X1VzZXI6UHNOUkJ3Y1YyRQ==",
6           "createdAt": "2020-02-06T13:38:04.517Z",
7           "updatedAt": "2020-02-06T13:38:04.517Z",
8           "username": "somefolk"
9         },
10        "sessionToken": "r:00afa413b9cadd1007ad9ccd3c00f1c9"
11      }
12    }
13  }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.9.0**

:::CodeblockTabs
Request

```graphql
1   query Me {
2     users {
3       results{
4         id,
5         createdAt,
6         updatedAt,
7         username
8       }
9     }
10  }
```

Response

```graphql
1   {
2     "data": {
3       "users": {
4         "me": {
5           "objectId": "NyU1lNlhPd",
6           "createdAt": "2019-07-29T09:09:58.222Z",
7           "updatedAt": "2019-07-29T09:09:58.222Z",
8           "username": "somefolk"
9         }
10      }
11    }
12  }
```
:::


::::

::::ExpandableHeading
**Parse Server 3.8.0**

:::CodeblockTabs
Request

```graphql
1   query Me{
2     viewer {
3       sessionToken
4       username
5     }
6   }
```

Response

```graphql
1   {
2     "data": {
3       "viewer": {
4         "sessionToken": "r:5c5024921339edf773b5b3e867d708be",
5         "username": "somefolk"
6       }
7     }
8   }
```
:::


::::

::::ExpandableHeading
**Parse Server 3.7.2**

:::CodeblockTabs
Request

```graphql
1   query Me {
2     users {
3       me {
4         objectId,
5         createdAt,
6         updatedAt,
7         username,
8         sessionToken
9       }
10    }
11  }
```

Response

```graphql
1   {
2     "data": {
3       "users": {
4         "me": {
5           "objectId": "NyU1lNlhPd",
6           "createdAt": "2019-07-29T09:09:58.222Z",
7           "updatedAt": "2019-07-29T09:09:58.222Z",
8           "username": "somefolk",
9           "sessionToken": "r:cbca71d29d7601761b48ed01bbe9638d"
10        }
11      }
12    }
13  }
```
:::


::::


[title] Untitled
[path] React Native/Parse SDK (REST)/


[title] Sign Up With VKontakte
[path] Platform/



# Sign In with VK (VKontakte) Tutorial

## Introduction

Sign In with VK (VKontakte) enables users to sign in to Apps using their VK accounts.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create a subdomain in Back4App.
- A <a href="https://vk.com/" target="_blank">VK account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a new VK App

Create a new VK Application by going to [VK Developers](https://vk.com/apps?act=manage) and clicking the `Create app button`

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Boa01ieeq3GJoROBAoueS_image.png)

Fill up the Title and choose the Platform as Standalone app, then click the Connect app button

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/oybDJT1inXp77Dcj1LsOh_image.png)

Choose a Category for your app and, if applicable, a Type of leaderboard, and Community. Click Save

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/P-epaj4HCCM_vQv1IUFpS_image.png)

Under the Settings tab of your VK Application, you will find your App ID, Secure key, and Service token among other useful info. Fill up your Website address and the Base domain for it. Save it.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uLP5qA1wjra7VzRHISZkx_image.png)

## 3 - Retrieve your Code

Copy the App ID from your to use as the `YOUR_CLIENT_ID` and use your Website address as `YOUR_REDIRECT_URI`, and choose a [scope](https://vk.com/dev/permissions) to use in `YOUR_SCOPE` from the available options.

Then visit the following URL changing the parameters above:

> [https://oauth.vk.com/authorize?client\_id=YOUR\_CLIENT\_ID\&scope=YOUR\_SCOPE\&redirect\_uri=https://localhost\&response\_type=token](https://oauth.vk.com/authorize?client_id=YOUR_CLIENT_ID\&scope=YOUR_SCOPE\&redirect_uri=https://localhost\&response_type=token)

It will ask you to log in to VK:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ql80wj-_wGwQB1qn6pmRc_image.png)

Alternatively, you can use the following CURL command to retrieve your token:

```curl
curl -X POST \
    -F \'client_id=YOUR_CLIENT_ID' 
    -F 'scope=YOUR_SCOPE'
    -F 'redirect_uri=YOUR_REDIRECT_URI' 
    -F 'response_type=token' 
    https://oauth.vk.com/authorize?
```

Run it and you should retrieve your access token:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/t6SnGMgZ2kuOVQVKUHgQf_image.png)

**REMEMBER**: the code can be used only once. If you get an error or don’t use your token, you must re-generate your Code to be able to run it again.

## 4 - Configure your Back4app App

In your Back4app App, go to Server Settings and open the VKontakte Login box

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pNdJNQzx_bpWot8RckzW2_image.png" signedSrc size="40" width="292" height="338" position="center" caption}

Fill up your Application Id and VKontakte Application Secret. Save it.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OtzKpW-akzILuxLH5S2bA_image.png" signedSrc size="70" width="603" height="574" position="center" caption}

## 5 - Start the development

Now that the Sign-in with VK is configured, you can start the development process.
The format for AUTHDATA is:

```json
   {
     "vkontakte": {
       "id": "user's vkontakte id (string)",
       "access_token": "an authorized vkontakte access token for the user"
     }
   }
```

Here is the method for the iOS SDK:

```swift
1   PFUser.logInWithAuthType(inBackground: "vkontakte", authData: ["access_token":tokenString, "id": user]).continueWith { task -> Any? in
2    
3   }
```

And here for the Android SDK

```java
1   Map<string, string, bool> authData = new HashMap<string, string, bool>(); 
2   authData.put("access_token", tokenString);
3   authData.put("id", user);
4   ParseUser.logInWithInBackground("vkontakte", authData){
5
6   }
```


[title] Sign Up With LinkedIn
[path] Platform/

# Sign In with LinkedIn Tutorial

## Introduction

Sign In with LinkedIn enables users to sign in to Apps using their LinkedIn accounts.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create a subdomain in Back4App.
- A <a href="https://www.linkedin.com/developers" target="_blank">LinkedIn Developer account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a new LinkedIn App

Log into your [LinkedIn Developer account](https://www.linkedin.com/developers) click Create App and choose OAuth client ID

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Se8FVnj5N-hwCKeFkccyQ_image.png)

Choose an App name and fill in the required fields such as Business email and App logo. Agree to the terms and click Create app

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xAl9LVxEfGKKXPq3HUPgm_image.png" signedSrc size="80" width="519" height="916" position="center" caption}

In your newly created App, click Verify to verify the ownership of the App. You must be the owner or administrator of the LinkedIn page to verify.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fQsUI-o0AjyznmSYsKPhB_image.png" signedSrc size="70" width="521" height="721" position="center" caption}

In the Verification page, click Generate URL

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LgtvzoOBG_PB3MASZ-KQZ_image.png" signedSrc size="80" width="494" height="419" position="center" caption}

Visit the generated Verification URL using the admin or owner account of the company’s page on LinkedIn.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cG24gUichkV2tSLLkRtri_image.png" signedSrc size="80" width="492" height="419" position="center" caption}

Click on Approve Verification

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uuv4oVWSDAVSLudidLKgM_image.png)

Make sure your App is verified

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HbiMmKh6v0UgmknjUBTLW_image.png)

In your App, go to the Auth tab, fill in the Redirect URLs field, and click Update

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/z_8wla-sJ34Dh8JcwOabQ_image.png" signedSrc size="70" width="517" height="676" position="center" caption}

## 3 - Retrieve your Code

Visit the following URL, changing the values for CLIENT\_ID, REDIRECT\_URL, and A\_RANDOM\_STRING for the ones you created.
The random string is to avoid CSRF attacks.

> https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&state=A_RANDOM_STRING&scope=r_emailaddress

Log in with your LinkedIn account and the redirected website will have your code in the URL:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GjE0lzofVYRwFQXS5Fscc_image.png)

Copy the Code part of the URL only and run the following CURL command replacing the values YOUR\_CODE, YOUR\_CLIENT\_ID, YOUR\_CLIENT\_SECRET, and YOUR\_REDIRECT\_URI for the values of your application

```curl
1   curl -X POST \
2     https://www.linkedin.com/oauth/v2/accessToken \
3     -H 'cache-control: no-cache' \
4     -H 'content-type: application/x-www-form-urlencoded' \
5     -d 'client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&redirect_uri=YOUR_REDIRECT_URI&code=YOUR_CODE&grant_type=authorization_code'

```

Run it and you should retrieve your access token:

**REMEMBER**: the code can be used only once. If you get an error or don’t use your token, you must re-generate your Code to be able to run it again.

## 4 - Start the development

Now that the Sign-in with LinkedIn is configured, you can start the development process.
The format for AUTHDATA is:

```json
1   {
2     "linkedin": {
3       "id": "user's LinkedIn id (string)",
4       "access_token": "an authorized LinkedIn access token for the user",
5       "is_mobile_sdk": true|false // set to true if you acquired the token through LinkedIn mobile SDK
6     }
7   }
```

Here is the method for the iOS SDK:

```swift
1   PFUser.logInWithAuthType(inBackground: "linkedin", authData: ["access_token":tokenString, "id": user, "is_mobile_sdk": true]).continueWith { task -> Any? in
2    
3   }
```

And here for the Android SDK:

```java
1   Map<string, string, bool> authData = new HashMap<string, string, bool>(); 
2   authData.put("access_token", tokenString);
3   authData.put("id", user);
4   authData.put("is_mobile_sdk", true);
5   Task<ParseUser> t = ParseUser.logInWithInBackground("google", authData);
6				                    t.continueWith(new Continuation<ParseUser, Void>() {
7					                        public Void then(Task task) throws Exception {
8						                            if (task.isCancelled()) {
9							                                Log.w(TAG, "Task cancelled");
10						                            } else if (task.isFaulted()) {
11							                                Log.w(TAG, "Save FAIL" + task.getError());
12							                                Utilities.showToast(getResources().getString(R.string.errorLogin) + task.getError(), MainActivity.this);
13						                            } else {
14							                                // the object was saved successfully.
15							                                ParseUser user = (ParseUser)task.getResult();
16							                                Log.w(TAG, "Success " + user.getObjectId() + " " + user.getUsername() + " " + user.getEmail() + " " + user.getSessionToken());
17                  }
18               }
19            }
```


[title] Deleting Files
[path] React Native/Parse SDK (REST)/Files/

# Delete Files from a React Native App

## Introduction

In this guide, you will learn the best way to delete files from back4app cloud in React Native Apps.

For managing application files in Back4app, we use the Parse.File utility class. You can perform storing, retrieving, and deleting operations using this class. In the previous section, [File Storage](https://www.back4app.com/docs/js-framework/react-native-files), we covered storing and retrieving files by creating a demo gallery App.

At this point, you should be aware that after creating and saving a Parse.File, the best practice is to always associate it with another data object. It will prevent the creation of orphan files in your project and make it possible for you to find and delete them on Back4app cloud.

Parse.File provides a way of deleting files, but it is a security sensitive operation that you should not perform on client-side. In this tutorial, you will learn the best practice for removing your application files.

## Goal

Add delete image action to a React Native gallery demo App

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- Complete the <a href="https://app.archbee.com/docs/_roxIyUMXoBue9I7uv49e/niJIQGQOc93TzrlyDmcBi" target="_blank">File Storage</a> tutorial
- Parse >= 4.2.0
:::

## 1 - Application setup

In the previous section, [File Storage](https://www.back4app.com/docs/js-framework/react-native-files), we built a demo gallery App to upload and display user pictures.
For this tutorial, You will increment this App by adding a delete button to each image item in the Gallery.js component and performing our delete operation.

If you already finished coding the demo gallery App then you can jump to the next step.

Alternatively, you can clone the code base for the App to follow this tutorial along.

To clone the project run:

git clone https\://github.com/templates-back4app/react-native-demo-gallery-app

Then, install project dependencies:

> 1   cd react
>
> **-**
>
> native
>
> **-**
>
> demo
>
> **-**
>
> gallery
>
> **-**
>
> app
> 2
> 3   # using yarn
> 4   yarn install
> 5
> 6   # using npm
> 7   npm install

Before running, remember to setup your Back4app credentials, App Id and JavascriptKey, on the initialize method. For information on App credentials setup see [Install Parse SDK](https://www.back4app.com/docs/js-framework/react-native-cli).

Finally, run the React Native Application:

> 1   # For Android
> 2   npx react
>
> **-**
>
> native run
>
> **-**
>
> android
> 3
> 4   # For iOS
> 5   npx react
>
> **-**
>
> native run
>
> **-**
>
> ios

## 2 - Creating a delete button

On the gallery App, the Gallery.js component renders the list of images from your Back4app cloud project. Next, you will create and add a button to every image item in the current list.

Open the Gallery.js file add the following content:

```javascript
1	import React, {useState, useEffect} from  'react';
2	import {Text, View, Image, FlatList, StyleSheet, Button, Alert} from  'react-native';
3	import Parse from  'parse/react-native.js';
4	
5	const Gallery = () => {
6	const [images, setImages] = useState([]);
7	
8	useEffect(() => {
9	  const fetchImages = async () => {
10	  let query = new Parse.Query('Gallery');
11	  const results = await  query.find();
12	  setImages(results);
13	  };
14	
15	  fetchImages();
16	}, []);
17	
18	async function onDeleteImage(image_id) {
19	  // TODO: implement this function
20	}
21	
22	return (
23	<FlatList
24	  style={styles.container}
25	  contentContainerStyle={styles.listContent}
26	  data={images}
27	  horizontal={false}
28	  numColumns={3}
29	  ListEmptyComponent={() =>  <Text>No images uploaded.</Text>}
30	  renderItem={({item}) => (
31	    <View>
32	      <Image
33	        source={ {uri: item.get('picture').url()} }
34	        style={styles.imageItem}
35	      />
36	     <Button
37	        title="Delete"
38	        color="red"
39	        onPress={() => onDeleteImage(item.id)}
40	      />
41	    </View>
42	  )}
43	  keyExtractor={(item) =>  item.id}
44	/>);
45	
46	};
47	
48	const styles = StyleSheet.create({
49	container: {
50	  flex: 1,
51	  backgroundColor: '#f5f5f5',
52	},
53	listContent: {
54	  justifyContent: 'center',
55	  alignItems: 'center',
56	},
57	imageItem: {
58	  width: 100,
59	  height: 100,
60	  resizeMode: 'cover',
61	  marginHorizontal: 5,
62	  marginVertical: 5,
63	},
64	});
65	export default Gallery;
```

We have refactored the renderItem function, including a delete button to all rendered images. However, the button click event still has no functionality implemented. You will do it on the next step.

## 3 - Creating a delete picture cloud function

You’ve learned that a file should always be associated with a data object on the [File Storage](https://www.back4app.com/docs/js-framework/react-native-files). Not associating files to data objects will result in orphan files. Those files are unreachable inside your App. Once you can’t find them, you also can’t delete them from your App. You can only erase them using the Purge Files option on Back4App Dashboard.

The deleting process consists of finding and then deleting it. In Parse, the destroy method is used to delete referenced files. However, using it on client side is not safe as it requires the masterKey.

When you build a React Native app, all your keys are bundled together, therefore, anyone could reverse engineer, decompile your app or proxy your network traffic from their device to find your masterKey. Using the master key allows you to bypass all of your app’s security mechanisms, such as [class-level permissions](https://docs.parseplatform.org/js/guide/#class-level-permissions) and [ACLs](https://docs.parseplatform.org/js/guide/#object-level-access-control). You can find more details about Parse security best practices [here](https://blog.back4app.com/parse-server-security/).

The best way to delete files is to do it on the server side using a [Cloud Code function](https://www.back4app.com/docs/get-started/cloud-functions). In our gallery app, we will create a cloud code function for that.

Let’s create a main.js file with the following cloud function:

```javascript
1	Parse.Cloud.define('deleteGalleryPicture', async (request) => {
2	        const {image_id} = request.params;
3	        const Gallery = Parse.Object.extend('Gallery');
4	        const query = new Parse.Query(Gallery);
5	        try {
6	                const Image = await query.get(image_id);
7	                const picture = Image.get('picture');
8	
9	                await picture.destroy({useMasterKey:  true});
10	                await Image.destroy();
11	                return 'Image removed.';
12	        } catch (error) {
13	                console.log(error);
14	                throw new Error('Error deleting image');
15	        }
16	});
```

For simplicity, we will use the Dashboard to upload cloud functions directly:

1. Open your App Dashboard at [Back4App Website ](https://www.back4app.com/)and click onCore, then Cloud Code Functions.
2. Uploadmain.jsfile on the root of the cloud/folder
3. Deploy the function to Back4app server

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/txkkWMnIXE9i1Zlm3ReEC_image.png)

After a few seconds your cloud code function will be available to be called via REST or Parse SDK.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/lwkNJD3OmKDdBOJT8_fDO_image.png)

## 4 - Calling delete cloud function from your App

Once you have successfully deployed your Back4app Cloud Code function, go ahead and implement the action for when the users presses delete button in our gallery app:

```javascript
1	// Triggers on hitting delete
2	async function onDeleteImage(image_id) {
3	  try {
4	    const params = {image_id};
5	    const result = await Parse.Cloud.run('deleteGalleryPicture', params);
6	    Alert.alert(result);
7	  } catch (error) {
8	    console.log('Delete Error: ', error);
9	  }
10	}
11
```

Now when you click on delete, your app will trigger deleteGalleryPicture cloud function that will successfully delete an image:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/mMm9rAr2IiwvMWj6WhCiF_image.png" signedSrc size="50" width="279" height="593" position="center" caption}

## 5 - Purging Files

In some situations, when you lost track of your application files you need to delete files from your Dashboard. This usually occurs when you create the orphan files mentioned in this article.

For information on how to clean up your application files:

1. Try [Back4app Files common questions](https://help.back4app.com/hc/en-us/articles/360002327652-How-to-delete-files-completely-)
2. Or See [App Settings documentation](https://www.back4app.com/docs/parse-dashboard/app-settings)

## Done!

At this point, you have succefully deployed a cloud code function and learned how to delete an image in a React Native application.

[title] Untitled
[path] React Native/


[title] Reading and Writing data
[path] Get started/

This guide will teach you how to **create, read, update, and delete** data objects on Back4app using the Parse SDK. Data storage on Back4app revolves around the `Parse.Object` class, which allows you to store key-value pairs of JSON-compatible data, providing flexibility and simplicity in data management.

## Objectives

- Understand how to perform data manipulation (CRUD) on Back4app using Parse SDK.
- Learn how to set up and use the Parse SDK across different platforms.

## Prerequisites

:::hint{type="info"}
**App on Back4app**: You need an app created on Back4app.

- [Guide to creating a new app](https://www.back4app.com/docs/get-started/new-parse-app).

**Parse SDK Installation**:

- [Parse SDK installation guide](https://www.back4app.com/docs/get-started/parse-sdk).
:::

## 1 - Creating Parse Objects

To store data in Back4app, you need to create a `ParseObject` associated with a specific class. For example, in a soccer-related application, you could create a `SoccerPlayers` class to store data about players.

:::CodeblockTabs
```javascript
async function saveNewPlayer() {
  const soccerPlayers = new Parse.Object("SoccerPlayers");
  soccerPlayers.set("playerName", "A. Wed");
  soccerPlayers.set("yearOfBirth", 1997);
  soccerPlayers.set("emailContact", "a.wed@email.io");
  soccerPlayers.set("attributes", ["fast", "good conditioning"]);

  try {
    const result = await soccerPlayers.save();
    console.log('New object created with objectId: ' + result.id);
  } catch (error) {
    console.error('Error: ' + error.message);
  }
}
```

Flutter

```dart
ParseObject soccerPlayers = ParseObject('SoccerPlayers')
  ..set('playerName', 'A. Wed')
  ..set('yearOfBirth', 1997)
  ..set('emailContact', 'a.wed@email.io')
  ..set('attributes', ['fast', 'good conditioning']);

ParseResponse response = await soccerPlayers.save();
if (response.success) {
  print('New object created with objectId: ${response.result.objectId}');
} else {
  print('Error: ${response.error.message}');
}
```

Android

```java
ParseObject soccerPlayers = new ParseObject("SoccerPlayers");
soccerPlayers.put("playerName", "A. Wed");
soccerPlayers.put("yearOfBirth", 1997);
soccerPlayers.put("emailContact", "a.wed@email.io");
soccerPlayers.put("attributes", Arrays.asList("fast", "good conditioning"));

soccerPlayers.saveInBackground(e -> {
  if (e == null) {
    Log.d("Parse", "New object created with objectId: " + soccerPlayers.getObjectId());
  } else {
    Log.e("ParseError", e.getMessage());
  }
});
```

iOS

```swift
let soccerPlayer = ParseObject(className: "SoccerPlayers")
soccerPlayer["playerName"] = "A. Wed"
soccerPlayer["yearOfBirth"] = 1997
soccerPlayer["emailContact"] = "a.wed@email.io"
soccerPlayer["attributes"] = ["fast", "good conditioning"]

soccerPlayer.save { result in
    switch result {
    case .success(let savedPlayer):
        print("New object created with objectId: \(savedPlayer.objectId!)")
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}
```

PHP

```php
$soccerPlayers = new ParseObject("SoccerPlayers");
$soccerPlayers->set("playerName", "A. Wed");
$soccerPlayers->set("yearOfBirth", 1997);
$soccerPlayers->set("emailContact", "a.wed@email.io");
$soccerPlayers->setArray("attributes", ["fast", "good conditioning"]);

try {
    $soccerPlayers->save();
    echo 'New object created with objectId: ' . $soccerPlayers->getObjectId();
} catch (ParseException $ex) {
    echo 'Error: ' . $ex->getMessage();
}
```

.NET

```csharp
ParseObject soccerPlayers = new ParseObject("SoccerPlayers");
soccerPlayers["playerName"] = "A. Wed";
soccerPlayers["yearOfBirth"] = 1997;
soccerPlayers["emailContact"] = "a.wed@email.io";
soccerPlayers["attributes"] = new List<string> { "fast", "good conditioning" };

await soccerPlayers.SaveAsync();
Console.WriteLine("New object created with objectId: " + soccerPlayers.ObjectId);
```

REST API

```curl
curl -X POST \
  -H "X-Parse-Application-Id: APPLICATION_ID" \
  -H "X-Parse-REST-API-Key: REST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "playerName": "A. Wed",
    "yearOfBirth": 1997,
    "emailContact": "a.wed@email.io",
    "attributes": ["fast", "good conditioning"]
  }' \
  https://parseapi.back4app.com/classes/SoccerPlayers
```
:::

After running this code, you can verify the new object in the **Database** section of the Back4app Dashboard. Note that you don’t need to manually create the SoccerPlayers class; it will be created automatically the first time an object is saved.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/KuqRY-K2J4cJ0sP4doJuA_screenshot-2024-11-13-at-161114.png)

## 2 - Reading Parse Objects

To retrieve saved data, you can use a `ParseQuery`. For instance, to fetch the player created above by its `objectId`:

:::CodeblockTabs
JS

```javascript
async function retrievePlayer() {
  const query = new Parse.Query("SoccerPlayers");
  
  try {
    const player = await query.get("HMcTr9rD3s");  // Replace with the actual objectId
    console.log("Player Name: " + player.get("playerName"));
    console.log("Year of Birth: " + player.get("yearOfBirth"));
    console.log("Email Contact: " + player.get("emailContact"));
  } catch (error) {
    console.error("Error retrieving object: " + error.message);
  }
}
```

Flutter

```dart
ParseResponse response = await ParseObject("SoccerPlayers").getObject("HMcTr9rD3s");  // Replace with the actual objectId

if (response.success) {
  ParseObject player = response.result;
  print("Player Name: ${player.get<String>('playerName')}");
  print("Year of Birth: ${player.get<int>('yearOfBirth')}");
  print("Email Contact: ${player.get<String>('emailContact')}");
} else {
  print("Error retrieving object: ${response.error.message}");
}
```

Android

```java
ParseQuery<ParseObject> query = ParseQuery.getQuery("SoccerPlayers");
query.getInBackground("HMcTr9rD3s", (player, e) -> {  // Replace with the actual objectId
  if (e == null) {
    Log.d("Parse", "Player Name: " + player.getString("playerName"));
    Log.d("Parse", "Year of Birth: " + player.getInt("yearOfBirth"));
    Log.d("Parse", "Email Contact: " + player.getString("emailContact"));
  } else {
    Log.e("ParseError", "Error retrieving object: " + e.getMessage());
  }
});
```

iOS

```swift
let query = ParseQuery(className: "SoccerPlayers")
query.get(objectId: "HMcTr9rD3s") { result in  // Replace with the actual objectId
    switch result {
    case .success(let player):
        print("Player Name: \(player["playerName"] ?? "No name")")
        print("Year of Birth: \(player["yearOfBirth"] ?? "No birth year")")
        print("Email Contact: \(player["emailContact"] ?? "No email")")
    case .failure(let error):
        print("Error retrieving object: \(error.localizedDescription)")
    }
}

```

PHP

```php
$query = new ParseQuery("SoccerPlayers");
try {
    $player = $query->get("HMcTr9rD3s");  // Replace with the actual objectId
    echo "Player Name: " . $player->get("playerName") . "\n";
    echo "Year of Birth: " . $player->get("yearOfBirth") . "\n";
    echo "Email Contact: " . $player->get("emailContact") . "\n";
} catch (ParseException $ex) {
    echo 'Error retrieving object: ' . $ex->getMessage();
}
```

.NET

```csharp
var query = ParseObject.GetQuery("SoccerPlayers");
var player = await query.GetAsync("HMcTr9rD3s");  // Replace with the actual objectId

Console.WriteLine("Player Name: " + player.Get<string>("playerName"));
Console.WriteLine("Year of Birth: " + player.Get<int>("yearOfBirth"));
Console.WriteLine("Email Contact: " + player.Get<string>("emailContact"));
```

REST API

```curl
curl -X GET \
  -H "X-Parse-Application-Id: APPLICATION_ID" \
  -H "X-Parse-REST-API-Key: REST_API_KEY" \
  https://parseapi.back4app.com/classes/SoccerPlayers/HMcTr9rD3s  # Replace with the actual objectId
```
:::

In addition to `objectId`, you can also query by other parameters (e.g., `yearOfBirth`, `playerName`), offering greater flexibility in data searches.

## 3 - Updating Parse Objects

To update an object, retrieve it first, set new values for the desired attributes, and call the `save()` method.

:::CodeblockTabs
```javascript
async function updatePlayer() {
  const query = new Parse.Query("SoccerPlayers");
  
  try {
    const player = await query.get("HMcTr9rD3s");  // Replace with the actual objectId
    player.set("yearOfBirth", 1998);
    await player.save();
    console.log("Object updated successfully!");
  } catch (error) {
    console.error("Error updating object: " + error.message);
  }
}
```

Flutter

```dart
ParseObject player = ParseObject("SoccerPlayers")..objectId = "HMcTr9rD3s";  // Replace with the actual objectId
player.set("yearOfBirth", 1998);

ParseResponse response = await player.save();
if (response.success) {
  print("Object updated successfully!");
} else {
  print("Error updating object: ${response.error.message}");
}

```

Android

```java
ParseQuery<ParseObject> query = ParseQuery.getQuery("SoccerPlayers");
query.getInBackground("HMcTr9rD3s", (player, e) -> {  // Replace with the actual objectId
  if (e == null) {
    player.put("yearOfBirth", 1998);
    player.saveInBackground(e1 -> {
      if (e1 == null) {
        Log.d("Parse", "Object updated successfully!");
      } else {
        Log.e("ParseError", "Error updating object: " + e1.getMessage());
      }
    });
  } else {
    Log.e("ParseError", "Error retrieving object: " + e.getMessage());
  }
});
```

iOS

```swift
let query = ParseQuery(className: "SoccerPlayers")
query.get(objectId: "HMcTr9rD3s") { result in  // Replace with the actual objectId
    switch result {
    case .success(var player):
        player["yearOfBirth"] = 1998
        player.save { saveResult in
            switch saveResult {
            case .success:
                print("Object updated successfully!")
            case .failure(let error):
                print("Error updating object: \(error.localizedDescription)")
            }
        }
    case .failure(let error):
        print("Error retrieving object: \(error.localizedDescription)")
    }
}
```

```php
$query = new ParseQuery("SoccerPlayers");
try {
    $player = $query->get("HMcTr9rD3s");  // Replace with the actual objectId
    $player->set("yearOfBirth", 1998);
    $player->save();
    echo "Object updated successfully!";
} catch (ParseException $ex) {
    echo 'Error updating object: ' . $ex->getMessage();
}
```

.NET

```csharp
var query = ParseObject.GetQuery("SoccerPlayers");
var player = await query.GetAsync("HMcTr9rD3s");  // Replace with the actual objectId

player["yearOfBirth"] = 1998;
await player.SaveAsync();
Console.WriteLine("Object updated successfully!");
```

REST API

```curl
curl -X PUT \
  -H "X-Parse-Application-Id: APPLICATION_ID" \
  -H "X-Parse-REST-API-Key: REST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"yearOfBirth": 1998}' \
  https://parseapi.back4app.com/classes/SoccerPlayers/HMcTr9rD3s  # Replace with the actual objectId
```
:::

## 4 - Deleting Parse Objects

To delete an object, retrieve it by `objectId` and use the `destroy()` method.

:::CodeblockTabs
```javascript
async function deletePlayer() {
  const query = new Parse.Query("SoccerPlayers");
  
  try {
    const player = await query.get("HMcTr9rD3s");  // Replace with the actual objectId
    await player.destroy();
    console.log("Object deleted successfully!");
  } catch (error) {
    console.error("Error deleting object: " + error.message);
  }
}
```

Flutter

```dart
ParseObject player = ParseObject("SoccerPlayers")..objectId = "HMcTr9rD3s";  // Replace with the actual objectId

ParseResponse response = await player.delete();
if (response.success) {
  print("Object deleted successfully!");
} else {
  print("Error deleting object: ${response.error.message}");
}
```

Android

```java
ParseQuery<ParseObject> query = ParseQuery.getQuery("SoccerPlayers");
query.getInBackground("HMcTr9rD3s", (player, e) -> {  // Replace with the actual objectId
  if (e == null) {
    player.deleteInBackground(e1 -> {
      if (e1 == null) {
        Log.d("Parse", "Object deleted successfully!");
      } else {
        Log.e("ParseError", "Error deleting object: " + e1.getMessage());
      }
    });
  } else {
    Log.e("ParseError", "Error retrieving object: " + e.getMessage());
  }
});
```

iOS

```swift
let query = ParseQuery(className: "SoccerPlayers")
query.get(objectId: "HMcTr9rD3s") { result in  // Replace with the actual objectId
    switch result {
    case .success(let player):
        player.delete { deleteResult in
            switch deleteResult {
            case .success:
                print("Object deleted successfully!")
            case .failure(let error):
                print("Error deleting object: \(error.localizedDescription)")
            }
        }
    case .failure(let error):
        print("Error retrieving object: \(error.localizedDescription)")
    }
}
```

PHP

```php
$query = new ParseQuery("SoccerPlayers");
try {
    $player = $query->get("HMcTr9rD3s");  // Replace with the actual objectId
    $player->destroy();
    echo "Object deleted successfully!";
} catch (ParseException $ex) {
    echo 'Error deleting object: ' . $ex->getMessage();
}
```

.NET

```csharp
var query = ParseObject.GetQuery("SoccerPlayers");
var player = await query.GetAsync("HMcTr9rD3s");  // Replace with the actual objectId

await player.DeleteAsync();
Console.WriteLine("Object deleted successfully!");
```

REST API

```curl
curl -X DELETE \
  -H "X-Parse-Application-Id: APPLICATION_ID" \
  -H "X-Parse-REST-API-Key: REST_API_KEY" \
  https://parseapi.back4app.com/classes/SoccerPlayers/HMcTr9rD3s  # Replace with the actual objectId
```
:::

:::hint{type="info"}
Again, you don’t have to retrieve the object by its objectId. Parse has many search alternatives to retrieve information from ParseObjects, which you can find out more about in the official Parse documentation for each distinct technology.
:::

## Best Practices and Additional Tips

- **Naming Conventions**: Use ClassNamesLikeThis for classes and keyNamesLikeThis for keys to keep your code organized and readable.
- **Common Error Checks**: If you experience issues connecting to the Parse SDK, verify your <a href="https://www.back4app.com/docs/get-started/parse-sdk" target="_blank">SDK installation and configuration</a>.
- **Automatic Fields**: Remember that each Parse object automatically includes `createdAt`, `updatedAt`, and `objectId` fields.

## Next Steps

After persisting save and reading your first data on Back4app, we recommend keeping exploring the data storage using the guides below. You will find how to store supported data types, save and query relational data, use geopoints, and create optimized data models.

::::LinkArray
:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pEkmfq5PdkxC3lmfRzYsE_1.png"}
&#x20;      ** **<a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">**React Native**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tPONkoW1aPPhiAdTvbvrx_sem-nome-rectangle-sticker-landscape.png"}
<a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">**Flutter**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vqJJw7ljIhPJ1Yfz7mD9n_3.png"}
<a href="https://www.back4app.com/docs/android/android-project-with-source-code-download" target="_blank">Android </a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rzkB1YU-Feimt_SWfZUuC_4.png"}
<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank">iOS</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GsQE6ru-jj2rRbUt7P3Tr_5.png"}
****<a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">**Javascript**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/35IqTes9QhdmijlReYBYZ_6.png"}
<a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">GraphQL</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CS13bUltyBMZaO7SPfWC0_1.png"}
<a href="https://www.back4app.com/docs/react/data-objects/react-crud-tutorial" target="_blank">ReactJS</a>
:::
::::

## Conclusion

This guide provides a solid starting point for working with data on Back4app, making data storage and manipulation across platforms easy using the Parse SDK. For any questions, feel free to reach out to <a href="https://www.back4app.com/support" target="_blank">Back4app support</a>!

[title] Finding objects
[path] GraphQL Cookbook/

# Finding objects through the Parse GraphQL API

## Problem

You want to find objects from your database through the Parse GraphQL API.

## Solution

Using the parse GraphQL, there are two different ways to find objects from your database:

- [Using generic query ](https://www.back4app.com/docs/parse-graphql/graphql-query-find-objects#query-generic)-  this is the query that you can use to find objects of any class.
- [Using class query ](https://www.back4app.com/docs/parse-graphql/graphql-query-find-objects#query-class)- this is the recommended query that you should use to find objects of a specific class.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

### **Using generic query**

When you use the find generic query, Parse Server behaves like a schemaless database. It means that you do not need to specify which object’s fields you want to retrieve. You just need to send the object’s className, and Parse Server will return all fields of the found objects.

Therefore, the objects’ find generic query is the query that you can use for finding objects of any class. If you want to find objects of a specific class, we recommend using the [class query](https://www.back4app.com/docs/parse-graphql/graphql-query-find-objects#query-class).

:::hint{type="info"}
This example will only work if you use a className with existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse Server 3.8.0**

:::CodeblockTabs
Request

```graphql
1   query FindObject {
2     find(className: "Hero") {
3     count,
4     results      
5     }
6   }
```

Response

```graphql
1   {
2     "data": {
3       "find": {
4         "count": 2,
5         "results": [
6           {
7             "objectId": "rR8jmFRnkS",
8             "name": "Luke Skywalker",
9             "createdAt": "2019-11-04T12:42:40.723Z",
10            "updatedAt": "2019-11-04T12:42:40.723Z"
11          },
12          {
13            "objectId": "tUEcddcgno",
14            "name": "R2-D2",
15            "createdAt": "2019-11-04T12:44:10.951Z",
16            "updatedAt": "2019-11-04T12:44:10.951Z"
17          }
18        ]
19      }
20    }
21  }
```
:::

### **Example 3.9.0 and later:**

Parse 3.9.0 and later does not have the generic methods GET and FIND. You must use the specific methods below to retrieve objects.

### **Using class query**

Once you have already created your object’s class in your application’s schema (for instance, using the [creating an object](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic) recipe), Parse Server instantly adds to your GraphQL API a new find\<ClassName> query to find objects of this class.

Therefore, the object’s class query is the recommended method for finding objects of a specific class. Since this query knows your class’ data, it will automatically make available for you additional features like code auto-complete and validation.

:::hint{type="info"}
This example will only work if you use a class' query of existing object. You can create an object using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object" target="_blank">creating an object</a> recipe.
:::

## **Parse Server 3.10.0 and later**

:::CodeblockTabs
Request

```graphql
1   query FindHero {
2     heroes{
3       count,
4       edges{
5         node{
6           name
7           createdAt
8           updatedAt
9         }
10      }
11    }
12  }
```

Response

```graphql
1   {
2     "data": {
3       "heroes": {
4         "count": 3,
5         "edges": [
6           {
7             "node": {
8               "name": "Luke Skywalker",
9               "createdAt": "2020-02-06T13:02:33.652Z",
10              "updatedAt": "2020-02-06T13:02:33.652Z"
11            }
12          },
13          {
14            "node": {
15              "name": "R2-D2",
16              "createdAt": "2020-02-06T13:13:26.678Z",
17              "updatedAt": "2020-02-06T13:13:26.678Z"
18            }
19          }
20        ]
21      }
22    }
23  }
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse 3.9.0**

**Class query:**

:::CodeblockTabs
Request

```graphql
1   query FindHero {
2     heroes{
3       count,
4       results {
5         id,
6         name,
7         createdAt,
8         updatedAt
9       }
10    }
11  }
```

Response

```graphql
1   {
2     "data": {
3       "heroes": {
4         "count": 2,
5         "results": [
6           {
7             "id": "CkhurmMjZW",
8             "name": "Luke Skywalker",
9             "createdAt": "2019-11-04T12:37:22.462Z",
10            "updatedAt": "2019-11-04T12:37:22.462Z"
11          },
12          {
13            "id": "n5GrpEi0iL",
14            "name": "R2-D2",
15            "createdAt": "2019-11-04T12:45:00.882Z",
16            "updatedAt": "2019-11-04T12:45:00.882Z"
17          }
18        ]
19      }
20    }
21  }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.8.0**

**Class query:**

:::CodeblockTabs
Request

```graphql
1   query FindHero {
2     heroes{
3       count,
4       results {
5         objectId,
6         name,
7         createdAt,
8         updatedAt
9       }
10    }
11  }
```

Response

```graphql
1   "data": {
2       "objects": {
3         "findHero": {
4           "count": 2,
5           "results": [
6             {
7               "objectId": "ffyOBOTk85",
8               "name": "Luke Skywalker",
9               "createdAt": "2019-07-15T01:25:20.875Z",
10              "updatedAt": "2019-07-15T01:25:20.875Z"
11            },
12            {
13              "objectId": "jJH0aQQjfs",
14              "name": "R2-D2",
15              "createdAt": "2019-07-15T02:22:04.982Z",
16              "updatedAt": "2019-07-15T02:22:04.982Z"
17            }
18          ]
19        }
20      }
21    }
22  }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.7.2**

**Generic query:**

:::CodeblockTabs
Request

```graphql
1   query FindObject {
2     objects {
3       find(className: "Hero") {
4         count,
5         results      
6       }
7     }
8   }
```

Response

```graphql
1   {
2     "data": {
3       "objects": {
4         "find": {
5           "count": 2,
6           "results": [
7             {
8               "objectId": "ffyOBOTk85",
9               "name": "Luke Skywalker",
10              "createdAt": "2019-07-15T01:25:20.875Z",
11             "updatedAt": "2019-07-15T01:25:20.875Z"
12            },
13            {
14              "objectId": "jJH0aQQjfs",
15              "name": "R2-D2",
16              "createdAt": "2019-07-15T02:22:04.982Z",
17              "updatedAt": "2019-07-15T02:22:04.982Z"
18            }
19          ]
20        }
21      }
22    }
23  }
```
:::

**Class query:**

:::CodeblockTabs
Request

```graphql
1   query FindHero {
2     objects {
3       findHero {
4         count,
5         results {
6           objectId,
7           name,
8           createdAt,
9           updatedAt
10        }
11      }
12    }
13  }
```

Response

```graphql
1   "data": {
2       "objects": {
3         "findHero": {
4           "count": 2,
5           "results": [
6             {
7               "objectId": "ffyOBOTk85",
8               "name": "Luke Skywalker",
9               "createdAt": "2019-07-15T01:25:20.875Z",
10              "updatedAt": "2019-07-15T01:25:20.875Z"
11            },
12            {
13              "objectId": "jJH0aQQjfs",
14              "name": "R2-D2",
15              "createdAt": "2019-07-15T02:22:04.982Z",
16              "updatedAt": "2019-07-15T02:22:04.982Z"
17            }
18          ]
19        }
20      }
21    }
22  }
```
:::
::::


[title] Quickstart
[path] Get started/

Back4app is a low-code backend platform that simplifies building modern applications. This guide will help you quickly set up Back4app and start saving data.

## Back4app Main Features

- Database (real-time capability)
- Cloud Code Functions
- APIs (GraphQL and REST)
- File Storage
- Authentication
- Web Deployment
- Push Notifications

## 5-minutes quick start

After creating your Back4app account and first app, go to your App Dashboard and get your App Keys under App Settings -> Security & Keys(check the image below). Note that you will always need two keys to connect with Back4app, the Application ID, and another key according to the SDK you will use.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pGTX6D2rGm8Ohe9w99cin_app-settings.png)

### 1. Install and Configure the Parse SDK

To integrate the Parse SDK, follow these general steps:

**Install the Parse SDK:**

- **JavaScript / Node.js**: `npm install parse --save`
- **React Native**: `npm install parse @react-native-async-storage/async-storage --save`, then run `cd ios && pod install`
- **Flutter**: Add `parse_server_sdk_flutter` to `pubspec.yaml`
- **Android (Java):** Add your app's `build.gradle` inside `dependencies{}`:&#x20;

:::CodeblockTabs
build.gradle

```java
implementation "com.github.parse-community.Parse-SDK-Android:parse:latest_version"
```
:::

- **iOS (Swift)**: Install CocoaPods `(sudo gem install cocoapods)` and add Parse to your `Podfile`

**Initialize the Parse SDK** with your Application ID and the appropriate key:

```javascript
Parse.initialize('YOUR_APPLICATION_ID', 'YOUR_JAVASCRIPT_KEY');
Parse.serverURL = 'https://parseapi.back4app.com/';
```

*The example above is for JavaScript/Node.js. Adjust accordingly for your platform.*

### 2. Save Data on Back4app

Create and save a sample object to confirm that the SDK is properly set up. Use the example below and adapt it to your programming language:

```javascript
async function saveNewPlayer() {
    const player = new Parse.Object('Player');
    player.set('name', 'Alex');
    player.set('yearOfBirth', 1997);
    player.set('emailContact', 'alex@email.com');
    player.set('attributes', ['fast', 'good endurance']);
    
    try {
        const result = await player.save();
        console.log('New object created with ID:', result.id);
    } catch (error) {
        console.error('Failed to save object:', error.message);
    }
}
```

After saving, you can verify the data in the **App Dashboard** on Back4app.

### 3. Platform-Specific Setup

**JavaScript - React / Angular**

- **Install** the SDK: `npm install parse --save`
- **Initialize** with your App Keys and Back4app server URL.

**Flutter**

1. Add `parse_server_sdk_flutter` in your `pubspec.yaml:`

:::CodeblockTabs
pubspec.yaml

```yaml
dependencies:
  parse_server_sdk_flutter: ^latest_version
```
:::

&#x20;   2\.  **Initialize** Parse in `main.dart:`

```dart
await Parse().initialize('YOUR_APPLICATION_ID', 'https://parseapi.back4app.com', clientKey: 'YOUR_CLIENT_KEY');
```

**Android (Kotlin/Java)**

- **Include** the SDK in `build.gradle` and configure network permissions in `AndroidManifest.xml`
- **Initialize** in `App.kt:`

```kotlin
Parse.initialize(new Parse.Configuration.Builder(this)
    .applicationId(getString(R.string.back4app_app_id))
    .clientKey(getString(R.string.back4app_client_key))
    .server(getString(R.string.back4app_server_url))
    .build()
);
```

**iOS (Swift)**

1. Add `ParseSwift` to your project using CocoaPods.
2. Initialize Parse in `AppDelegate.swift:`

:::CodeblockTabs
AppDelegate.swift

```swift
let configuration = ParseClientConfiguration {
    $0.applicationId = "YOUR_APPLICATION_ID"
    $0.clientKey = "YOUR_CLIENT_KEY"
    $0.server = "https://parseapi.back4app.com"
}
Parse.initialize(with: configuration)
```
:::

**PHP**

1. **Install the SDK** with Composer by creating a composer.json with:

:::CodeblockTabs
composer.json

```json
{
    "require": {
        "parse/php-sdk" : "1.6.*"
    }
}
```
:::

Then, run `composer.install`.

&#x20;   2\. **Initialize** Parse in your PHP script:

```php
require 'vendor/autoload.php';
ParseClient::initialize('YOUR_APP_ID', 'YOUR_REST_KEY', 'YOUR_MASTER_KEY');
ParseClient::setServerURL('https://parseapi.back4app.com', '/');
```

&#x20;   3\. **Save Data** in PHP:

:::CodeblockTabs
composer.json

```php
$player = new ParseObject("Player");
$player->set("name", "Alex");
$player->set("yearOfBirth", 1997);
$player->set("emailContact", "alex@email.com");
$player->setArray("attributes", ["fast", "good endurance"]);
$player->save();
```
:::

**.NET (C#)**

1. **Install** Parse SDK through NuGet Package Manager in Visual Studio.
2. **Initialize** Parse in your application:

```csharp
ParseClient.Initialize(new ParseClient.Configuration {
    ApplicationId = "YOUR_APP_ID",
    Server = "https://parseapi.back4app.com",
    ClientKey = "YOUR_CLIENT_KEY"
});
```

&#x20;   3\. **Save Data** in C#:

```csharp
var player = new ParseObject("Player")
{
    ["name"] = "Alex",
    ["yearOfBirth"] = 1997,
    ["emailContact"] = "alex@email.com",
    ["attributes"] = new List<object> { "fast", "good endurance" }
};
await player.SaveAsync();
```

**REST API**

1. **Save Data** via REST by sending a POST request:

```bash
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Alex", "yearOfBirth":1997, "emailContact":"alex@email.com", "attributes":["fast", "good endurance"]}' \
https://parseapi.back4app.com/classes/Player
```

### 4. Additional Resources and Examples

There are many example apps and starter projects to get going

- <a href="https://github.com/templates-back4app/react-js-slack-clone" target="_blank">ReactJS Slack Clone</a> - A React template using real-time, relational queries and authentication.
- <a href="https://github.com/templates-back4app/flutter-user-signup" target="_blank">Flutter User Login/SignUp</a> - A user sign-up/login flutter template using Parse.User class.
- <a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">React Native Associations</a>  - A template on React Native digging deeper into associations and relational queries using Pointers andRelations.
- <a href="https://www.back4app.com/docs/flutter/parse-sdk/flutter-save-file" target="_blank">Flutter File Storage</a>  - Saving files from a Flutter app.
- <a href="https://github.com/templates-back4app/AndroidGeoLocationKotlin" target="_blank">GeoPointers on Kotlin-Android</a>  - Exploring GeoPointers in Android.
- <a href="https://github.com/templates-back4app/ios-template-todo-list" target="_blank">ToDo List example is Swift-iOS</a>  - A ToDo List example in Swift.

Find more examples in <a href="https://github.com/templates-back4app" target="_blank">Back4app Templates.</a>

## &#x20;What to do Next?

After completing the quick start, we encourage you to explore Back4app's key features through the guides below. You'll learn how to store and query relational data, implement cloud functions for backend logic, use real-time subscriptions to keep users updated, manage file storage, send push notifications, and set up authentication. Select the technology that best fits your project and enjoy the journey!

::::LinkArray
:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pEkmfq5PdkxC3lmfRzYsE_1.png"}
&#x20;      ** **<a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">**React Native**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tPONkoW1aPPhiAdTvbvrx_sem-nome-rectangle-sticker-landscape.png"}
<a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">**Flutter**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vqJJw7ljIhPJ1Yfz7mD9n_3.png"}
<a href="https://www.back4app.com/docs/android/android-project-with-source-code-download" target="_blank">Android </a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rzkB1YU-Feimt_SWfZUuC_4.png"}
<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank">iOS</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GsQE6ru-jj2rRbUt7P3Tr_5.png"}
****<a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">**Javascript**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/35IqTes9QhdmijlReYBYZ_6.png"}
<a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">GraphQL</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/D4RecbrtvjfbKUfb4yRbP_7.png"}
<a href="https://www.back4app.com/docs/js-framework/ionic/ionic-template" target="_blank">Ionic</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yOia2J8LzE-r1WSHQ7ZSQ_8.png"}
<a href="https://www.back4app.com/docs/xamarin/xamarin-templates" target="_blank">Xamarin</a>
:::
::::


[title] SignIn with Facebook
[path] React Native/Parse SDK (REST)/Users/

# React Native Facebook login

## Introduction

In the last tutorials, you built a User login/logout feature to your App using the Parse.User class. Now you will learn how to use Facebook FBSDK Login to retrieve user data from Facebook and log in, sign up or link existent users with it. You will also install and configure the react-native-fbsdk lib to achieve that.

The Parse.User.linkWith method is responsible for signing up and logging in users using any third-party authentication method, as long as you pass the right parameters requested by each different provider. After linking the user data to a new or existent Parse.User, Parse will store a valid user session in your device. Future calls to methods like currentAsync will successfully retrieve your User data, just like with regular logins.

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## Goal

To build a User LogIn feature using Facebook FBSDK Login on Parse for a React Native App.

## 1 - Installing dependencies

The most popular way to add Facebook login on React Native is using react-native-fbsdk to handle it. Since this library configuration depends on your development environment, target platform, and preferences, set it up following the [official docs](https://github.com/facebook/react-native-fbsdk).

:::hint{type="info"}
**Note:** If you are developing for iOS, make sure that your project has support for Swift files, containing a Bridging Header. Also, pay close attention to where you add the Facebook App IDs inside yourinfo.plist file and if your Pods files are correctly generated.
:::

## 2 - Using FBSDK Login with Parse

Let’s now create a new method inside the UserLogIn component calling Facebook FBSDK authentication modal with LoginManager.logInWithPermissions, requesting permission only to access the user email. If the user successfully signs in with Facebook, you can then call AccessToken.getCurrentAccessToken to retrieve the user access token from Facebook. After that, you still need to get the user id and email using a GraphRequest through FBSDK GraphRequestManager.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogInFacebook = async function () {
2	  try {
3	    // Login using the Facebook login dialog asking form email permission
4	    return await LoginManager.logInWithPermissions(['email']).then(
5	      (loginResult) => {
6	        if (loginResult.isCancelled) {
7	          console.log('Login cancelled');
8	          return false;
9	        } else {
10	          // Retrieve access token from FBSDK to be able to linkWith Parse
11	          AccessToken.getCurrentAccessToken().then((data) => {
12	            const facebookAccessToken = data.accessToken;
13	            // Callback that will be called after FBSDK successfuly retrieves user email and id from FB
14	            const responseEmailCallback = async (
15	              error,
16	              emailResult,
17	            ) => {
18	              if (error) {
19	                console.log('Error fetching data: ' + error.toString());
20	              } else {
21	                // Format authData to provide correctly for Facebook linkWith on Parse
22	                const facebookId = emailResult.id;
23	                const facebookEmail = emailResult.email;
24	                const authData = {
25	                  id: facebookId,
26	                  access_token: facebookAccessToken,
27	                };
28	                // Log in or sign up on Parse using this Facebook credentials
29	                let userToLogin = new Parse.User();
30	                // Set username and email to match provider email
31	                userToLogin.set('username', facebookEmail);
32	                userToLogin.set('email', facebookEmail);
33	                return await userToLogin
34	                  .linkWith('facebook', {
35	                    authData: authData,
36	                  })
37	                  .then(async (loggedInUser) => {
38	                    // logIn returns the corresponding ParseUser object
39	                    Alert.alert(
40	                      'Success!',
41	                      `User ${loggedInUser.get(
42	                        'username',
43	                      )} has successfully signed in!`,
44	                    );
45	                    // To verify that this is in fact the current user, currentAsync can be used
46	                    const currentUser = await Parse.User.currentAsync();
47	                    console.log(loggedInUser === currentUser);
48	                    // Navigation.navigate takes the user to the screen named after the one
49	                    // passed as parameter
50	                    navigation.navigate('Home');
51	                    return true;
52	                  })
53	                  .catch(async (error) => {
54	                    // Error can be caused by wrong parameters or lack of Internet connection
55	                    Alert.alert('Error!', error.message);
56	                    return false;
57	                  });
58	              }
59	            };
60	
61	            // Formats a FBSDK GraphRequest to retrieve user email and id
62	            const emailRequest = new GraphRequest(
63	              '/me',
64	              {
65	                accessToken: facebookAccessToken,
66	                parameters: {
67	                  fields: {
68	                    string: 'email',
69	                  },
70	                },
71	              },
72	              responseEmailCallback,
73	            );
74	
75	            // Start the graph request, which will call the callback after finished
76	            new GraphRequestManager().addRequest(emailRequest).start();
77	
78	            return true;
79	          });
80	        }
81	      },
82	      (error) => {
83	        console.log('Login fail with error: ' + error);
84	        return false;
85	      },
86	    );
87	  } catch (error) {
88	    Alert.alert('Error!', error.code);
89	    return false;
90	  }
91	};
```

```typescript
1	const doUserLogInFacebook = async function (): Promise<boolean> {
2	  try {
3	    // Login using the Facebook login dialog asking form email permission
4	    return await LoginManager.logInWithPermissions(['email']).then(
5	      (loginResult: object) => {
6	        if (loginResult.isCancelled) {
7	          console.log('Login cancelled');
8	          return false;
9	        } else {
10	          // Retrieve access token from FBSDK to be able to linkWith Parse
11	          AccessToken.getCurrentAccessToken().then((data: object) => {
12	            const facebookAccessToken = data.accessToken;
13	            // Callback that will be called after FBSDK successfuly retrieves user email and id from FB
14	            const responseEmailCallback = async (
15	              error: string,
16	              emailResult: object,
17	            ) => {
18	              if (error) {
19	                console.log('Error fetching data: ' + error.toString());
20	              } else {
21	                // Format authData to provide correctly for Facebook linkWith on Parse
22	                const facebookId: string = emailResult.id;
23	                const facebookEmail: string = emailResult.email;
24	                const authData: object = {
25	                  id: facebookId,
26	                  access_token: facebookAccessToken,
27	                };
28	                // Log in or sign up on Parse using this Facebook credentials
29	                let userToLogin: Parse.User = new Parse.User();
30	                // Set username and email to match provider email
31	                userToLogin.set('username', facebookEmail);
32	                userToLogin.set('email', facebookEmail);
33	                return await userToLogin
34	                  .linkWith('facebook', {
35	                    authData: authData,
36	                  })
37	                  .then(async (loggedInUser: Parse.User) => {
38	                    // logIn returns the corresponding ParseUser object
39	                    Alert.alert(
40	                      'Success!',
41	                      `User ${loggedInUser.get(
42	                        'username',
43	                      )} has successfully signed in!`,
44	                    );
45	                    // To verify that this is in fact the current user, currentAsync can be used
46	                    const currentUser: Parse.User = await Parse.User.currentAsync();
47	                    console.log(loggedInUser === currentUser);
48	                    // Navigation.navigate takes the user to the screen named after the one
49	                    // passed as parameter
50	                    navigation.navigate('Home');
51	                    return true;
52	                  })
53	                  .catch(async (error: object) => {
54	                    // Error can be caused by wrong parameters or lack of Internet connection
55	                    Alert.alert('Error!', error.message);
56	                    return false;
57	                  });
58	              }
59	            };
60	
61	            // Formats a FBSDK GraphRequest to retrieve user email and id
62	            const emailRequest = new GraphRequest(
63	              '/me',
64	              {
65	                accessToken: facebookAccessToken,
66	                parameters: {
67	                  fields: {
68	                    string: 'email',
69	                  },
70	                },
71	              },
72	              responseEmailCallback,
73	            );
74	
75	            // Start the graph request, which will call the callback after finished
76	            new GraphRequestManager().addRequest(emailRequest).start();
77	
78	            return true;
79	          });
80	        }
81	      },
82	      (error: string) => {
83	        console.log('Login fail with error: ' + error);
84	        return false;
85	      },
86	    );
87	  } catch (error: object) {
88	    Alert.alert('Error!', error.code);
89	    return false;
90	  }
91	};
```
:::

Note that after the GraphRequest succeeds, this function uses Parse.User.linkWith on a new Parse.User object to register a new user or log in a previous one with these credentials, passing his Facebook authentication data accordingly.

Add this function to your UserSignIn component and assign it to your Facebook button onPress parameter. Go ahead and test your new function, you will see that the user will be redirected to your home screen after successfully signing in.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Iro2fj5ZeoN7wXoBDLaHC_image.png" signedSrc size="50" width="347" height="729" position="center" caption}

## 3 - Verifying user sign in and session creation

To make sure that the Facebook sign-in worked, you can look at your Parse dashboard and see your new User (if your Facebook authentication data didn’t belong to another user), containing the Facebook authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fRO9633LiJUBYLwmYigzD_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to that User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uyh9t2l7jBOcicRezlAuc_image.png)

## 4 - Linking an existing User to FBSDK Login

Another linkWith possible use is to link an existing user with another auth provider, in this case, Facebook. Add this function that calls linkWith the same way you did in UserLogIn to your HelloUser component or directly to your home screen. The only difference here is that instead of calling the method from an empty Parse.User, you will use it from the logged-in user object.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLinkFacebook = async function () {
2	  try {
3	    // Login using the Facebook login dialog asking form email permission
4	    return await LoginManager.logInWithPermissions(['email']).then(
5	      (loginResult) => {
6	        if (loginResult.isCancelled) {
7	          console.log('Login cancelled');
8	          return false;
9	        } else {
10	          // Retrieve access token from FBSDK to be able to linkWith Parse
11	          AccessToken.getCurrentAccessToken().then((data) => {
12	            const facebookAccessToken = data.accessToken;
13	            // Callback that will be called after FBSDK successfuly retrieves user email and id from FB
14	            const responseEmailCallback = async (
15	              error,
16	              emailResult,
17	            ) => {
18	              if (error) {
19	                console.log('Error fetching data: ' + error.toString());
20	              } else {
21	                // Format authData to provide correctly for Facebook linkWith on Parse
22	                const facebookId = emailResult.id;
23	                const authData = {
24	                  id: facebookId,
25	                  access_token: facebookAccessToken,
26	                };
27	                let currentUser = await Parse.User.currentAsync();
28	                return await currentUser
29	                  .linkWith('facebook', {
30	                    authData: authData,
31	                  })
32	                  .then(async (loggedInUser) => {
33	                    // logIn returns the corresponding ParseUser object
34	                    Alert.alert(
35	                      'Success!',
36	                      `User ${loggedInUser.get(
37	                        'username',
38	                      )} has successfully linked his Facebook account!`,
39	                    );
40	                    // To verify that this is in fact the current user, currentAsync can be used
41	                    currentUser = await Parse.User.currentAsync();
42	                    console.log(loggedInUser === currentUser);
43	                    return true;
44	                  })
45	                  .catch(async (linkWithError) => {
46	                    // Error can be caused by wrong parameters or lack of Internet connection
47	                    Alert.alert('Error!', linkWithError.message);
48	                    return false;
49	                  });
50	              }
51	            };
52	
53	            // Formats a FBSDK GraphRequest to retrieve user email and id
54	            const emailRequest = new GraphRequest(
55	              '/me',
56	              {
57	                accessToken: facebookAccessToken,
58	                parameters: {
59	                  fields: {
60	                    string: 'email',
61	                  },
62	                },
63	              },
64	              responseEmailCallback,
65	            );
66	
67	            // Start the graph request, which will call the callback after finished
68	            new GraphRequestManager().addRequest(emailRequest).start();
69	
70	            return true;
71	          });
72	        }
73	      },
74	      (error) => {
75	        console.log('Login fail with error: ' + error);
76	        return false;
77	      },
78	    );
79	  } catch (error) {
80	    Alert.alert('Error!', error.code);
81	    return false;
82	  }
83	};
```

```typescript
1	const doUserLinkFacebook = async function (): Promise<boolean> {
2	  try {
3	    // Login using the Facebook login dialog asking form email permission
4	    return await LoginManager.logInWithPermissions(['email']).then(
5	      (loginResult: object) => {
6	        if (loginResult.isCancelled) {
7	          console.log('Login cancelled');
8	          return false;
9	        } else {
10	          // Retrieve access token from FBSDK to be able to linkWith Parse
11	          AccessToken.getCurrentAccessToken().then((data: object) => {
12	            const facebookAccessToken = data.accessToken;
13	            // Callback that will be called after FBSDK successfuly retrieves user email and id from FB
14	            const responseEmailCallback = async (
15	              error: string,
16	              emailResult: object,
17	            ) => {
18	              if (error) {
19	                console.log('Error fetching data: ' + error.toString());
20	              } else {
21	                // Format authData to provide correctly for Facebook linkWith on Parse
22	                const facebookId: string = emailResult.id;
23	                const authData: object = {
24	                  id: facebookId,
25	                  access_token: facebookAccessToken,
26	                };
27	                let currentUser: Parse.User = await Parse.User.currentAsync();
28	                return await currentUser
29	                  .linkWith('facebook', {
30	                    authData: authData,
31	                  })
32	                  .then(async (loggedInUser: Parse.User) => {
33	                    // logIn returns the corresponding ParseUser object
34	                    Alert.alert(
35	                      'Success!',
36	                      `User ${loggedInUser.get(
37	                        'username',
38	                      )} has successfully linked his Facebook account!`,
39	                    );
40	                    // To verify that this is in fact the current user, currentAsync can be used
41	                    currentUser = await Parse.User.currentAsync();
42	                    console.log(loggedInUser === currentUser);
43	                    return true;
44	                  })
45	                  .catch(async (linkWithError: object) => {
46	                    // Error can be caused by wrong parameters or lack of Internet connection
47	                    Alert.alert('Error!', linkWithError.message);
48	                    return false;
49	                  });
50	              }
51	            };
52	
53	            // Formats a FBSDK GraphRequest to retrieve user email and id
54	            const emailRequest = new GraphRequest(
55	              '/me',
56	              {
57	                accessToken: facebookAccessToken,
58	                parameters: {
59	                  fields: {
60	                    string: 'email',
61	                  },
62	                },
63	              },
64	              responseEmailCallback,
65	            );
66	
67	            // Start the graph request, which will call the callback after finished
68	            new GraphRequestManager().addRequest(emailRequest).start();
69	
70	            return true;
71	          });
72	        }
73	      },
74	      (error: string) => {
75	        console.log('Login fail with error: ' + error);
76	        return false;
77	      },
78	    );
79	  } catch (error: object) {
80	    Alert.alert('Error!', error.code);
81	    return false;
82	  }
83	};
```
:::

Assign this function to a Facebook button onPress parameter on your home screen. Test your new function, noting that the Parse.User object authData value will be updated with the new auth provider data. Verify if the user has indeed updated in your Parse server dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/boZiySaGlvpvsSo0AIOfN_image.png)

## Conclusion

At the end of this guide, you learned how to log in or sign up Parse users on React Native using Facebook FBSDK Login with react-native-fbsdk. In the next guide, we will show you how to perform useful user queries.

[title] App Settings
[path] Parse Dashboard/

# App Settings

## Introduction

In this tutorial, you will learn about the App Settings section.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read, however, you must be the owner to edit this page.**
:::

## Get started

In this guide, you are going to learn more about the main features available in this section. Please, navigate through the tabs according to your needs:

::::ExpandableHeading
### General

This page shows the general app settings like app name, collaborators and actions that will affect your app, please check the details:

- **App Information**

To edit your app name via App settings, you must put the new data in App name field and click on Save button.

- **Collaborators**

In this section, you can add new people to contribute to your app. You will be able to enter the collaborator email and click on the Save button.

Don’t worry, if your collaborator doesn’t have a created account yet, you will see the following error message:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/UDezV-pJCuBZ1IEIUaRR4_image.png)

You can click on the Send Invite button to send an invitation to their email!

- **App Management**

Clean up app

This button can be used to delete the files that aren’t associated with any object and they’re counting space in File Storage.

:::hint{type="danger"}
In case that you are using an Array of files, we highly recommended that you don’t use this feature.
:::

For security reasons, to delete completely your files, you must perform the following steps:

1. First of all, you’ll need to check what is the class and object that you require to delete the file.
2. You’ll need to add a query to update the field from the image with a ‘null’ at column space. By following this step, you delete the reference to the object and file!
3. Use the button Clean Up Files at *Dashboard* > *App Settings* > *Clean Up Files*, to delete files that are not referenced to any objects.

With the steps above, you have verified how it is possible to delete these files that have no relation.

Clean up log

By clicking on this button, this action will purge your System Log.
::::

:::ExpandableHeading
### Security & Keys

In this page, you will find the unique identifiers to access this app.

Here you can find the same keys as shown in Core Settings > Settings.


:::




[title] Environment Variables
[path] Platform/

# Setting up Environment Variables on Parse

## Introduction

In this guide, you will learn how to configure Environment variables.

## Goal

- Learn how to set and use Environment Variables page.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read or edit this page.**
:::

## Environment Variables

They are variables that describes your environment. While constants cannot be reassigned during the execution, an *Environment Variables* may change.

One of the most popular *Environment Variable* is **PATH** which contains path to the folders that might contain the executables.

In a few words, an environment variable consists of a name / value pair and any number can be created and available for reference at any given time.

Here at Back4App, this block looks like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YCzjuWr4srI06MPUbYNzj_image.png" signedSrc size="40" width="238" height="303" position="center" caption}

### **How to use it?**

In this section, we are going to show you how to set an *Environment Variable* and use it on our Cloud Code.

The first step, what you need to is to access Server Settings > Environment Variables > Settings and set *environment variables* using KEY=VALUE format. Please, put each variable in one line. Something like the example below:

> 1    NODE_ENV=PRODUCTION

To use it on your Cloud Code, you can test it following the guide below:

:::::Tabs
::::Tab{title="Parse Server 3.X"}
:::CodeblockTabs
main.js

```javascript
1   Parse.Cloud.define("getEnvironmentVariable", (request) => {
2     const NODE_ENV = process.env.NODE_ENV;
3  
4     return `NODE_ENV = ${NODE_ENV}`
5   });
```
:::
::::

::::Tab{title="Parse Server 2.X"}
:::CodeblockTabs
main.js

```javascript
1   Parse.Cloud.define("getEnvironmentVariable", function(request, response) {
2      var NODE_ENV = process.env.NODE_ENV;
3
4      response.success(NODE_ENV);
5   });
```
:::
::::
:::::

To test it, you can use API Console to call this Cloud Code function and test it.

[title] Prepare Environment
[path] React Native/Relay (GraphQL)/

# Relay Setup

## Introduction

In our previous guide, we’ve learned how to get the schema file, save it, and paste it on our React Native application. In this guide, we are going to install Relay and prepare our environment to start developing a React Native Application.

## Goal

In order to set up Relay we will need first to install Relay on our React Native application and then prepare the Relay environment.

## Prerequisites

:::hint{type="info"}
- We recommend a basic understanding of the following topics: <a href="https://relay.dev/" target="_blank">Relay Modern</a>, <a href="https://babeljs.io/" target="_blank">Babel JS</a>, JavaScript (ECS5 and ECS6), <a href="https://github.com/graphql/graphql-js" target="_blank">GraphQL.js</a> README
- A React Native basic project running on your local environment.
- A schema.graphql file download on your React Native project.
:::

In the following steps you’ll find a basic Relay installation focused on a React Native Application. If you want to deep dive into this topic please visit the <a href="https://relay.dev/docs/en/installation-and-setup#docsNav" target="_blank">official documentation</a>.

## 1 - Installing React Relay Official API

Let’s start installing the official library *react-relay*. This library is the Relay Core Team official API and has everything to build the data fragments.

> 1   yarn add react-relay@10.0.0

:::hint{type="success"}
Important installing Relay Modern for this tutorial with 10.0.0 as version. From verion 11.0.0 the approach to use the react-relay is different because of the new hooks.
:::

## 2 - Relay Config

The Relay Config has the info necessary for the Relay Compiler. Inside it, we will specify where Relay Compiler can find the schema.file, which folders should be looked up, and other configs. Let’s first install the relay-config package:

> 1   yarn add --dev relay-config

:::hint{type="success"}
NOTE: This tutorial uses yarn as a package client, but you can use npm anyway.
:::

Now let’s create the Relay Config file where Relay will find the schema path.

- **Create a new file inside the root of the application.**
- **Name it as relay.config.js.**

Open the file and save it with the information below:

> 1   module.exports = {
> 2     schema: './data/schema.graphql',
> 3     src: './',
> 4   };

## 3 - Relay Babel Plugin

To convert the GraphQL artifacts at runtime, we need to install a babel plugin.

> 1   yarn add --dev babel-plugin-relay graphql

Now, inside your babel config file, you must add on plugins array the Relay Babel Plugin. The final babel.config.js will look like this:

> 1    {
> 2     "plugins": [
> 3       "relay"
> 4     ]
> 5    }

In expo projects follow the same approach adding the plugins array inside of the babel.config.js right after the presets array. The final result should look:

```javascript
1	module.exports = function (api) {
2	  api.cache(true);
3	  return {
4	    presets: [
5	      "babel-preset-expo",
6	    ],
7	    plugins: [
8	      'relay'
9	    ]
10	  };
11	};
```

:::hint{type="success"}
The relay plugin must run before other plugins for a correct transformation of GraphQL artifacts. Check the plugin babel docs to know more.
:::

## 4 - Relay Compiler

Since our first documentation, we have been explaining about the Relay Compiler. For our application to compile our data fragments, we will install it now.

> 1   yarn add 
>
> **--**
>
> dev relay
>
> **-**
>
> compiler

Let’s open our package.json and configure a new script command to run the relay compiler.

> 1    "
>
> relay
>
> ": "
>
> relay-compiler
>
>  \--
>
> watchman false
>
> "

Watchman is responsible for configuring whether the Relay Compiler must be looking for any changes on relay fragments. If true it will rerun at each change. If false it will run after you run the yarn relay by itself.

The package.json file should look like this:

```json
1	  "scripts": {
2	    "android": "react-native run-android",
3	    "ios": "react-native run-ios",
4	    "start": "react-native start",
5	    "test": "jest",
6	    "lint": "eslint .",
7	    "relay": "relay-compiler --watchman false"
8	  },
```

## 5 - Run yarn relay

Finally, with installation steps done, we can run the yarn relay command on the root of the application:

> 1   yarn relay

Since we don’t build any data fragment, the Relay Compiler returns any file changed:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TsgibxDpL2xrdQWVEDSuq_image.png" signedSrc size="80" width="385" height="101" position="center" caption}

Nice, your application has the Relay already installed and configured. Now, let’s implement our Environment to start making requests to the Back4App server.

## 6 - Preparing the fetch environment

The Relay Environment defines how the Network Layer will handle the requests and how the *Relay Store* must work. The Store controls our data on the frontend by updating it only when changed and caching it. A simple Environment will need at least the Network Layer and the Store.

**Network Layer**

The <a href="https://relay.dev/docs/en/network-layer" target="_blank">Network Layer</a> is easy to build. It will handle each request of our application, making queries, mutations, and subscriptions (if your application supports). Consuming it, the Relay will know-how to prepare the calls to access the application server.

**Relay Store**

The <a href="https://relay.dev/docs/en/relay-store.html" target="_blank">Store</a> is responsible for update the data of our application on the client-side. Each request can have an updater function. Inside the updater function, you can use a list of helper functions to update your data with the Store Interface.

In order to prepare the Relay environment we need to create an environment config file. The file will be read by the Query Renderer every time a query is performed. Let’s prepare this file.

### **6.1 - Create the Environment File**

We will follow the principle of design from Relay Library, the collocation concept. To start, let’s create a folder on the root of the application and name it relay. Inside it, create a file and name it environment.js.

### **6.2 - Configure the Environment**

After that, import from relay-runtime all we need:

```javascript
1	import {
2	    Environment,
3	    Network,
4	    RecordSource,
5	    Store,
6	} from 'relay-runtime';
```

### **6.3 - Configure the Network Layer**

The network layer needs a function to fetch the data from the server. First of all, let’s create a fetch function responsible to perform a POST request:

```javascript
1	const fetchQuery = async (request, variables) => {
2	  const body = JSON.stringify({
3	    query: request.text,
4	    variables,
5	  });
6	
7	  const headers = {
8	    Accept: 'application/json',
9	    'Content-type': 'application/json',
10	    'X-Parse-Application-Id': 'X-Parse-Application-Id',
11	    'X-Parse-Client-Key': 'X-Parse-Client-Key',
12	  };
13	
14	  try {
15	      const response = await fetch(`https://parseapi.back4app.com/graphql`, {
16	        method: 'POST',
17	        headers,
18	        body,
19	      });
20	  
21	      const data = await response.json();
22	  
23	      if (data.errors) {
24	        throw data.errors;
25	      }
26	  
27	      return data;
28	    } catch (err) {
29	      console.log('err on fetch graphql', err);
30	  
31	      throw err;
32	    }
33	};
```

:::hint{type="success"}
We wrapper the request for the backend by a try-catch. Having an error will be thrown and the application will handle it. Otherwise will follow the normal behavior and return the data.
:::

On Network Layer it’s also where you configure your application-server connection. At Back4App we use two main keys: Application Id and Client Key. The keys must be informed on the headers as long as the server URL. To get this information go to your App, and click on API Console -> GraphQL menu.

With the GraphQL console open, you will see the URL on the top, and on the bottom the application keys necessary. Replace with your info the fetch function.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jqLTXI5I-dNrDTnR6eJ0v_image.png)

:::hint{type="success"}
Remember to do not expose the master key
:::

### **6.4 - Export the Environment**

The relay-runtime provides the functions necessary to consume the Network Layer and creates the Store. Finally, let’s combine them into a new Environment and export to our application. Use the code below on your Environment file.

```javascript
1	const environment = new Environment({
2	  network: Network.create(fetchQuery),
3	  store: new Store(new RecordSource()),
4	});
5	
6	export default environment;
```

Environment final file will look like this:

```javascript
1	import {
2	  Environment,
3	  Network,
4	  RecordSource,
5	  Store,
6	} from 'relay-runtime';
7	
8	const fetchQuery = async (request, variables) => {
9	  const body = JSON.stringify({
10	    query: request.text,
11	    variables,
12	  });
13	
14	  const headers = {
15	    Accept: 'application/json',
16	    'Content-type': 'application/json',
17	    'X-Parse-Application-Id': 'X-Parse-Application-Id',
18	    'X-Parse-Client-Key': 'X-Parse-Client-Key',
19	  };
20	
21	  try {
22	        const response = await fetch(`https://parseapi.back4app.com/graphql`, {
23	          method: 'POST',
24	          headers,
25	          body,
26	        });
27	    
28	        const data = await response.json();
29	    
30	        if (data.errors) {
31	          throw data.errors;
32	        }
33	    
34	        return data;
35	      } catch (err) {
36	        console.log('err on fetch graphql', err);
37	    
38	        throw err;
39	      }
40	};
41	
42	const environment = new Environment({
43	  network: Network.create(fetchQuery),
44	  store: new Store(new RecordSource()),
45	});
46	
47	export default environment;
```

## Conclusion

Awesome. Now with the Relay and Relay environment installed and configured it’s time to start to consume the Back4App GraphQL API. So, the next step is to create our first Query Renderer and communicate it to our server.

[title] Data types
[path] React Native/Parse SDK (REST)/Data objects/

# Parse Data Types in a React Native component

## Introduction

In the heart of Parse Core feature is the data objects management. Parse allows you to store and query its data straightforwardly using its SDKs or APIs(REST or GraphQL). All the data object features are built using the Parse.Object class, which fields may contain key-value pairs of several JSON-compatible data types. The primary data types that can be assigned to the object fields are the following:

- Number: integer (42) or floating-point (42.5) numbers, as long as ‘.’ is the decimal separator;
- boolean: true or false values;
- string: a string that can be as long as 2147483647 characters. Be aware that values this huge will slow down data operations;
- DateTime\:DateTimeobjects stored in UTC format as default. If you need to use another timezone, conversion should be done manually;
- array: an array containing data in any Parse compatible data.
- object\:a JSON object also containing any Parse data. When available in SDK, aninclude()call will bring details from the Object property.

:::hint{type="success"}
When you choose to use the Array type, we recommend keeping array objects small as this can affect your data operations’ overall performance. Our recommendation is to use the Array type if it does not exceed 20 elements and does not grow over time. Instead of the Array type, you can use the Pointer and Relations types as an alternative.
:::

In this guide, you will learn how to store data in each of the basic data types listed above. You will build a React Native product registration component, which will show you how to format, convert and save data to your Parse Server in React Native.

Parse also offers the datatypes GeoPoint to use the power of geolocation resources, and the Parse-specific relational data using the types Pointer or Relation. You will see both covered in the next following guides.

::embed[]{url="https://www.youtube.com/embed/gEr9KcjTpOA"}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

To understand the Parse-compatible basic data types, and to store each data type on Parse froma React Native component.

## 1 - The Product Creation Component

Let’s first create the component structure. Let’s make it simple and create a form screen with one text inputs to each data type, one switch toggle, and a submit button to save the object. These inputs will collect your Product field values: name(string), quantity(number), price(number), available(boolean), expiration date(DateTime), and categories(array). Also, you will save an additional object type field in your saving method as well, but this one won’t need an input field.

Create a separate component in a file called ProductCreation.js/ProductCreation.tsx including the following code, or add it to your main application file (App.js/App.tsx or index.js). You can use this layout with complete stylings using react-native-paper or set up your custom form.

:::CodeblockTabs
ProductCreation.js

```javascript
1	import React, {useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  SafeAreaView,
6	  StatusBar,
7	  StyleSheet,
8	  View,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import {
12	  Button as PaperButton,
13	  Switch as PaperSwitch,
14	  Text as PaperText,
15	  TextInput as PaperTextInput,
16	} from 'react-native-paper';
17	
18	export const ProductCreation = () => {
19	  // State variables
20	  const [productName, setProductName] = useState('');
21	  const [productQuantity, setProductQuantity] = useState('');
22	  const [productPrice, setProductPrice] = useState('');
23	  const [productAvailable, setProductAvailable] = useState(false);
24	  const [productExpirationDate, setProductExpirationDate] = useState('');
25	  const [productCategories, setProductCategories] = useState('');
26	
27	  const toggleProductAvailable = () => setProductAvailable(!productAvailable);
28	
29	  return (
30	    <>
31	      <StatusBar backgroundColor="#208AEC" />
32	      <SafeAreaView style={Styles.container}>
33	        <View style={Styles.header}>
34	          <Image
35	            style={Styles.header_logo}
36	            source={ { uri: 'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png', } }
37	          />
38	          <PaperText style={Styles.header_text_bold}>
39	            {'React Native on Back4App'}
40	          </PaperText>
41	          <PaperText style={Styles.header_text}>{'Product Creation'}</PaperText>
42	        </View>
43	        <View style={Styles.wrapper}>
44	          {/* Boolean type input  */}
45	          <View style={Styles.switch_container}>
46	            <PaperText>{'Available?'}</PaperText>
47	            <PaperSwitch
48	              value={productAvailable}
49	              onValueChange={toggleProductAvailable}
50	            />
51	          </View>
52	          {/* String type input */}
53	          <PaperTextInput
54	            value={productName}
55	            onChangeText={(text) => setProductName(text)}
56	            label="Name"
57	            mode="outlined"
58	            style={Styles.form_input}
59	          />
60	          {/* Number type input (integer) */}
61	          <PaperTextInput
62	            value={productQuantity}
63	            onChangeText={(text) => setProductQuantity(text)}
64	            label="Quantity"
65	            mode="outlined"
66	            keyboardType={'number-pad'}
67	            style={Styles.form_input}
68	          />
69	          {/* Number type input (float) */}
70	          <PaperTextInput
71	            value={productPrice}
72	            onChangeText={(text) => setProductPrice(text)}
73	            label="Price"
74	            mode="outlined"
75	            keyboardType={'numeric'}
76	            style={Styles.form_input}
77	          />
78	          {/* Date type input  */}
79	          <PaperTextInput
80	            value={productExpirationDate}
81	            onChangeText={(text) => setProductExpirationDate(text)}
82	            label="Expiration Date (mm/dd/yyyy)"
83	            mode="outlined"
84	            keyboardType={'numbers-and-punctuation'}
85	            style={Styles.form_input}
86	          />
87	          {/* Array type input  */}
88	          <PaperTextInput
89	            value={productCategories}
90	            onChangeText={(text) => setProductCategories(text)}
91	            label="Categories (separated by commas)"
92	            mode="outlined"
93	            style={Styles.form_input}
94	          />
95	          {/* Product create button */}
96	          <PaperButton
97	            onPress={() => createProduct()}
98	            mode="contained"
99	            icon="plus"
100	            style={Styles.submit_button}>
101	            {'Create Product'}
102	          </PaperButton>
103	        </View>
104	      </SafeAreaView>
105	    </>
106	  );
107	};
108	
109	// These define the screen component styles
110	const Styles = StyleSheet.create({
111	  container: {
112	    flex: 1,
113	    backgroundColor: '#FFF',
114	  },
115	  wrapper: {
116	    width: '90%',
117	    alignSelf: 'center',
118	  },
119	  header: {
120	    alignItems: 'center',
121	    paddingTop: 10,
122	    paddingBottom: 20,
123	    backgroundColor: '#208AEC',
124	  },
125	  header_logo: {
126	    width: 170,
127	    height: 40,
128	    marginBottom: 10,
129	    resizeMode: 'contain',
130	  },
131	  header_text_bold: {
132	    color: '#fff',
133	    fontSize: 14,
134	    fontWeight: 'bold',
135	  },
136	  header_text: {
137	    marginTop: 3,
138	    color: '#fff',
139	    fontSize: 14,
140	  },
141	  form_input: {
142	    height: 44,
143	    marginBottom: 16,
144	    backgroundColor: '#FFF',
145	    fontSize: 14,
146	  },
147	  switch_container: {
148	    flexDirection: 'row',
149	    alignItems: 'center',
150	    justifyContent: 'space-between',
151	    paddingVertical: 12,
152	    marginBottom: 16,
153	    borderBottomWidth: 1,
154	    borderBottomColor: 'rgba(0, 0, 0, 0.3)',
155	  },
156	  submit_button: {
157	    width: '100%',
158	    maxHeight: 50,
159	    alignSelf: 'center',
160	    backgroundColor: '#208AEC',
161	  },
162	});
```

ProductCreation.tsx

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  SafeAreaView,
6	  StatusBar,
7	  StyleSheet,
8	  View,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import {
12	  Button as PaperButton,
13	  Switch as PaperSwitch,
14	  Text as PaperText,
15	  TextInput as PaperTextInput,
16	} from 'react-native-paper';
17	
18	export const ProductCreation: FC<{}> = ({}): ReactElement => {
19	  // State variables
20	  const [productName, setProductName] = useState('');
21	  const [productQuantity, setProductQuantity] = useState('');
22	  const [productPrice, setProductPrice] = useState('');
23	  const [productAvailable, setProductAvailable] = useState(false);
24	  const [productExpirationDate, setProductExpirationDate] = useState('');
25	  const [productCategories, setProductCategories] = useState('');
26	
27	  const toggleProductAvailable = () => setProductAvailable(!productAvailable);
28	
29	  return (
30	    <>
31	      <StatusBar backgroundColor="#208AEC" />
32	      <SafeAreaView style={Styles.container}>
33	        <View style={Styles.header}>
34	          <Image
35	            style={Styles.header_logo}
36	            source={ { uri: 'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png', } }
37	          />
38	          <PaperText style={Styles.header_text_bold}>
39	            {'React Native on Back4App'}
40	          </PaperText>
41	          <PaperText style={Styles.header_text}>{'Product Creation'}</PaperText>
42	        </View>
43	        <View style={Styles.wrapper}>
44	          {/* Boolean type input  */}
45	          <View style={Styles.switch_container}>
46	            <PaperText>{'Available?'}</PaperText>
47	            <PaperSwitch
48	              value={productAvailable}
49	              onValueChange={toggleProductAvailable}
50	            />
51	          </View>
52	          {/* String type input */}
53	          <PaperTextInput
54	            value={productName}
55	            onChangeText={(text) => setProductName(text)}
56	            label="Name"
57	            mode="outlined"
58	            style={Styles.form_input}
59	          />
60	          {/* Number type input (integer) */}
61	          <PaperTextInput
62	            value={productQuantity}
63	            onChangeText={(text) => setProductQuantity(text)}
64	            label="Quantity"
65	            mode="outlined"
66	            keyboardType={'number-pad'}
67	            style={Styles.form_input}
68	          />
69	          {/* Number type input (float) */}
70	          <PaperTextInput
71	            value={productPrice}
72	            onChangeText={(text) => setProductPrice(text)}
73	            label="Price"
74	            mode="outlined"
75	            keyboardType={'numeric'}
76	            style={Styles.form_input}
77	          />
78	          {/* Date type input  */}
79	          <PaperTextInput
80	            value={productExpirationDate}
81	            onChangeText={(text) => setProductExpirationDate(text)}
82	            label="Expiration Date (mm/dd/yyyy)"
83	            mode="outlined"
84	            keyboardType={'numbers-and-punctuation'}
85	            style={Styles.form_input}
86	          />
87	          {/* Array type input  */}
88	          <PaperTextInput
89	            value={productCategories}
90	            onChangeText={(text) => setProductCategories(text)}
91	            label="Categories (separated by commas)"
92	            mode="outlined"
93	            style={Styles.form_input}
94	          />
95	          {/* Product create button */}
96	          <PaperButton
97	            onPress={() => createProduct()}
98	            mode="contained"
99	            icon="plus"
100	            style={Styles.submit_button}>
101	            {'Create Product'}
102	          </PaperButton>
103	        </View>
104	      </SafeAreaView>
105	    </>
106	  );
107	};
108	
109	// These define the screen component styles
110	const Styles = StyleSheet.create({
111	  container: {
112	    flex: 1,
113	    backgroundColor: '#FFF',
114	  },
115	  wrapper: {
116	    width: '90%',
117	    alignSelf: 'center',
118	  },
119	  header: {
120	    alignItems: 'center',
121	    paddingTop: 10,
122	    paddingBottom: 20,
123	    backgroundColor: '#208AEC',
124	  },
125	  header_logo: {
126	    width: 170,
127	    height: 40,
128	    marginBottom: 10,
129	    resizeMode: 'contain',
130	  },
131	  header_text_bold: {
132	    color: '#fff',
133	    fontSize: 14,
134	    fontWeight: 'bold',
135	  },
136	  header_text: {
137	    marginTop: 3,
138	    color: '#fff',
139	    fontSize: 14,
140	  },
141	  form_input: {
142	    height: 44,
143	    marginBottom: 16,
144	    backgroundColor: '#FFF',
145	    fontSize: 14,
146	  },
147	  switch_container: {
148	    flexDirection: 'row',
149	    alignItems: 'center',
150	    justifyContent: 'space-between',
151	    paddingVertical: 12,
152	    marginBottom: 16,
153	    borderBottomWidth: 1,
154	    borderBottomColor: 'rgba(0, 0, 0, 0.3)',
155	  },
156	  submit_button: {
157	    width: '100%',
158	    maxHeight: 50,
159	    alignSelf: 'center',
160	    backgroundColor: '#208AEC',
161	  },
162	});
```
:::

After setting up this screen, your application should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/38EB6MiBYSc08XDNHaqfa_image.png" signedSrc size="50" width="345" height="736" position="center" caption}

Note that each Product attribute has its text input field, except for the boolean switch input, meaning that the data in them needs conversion to the corresponding data type before saving.

## 2 - Converting input data

Before saving your data to the Parse.object, you need to correctly format the number, DateTime, and array inputs. Let’s now create a saving function, which will retrieve data from your state variables and apply the suitable data conversion:

:::CodeblockTabs
ProductCreation.js

```javascript
1	const createProduct = async function () {
2	  try {
3	    // These values come from state variables
4	    // Convert data values to corresponding data types
5	    const productNameValue = productName;
6	    const productQuantityValue = Number(productQuantity);
7	    const productPriceValue = Number(productPrice);
8	    const productAvailableValue = productAvailable;
9	    const productExpirationDateValue = new Date(productExpirationDate);
10	    const productCategoriesValue = productCategories.split(',');
11	  } catch (error) {
12	    // Error can be caused by wrong type of values in fields
13	    Alert.alert('Error!', error.message);
14	    return false;
15	  }
16	};
```

ProductCreation.tsx

```typescript
1	const createProduct = async function (): Promise<[boolean]> {
2	  try {
3	    // These values come from state variables
4	    // Convert data values to corresponding data types
5	    const productNameValue: string = productName;
6	    const productQuantityValue: number = Number(productQuantity);
7	    const productPriceValue: number = Number(productPrice);
8	    const productAvailableValue: boolean = productAvailable;
9	    const productExpirationDateValue: Date = new Date(productExpirationDate);
10	    const productCategoriesValue: string[] = productCategories.split(',');
11	  } catch (error) {
12	    // Error can be caused by wrong type of values in fields
13	    Alert.alert('Error!', error.message);
14	    return false;
15	  }
16	};
```
:::

The number data conversion is done casting the value as a Number JavaScript object. DateTime is converted using the Date JavaScript object constructor; the array one is created by using the String.split method in JavaScript, creating an array containing each entry of the categories field separated by commas.

Note that your data is now contained inside a single object, which can be set in a new Parse.object instance to be saved to the server using the Parse.Object.set() method, which takes two arguments: the field name and the value to be set. Let’s also set a new field called completeData, which will be your object type field, assigning the same data object to it.

Go ahead and complete the createProduct function with the following:

:::CodeblockTabs
ProductCreation.js

```javascript
1	const createProduct = async function () {
2	  try {
3	    // These values come from state variables
4	    // Convert data values to corresponding data types
5	    const productNameValue = productName;
6	    const productQuantityValue = Number(productQuantity);
7	    const productPriceValue = Number(productPrice);
8	    const productAvailableValue = productAvailable;
9	    const productExpirationDateValue = new Date(productExpirationDate);
10	    const productCategoriesValue = productCategories.split(',');
11	  } catch (error) {
12	    // Error can be caused by wrong type of values in fields
13	    Alert.alert('Error!', error.message);
14	    return false;
15	  }
16	
17	  // Creates a new Product parse object instance
18	  let Product = new Parse.Object('Product');
19	  
20	  // Set data to parse object
21	  Product.set('name', productNameValue);
22	  Product.set('quantity', productQuantityValue);
23	  Product.set('price', productPriceValue);
24	  Product.set('available', productAvailableValue);
25	  Product.set('expirationDate', productExpirationDateValue);
26	  Product.set('categories', productCategoriesValue);
27	  Product.set('completeData', {
28	    name: productNameValue,
29	    quantity: productQuantityValue,
30	    price: productPriceValue,
31	    available: productAvailableValue,
32	    expirationDate: productExpirationDateValue,
33	    categories: productCategoriesValue,
34	  });
35	
36	  // After setting the values, save it on the server
37	  try {
38	    let savedProduct = await Product.save();
39	    // Success
40	    Alert.alert('Success!', JSON.stringify(savedProduct));
41	    return true;
42	  } catch (error) {
43	    // Error can be caused by lack of Internet connection
44	    Alert.alert('Error!', error.message);
45	    return false;
46	  };
47	};
```

ProductCreation.tsx

```typescript
1	const createProduct = async function (): Promise<[boolean]> {
2	  try {
3	    // These values come from state variables
4	    // Convert data values to corresponding data types
5	    const productNameValue: string = productName;
6	    const productQuantityValue: number = Number(productQuantity);
7	    const productPriceValue: number = Number(productPrice);
8	    const productAvailableValue: boolean = productAvailable;
9	    const productExpirationDateValue: Date = new Date(productExpirationDate);
10	    const productCategoriesValue: string[] = productCategories.split(',');
11	  } catch (error) {
12	    // Error can be caused by wrong type of values in fields
13	    Alert.alert('Error!', error.message);
14	    return false;
15	  }
16	
17	  // Creates a new Product parse object instance
18	  let Product: Parse.Object = new Parse.Object('Product');
19	  
20	  // Set data to parse object
21	  Product.set('name', productNameValue);
22	  Product.set('quantity', productQuantityValue);
23	  Product.set('price', productPriceValue);
24	  Product.set('available', productAvailableValue);
25	  Product.set('expirationDate', productExpirationDateValue);
26	  Product.set('categories', productCategoriesValue);
27	  Product.set('completeData', {
28	    name: productNameValue,
29	    quantity: productQuantityValue,
30	    price: productPriceValue,
31	    available: productAvailableValue,
32	    expirationDate: productExpirationDateValue,
33	    categories: productCategoriesValue,
34	  });
35	
36	  // After setting the values, save it on the server
37	  try {
38	    let savedProduct: Parse.Object = await Product.save();
39	    // Success
40	    Alert.alert('Success!', JSON.stringify(savedProduct));
41	    return true;
42	  } catch (error) {
43	    // Error can be caused by lack of Internet connection
44	    Alert.alert('Error!', error.message);
45	    return false;
46	  };
47	};
```
:::

You can now test the component, insert the createProduct function in it, and call it inside your form submit button onPress property. After creating a product, you should see an alert containing its data like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/v4m5GMpsyodUcAeq2MHf0_image.png" signedSrc size="50" width="344" height="731" position="center" caption}

To certify that your data was saved on the server using the correct data types, you can look at your Parse dashboard. Click on the Product data table and note that every column has its data type written at the header. Your class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BkePakZS4Q8hO3cOjaWqp_image.png)

## Conclusion

At the end of this guide, you learned how to save each of the basic data types available on Parse using a React Native component. In the next guide, you will learn about the relational data on Parse.

[title] API Reference
[path] Parse Dashboard/

# API Reference

## Introduction

In this tutorial, you will learn what is and how to use the API Reference.

## Goal

- Access and use the API Reference.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read or edit this page.**
:::

### API Reference

The API Documentation tool generates docs for any objects saved in your database and with this tool, we provide code snippets for each of the supported programming languages, so your documentation lives right alongside your data. Your API keys are provided in the docs so you don’t need to jump back and forth between two different things.

Back4App’s API Documentation tool creates this dynamic environment for any project.

You can access it going to: Dashboard > API Reference or you can find the button in any class to the API Reference panel.

**How it works?**

We’ll briefly go through each of the sections.

**Introduction**

Contains background information on the Back4App API.

**Getting Started**

The main sections that we need to highlight are:

- **Installing Parse SDK **provides links to each of the Parse SDKs. These are libraries for integrating the frontend of your project with your Back4App server.
- **Initializing Parse SDK** presents your keys. You’ll also notice on the left you can page between different languages to see how to initialize your project. This is great because you can copy this code directly, and not have to track down your keys separately!
  api-reference-initializing-sdk
- **Objects API **this section provides detailed information on what Parse Objects are and how the API works. It also explains how to create classes on the fly- unlike what we did with the data browser. I highly recommend reading this section!&#x20;
- **User API **another important section with API calls for interacting with the special User class. It has the same methods as normal objects but adds some authentication items.
- **Queries **can be used to explore the Query capabilities.
- **Errors **if your server sends back an error, this is where you can find out what the numeric code means.

Read more about it in this <a href="https://blog.back4app.com/2018/12/14/the-api-reference-tool/" target="_blank">post blog</a>.


[title] Create subdomain
[path] Platform/

# Activating Web Hosting

## Introduction

Back4App offers a web hosting feature where is possible to host your website or even [a simple NodeJS application](https://www.back4app.com/docs/advanced-guides/web-application-hosting). By default, enabling the web hosting tool, you will configure a subdomain on Back4App and at any time you can point it out to your own domain. Web hosting tool gives you the flexibility you need to host your entire platform on Back4App. See how to do that below.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A verified account at Back4App.
- An app created in Back4App.
  - Check this <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## How can I use the created subdomain?

When you enable the Webhosting feature is a guarantee that you can use more functionalities in Back4App side, so, it has the many different purposes that are described below:

**1. Host Static Pages**

The Back4App Documentation comprises various guides and tutorials to build a Web App using Angular, Ionic, React and Node!!!

**2. Custom Domain appoint to your Back4App subdomain**

This amazing possibility allows you set up a custom domain. All you need to do is create a CNAME DNS entry in your DNS provider, for example:

myapp.mydomain.com to myapp.b4a.app

# Let’s get started to enable the Webhosting!

## 1 - Locate the feature block

To locate and enable your Webhosting feature, log in to your Back4App account and go to the My Apps option and then click on Server Settings. After that, search for the Webhosting and Custom Domains block and select Settings. The Webhosting and Custom Domains section looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZDeY9ygJSM4s5bh7sf8uF_image.png)

## 2 - Enable the Activate Hosting box

Now, tick the checkbox with tag Activate Back4App Hosting and insert the available Subdomain name you desired to have, and then click on the Save Button to preserve the changes, same as shown in the example below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fzitSjtT2FcV2QqO6zOMF_image.png" signedSrc size="60" width="616" height="645" position="center" caption}

With the guide described above, you’ll be able to set up a Subdomain with Back4App and enable as well with various features, including Hosting Pages and creating your own Custom Domain!


[title] Join Queries
[path] React Native/Parse SDK (REST)/Data objects/

# Join Query using Parse

## Introduction

In this guide, you will perform relational queries in Parse mimicking the behavior of SQL JOIN queries using a NoSQL database.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
:::

## Goal

Query relational data stored on Back4App in SQL JOIN query fashion.

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

```javascript
1   // This will create your query
2   let parseQuery = new Parse.Query("Profile");
3   // The query will resolve only after calling this method
4   let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Save some data on Back4App

Let’s create two example classes, TableA and TableB, which will be the targets of our queries in this guide. On Parse JS Console it 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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gwr8EqE21xS3619i4j7d__image.png)

Go ahead and create the classes with the following content:

```javascript
1	// Create TableA and its records
2	let TableARecord1 = new Parse.Object('TableA');
3	TableARecord1.set('FieldA', 'Value A 1');
4	TableARecord1 = await TableARecord1.save();
5	
6	let TableARecord2 = new Parse.Object('TableA');
7	TableARecord2.set('FieldA', 'Value A 2');
8	TableARecord2 = await TableARecord2.save();
9	
10	let TableARecord3 = new Parse.Object('TableA');
11	TableARecord3.set('FieldA', 'Value A 3');
12	TableARecord3 = await TableARecord3.save();
13	
14	// Create TableB and its records, some of them linked to TableA
15	let TableBRecord1 = new Parse.Object('TableB');
16	TableBRecord1.set('FieldB', 'Value B 1');
17	TableBRecord1.set('link', TableARecord1);
18	TableBRecord1 = await TableBRecord1.save();
19	
20	let TableBRecord2 = new Parse.Object('TableB');
21	TableBRecord2.set('FieldB', 'Value B 2');
22	TableBRecord2.set('link', TableARecord1);
23	TableBRecord2 = await TableBRecord2.save();
24	
25	let TableBRecord3 = new Parse.Object('TableB');
26	TableBRecord3.set('FieldB', 'Value B 3');
27	TableBRecord3.set('link', TableARecord3);
28	TableBRecord3 = await TableBRecord3.save();
29	
30	let TableBRecord4 = new Parse.Object('TableB');
31	TableBRecord4.set('FieldB', 'Value B 4');
32	TableBRecord4 = await TableBRecord4.save();
33	
34	console.log('Success!');
```

## 3 - Querying the data

Now that you have populated the classes, we can now perform the relational queries in it. Let’s begin by performing the INNER JOIN, introducing the join relational query that we will use in all of our examples. This query represents the results of two combined queries between tables A and B, returning all the records that are related by a specific condition using the Parse.Query.matchesQuery method.

```javascript
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// INNER JOIN, get only the records in TableA that have matching records in TableB
13	console.log("INNER JOIN");
14	console.log("TABLE A ID | FIELD A | FIELD B");
15	for (let joinResult of joinQueryResults) {
16	  console.log(
17	    `${joinResult.get("link").id} | ${joinResult
18	      .get("link")
19	      .get("FieldA")} | ${joinResult.get("FieldB")}`
20	  );
21	}
```

The INNER JOIN SQL query behavior is exactly the one achieved in our generic join relational query, so we need to print its results in the console. Remember that with a Parse.Object you can use the get method to retrieve data by using the column name.

Let’s now perform a LEFT OUTER JOIN consisting of getting all the records on TableA and showing the relational data on TableB, when available:

```javascript
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// LEFT OUTER JOIN, get records in TableA that have matching records in TableB and also every
13	// other TableA record
14	let queryTableA = new Parse.Query("TableA");
15	queryTableA.limit(10);
16	let queryTableAResults = await queryTableA.find();
17	console.log("LEFT JOIN");
18	console.log("TABLE A ID | FIELD A | FIELD B");
19	for (let result of queryTableAResults) {
20	  // Get all entries from JOIN query that have a link to this TableA entry
21	  let joinQueryResultsFiltered = joinQueryResults.filter(
22	    (joinQueryResult) =>
23	      joinQueryResult.get("link") !== undefined &&
24	      joinQueryResult.get("link").id == result.id
25	  );
26	  if (joinQueryResultsFiltered.length > 0) {
27	    for (let joinResult of joinQueryResultsFiltered) {
28	      let fieldBValue = joinResult.get("FieldB");
29	      console.log(`${result.id} | ${result.get("FieldA")} | ${fieldBValue}`);
30	    }
31	  } else {
32	    console.log(`${result.id} | ${result.get("FieldA")} | `);
33	  }
34	}
```

TheRIGHT OUTER JOINis the opposite of the left one, fetching the records fromTableB.

```mysql
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// RIGHT OUTER JOIN, get records in TableA that have matching records in TableB and also every
13	// other TableB record
14	let queryTableB = new Parse.Query("TableB");
15	queryTableB.limit(10);
16	let queryTableBResults = await queryTableB.find();
17	console.log("RIGHT JOIN");
18	console.log("TABLE B ID | FIELD A | FIELD B");
19	for (let result of queryTableBResults) {
20	  // Get all entries from JOIN query that matches this TableB entry
21	  let joinQueryResultsFiltered = joinQueryResults.filter(
22	    (joinQueryResult) => joinQueryResult.id == result.id
23	  );
24	  if (joinQueryResultsFiltered.length > 0) {
25	    for (let joinResult of joinQueryResultsFiltered) {
26	      let fieldAValue = "";
27	      if (joinResult.get("link") !== undefined) {
28	        fieldAValue = joinResult.get("link").get("FieldA");
29	      }
30	      console.log(
31	        `${result.id} | ${fieldAValue} | ${joinResult.get("FieldB")}`
32	      );
33	    }
34	  } else {
35	    console.log(`${result.id} | | ${result.get("FieldB")}`);
36	  }
37	}
```

Finally, we have theFULL OUTER JOINwhich is the combination of the left and right inner joins:

```mysql
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// FULL OUTER JOIN, combining LEFT and RIGHT OUTER JOIN results
13	console.log("FULL JOIN");
14	console.log("TABLE ID | FIELD A | FIELD B");
15	// First print all INNER JOIN results
16	for (let joinResult of joinQueryResults) {
17	  console.log(
18	    `${joinResult.get("link").id} | ${joinResult
19	      .get("link")
20	      .get("FieldA")} | ${joinResult.get("FieldB")}`
21	  );
22	}
23	// Print LEFT JOIN leftovers
24	let outerQueryTableA = new Parse.Query("TableA");
25	outerQueryTableA.limit(10);
26	let outerQueryTableAResults = await outerQueryTableA.find();
27	// Get all entries from query that doesn't match the JOIN query results
28	let filteredOuterQueryTableAResults = outerQueryTableAResults.filter(
29	  (outerQueryTableAResult) =>
30	    joinQueryResults.find(
31	      (joinQueryResult) =>
32	        joinQueryResult.get("link") !== undefined &&
33	        joinQueryResult.get("link").id === outerQueryTableAResult.id
34	    ) === undefined
35	);
36	for (let result of filteredOuterQueryTableAResults) {
37	  console.log(`${result.id} | ${result.get("FieldA")} | `);
38	}
39	// Print RIGHT JOIN leftovers
40	let outerQueryTableB = new Parse.Query("TableB");
41	outerQueryTableB.limit(10);
42	let outerQueryTableBResults = await outerQueryTableB.find();
43	// Get all entries from query that doesn't match the JOIN query results
44	let filteredOuterQueryTableBResults = outerQueryTableBResults.filter(
45	  (outerQueryTableBResult) =>
46	    joinQueryResults.find(
47	      (joinQueryResult) => joinQueryResult.id === outerQueryTableBResult.id
48	    ) === undefined
49	);
50	for (let result of filteredOuterQueryTableBResults) {
51	  console.log(`${result.id} | | ${result.get("FieldB")}`);
52	}
```

## Conclusion

At the end of this guide, you learned how to perform on Back4App relational queries using Parse and emulating the most common SQL JOIN queries in a NoSQL database.

[title] Creating an object
[path] GraphQL Cookbook/

# Creating an object through the Parse GraphQL API

## Problem

You want to create a new object in your database through the Parse GraphQL API.

## Solution

Using the parse GraphQL, there are two different ways to create a new object in your database:

- [Using generic mutation ](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic)- - this is the mutation that you must use if you do not have already created your object’s class.
- [Using class mutation ](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-class)- this is the recommended mutation if you have already created your object’s class.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

### **Using generic mutation**

When you use the create generic mutation, Parse Server behaves like a schemaless database. It means that you do not need to define your object’s class in your application’s schema beforehand. You just need to send your object’s data, and Parse Server will not only store it, but also learn from it, and automatically create a new class in your application’s schema.

Therefore, the objects’ create generic mutation is the method that you must use for creating a new object if you do not have already created your object’s class. You can also use this mutation for creating an object of pre-existing classes, but, for these cases, we recommend using the [class mutation](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-class).

**Class:**

:::CodeblockTabs
Request

```graphql
     #In Parse 3.10.0 and later you must first create the Class itself:
1    mutation CreateClass {
2      createClass(input:{
3        name: "Hero"
4        schemaFields: {
5          addStrings: [{name: "name"}]
6          addNumbers: [{name: "height"}]
7        }
8      }){
9        class{
10         schemaFields{
11           name
12           __typename
13         }
14       }
15     }
16   }
```

Response

```graphql
1   {
2     "data": {
3       "createClass": {
4         "class": {
5           "schemaFields": [
6             {
7               "name": "objectId",
8               "__typename": "SchemaStringField"
9             },
10            {
11              "name": "updatedAt",
12              "__typename": "SchemaDateField"
13            },
14            {
15              "name": "createdAt",
16              "__typename": "SchemaDateField"
17            },
18            {
19              "name": "name",
20              "__typename": "SchemaStringField"
21            },
22            {
23              "name": "height",
24              "__typename": "SchemaNumberField"
25            },
26            {
27              "name": "ACL",
28              "__typename": "SchemaACLField"
29            }
30          ]
31        }
32      }
33    }
34  }
```
:::

**Object:**

:::CodeblockTabs
Request

```graphql
1   mutation CreateObject{
2     createHero(input: {fields: {name: "Luke Skywalker"}}){
3       hero{
4         id
5         name
6       }
7     }
8   }
```

Response

```graphql
1   {
2     "data": {
3       "createHero": {
4         "hero": {
5           "id": "SGVybzo5QjFPMUFxcXN1",
6           "name": "Luke Skywalker"
7         }
8       }
9     }
10  }
```
:::

### **Using class mutation**

Once you have already created your object’s class in your application’s schema (for instance, using the [generic mutation](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic)), Parse Server instantly adds to your GraphQL API a new create\<ClassName> mutation to create a new object of this class.

Therefore, the object’s class mutation is the recommended method for creating a new object if you have already created your object’s class. Since this mutation knows your class’ data, it will automatically make available for you additional features like code auto-complete and validation. You also don’t need to specify the data types when sending dates, pointers, relations, files, geo points, polygons, or bytes through the class create mutation.

:::hint{type="info"}
**This example will only work if you have already created your object’s class. You can create a class using the** <a href="https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic" target="_blank">**generic mutation**</a>**.**
:::

:::CodeblockTabs
Request

```graphql
1   mutation CreateObject{
2     createHero(input: {fields: {name: "R2-D2"}}){
3       hero{
4         id
5         createdAt
6       }
7     }
8   }
```

Response

```graphql
   #Notice the id property refers to the Global id in the Relay specification, not to confuse with the objectId from Parse.
1   {
2     "data": {
3       "createHero": {
4         "hero": {
5           "id": "SGVybzpVRm5TVDM1YnBp",
6           "createdAt": "2020-02-06T13:13:26.678Z"
7         }
8       }
9     }
10  }
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse Server 3.9.0**

**Generic mutation**

**Class:**

:::CodeblockTabs
Request

```graphql
     #In Parse 3.9.0 you also must first create the Class itself:
1   mutation CreateClass {
2     createClass(
3       name: "Hero"
4       schemaFields: {
5         addStrings: [{name: "name"}]
6         addNumbers: [{name: "height"}]
7       }){
8       schemaFields{
9         name
10        __typename
11      }
12    }
13  }
```

Response

```graphql

1   {
2     "data": {
3       "createClass": {
4         "schemaFields": [
5           {
6             "name": "objectId",
7             "__typename": "SchemaStringField"
8           },
9           {
10            "name": "updatedAt",
11            "__typename": "SchemaDateField"
12          },
13          {
14            "name": "createdAt",
15            "__typename": "SchemaDateField"
16          },
17          {
18            "name": "name",
19            "__typename": "SchemaStringField"
20          },
21          {
22            "name": "height",
23            "__typename": "SchemaNumberField"
24          },
25          {
26            "name": "ACL",
27            "__typename": "SchemaACLField"
28          }
29        ]
30      }
31    }
32  }
```
:::

**Object:**

:::CodeblockTabs
Request

```graphql
   #And then create the Object:
   #In Parse 3.9.0 you must first create the Class itself:
1   mutation CreateObject{
2     createHero(fields:{
3       name: "Luke Skywalker"
4     }){
5       id
6       createdAt
7     }
8   }
```

Response

```graphql
1   {
2     "data": {
3       "createHero": {
4         "id": "CkhurmMjZW",
5         "createdAt": "2019-11-04T12:37:22.462Z"
6       }
7     }
8   }
```
:::

**Class mutation**

:::CodeblockTabs
Parse Server 3.9.0

```graphql
1   mutation CreateObject{
2     createHero(fields:{
3       name: "R2-D2"
4     }){
5       id
6       createdAt
7     }
8   }
```

Result Parse 3.9.0

```graphql
1   {
2     "data": {
3       "createHero": {
4         "id": "n5GrpEi0iL",
5         "createdAt": "2019-11-04T12:45:00.882Z"
6       }
7     }
8   }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.8.0**

**Generic mutation:**

:::CodeblockTabs
Parse Server 3.8.0

```graphql
1   mutation CreateObject {
2     create(className: "Hero" fields:{
3       name: "Luke Skywalker"
4     }){
5       objectId
6       createdAt
7     }
8   }
```

Result Parse 3.8.0

```graphql
1   {
2     "data": {
3       "objects": {
4         "create": {
5           "objectId": "ffyOBOTk85",
6           "createdAt": "2019-07-15T01:25:20.875Z"
7         }
8       }
9     }
10   }
```
:::

**Class mutation:**

:::CodeblockTabs
Parse Server 3.8.0

```graphql
1   mutation CreateHero {
2     createHero(fields: { name: "R2-D2" }) {
3       objectId,
4       createdAt
5     }
6   }
```

Result Parse 3.8.0

```graphql
1   {
2     "data": {
3       "createHero": {
4         "objectId": "tUEcddcgno",
5         "createdAt": "2019-11-04T12:44:10.951Z"
6       }
7     }
8   }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.7.2**

**Generic mutation:**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   mutation CreateObject {
2     objects {
3       create(className: "Hero", fields: { name: "Luke Skywalker" }) {
4         objectId,
5         createdAt
6       }
7     }
8   }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "create": {
5           "objectId": "Kr9aqnzfui",
6           "createdAt": "2019-07-15T01:25:20.875Z"
7         }
8       }
9     }
10  }
```
:::

**Class mutation:**

:::CodeblockTabs
Parse Server 3.7.2

```graphql
1   mutation CreateHero {
2     objects {
3       createHero(fields: { name: "R2-D2" }) {
4         objectId,
5         createdAt
6       }
7     }
8   }
```

Result Parse 3.7.2

```graphql
1   {
2     "data": {
3       "objects": {
4         "createHero": {
5           "objectId": "jJH0aQQjfs",
6           "createdAt": "2019-07-15T02:22:04.982Z"
7         }
8       }
9     }
10  }
```
:::
::::


[title] Importing CSV File
[path] Parse Dashboard/Core/

## Introduction

Importing CSV files allow users to import data easily into Parse tables.

## Prerequisites

:::hint{type="info"}
**To begin with this tutorial, you will need:**

- An app created at Back4App.
  - See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a Class for importing data

In your newly created App, go to the Database Browser and click the Create a class button

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MIuWuylAQjxa-xjLXkykN_image.png" signedSrc="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MIuWuylAQjxa-xjLXkykN_image.png" size="50" width="448" height="579" position="center" caption}

Choose to create a Custom class and give it a name. I called mine TestClass but you can call yours anything you like.
Remember that by convention classes start with an Uppercase letter, are CamelCase and does not contain special characters such as spaces and symbols.
Click Create class when you’re done.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xzqZee4RmhJgZo1hoGClT_image.png" signedSrc="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xzqZee4RmhJgZo1hoGClT_image.png" size="60" width="988" height="826" position="center" caption}

## 3 - Creating the CSV file

The CSV file must be in the correct format in order to be imported.

**Correct format parameters**:

Separation character must be a comma , and NOT a semicolon ;.


Adjust your Spreadsheet software to output commas as separation characters


First line will be the Column Names


Parse will automatically convert `Strings`, `Numbers` and `Booleans`


Dates must have two columns: `date.__typespecifies` the Date type, `date.isospecifies` the Date Format


GeoPoints must have three columns: `columnName.__typespecifies` the Geopoint type,
`columnName.latitudespecifies` the Latitude, `columnName.longitudespecifies` the Longitude.


Arrays are passed in double quotes


Pointers require three columns: `columnName.__typespecifies` the Pointer type, `columnName.classNamespecifies` the target class, `columnName.objectIdspecifies` the objectId of the target class

So, for your reference, a few examples:

Three columns: columnStringA will hold strings, columnStringB will also hold strings, columnNumberC will hold numbers

columnStringA,columnStringB,columnNumberC
stringA,StringB,12
stringA2,StringB2,13

Two columns: columnStringA will hold strings, columnBooleanB will hold Booleans

columnStringA,columnBooleanB
stringA,TRUE
stringA2,FALSE

Two columns: columnStringA and columnStringB will hold strings, columnArrayC will hold arrays

columnStringA,colmnStringB,columnArrayC
"stringA, with, commas",stringB,`"[1,2,3]"`
stringA2,"stringB, with, commas",`"["A", "B", "C"]"`

Two columns: columnStringA will hold strings, second column will hold a date in the ISO format

columnStringA,`date.__type`,`date.iso`
stringA,Date,2020-01-16
stringA2,Date,2020-01-17

Two columns: columnStringA will hold strings, second column will hold a GeoPoint

columnStringA,`geo.__type`,`geo.latitude`,`geo.longitude`
stringA,GeoPoint,1,2
stringA2,GeoPoint,-5,-6

You can find a sample CSV file for download here:

[Download it here](https://www.back4app.com/docs/assets/downloads/SampleCSVFile.csv)

For Pointers, please check the example below:

One column: holding a Pointer

`team.__type`,team.className,team.objectId
Pointer,Team,XWDSM4xxQ8
Pointer,Team,CD9nAlDaEG
Pointer,Team,kRGJPuZyXD

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uE9mTlnoAR6UhMO1R2lwa_image.png" signedSrc="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uE9mTlnoAR6UhMO1R2lwa_image.png" size="50" width="410" height="96" position="center" caption}

In this example, please consider that objectId correspond to the existing ones at the Team class.

Check the sample CSV file for download:

[Download it here](https://www.back4app.com/docs/assets/downloads/import-pointer-csv.csv)

## 4 - Importing the Data

With your newly created class selected in the Database Browser, on the top right corner of the screen, click the Notes button and select Import data

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N3EnnPmrRItGfbN5Hz4_3_image.png" signedSrc="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N3EnnPmrRItGfbN5Hz4_3_image.png" size="50" width="414" height="619" position="center" caption}

Click the Upload a file button, choose your CSV file and click the Import button

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VakvVAlB0vKaCmJe6aWfA_image.png)

## 5 - Wait for an eMail confirmation

As CSV files can get quite big, an asynchronous operation is fired to import your data in background, which means you will not see any progress of importing nor success message.
At the end of the operation you will receive an email message either telling the operation was successful and your data was imported, or telling the operation wasn’t successful and explaining which columns/lines failed.

If you try to refresh your dashboard before receiving this email, you might see missing data or even no data at all, as the operation is still ongoing.

Once you get the email and if your import was successful, we recommend hard refreshing your browser (CMD + Shift + R on a Mac, CTRL + Shift + R in Windows, both for Chrome) to ensure the new schemas are retrieved and your data displays properly.


[title] Data objects
[path] React Native/Parse SDK (REST)/


[title] Query Renderer
[path] React Native/Relay (GraphQL)/

# Query Renderer on Back4App

## Introduction

In our previous <a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">guide</a>, we have learned how to prepare our Relay Environment. Now you’re ready to start developing your React Native app.

In this guide, you will learn how to make your first query on Back4App. We’re going to dive into the Relay Query Render, understanding its main principles and use it to consume your first data from Back4App.

## Goals

Get an overview of Relay Query Renderer;

Make a query on Back4App GraphQL API from a React Native App using Relay;

## Prerequisites

:::hint{type="info"}
- Application created at Back4App dashboard
- React Native application and with Relay Environment configured by the previous docs.
- Read about Relay Node and Connections
:::

## What is the Query Renderer?

As well as React builds a tree of components, Relay builds a tree of data components. This means that each component of the application will be the owner of their fragment data. The fragment will contain the data information necessary to render it on screen and Relay ensures that this data is available before rendering the component.

Handling this whole approach, the Query Renderer is a root component necessary to compose those fragments and prepare the query to be fetched from our back-end.

## Why understand the Query renderer?

Understanding the use of Query Renderer makes it important to abstract your application in different ways. A good abstract of code could prevent hours of work, errors, debugging time, etc.

## How it works the renderer together with Back4app APIs

In the last tutorial, we have prepared the Relay Environment file, which specifies the Back4App connection info. Using this configuration, Relay will take care of the communication with Back4App APIs. You don’t need to worry about the connection. Just focus on building the data components.

## 1 - Creating a Class on Back4App Dashboard

Let’s create your first class and populate it with a few objects using the Back4App GraphQL Console. The Person class has 2 fields name which is a string and salary which is an integer. Go to the Dashboard->Core->GraphQL Console and use the code below:

```graphql
1	mutation CreateClass {
2	  createClass(input:{
3	    name: "Person"
4	    schemaFields: {
5	      addStrings: [{name: "name"}]
6	      addNumbers: [{name: "salary"}]
7	    }
8	  }){
9	    class{
10	      schemaFields{
11	        name
12	        __typename
13	      }
14	    }
15	  }
16	}
```

You’ll see the result below:

- **creating class **

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/POIZNKLV_Hb36QMPoWhAK_image.png)

Now let’s create some objects inside this Class. Go to the create object mutation [guide](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object#mutation-generic) and see how to handle this case. Make sure you are using the latest Parse Server Version in order to use the most recent GraphQL API notation available on Back4App.

```graphql
1	mutation CreateObject{
2	  createHero(input: {fields: {name: "Allana Foley", salary: 1000}}){
3	    person {
4	      id
5	      name
6	      salary
7	    }
8	  }
9	}
```

- **creating object **

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/l1kwoq9WTDEOK9lTsqMfy_image.png)

Now, the Person class is created and has a name and salary field.

After creating a new class, Back4App automatically generates all the necessary resources to use the back-end safely.

One example is the list of objects. Back4App already created the connections necessary to query the list of Person: People.

To better understand, go to the playground, refresh and open the docs tab and look for People. Back4App generated the connection field. You can also query the class person as a list. Note that the Query.people field is a PersonConnection.

Relay will consume the connection field to render a list of the Person’s objects.

Person Field doc:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/or6J-14M3FXWU4lna7dmQ_image.png" signedSrc size="50" width="292" height="847" position="center" caption}

And People (Person) connection Field docs:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/f2yzig7_jejw4Ibf4K5q4_image.png" signedSrc size="50" width="295" height="321" position="center" caption}

## 2 - Updating the Schema

It is important to remember that if a new class goes into your application, it will be necessary to update the schema inside the root of the React Native application.

If necessary, go to <a href="https://www.back4app.com/docs/react-native/graphql/download-schema" target="_blank">Download Schema</a> docs and repeat the steps to update the schema.

## 3 - First example of fragment container

Before we continue the tutorial, let’s introduce you to the fragment container.

Let’s create a component that will be the Person info owner. This component will contain the person’s name and salary. Here you can ask any person field to build your component. Now, we’re going to proceed with these two fields.

- Creates a file and name it PersonCard.js
- inside of it, let’s create a simple function component

```javascript
1	import React from 'react';
2	
3	const PersonCard = () => {
4	    return (
5	        <div>
6	            
7	        </div>
8	    );
9	};
```

Replace the line of export default by the code below:

```javascript
1	export default createFragmentContainer(PersonCard, {
2	    person: graphql`
3	    fragment PersonCard_person on Person {
4	      id
5	      name
6	      salary
7	    }
8	  `,
9	});
```

The code above will create a fragment of a Person that asks only for id, name, and salary.

Finish updating the rest of the component with the following code:

```javascript
1	const PersonCard = ({person}) => {
2	    return (
3	        <View>
4	            <Text>Name: {person.name}</Text>
5	            <Text>Salary: {person.salary}</Text>
6	        </View>
7	    );
8	};
```

The final result should look like this:

```javascript
1	import React from "react";
2	import { createFragmentContainer, graphql } from "react-relay";
3	import { View, Text } from "react-native";
4	
5	const PersonCard = ({person}) => {
6	    return (
7	        <View>
8	            <Text>Name: {person.name}</Text>
9	            <Text>Salary: {person.salary}</Text>
10	        </View>
11	    );
12	};
13	
14	export default createFragmentContainer(PersonCard, {
15	    person: graphql`
16	    fragment PersonCard_person on Person {
17	      id
18	      name
19	      salary
20	    }
21	  `,
22	});
```

## 4 - Creating The Query Renderer

The next step is to create the Query Renderer for your objects list. The Query Renderer is a root component for retrieving the data components and preparing them to fetch data from the backend. You will learn how to retrieve data for a Person Class on Back4App.

### **4.1 - Creating the file**

- Create a new file and name it PersonRenderer.js
- Copy the code below and paste it into PersonRenderer file.

```javascript
1	const PersonRenderer = () => {
2	  return (
3	    <QueryRenderer
4	      environment={Environment}
5	      query={graphql``}
6	      variables={null}
7	      render={({error, props}) => {
8	        if (error) {
9	          return (
10	            <View>
11	              <Text>{error.message}</Text>
12	            </View>
13	          );
14	        } else if (props) {
15	          // @todo here will be implement the person card for each item from result
16	        }
17	        return (
18	          <View>
19	            <Text>loading</Text>
20	          </View>
21	        );
22	      }}
23	    />
24	  );
25	};
26	
27	export default PersonRenderer;
```

### **4.2 - Understanding the props of QueryRenderer**

Let’s start with a Query Renderer with their props empty: graphql, variables, and render. Step by step, you will implement each one incrementally.

First of all, your application needs to inform the query for Query Renderer. Here, our application will consume a list of People. On query props, paste the following code:

```graphql
1	graphql`
2	  query PersonRendererQuery {
3	    people {
4	      edges {
5	        node {
6	          ...PersonCard_person
7	        }
8	      }
9	    }
10	  }`
```

The graphql comes from react-relay and implements the query as a string.

:::hint{type="info"}
It is Important to understand edges and node connection. The query above is consuming a node connection of people from the Back4App server. Every time you create a new class, it will be followed by a node connection.
:::

**Variables**

When necessary, the query renderer will consume variables. A good example: when the application requests a query for a person by id. As this is not the case right now, let’s pass by null on the variables props.

**Populating the Person Card**

This query will return a list of people. The query renderer ensures that the data will be available to render. If it doesn’t, shoot an error. The props responsible for this is the render.

Populate the render props with the following code:

```javascript
1	render={({error, props}) => {
2	  if (error) {
3	    return (
4	      <View>
5	        <Text>{error.message}</Text>
6	      </View>
7	    );
8	  } else if (props) {
9	     return props.people.edges.map(({node}) => <PersonCard person={node} />);
10	  }
11	  return (
12	    <View>
13	      <Text>loading</Text>
14	    </View>
15	  );
16	}}
```

Replace the commented todo for a javascript map for rendering a person card for each result from the list.

As said, the query renderer takes responsibility for making the data available only when it is ready. Until then, a loading message will be displayed. If an error occurs, it will be displayed on the screen preventing an unexpected application crash.

By last, let improves the render of person replacing the .map by a new function. Put it before the Query Renderer:

```graphql
1   const renderPersons = (people) => {
2     return people.edges.map(({node}) => <PersonCard person={node} />);
3   };
```

And the final result should look like:

```graphql
1	import React from "react";
2	import { QueryRenderer } from "react-relay";
3	import Environment from "./relay/Environment";
4	import PersonCard from "./PersonCard";
5	import { View, Text } from "react-native";
6	
7	const PersonRenderer = () => {
8	  const renderPersons = (people) => {
9	    return people.edges.map(({node}) => <PersonCard person={node} />);
10	  };
11	
12	  return (
13	    <QueryRenderer
14	      environment={Environment}
15	      query={graphql`
16	        query PersonRendererQuery {
17	          people {
18	            edges {
19	              node {
20	                ...PersonCard_person
21	              }
22	            }
23	          }
24	        }
25	      `}
26	      variables={null}
27	      render={({error, props}) => {
28	        if (error) {
29	          return (
30	            <View>
31	              <Text>{error.message}</Text>
32	            </View>
33	          );
34	        } else if (props) {
35	          return renderPersons(props.people);
36	        }
37	        return (
38	          <View>
39	            <Text>loading</Text>
40	          </View>
41	        );
42	      }}
43	    />
44	  );
45	};
46	
47	export default PersonRenderer;
```

## 5 - Making your first query

Now it is time to fetch the Person using the PersonRenderer. If everything is ok, your application now has two new components: PersonRenderer and PersonCard.

Before starting the application, the Relay needs the Relay Compiler to run and generate the component types. For this, run into your terminal:

> yarn relay

On app.js add the followng code:

```javascript
1	import React from 'react';
2	import { SafeAreaView, StatusBar, View, Text } from 'react-native';
3	
4	import Providers from './Providers';
5	import PersonRenderer from '../components/home/person/PersonRenderer';
6	
7	const App = () => {
8	  return (
9	    <Providers>
10	      <StatusBar barStyle="dark-content" />
11	      <SafeAreaView>
12	        <View
13	          style={ {
14	            flexDirection: 'column',
15	            justifyContent: 'center',
16	            alignItems: 'center',
17	            marginTop: 100,
18	          } }>
19	          <Text style={ {fontWeight: "bold", textAlign: "center"} }>
20	            Back4App React Native Relay - Query Renderer List Example
21	          </Text>
22	          <PersonRenderer />
23	        </View>
24	      </SafeAreaView>
25	    </Providers>
26	  );
27	};
28	
29	export default App;
```

:::hint{type="info"}
The code of app.js comes originally from create-react-native-app. It added a View with a style to center the content on the screen with a margin of 10px from the top. Inside of it has a text with a label to give some context for the print and the PersonRenderer to show the list of person.
:::

You need to get the following result:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8weoMupLKSfT9rOv8hZha_image.png" signedSrc size="80" width="772" height="288" position="center" caption}

- **Rendering**

In our Back4App React Native application, we import the PersonRenderer directly into the App.js. As the PersonRenderer is a root component and has its QueryRenderer, the Person must be displayed without any error:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/UI6gJgtD9X82_SeJ3s9Zv_image.png" signedSrc size="50" width="742" height="1536" position="center" caption}

## 6 - Typing the components

:::hint{type="info"}
This step makes sense for an application that works with typescript. If your application doesn’t use typescript, go ahead.
:::

One of the powers of the Relay Compiler is to generate the types from each data component. Let’s type the PersonRenderer and PersonCard to make the application more powerful.

**Typing PersonRenderer**

Type the renderPerson function arg people into PersonRenderer:

```javascript
1   const renderPersons = (people: PersonRendererQuery['response']['people']) => {
2     return people.edges.map(({node}) => <PersonCard person={node} />);
3   };
```

Import the PersonRendererQuery type from \_\_generated\_\_ folder created inside of the same folder of the PersonRenderer.

**Typing PersonCard**

Go ahead to PersonCard, create a new type object and name it PersonCardProps:

```javascript
1    type PersonCardProps = {};
```

Import thePersonCard\_persontype from\_\_generated\_\_folders:

```javascript
1    import {PersonCard_person} from './__generated__/PersonCard_person.graphql';
```

Add the person inside the type PersonCardProps:

```javascript
1    type PersonCardProps = {
2      person: PersonCard_person;
3    };
```

On props arguments from PersonCard, type the component with thePersonCardProps:

> 1   
>
> **const**
>
>  PersonCard 
>
> **=**
>
>  ({person}: PersonCardProps) 
>
> **=>**
>
>  \{ ... }

The final result of both components should look like:

- **PersonRenderer**

```javascript
1	import React from 'react';
2	import {graphql, QueryRenderer} from 'react-relay';
3	import {Environment} from '../../../relay';
4	import PersonCard from './PersonCard';
5	import {View, Text} from 'react-native';
6	import {PersonRendererQuery} from './__generated__/PersonRendererQuery.graphql';
7	
8	const PersonRenderer = () => {
9	const renderPersons = (people: PersonRendererQuery['response']['people']) => {
10	    return people.edges.map(({node}) => <PersonCard person={node} />);
11	  };
12	
13	  return (
14	    <QueryRenderer
15	      environment={Environment}
16	      query={graphql`
17	        query PersonRendererQuery {
18	          people {
19	            edges {
20	              node {
21	                ...PersonCard_person
22	              }
23	            }
24	          }
25	        }
26	      `}
27	      variables={null}
28	      render={({error, props}) => {
29	        if (error) {
30	          return (
31	            <View>
32	              <Text>{error.message}</Text>
33	            </View>
34	          );
35	        } else if (props) {
36	          return renderPersons(props.people);
37	        }
38	        return (
39	          <View>
40	            <Text>loading</Text>
41	          </View>
42	        );
43	      }}
44	    />
45	  );
46	};
47	
48	export default PersonRenderer;
```

- **PersonCard**

```javascript
1	import React from 'react';
2	
3	import {createFragmentContainer, graphql} from 'react-relay';
4	
5	import {View, Text} from 'react-native';
6	import {PersonCard_person} from './__generated__/PersonCard_person.graphql';
7	
8	type PersonCardProps = {
9	  person: PersonCard_person;
10	};
11	
12	const PersonCard = ({person}: PersonCardProps) => {
13	  return (
14	    <View>
15	      <Text>Name: {person.name}</Text>
16	      <Text>Salary: {person.salary}</Text>
17	    </View>
18	  );
19	};
20	
21	export default createFragmentContainer(PersonCard, {
22	  person: graphql`
23	    fragment PersonCard_person on Person {
24	      id
25	      name
26	      salary
27	    }
28	  `,
29	});
```

## Conclusion

The final result of QueryRenderer demonstrated how the application can be abstracted. The application can display the Person inside of the Query Renderer. As the PersonCard has more components, it doesn’t change the way the Query Renderer was built.

The PersonRenderer was built to show how a query can be done in easy steps, combined with the power of the Back4App server. In the next guide, you will learn how to retrieve a specific Person object and show their attributes.

[title] Download Schema
[path] React Native/Relay (GraphQL)/

# Download the GraphQL Schema

## Introduction

In our previous guide we’ve learned more about the amazing GraphQL client: Relay. Now it’s time to understand how you can use Relay to fetch data from Back4App in order to use it on your React Native App. In this guide you will learn how to download the GraphQL schema file and place it on your react native project.

## Goal

To prepare your React Native project to use Back4App GraphQL API by downloading the schema.

## Prerequisites

:::hint{type="info"}
**This is not a tutorial yet, but to feel free confortable reading it you will need:**

- Basic JavaScript knowledge.
- Basic understand about GraphQL. If you don’t have, the <a href="https://github.com/graphql/graphql-js" target="_blank">GraphQL.js</a> its a perfect place to start
- A React Native basic project running on your local environment.
:::

## 1 - Downloading the Schema

The schema is your source of truth from the server that will be located on your frontend. On Back4App the schema is an automatic file generated once you define your data model. To better understand the Back4App GraphQL schema you can open it on GraphQL console by doing the following steps:

- **go to your app Back4App dashboard;**
- **On the left menu, click on API Console, under the Core tab;**
- **Choose GraphQL and you will see something like this:**

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/KdHtTFz1mPU1egcwkRRPY_image.png" signedSrc size="50" width="301" height="381" position="center" caption}

Welcome to GraphQL playground. Here you can write and run queries, mutations using SDL language.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/McqyWZu540CO0FabPC8Ej_image.png)

On the top-right side you will see two tabs: DOCS and SCHEMA. On the DOCS tab you will find an easy to read documentation of the GraphQL API. The documentation is based on the object types that are generated once you build your data model on Back4App. For devs this is awesome because it can be used as a quick reference to build your query and mutations.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4UuV6znwUAYhlGGwU_pzH_image.png" signedSrc size="70" width="333" height="816" position="center" caption}

Back4App also also generates a specification to your GraphQL API: the popular known schema. The schema is found on the SCHEMA tab and is written using SDL(Schema Definition Language). The schema will be used as a source of truth on your frontend.

Go ahead, click over the SDL download and get the file to use on the next step.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QK48B1doTZ_y9nABX-iD8_image.png" signedSrc size="70" width="350" height="816" position="center" caption}

## 2 - Pasting the Schema on React Native App

In order Relay can read the schema file on the React Native app you need to paste the schema file on a specific path. Let’s make this on your React Native project:

- Create a folder on your application root path and name it data.
- Paste the schema file (SDL) file on this folder

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/KKTHuylEAYseoozmu_dwp_image.png" signedSrc size="50" width="219" height="522" position="center" caption}

Done. Your frontend already has the source of truth from your backend with your GraphQL schema. The final configuration is shown below.

:::hint{type="danger"}
Important: Every time you change your data model on Back4App your schema file will change. It is very important to keep the schema file always UPDATED on your front-end so every time you change the schema you need to upload it again on your React Native App project
:::

## Conclusion

With the schema already placed on your react native application, now you will learn how to configure and prepare your environment to be able to fetch your components queries.

[title] Current User
[path] React Native/Parse SDK (REST)/Users/

# Get current User for React Native

## Introduction

After implementing user registration and login to your, you need to retrieve the currently logged user data to perform different actions and requests. Since React Native uses AsyncStorage as the local storage, this data can be retrieved using Parse.currentAsync inside your app’s component.

::embed[]{url="https://www.youtube.com/embed/Iy_6bCh_ecg"}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## Goal

Get the current user data fetching using Parse for a React Native App.

## 1 - Retrieving Current User

The method Parse.currentAsync can be used anywhere on your code, after properly configuring your app to use Parse and AsyncStorage. Its response will be your current user’s object (Parse.User) or null if there is no logged-in user currently.

:::CodeblockTabs
JavaScript

```javascript
1	const getCurrentUser = async function () {
2	  const currentUser = await Parse.User.currentAsync();
3	  if (currentUser !== null) {
4	    Alert.alert(
5	      'Success!',
6	      `${currentUser.get('username')} is the current user!`,
7	    );
8	  }
9	  return currentUser;
10	};
```

```typescript
1	const getCurrentUser = async function (): Promise<Parse.User> {
2	  const currentUser: Parse.User = await Parse.User.currentAsync();
3	  if (currentUser !== null) {
4	    Alert.alert(
5	      'Success!',
6	      `${currentUser.get('username')} is the current user!`,
7	    );
8	  }
9	  return currentUser;
10	};
```
:::

This method is essential in situations where you don’t have access to your application state or your user data, making it possible to perform relevant Parse requests in any component of your app.

## 2 - Using Current User in a React Native component

In our previous guides, Parse.currentAsync was already used for testing and inside the HelloUser component. Here is the complete component again:

:::CodeblockTabs
HelloUser.js

```javascript
1	import React, {FC, ReactElement, useEffect, useState} from 'react';
2	import {Text, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import Styles from './Styles';
5	
6	export const HelloUser = () => {
7	  // State variable that will hold username value
8	  const [username, setUsername] = useState('');
9	
10	  // useEffect is called after the component is initially rendered and
11	  // after every other render
12	  useEffect(() => {
13	    // Since the async method Parse.User.currentAsync is needed to
14	    // retrieve the current user data, you need to declare an async
15	    // function here and call it afterwards
16	    async function getCurrentUser() {
17	      // This condition ensures that username is updated only if needed
18	      if (username === '') {
19	        const currentUser = await Parse.User.currentAsync();
20	        if (currentUser !== null) {
21	          setUsername(currentUser.getUsername());
22	        }
23	      }
24	    }
25	    getCurrentUser();
26	  }, [username]);
27	
28	  // Note the conditional operator here, so the "Hello" text is only
29	  // rendered if there is an username value
30	  return (
31	    <View style={Styles.login_wrapper}>
32	      <View style={Styles.form}>
33	        {username !== '' && <Text>{`Hello ${username}!`}</Text>}
34	      </View>
35	    </View>
36	  );
37	};
```

HelloUser.tsx

```typescript
1	import React, {FC, ReactElement, useEffect, useState} from 'react';
2	import {Text, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import Styles from './Styles';
5	
6	export const HelloUser: FC<{}> = ({}): ReactElement => {
7	  // State variable that will hold username value
8	  const [username, setUsername] = useState('');
9	
10	  // useEffect is called after the component is initially rendered and
11	  // after every other render
12	  useEffect(() => {
13	    // Since the async method Parse.User.currentAsync is needed to
14	    // retrieve the current user data, you need to declare an async
15	    // function here and call it afterwards
16	    async function getCurrentUser() {
17	      // This condition ensures that username is updated only if needed
18	      if (username === '') {
19	        const currentUser = await Parse.User.currentAsync();
20	        if (currentUser !== null) {
21	          setUsername(currentUser.getUsername());
22	        }
23	      }
24	    }
25	    getCurrentUser();
26	  }, [username]);
27	
28	  // Note the conditional operator here, so the "Hello" text is only
29	  // rendered if there is an username value
30	  return (
31	    <View style={Styles.login_wrapper}>
32	      <View style={Styles.form}>
33	        {username !== '' && <Text>{`Hello ${username}!`}</Text>}
34	      </View>
35	    </View>
36	  );
37	};
```
:::

In this case, the Parse.currentAsync method retrieves the username and updates the state variable that is rendered inside the component JSX.

## Conclusion

At the end of this guide, you learned how to retrieve the current Parse user data from local storage on React Native. In the next guide, we will show you how to allow your user to reset his password.

[title] Query Cookbook
[path] React Native/Parse SDK (REST)/Data objects/

# Parse Query Cookbook in React Native

## Introduction

We’ve already seen how a Parse.Query with get can retrieve a single Parse.Object from Back4App. There are many other ways to retrieve data with Parse.Query - you can retrieve many objects at once, use conditions on the objects you wish to retrieve, and more.

In this guide, you will ding deep into the Parse.Query class and see all the methods you can use to build your Queries. You will use a simple database class with some mocked data to perform the Queries using the Javascript Console on Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
:::

## Goal

Explore the Parse.Query class different methods.

## The Parse.Query class

Any query operation on Parse uses the Parse.Query object type, which will help you retrieve specific data from your Back4App throughout your app. 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.

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.

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## Using the JS Console on Back4App

Inside your Back4App application’s dashboard, you will find a very useful API console in which you can run JavaScript code directly. In this guide you will use to store and query data objects from Back4App. On your App main dashboard go to Core->API Console->JS Console.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wCOUHbylQUagFfnnZoMGa_image.png)

## Save your Data Objects

To run the queries on this guide you’ll need first to populate your App with some data. Let’s create a sample class called Profile, which mocks a social media profile class using famous people names and the following fields:

- string typename:
- Date typebirthDay:
- Number (integer) typefriendCount:
- Array (string array) typefavoriteFoods:
- Array (Number array) typeluckyNumbers:
- GeoPoint typelastLoginLocation:
- Nullable pointer typepremiumMembership, related to a Membership class containing string name and Date expirationDatefields.

Here is the Parse.Object classes creation code, so go ahead and run it in your API console:

```javascript
1	// Add Profile objects and create table
2	// Adam Sandler
3	let Profile = new Parse.Object('Profile');
4	Profile.set('name', 'Adam Sandler');
5	Profile.set('birthDay', new Date('09/09/1966'));
6	Profile.set('friendCount', 2);
7	Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8	Profile.set('luckyNumbers', [2, 7]);
9	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
10	await Profile.save();
11	
12	// Britney Spears
13	Profile = new Parse.Object('Profile');
14	Profile.set('name', 'Britney Spears');
15	Profile.set('birthDay', new Date('12/02/1981'));
16	Profile.set('friendCount', 52);
17	Profile.set('favoriteFoods', ['Cake', 'Bread']);
18	Profile.set('luckyNumbers', [22, 7]);
19	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
20	await Profile.save();
21	
22	// Carson Kressley
23	Profile = new Parse.Object('Profile');
24	Profile.set('name', 'Carson Kressley');
25	Profile.set('birthDay', new Date('11/11/1969'));
26	Profile.set('friendCount', 12);
27	Profile.set('favoriteFoods', ['Fish', 'Cookies']);
28	Profile.set('luckyNumbers', [8, 2]);
29	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
30	await Profile.save();
31	
32	// Dan Aykroyd
33	// Creates related object Membership for this user only
34	let Membership = new Parse.Object('Membership');
35	Membership.set('name', 'Premium');
36	Membership.set('expirationDate', new Date('10/10/2030'))
37	await Membership.save();
38	Profile = new Parse.Object('Profile');
39	Profile.set('name', 'Dan Aykroyd');
40	Profile.set('birthDay', new Date('07/01/1952'));
41	Profile.set('friendCount', 66);
42	Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
43	Profile.set('luckyNumbers', [22, 77]);
44	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
45	Profile.set('premiumMembership', Membership);
46	await Profile.save();
47	
48	// Eddie Murphy
49	Profile = new Parse.Object('Profile');
50	Profile.set('name', 'Eddie Murphy');
51	Profile.set('birthDay', new Date('04/03/1961'));
52	Profile.set('friendCount', 49);
53	Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
54	Profile.set('luckyNumbers', [6, 5]);
55	Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
56	await Profile.save();
57	
58	// Fergie
59	Profile = new Parse.Object('Profile');
60	Profile.set('name', 'Fergie');
61	Profile.set('birthDay', new Date('03/27/1975'));
62	Profile.set('friendCount', 55);
63	Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
64	Profile.set('luckyNumbers', [13, 7]);
65	Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
66	await Profile.save();
67	
68	console.log('Success!');
```

After running this code, you should now have a Profile class in your database with six objects created. Your new class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3N7_vbgwgh6HMao1XuEh3_image.png)

Let’s now take a look at examples from every Parse.Query method, along with brief explanations on what they do. Please note that some methods in this list can take options as an additional argument, but in most cases, it is only related to masterKey usage and not relevant to this guide content, so this possibility will be omitted whenever not relevant.

## Query retrievers

These methods are responsible for running the query and retrieving its results, being always present in your query implementation.

:::CodeblockTabs
cancel

```javascript
//Cancels the current network request.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.find();
3	// This is hard to test in small databases, since by the time
4	// "cancel" is called, the "find" request is already resolved
5	queryResult = query.cancel();
6	console.log(queryResult);
```

count

```javascript
//Retrieves the count of Parse.Object results that meet the query criteria.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.count();
3	console.log(queryResult);
```

distinct

```javascript
//Runs the query and returns a list of unique values from the results and the specified key.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.distinct('favoriteFoods');
3	console.log(queryResult);
```

find

```javascript
//This is the basic method for retrieving your query results, always returning an array of Parse.Object instances, being empty when none are found.
1	let query = new Parse.Query('Profile');
2	// When using .find() in a query without other operations, you will
3	// get every object saved in your class
4	let queryResult = await query.find();
5	console.log(queryResult);
```

findAll

```javascript
//Retrieves a complete list of Parse.Objects that satisfy this query.
1	let query = new Parse.Query('Profile');
2	// When using .findAll() in a query without other operations, you will
3	// get every object saved in your class
4	let queryResult = await query.findAll();
5	console.log(queryResult);
```

first

```javascript
//Retrieves the first Parse.Object instance that meets the query criteria.
1	let query = new Parse.Query('Profile');
2	// Pay attention to your query ordering when using .first
3	let queryResult = await query.first();
4	console.log(queryResult);
```

get

```javascript
//This quick method is used to retrieve a single Parse.Object instance when you know its objectId.
1	let query = new Parse.Query('Profile');
2	// Since objectId is randomly set in your database, make sure to inform a
3	// valid one when testing this method
4	let queryResult = await query.get('C6ENdLnFdQ');
5	console.log(queryResult);
```
:::

## Query conditioners

These methods give you the possibility of applying conditional constraints to your query, which are arguably the most important operations in querying. Remember that these operations can all be chained before the results are retrieved, so many combinations can be achieved to solve your querying needs.

:::CodeblockTabs
\_addCondition

```javascript
//  Helper that is called by Parse for filtering objects using conditional constants, such as “$gt”, “$eq” and so on. Only usable by users in very specific cases.
1	let query = new Parse.Query('Profile');
2	query._addCondition('friendCount', '$gt', 25);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

\_regexStartWith

```javascript
//Helper used by Parse that converts string for regular expression at the beginning.
1	let query = new Parse.Query('Profile');
2	let result = query._regexStartWith('text');
3	console.log(result);
```

containedBy

```javascript
//Filters objects in which a key value must be contained by the provided array of values. Get objects where all array elements match.
1	let query = new Parse.Query('Profile');
2	query.containedBy('luckyNumbers', [2, 7]);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

containedIn

```javascript
//Filters objects in which a key value is contained in the provided array of values.
1	let query = new Parse.Query('Profile');
2	// containedIn can be used on any data type such as numbers and strings
3	query.containedIn('luckyNumbers', [2, 7]);
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

:::CodeblockTabs
contains

```javascript
//Filters objects in which a string key value contains the provided text value. Be aware that this condition is case-sensitive.
1	let query = new Parse.Query('Profile');
2	// This can be slow in large databases and are case sensitive
3	query.contains('name', 'da');
4	let queryResult = await query.find();
5	console.log(queryResult);
```

containsAll

```javascript
//Filters objects in which an array type key value must contain every value provided.
1	let query = new Parse.Query('Profile');
2	query.containsAll('luckyNumbers', [2, 7]);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

containsAllStartingWith

```javascript
//Filters objects in which an array type key value must contain every string value provided.
1	let query = new Parse.Query('Profile');
2	// These should be string values
3	query.containsAllStartingWith('favoriteFoods', ['Shrimp', 'Lobster']);
4	let queryResult = await query.find();
5	console.log(queryResult);
```

doesNotExist

```javascript
//Filters objects in which a key value is not set.
1	let query = new Parse.Query('Profile');
2	query.doesNotExist('premiumMembership');
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

:::CodeblockTabs
doesNotMatchKeyInQuery

```javascript
//  Requires that a key’s value does not match a value in an object returned by a different Parse.Query. Useful for multi-object querying.
1	let query = new Parse.Query('Profile');
2	// Multiple queries can be combined using this, useful when there are more objects
3	// related, not our example case
4	query.doesNotMatchKeyInQuery('friendCount', 'friendCount', new Parse.Query('Profile').lessThan('friendCount', 50));
5	query.greaterThan('friendCount', 10);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

doesNotMatchQuery

```javascript
//Requires that an object contained in the given key does not match another query. Useful for multi-object querying.
1	let innerQuery = new Parse.Query('Membership');
2	innerQuery.greaterThan('expirationDate', new Date());
3	let query = new Parse.Query('Profile');
4	query.exists('premiumMembership');
5	query.doesNotMatchQuery('premiumMembership', innerQuery);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

endsWith

```javascript
//Filters objects in which a string key’s value ends with the provided text value.
1	let query = new Parse.Query('Profile');
2	// This is faster than other string searches by using backend index
3	query.endsWith('name', 'ie');
4	let queryResult = await query.find();
5	console.log(queryResult);
```

equalTo

```javascript
//Filters objects in which a specific key’s value is equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// equalTo can be used in any data type
3	query.equalTo('friendCount', 2);
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

:::CodeblockTabs
exists

```javascript
//Filters objects in which a key value is set.
1	let query = new Parse.Query('Profile');
2	query.exists('premiumMembership');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

fullText

```javascript
//Filters objects in which a string key’s value wrap matches the provided text value in it. It can have many customizable options to improve its results, like language specification and score order. Check the Parse docs to have a complete understanding of that.
1	let query = new Parse.Query('Profile');
2	// fullText can be very powerful in text search queries, but
3	// also slow in large datasets when its options are not optimized
4	query.fullText('name', 'Spears', { diacriticSensitive: false, caseSensitive: false });
5	// In order to sort you must use select and ascending ($score is required)
6	query.ascending('$score');
7	query.select('$score');
8	let queryResult = await query.find();
9	console.log(queryResult);
```

greaterThan

```javascript
//Filters objects in which a specific key’s value is greater than the provided value.
1	let query = new Parse.Query('Profile');
2	// greaterThan can be used on numbers and dates
3	query.greaterThan('birthDay', new Date('08/19/1980'));
4	let queryResult = await query.find();
5	console.log(queryResult);
```

greaterThanOrEqualTo

```javascript
//Filters objects in which a specific key’s value is greater than or equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// greaterThanOrEqualTo can be used on numbers and dates
3	query.greaterThanOrEqualTo('friendCount', 49);
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

:::CodeblockTabs
lessThan

```javascript
//  Filters objects in which a specific key’s value is lesser than the provided value.
1	let query = new Parse.Query('Profile');
2	// lessThan can be used on numbers and dates
3	query.lessThan('birthDay', new Date('08/19/1980'));
4	let queryResult = await query.find();
5	console.log(queryResult);
```

lessThanOrEqualTo

```javascript
//Filters objects in which a specific key’s value is lesser than or equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// lessThanOrEqualTo can be used on numbers and dates
3	query.lessThanOrEqualTo('friendCount', 49);
4	let queryResult = await query.find();
5	console.log(queryResult);
```

matches

```javascript
//Filters objects in which a string type key value must match the provided regular expression and its modifiers.
1	let query = new Parse.Query('Profile');
2	// Using the "i" modifier is a quick way to achieve
3	// case insensitive string querying
4	query.matches('name', 'da', 'i');
5	let queryResult = await query.find();
6	console.log(queryResult);
```

matchesKeyInQuery

```javascript
//Requires that a key’s value matches a value in an object returned by a different Parse.Query. Useful for multi-object querying.
1	let query = new Parse.Query('Profile');
2	// Multiple queries can be combined using this, useful when there are more objects
3	// related, not our example case
4	query.matchesKeyInQuery('friendCount', 'friendCount', new Parse.Query('Profile').lessThan('friendCount', 50));
5	query.greaterThan('friendCount', 10);
6	let queryResult = await query.find();
7	console.log(queryResult);
```
:::

:::CodeblockTabs
matchesQuery

```javascript
//Requires that an object contained in the given key matches another query. Useful for multi-object querying.
1	let innerQuery = new Parse.Query('Membership');
2	innerQuery.greaterThan('expirationDate', new Date());
3	let query = new Parse.Query('Profile');
4	query.exists('premiumMembership');
5	query.matchesQuery('premiumMembership', innerQuery);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

notEqualTo

```javascript
//Filters objects in which a specific key’s value is not equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// notEqualTo can be used in any data type
3	query.notEqualTo("friendCount", 2);
4	let queryResult = await query.find();
5	console.log(queryResult);
```

startsWith

```javascript
//Filters objects in which a string key’s value starts with the provided text value.
1	let query = new Parse.Query('Profile');
2	// This is faster than other string searches by using backend index
3	query.startsWith('name', 'Brit');
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

## Query ordering

Essential in most queries, ordering can be easily achieved in Parse and even chained between two or more ordering constraints.

:::CodeblockTabs
addAscending

```javascript
//  Sort the results in ascending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.addAscending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

addDescending

```javascript
//Sort the results in descending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.addDescending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

ascending

```javascript
//Sort the results in ascending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.ascending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

descending

```javascript
//Sort the results in descending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.descending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

sortByTextScore

```javascript
//Sorts by text score when using Parse.Query.fullText
1	let query = new Parse.Query('Profile');
2	query.fullText('name', 'Dan', { diacriticSensitive: false, caseSensitive: false });
3	query.sortByTextScore();
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

## Field selecting

These methods affect which field values can be in your query results.

:::CodeblockTabs
exclude

```javascript
//  Return all fields in the returned objects except the ones specified.
1	let query = new Parse.Query('Profile');
2	query.exclude('name');
3	let queryResult = await query.find();
4	console.log(queryResult[0].get('name') === undefined);
5	console.log(queryResult[0].get('birthDay'));
```

include

```javascript
//Includes nested Parse.Objects for the provided key.
1	let query = new Parse.Query('Profile');
2	query.exists('premiumMembership');
3	query.include('premiumMembership');
4	let queryResult = await query.find();
5	console.log(queryResult[0].get('premiumMembership'));
```

includeAll

```javascript
//Includes all nested Parse.Objects.
1	let query = new Parse.Query('Profile');
2	query.exists('premiumMembership');
3	query.includeAll();
4	let queryResult = await query.find();
5	console.log(queryResult[0].get('premiumMembership'));
```

select

```javascript
//Return only the specified fields in the returned objects.
1	let query = new Parse.Query('Profile');
2	query.select('name');
3	let queryResult = await query.find();
4	console.log(queryResult[0].get('birthDay') === undefined);
5	console.log(queryResult[0].get('name'));
```
:::

## Geopoint querying

These are methods specific to GeoPoint querying.

:::CodeblockTabs
near

```javascript
//  Order objects by how near the key value is from the given GeoPoint.
1	let query = new Parse.Query('Profile');
2	query.near('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
3	let queryResult = await query.find();
4	console.log(queryResult);
```

polygonContains

```javascript
//Find objects whose key value contains the specified GeoPoint.
1	let query = new Parse.Query('Profile');
2	query.polygonContains('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
3	let queryResult = await query.find();
4	console.log(queryResult);
```

withinGeoBox

```javascript
//Find objects whose key value is contained within the specified bounding box, composed by two GeoPoint values that set the lower-left and upper-right corner values.
1	let query = new Parse.Query('Profile');
2	query.withinGeoBox('lastLoginLocation', new Parse.GeoPoint(37.48412167489413, -122.11268034622319), new Parse.GeoPoint(37.28412167489413, -121.91268034622319));
3	let queryResult = await query.find();
4	console.log(queryResult);
```

withinKilometers

```javascript
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1	let query = new Parse.Query('Profile');
2	query.withinKilometers('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319), 100, true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

:::CodeblockTabs
withinMiles

```javascript
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1	let query = new Parse.Query('Profile');
2	query.withinMiles('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319), 60, true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

withinPolygon

```javascript
//Find objects whose key value is contained within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.
1	let query = new Parse.Query('Profile');
2	query.withinPolygon('lastLoginLocation', [new Parse.GeoPoint(37.48412167489413, -122.11268034622319), new Parse.GeoPoint(37.48412167489413, -121.91268034622319), new Parse.GeoPoint(37.28412167489413, -121.91268034622319), new Parse.GeoPoint(37.28412167489413, -122.01268034622319)]);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

withinRadians

```javascript
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1	let query = new Parse.Query('Profile');
2	query.withinRadians('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319), 1.5, true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## Pagination

These methods are related to pagination utilities, useful for queries that will retrieve a large number of results.

:::CodeblockTabs
limit

```javascript
//  Sets the maximum value of returned results, the default value is 100.
1	let query = new Parse.Query('Profile');
2	query.limit(2);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

skip

```javascript
//Skips the first n results in the query, essential for pagination.
1	let query = new Parse.Query('Profile');
2	query.skip(2);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

withCount

```javascript
//Sets a flag that will wrap or not the query response in an object containing results, holding the array of Parse.Object and count integer holding the total number of results.
1	let query = new Parse.Query('Profile');
2	query.withCount(true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## Response handling

These methods are helpers for handling the query responses, making it possible to queue callbacks that will be called after your query is resolved. They act as query resolvers as well, like find and first.

:::CodeblockTabs
each

```javascript
//  Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.each((result) => console.log(result));
3	console.log(queryResult);
```

eachBatch

```javascript
//Iterates over each result from the query and calls a callback for each batch of results, in an unspecified order. The batchSize value needs to be passed inside the options object parameter, being the default 100. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.eachBatch((result) => console.log(result), {batchSize: 2});
3	console.log(queryResult);
```

filter

```javascript
//Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises. Differs from each for passing more parameters down on callback execution.
1   let query = new Parse.Query('Profile');
2   let queryResult = await query.filter((currentObject, index, query) => console.log(`${index} - ${currentObject} - ${query}`));
3   console.log(queryResult);
```

map

```javascript
//Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises. Differs from each for passing more parameters down on callback execution.
1   let query = new Parse.Query('Profile');
2   let queryResult = await query.map((currentObject, index, query) => console.log(`${index} - ${currentObject} - ${query}`));
3   console.log(queryResult);
```

reduce

```javascript
//Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises. Differs from each for passing more parameters down on callback execution and by allowing direct accumulator handling. The initialValue is the value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first object in the query will be used and skipped.
1   let query = new Parse.Query('Profile');
2   let queryResult = await query.reduce((accumulator, currentObject, index) => console.log(`${index} - ${currentObject} - ${accumulator}`));
3   console.log(queryResult);
```
:::

## Compound query

These methods will create compound queries, which can combine more than one Parse.Query instance to achieve more complex results.

:::CodeblockTabs
\_andQuery

```javascript
//  Helper that is used by Parse to add a constraint that all of passed in queries must match when using Parse.Query.and.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = new Parse.Query('Profile');
6	query._andQuery([query1, query2]);
7	let queryResult = await query.find();
8	console.log(queryResult);
```

\_norQuery

```javascript
//Helper that is used by Parse when using Parse.Query.nor.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = new Parse.Query('Profile');
6	query._norQuery([query1, query2]);
7	let queryResult = await query.find();
8	console.log(queryResult);
```

\_orQuery

```javascript
//Helper that is used by Parse to add a constraint that any of passed in queries must match when using Parse.Query.or.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = new Parse.Query('Profile');
6	query._orQuery([query1, query2]);
7	let queryResult = await query.find();
8	console.log(queryResult);
```

and

```javascript
//Compose a compound query that is the AND of the passed queries.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = Parse.Query.and(query1, query2);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

nor

```javascript
//Compose a compound query that is the NOR of the passed queries.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = Parse.Query.nor(query1, query2);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

or

```javascript
//Compose a compound query that is the OR of the passed queries.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = Parse.Query.or(query1, query2);
6	let queryResult = await query.find();
7	console.log(queryResult);
```
:::

## Database related

These methods are related to database preferences and operations.

:::CodeblockTabs
aggregate

```javascript
//  Executes an aggregate query, retrieving objects over a set of input values. Please refer to MongoDB documentation on aggregate for better understanding.
1   let query = new Parse.Query('Profile');
2   let queryResult = await query.aggregate({ limit: 5 });
3   console.log(queryResult);
```

explain

```javascript
//Investigates the query execution plan, related to MongoDB explain operation.
1	let query = new Parse.Query('Profile');
2	query.explain(true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

readPreference

```javascript
//SWhen using a MongoDB replica set, use this method to choose from which replica the objects will be retrieved. The possible values are PRIMARY (default), PRIMARY_PREFERRED, SECONDARY, SECONDARY_PREFERRED, or NEAREST.
1	let query = new Parse.Query('Profile');
2	query.readPreference(''PRIMARY'');
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## Local datastore

These methods enable selecting the source of the queries and using a local datastore.

:::CodeblockTabs
fromLocalDatastore

```javascript
//  Changes the source of this query to all pinned objects.
1	// This should be set before using fromLocalDatastore
2	Parse.enableLocalDatastore();
3	let query = new Parse.Query('Profile');
4	query.fromLocalDatastore();
5	let queryResult = await query.find();
6	console.log(queryResult);
```

fromNetwork

```javascript
//Changes the source of this query to your online server.
1	let query = new Parse.Query('Profile');
2	query.fromNetwork();
3	let queryResult = await query.find();
4	console.log(queryResult);
```

fromPin

```javascript
//Changes the source of this query to the default group of pinned objects.
1	let query = new Parse.Query('Profile');
2	query.fromPin();
3	let queryResult = await query.find();
4	console.log(queryResult);
```

fromPinWithName

```javascript
//Changes the source of this query to a specific group of pinned objects.
1	let query = new Parse.Query('Profile');
2	query.fromPinWithName('pinnedObjects');
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## JSON specifics

Methods that allow queries to be represented as JSON and retrieved.

:::CodeblockTabs
toJSON

```json
//  Returns a JSON representation of this query operations.
1	let query = new Parse.Query('Profile');
2	query.greaterThan('friendCount', 10);
3	let queryJSON = await query.toJSON();
4	console.log(queryJSON);
```

withJSON

```json
//Add a previously generated JSON representation of query operations to this query.
1	let query = new Parse.Query('Profile');
2	query.greaterThan('friendCount', 10);
3	let queryJSON = await query.toJSON();
4	let query2 = new Parse.Query('Profile');
5	query2.withJSON(queryJSON);
6	let queryResult = await query2.find();
7	console.log(queryResult);

//Static method to restore Parse.Query by JSON representation, internally calling Parse.Query.withJSON.
1	let query = new Parse.Query('Profile');
2	query.greaterThan('friendCount', 10);
3	let queryJSON = await query.toJSON();
4	let query2 = Parse.Query.withJSON('Profile', queryJSON);
5	let queryResult = await query2.find();
6	console.log(queryResult);
```
:::

## Conclusion

At the end of this guide, you learned how to perform every data query method in Parse. In the next guide, you will learn about complex Parse querying in React Native.

[title] Basic Queries
[path] React Native/Parse SDK (REST)/Data objects/

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

::embed[]{url="https://www.youtube.com/embed/IeQievfY-tM"}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

Query data stored on Back4App from a React Native App.

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

```javascript
1   // This will create your query
2   let parseQuery = new Parse.Query("Profile");
3   // The query will resolve only after calling this method
4   let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

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

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HRUb7bqUJv_QzXE2pjd4v_image.png)

Go ahead and create the user Profile class with the following example content:

```javascript
1	// Add Profile objects and create table
2	// Adam Sandler
3	let Profile = new Parse.Object('Profile');
4	Profile.set('name', 'Adam Sandler');
5	Profile.set('birthDay', new Date('09/09/1966'));
6	Profile.set('friendCount', 2);
7	Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8	await Profile.save();
9	
10	// Adam Levine
11	Profile = new Parse.Object('Profile');
12	Profile.set('name', 'Adam Levine');
13	Profile.set('birthDay', new Date('03/18/1979'));
14	Profile.set('friendCount', 52);
15	Profile.set('favoriteFoods', ['Cake', 'Bread']);
16	await Profile.save();
17	
18	// Carson Kressley
19	Profile = new Parse.Object('Profile');
20	Profile.set('name', 'Carson Kressley');
21	Profile.set('birthDay', new Date('11/11/1969'));
22	Profile.set('friendCount', 12);
23	Profile.set('favoriteFoods', ['Fish', 'Cookies']);
24	await Profile.save();
25	
26	// Dan Aykroyd
27	Profile = new Parse.Object('Profile');
28	Profile.set('name', 'Dan Aykroyd');
29	Profile.set('birthDay', new Date('07/01/1952'));
30	Profile.set('friendCount', 66);
31	Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
32	await Profile.save();
33	
34	// Eddie Murphy
35	Profile = new Parse.Object('Profile');
36	Profile.set('name', 'Eddie Murphy');
37	Profile.set('birthDay', new Date('04/03/1961'));
38	Profile.set('friendCount', 49);
39	Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
40	await Profile.save();
41	
42	// Fergie
43	Profile = new Parse.Object('Profile');
44	Profile.set('name', 'Fergie');
45	Profile.set('birthDay', new Date('03/27/1975'));
46	Profile.set('friendCount', 55);
47	Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
48	await Profile.save();
49	
50	console.log('Success!');
```

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

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('Profile');
3	
4	// `contains` is a basic query method that checks if string field
5	// contains a specific substring
6	parseQuery.contains('name', 'Adam');
7	
8	// The query will resolve only after calling this method, retrieving
9	// an array of `Parse.Objects`
10	let queryResults = await parseQuery.find();
11	
12	// Let's show the results
13	for (let result of queryResults) {
14	  // You access `Parse.Objects` attributes by using `.get`
15	  console.log(result.get('name'));
16	};
```

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.

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('Profile');
3	
4	// `greaterThan` is a basic query method that does what it
5	// says on the tin
6	parseQuery.greaterThan('friendCount', 20);
7	
8	// The query will resolve only after calling this method, retrieving
9	// an array of `Parse.Objects`
10	let queryResults = await parseQuery.find();
11	
12	// Let's show the results
13	for (let result of queryResults) {
14	  // You access `Parse.Objects` attributes by using `.get`
15	  console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}`);
16	};
```

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.

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('Profile');
3	
4	// `descending` and `ascending` can and should be chained
5	// with other query methods to improve your queries
6	parseQuery.descending('birthDay');
7	
8	// The query will resolve only after calling this method, retrieving
9	// an array of `Parse.Objects`
10	let queryResults = await parseQuery.find();
11	
12	// Let's show the results
13	for (let result of queryResults) {
14	  // You access `Parse.Objects` attributes by using `.get`
15	  console.log(`name: ${result.get('name')}, birthday: ${result.get('birthDay')}`);
16	};
```

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:

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('Profile');
3	
4	parseQuery.contains('name', 'Adam');
5	parseQuery.greaterThan('friendCount', 20);
6	parseQuery.descending('birthDay');
7	
8	// The query will resolve only after calling this method, retrieving
9	// an array of `Parse.Objects`
10	let queryResults = await parseQuery.find();
11	
12	// Let's show the results
13	for (let result of queryResults) {
14	  // You access `Parse.Objects` attributes by using `.get`
15	  console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}, birthday: ${result.get('birthDay')}`);
16	};
```

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

:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  Button as PaperButton,
8	  Text as PaperText,
9	} from 'react-native-paper';
10	
11	export const BookList = () => {
12	  // State variable
13	  const [queryResults, setQueryResults] = useState(null);
14	
15	  const doQueryByName = async function () {
16	    // Create our Parse.Query instance so methods can be chained
17	    // Reading parse objects is done by using Parse.Query
18	    const parseQuery = new Parse.Query('Profile');
19	
20	    // `contains` is a basic query method that checks if string field
21	    // contains a specific substring
22	    parseQuery.contains('name', 'Adam');
23	
24	    try {
25	      let profiles = await parseQuery.find();
26	      setQueryResults(profiles);
27	      return true;
28	    } catch (error) {
29	      // Error can be caused by lack of Internet connection
30	      Alert.alert('Error!', error.message);
31	      return false;
32	    }
33	  };
34	
35	  const doQueryByFriendCount = async function () {
36	    // Create our Parse.Query instance so methods can be chained
37	    // Reading parse objects is done by using Parse.Query
38	    const parseQuery = new Parse.Query('Profile');
39	
40	    // `greaterThan` is a basic query method that does what it
41	    // says on the tin
42	    parseQuery.greaterThan('friendCount', 20);
43	
44	    try {
45	      let profiles = await parseQuery.find();
46	      setQueryResults(profiles);
47	      return true;
48	    } catch (error) {
49	      // Error can be caused by lack of Internet connection
50	      Alert.alert('Error!', error.message);
51	      return false;
52	    }
53	  };
54	
55	  const doQueryByOrdering = async function () {
56	    // Create our Parse.Query instance so methods can be chained
57	    // Reading parse objects is done by using Parse.Query
58	    const parseQuery = new Parse.Query('Profile');
59	
60	    // `descending` and `ascending` can and should be chained
61	    // with other query methods to improve your queries
62	    parseQuery.descending('birthDay');
63	
64	    try {
65	      let profiles = await parseQuery.find();
66	      setQueryResults(profiles);
67	      return true;
68	    } catch (error) {
69	      // Error can be caused by lack of Internet connection
70	      Alert.alert('Error!', error.message);
71	      return false;
72	    }
73	  };
74	
75	  const doQueryByAll = async function () {
76	    // Create our Parse.Query instance so methods can be chained
77	    // Reading parse objects is done by using Parse.Query
78	    const parseQuery = new Parse.Query('Profile');
79	
80	    parseQuery.contains('name', 'Adam');
81	    parseQuery.greaterThan('friendCount', 20);
82	    parseQuery.descending('birthDay');
83	
84	    try {
85	      let profiles = await parseQuery.find();
86	      setQueryResults(profiles);
87	      return true;
88	    } catch (error) {
89	      // Error can be caused by lack of Internet connection
90	      Alert.alert('Error!', error.message);
91	      return false;
92	    }
93	  };
94	
95	  const clearQueryResults = async function () {
96	    setQueryResults(null);
97	    return true;
98	  };
99	
100	  return (
101	    <>
102	      <View style={Styles.header}>
103	        <Image
104	          style={Styles.header_logo}
105	          source={ {
106	            uri:
107	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
108	          } }
109	        />
110	        <PaperText style={Styles.header_text}>
111	          <PaperText style={Styles.header_text_bold}>
112	            {'React Native on Back4App - '}
113	          </PaperText>
114	          {' Basic Queries'}
115	        </PaperText>
116	      </View>
117	      <ScrollView style={Styles.wrapper}>
118	        <View>
119	          <Title>{'Result List'}</Title>
120	          {/* Book list */}
121	          {queryResults !== null &&
122	            queryResults !== undefined &&
123	            queryResults.map((profile) => (
124	              <List.Item
125	                key={profile.id}
126	                title={profile.get('name')}
127	                description={`Friend count: ${profile.get(
128	                  'friendCount',
129	                )}, Birthday: ${profile.get('birthDay')}`}
130	                titleStyle={Styles.list_text}
131	                style={Styles.list_item}
132	              />
133	            ))}
134	          {queryResults === null ||
135	          queryResults === undefined ||
136	          (queryResults !== null &&
137	            queryResults !== undefined &&
138	            queryResults.length <= 0) ? (
139	            <PaperText>{'No results here!'}</PaperText>
140	          ) : null}
141	        </View>
142	        <View>
143	          <Title>{'Query buttons'}</Title>
144	          <PaperButton
145	            onPress={() => doQueryByName()}
146	            mode="contained"
147	            icon="search-web"
148	            color={'#208AEC'}
149	            style={Styles.list_button}>
150	            {'Query by name'}
151	          </PaperButton>
152	          <PaperButton
153	            onPress={() => doQueryByFriendCount()}
154	            mode="contained"
155	            icon="search-web"
156	            color={'#208AEC'}
157	            style={Styles.list_button}>
158	            {'Query by friend count'}
159	          </PaperButton>
160	          <PaperButton
161	            onPress={() => doQueryByOrdering()}
162	            mode="contained"
163	            icon="search-web"
164	            color={'#208AEC'}
165	            style={Styles.list_button}>
166	            {'Query by ordering'}
167	          </PaperButton>
168	          <PaperButton
169	            onPress={() => doQueryByAll()}
170	            mode="contained"
171	            icon="search-web"
172	            color={'#208AEC'}
173	            style={Styles.list_button}>
174	            {'Query by all'}
175	          </PaperButton>
176	          <PaperButton
177	            onPress={() => clearQueryResults()}
178	            mode="contained"
179	            icon="delete"
180	            color={'#208AEC'}
181	            style={Styles.list_button}>
182	            {'Clear Results'}
183	          </PaperButton>
184	        </View>
185	      </ScrollView>
186	    </>
187	  );
188	};
189	
190	// These define the screen component styles
191	const Styles = StyleSheet.create({
192	  header: {
193	    alignItems: 'center',
194	    paddingTop: 30,
195	    paddingBottom: 50,
196	    backgroundColor: '#208AEC',
197	  },
198	  header_logo: {
199	    height: 50,
200	    width: 220,
201	    resizeMode: 'contain',
202	  },
203	  header_text: {
204	    marginTop: 15,
205	    color: '#f0f0f0',
206	    fontSize: 16,
207	  },
208	  header_text_bold: {
209	    color: '#fff',
210	    fontWeight: 'bold',
211	  },
212	  wrapper: {
213	    width: '90%',
214	    alignSelf: 'center',
215	  },
216	  list_button: {
217	    marginTop: 6,
218	    marginLeft: 15,
219	    height: 40,
220	  },
221	  list_item: {
222	    borderBottomWidth: 1,
223	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
224	  },
225	  list_text: {
226	    fontSize: 15,
227	  },
228	});
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  Button as PaperButton,
8	  Text as PaperText,
9	} from 'react-native-paper';
10	
11	export const QueryList: FC<{}> = ({}): ReactElement => {
12	  // State variable
13	  const [queryResults, setQueryResults] = useState(null);
14	
15	  const doQueryByName = async function (): Promise<boolean> {
16	    // Create our Parse.Query instance so methods can be chained
17	    // Reading parse objects is done by using Parse.Query
18	    const parseQuery: Parse.Query = new Parse.Query('Profile');
19	
20	    // `contains` is a basic query method that checks if string field
21	    // contains a specific substring
22	    parseQuery.contains('name', 'Adam');
23	
24	    try {
25	      let profiles: [Parse.Object] = await parseQuery.find();
26	      setQueryResults(profiles);
27	      return true;
28	    } catch (error) {
29	      // Error can be caused by lack of Internet connection
30	      Alert.alert('Error!', error.message);
31	      return false;
32	    }
33	  };
34	
35	  const doQueryByFriendCount = async function (): Promise<boolean> {
36	    // Create our Parse.Query instance so methods can be chained
37	    // Reading parse objects is done by using Parse.Query
38	    const parseQuery: Parse.Query = new Parse.Query('Profile');
39	
40	    // `greaterThan` is a basic query method that does what it
41	    // says on the tin
42	    parseQuery.greaterThan('friendCount', 20);
43	
44	    try {
45	      let profiles: [Parse.Object] = await parseQuery.find();
46	      setQueryResults(profiles);
47	      return true;
48	    } catch (error) {
49	      // Error can be caused by lack of Internet connection
50	      Alert.alert('Error!', error.message);
51	      return false;
52	    }
53	  };
54	
55	  const doQueryByOrdering = async function (): Promise<boolean> {
56	    // Create our Parse.Query instance so methods can be chained
57	    // Reading parse objects is done by using Parse.Query
58	    const parseQuery: Parse.Query = new Parse.Query('Profile');
59	
60	    // `descending` and `ascending` can and should be chained
61	    // with other query methods to improve your queries
62	    parseQuery.descending('birthDay');
63	
64	    try {
65	      let profiles: [Parse.Object] = await parseQuery.find();
66	      setQueryResults(profiles);
67	      return true;
68	    } catch (error) {
69	      // Error can be caused by lack of Internet connection
70	      Alert.alert('Error!', error.message);
71	      return false;
72	    }
73	  };
74	
75	  const doQueryByAll = async function (): Promise<boolean> {
76	    // Create our Parse.Query instance so methods can be chained
77	    // Reading parse objects is done by using Parse.Query
78	    const parseQuery: Parse.Query = new Parse.Query('Profile');
79	
80	    parseQuery.contains('name', 'Adam');
81	    parseQuery.greaterThan('friendCount', 20);
82	    parseQuery.descending('birthDay');
83	
84	    try {
85	      let profiles: [Parse.Object] = await parseQuery.find();
86	      setQueryResults(profiles);
87	      return true;
88	    } catch (error) {
89	      // Error can be caused by lack of Internet connection
90	      Alert.alert('Error!', error.message);
91	      return false;
92	    }
93	  };
94	
95	  const clearQueryResults = async function (): Promise<boolean> {
96	    setQueryResults(null);
97	    return true;
98	  };
99	
100	  return (
101	    <>
102	      <View style={Styles.header}>
103	        <Image
104	          style={Styles.header_logo}
105	          source={ {
106	            uri:
107	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
108	          } }
109	        />
110	        <PaperText style={Styles.header_text}>
111	          <PaperText style={Styles.header_text_bold}>
112	            {'React Native on Back4App - '}
113	          </PaperText>
114	          {' Basic Queries'}
115	        </PaperText>
116	      </View>
117	      <ScrollView style={Styles.wrapper}>
118	        <View>
119	          <Title>{'Result List'}</Title>
120	          {/* Book list */}
121	          {queryResults !== null &&
122	            queryResults !== undefined &&
123	            queryResults.map((profile: Parse.Object) => (
124	              <List.Item
125	                key={profile.id}
126	                title={profile.get('name')}
127	                description={`Friend count: ${profile.get(
128	                  'friendCount',
129	                )}, Birthday: ${profile.get('birthDay')}`}
130	                titleStyle={Styles.list_text}
131	                style={Styles.list_item}
132	              />
133	            ))}
134	          {queryResults === null ||
135	          queryResults === undefined ||
136	          (queryResults !== null &&
137	            queryResults !== undefined &&
138	            queryResults.length <= 0) ? (
139	            <PaperText>{'No results here!'}</PaperText>
140	          ) : null}
141	        </View>
142	        <View>
143	          <Title>{'Query buttons'}</Title>
144	          <PaperButton
145	            onPress={() => doQueryByName()}
146	            mode="contained"
147	            icon="search-web"
148	            color={'#208AEC'}
149	            style={Styles.list_button}>
150	            {'Query by name'}
151	          </PaperButton>
152	          <PaperButton
153	            onPress={() => doQueryByFriendCount()}
154	            mode="contained"
155	            icon="search-web"
156	            color={'#208AEC'}
157	            style={Styles.list_button}>
158	            {'Query by friend count'}
159	          </PaperButton>
160	          <PaperButton
161	            onPress={() => doQueryByOrdering()}
162	            mode="contained"
163	            icon="search-web"
164	            color={'#208AEC'}
165	            style={Styles.list_button}>
166	            {'Query by ordering'}
167	          </PaperButton>
168	          <PaperButton
169	            onPress={() => doQueryByAll()}
170	            mode="contained"
171	            icon="search-web"
172	            color={'#208AEC'}
173	            style={Styles.list_button}>
174	            {'Query by all'}
175	          </PaperButton>
176	          <PaperButton
177	            onPress={() => clearQueryResults()}
178	            mode="contained"
179	            icon="delete"
180	            color={'#208AEC'}
181	            style={Styles.list_button}>
182	            {'Clear Results'}
183	          </PaperButton>
184	        </View>
185	      </ScrollView>
186	    </>
187	  );
188	};
189	
190	// These define the screen component styles
191	const Styles = StyleSheet.create({
192	  header: {
193	    alignItems: 'center',
194	    paddingTop: 30,
195	    paddingBottom: 50,
196	    backgroundColor: '#208AEC',
197	  },
198	  header_logo: {
199	    height: 50,
200	    width: 220,
201	    resizeMode: 'contain',
202	  },
203	  header_text: {
204	    marginTop: 15,
205	    color: '#f0f0f0',
206	    fontSize: 16,
207	  },
208	  header_text_bold: {
209	    color: '#fff',
210	    fontWeight: 'bold',
211	  },
212	  wrapper: {
213	    width: '90%',
214	    alignSelf: 'center',
215	  },
216	  list_button: {
217	    marginTop: 6,
218	    marginLeft: 15,
219	    height: 40,
220	  },
221	  list_item: {
222	    borderBottomWidth: 1,
223	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
224	  },
225	  list_text: {
226	    fontSize: 15,
227	  },
228	});
```
:::

This is how the component should look like after rendering and querying by all the query functions:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Xrlx6Jf234xT-UHjkVBLs_image.png" signedSrc size="50" width="348" height="734" position="center" caption}

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

[title] OTP Auth
[path] React Native/Parse SDK (REST)/Cloud Functions/

# OTP Authentication for React Native using Cloud Functions

## Introduction

In this guide, you will learn how to use a Parse Cloud Code function to integrate Twilio Verify API in a React Native App to enable an OTP login feature. Cloud Code functions are the perfect place for this type of operations since it allows you to handle sensitive data and use other APIs outside of your application.

In this guide you will also check how to implement a React Native component using the OTP integration, improving our previous guide User Email Verification example.

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Understand how to <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">deploy a cloud function</a> on Back4App.
- An active account at Twilio, you can create a free trial one <a href="https://login.twilio.com/u/signup?state=hKFo2SBWNGZlcUY5cjJ3WGFZaHQwcnIzME5KQTVpVG51YjBZU6Fur3VuaXZlcnNhbC1sb2dpbqN0aWTZIHg1LVRWTXpRaUphT3d4MGhZWUNXQm5WQi0xdDM1WDhLo2NpZNkgTW05M1lTTDVSclpmNzdobUlKZFI3QktZYjZPOXV1cks" target="_blank">here</a>.
- An active account at SendGrid, you can create a free trial one <a href="https://login.twilio.com/u/signup?state=hKFo2SBFZTlfRWNTc2tFLWRDQUMtQUJJc3pGNUFnZFRGdlNtdqFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIHM3ek1JeWJoUGVBN3dNdlJSc0dlYTYwLWtVbWpPQ0NNo2NpZNkgR244UWMyZ1FOa2trZ0llT2s4QlJqRWZ5eWNoMTU2VUk" target="_blank">here</a>.
:::

## Goal

Integrate Twilio Verify API using Parse Cloud Code functions on Back4App and use it in a React Native App.

## 1 - Setting up Twilio Verify

Twilio is a well-known 2FA token and OTP service provider, used by a wide range of companies nowadays. The Verify API is a simplified API set to help developers to verify phones, emails and to perform passwordless logins with no hassle. In this guide, you will be using the SMS and email verification methods to enable your application to have an OTP feature. We will be following the official Verify [docs](https://www.twilio.com/docs/verify/api), so reference it if any step becomes confused to you.

After creating your Twilio and SendGrid accounts (SendGrid is needed for sending automated emails containing your user tokens), write down the following identifiers, that can be retrieved from each services dashboard:

- Twilio’s account SID and auth token;
- SendGrid ID and a simple email template ID.

Now, create a new Verify service in Twilio Verify [dashboard](https://www.twilio.com/console/verify/dashboard), which is a set of configurations that will manage our OTP requests and verifications. Make sure to write down your service SID here. If you want, you can also enable email code sending by adding your SendGrid keys by creating a new email integration within the Verify service [dashboard](https://www.twilio.com/docs/verify/email).

That’s it, all setup is done and we may now proceed to create our Cloud Code functions.

## 2 - Integrating via Cloud Code Functions

As discussed before, using Cloud Code functions in your application enables great flexibility in your code, making it possible to detach reusable methods from your app and to better control their behavior. You can check or review how to use them in [our Cloud functions starter guide](https://www.back4app.com/docs/get-started/cloud-functions).

Let´s create our first cloud function called requestOTP, in which an OTP will be created and sent to the user through the chosen method. The function needs to receive two parameters, userData containing an email or phone number and verificationType, specifying which verification method to be used, email or sms.

Here is the function code:

```javascript
1	// Define Twilio keys and require the library helper
2	const accountSid = 'YOUR_TWILIO_ACCOUNT_SID_HERE';
3	const authToken = 'YOUR_TWILIO_AUTH_TOKEN_HERE';
4	const client = require('twilio')(accountSid, authToken);
5	
6	// Request OTP
7	Parse.Cloud.define('requestOTP', async (request) => {
8	  const userData = request.params.userData;
9	  const verificationType = request.params.verificationType;
10	  let verification_request = await client.verify
11	    .services('YOUR_TWILIO_VERIFY_SERVICE_ID_HERE')
12	    .verifications.create({ to: userData, channel: verificationType });
13	  return { status: verification_request.status, error: '' };
14	});
```

Note that at the top we defined our Twilio API keys and also imported the helper library. Back4App’s Parse Server implementation provides us access to the Twilio library from default, so you don’t need to install it on your server.

The second and last cloud function is called verifyOTP, it verifies your user token in Twilio’s Verify and logs him in automatically, creating a session and passing its ID back to your application, so you can log in with no password from there. There are four required parameters, being the first two the same ones from the previous function, with the addition of userToken, containing the informed verifying token, and also userInstallationId, which will be explained later on.

```javascript
1	// Since we need in UUID v4 id for creating a new session, this function
2	// mocks the creation of one without the need to import the `uuidv4` module
3	// For a more robust solution, consider using the uuid module, which uses
4	// higher quality RNG APIs.
5	// Adapted from https://stackoverflow.com/a/2117523
6	function uuidv4() {
7	  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
8	    var r = (Math.random() * 16) | 0,
9	      v = c == 'x' ? r : (r & 0x3) | 0x8;
10	    return v.toString(16);
11	  });
12	}
13	
14	// Verify OTP
15	Parse.Cloud.define('verifyOTP', async (request) => {
16	  const { userData, verificationType, userToken, userInstallationId } =
17	    request.params;
18	  let verification_check = await client.verify
19	    .services('YOUR_TWILIO_VERIFY_SERVICE_ID_HERE')
20	    .verificationChecks.create({ to: userData, code: userToken });
21	  // Status can be 'approved' if correct or 'pending' if incorrect
22	  if (verification_check.status === 'approved') {
23	    try {
24	      // Get user to login
25	      let user = null;
26	      if (verificationType === 'sms') {
27	        user = await new Parse.Query(Parse.User)
28	          .equalTo('phoneNumber', userData)
29	          .first({ useMasterKey: true });
30	      } else {
31	        user = await new Parse.Query(Parse.User)
32	          .equalTo('email', userData)
33	          .first({ useMasterKey: true });
34	      }
35	      // Create new session for login use in
36	      // Manually create session (without using Parse.Session because of it's update restrictions)
37	      // Adapted from https://stackoverflow.com/a/67432715
38	      let session = new Parse.Object('_Session', {
39	        user: user,
40	        installationId: userInstallationId,
41	        sessionToken: `r:${uuidv4()}`,
42	      });
43	      session = await session.save(undefined, { useMasterKey: true });
44	      return { sessionId: session.get('sessionToken') };
45	    } catch (error) {
46	      console.log(error);
47	      return { error: `${error}` };
48	    }
49	  }
50	  return { error: 'Could not validate your token or account! Try again!' };
51	});
```

Make sure to deploy these functions in your Parse Server before moving to the next step.

## 3 - Creating an OTP feature in React Native

Let’s now use the same project example from the User Email Verification [guide](https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-email-verification) as a base and add some changes to it enabling the new OTP feature. We recommend downloading the project example and setting it up before continuing with the guide.

First, to allow users to log in using their phone number, add a new input field and add the value to the user registration saving method in the UserRegistration.js (or UserRegistration.tsx) file:

:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  Text,
6	  TextInput,
7	  TouchableOpacity,
8	  View,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import {useNavigation} from '@react-navigation/native';
12	import {StackActions} from '@react-navigation/native';
13	import Styles from './Styles';
14	
15	export const UserRegistration = () => {
16	  const navigation = useNavigation();
17	
18	  const [username, setUsername] = useState('');
19	  const [password, setPassword] = useState('');
20	  const [email, setEmail] = useState('');
21	  const [phoneNumber, setPhoneNumber] = useState('');
22	
23	  const doUserSignUp = async function () {
24	    // Note that this values come from state variables that we've declared before
25	    const usernameValue = username;
26	    const passwordValue = password;
27	    const emailValue = email;
28	    const phoneNumberValue = phoneNumber;
29	    try {
30	      // Since the signUp method returns a Promise, we need to call it using await
31	      // Note that now you are setting the user email value as well
32	      let createdUser = await Parse.User.signUp(usernameValue, passwordValue, {
33	        email: emailValue,
34	        phoneNumber: phoneNumberValue,
35	      });
36	
37	      // Parse.User.signUp returns the already created ParseUser object if successful
38	      Alert.alert(
39	        'Success!',
40	        `User ${createdUser.get(
41	          'username',
42	        )} was successfully created! Verify your email to login`,
43	      );
44	      // Since email verification is now required, make sure to log out
45	      // the new user, so any Session created is cleared and the user can
46	      // safely log in again after verifying
47	      await Parse.User.logOut();
48	      // Go back to the login page
49	      navigation.dispatch(StackActions.popToTop());
50	      return true;
51	    } catch (error) {
52	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
53	      Alert.alert('Error!', error.message);
54	      return false;
55	    }
56	  };
57	
58	  return (
59	    <View style={Styles.login_wrapper}>
60	      <View style={Styles.form}>
61	        <TextInput
62	          style={Styles.form_input}
63	          value={username}
64	          placeholder={'Username'}
65	          onChangeText={(text) => setUsername(text)}
66	          autoCapitalize={'none'}
67	          keyboardType={'email-address'}
68	        />
69	        <TextInput
70	          style={Styles.form_input}
71	          value={email}
72	          placeholder={'Email'}
73	          onChangeText={(text) => setEmail(text)}
74	          autoCapitalize={'none'}
75	          keyboardType={'email-address'}
76	        />
77	        <TextInput
78	          style={Styles.form_input}
79	          value={phoneNumber}
80	          placeholder={'Phone (international format +15017122661)'}
81	          onChangeText={(text) => setPhoneNumber(text)}
82	          autoCapitalize={'none'}
83	          keyboardType={'phone-pad'}
84	        />
85	        <TextInput
86	          style={Styles.form_input}
87	          value={password}
88	          placeholder={'Password'}
89	          secureTextEntry
90	          onChangeText={(text) => setPassword(text)}
91	        />
92	        <TouchableOpacity onPress={() => doUserSignUp()}>
93	          <View style={Styles.button}>
94	            <Text style={Styles.button_label}>{'Sign Up'}</Text>
95	          </View>
96	        </TouchableOpacity>
97	      </View>
98	      <View style={Styles.login_social}>
99	        <View style={Styles.login_social_separator}>
100	          <View style={Styles.login_social_separator_line} />
101	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
102	          <View style={Styles.login_social_separator_line} />
103	        </View>
104	        <View style={Styles.login_social_buttons}>
105	          <TouchableOpacity>
106	            <View
107	              style={[
108	                Styles.login_social_button,
109	                Styles.login_social_facebook,
110	              ]}>
111	              <Image
112	                style={Styles.login_social_icon}
113	                source={require('./assets/icon-facebook.png')}
114	              />
115	            </View>
116	          </TouchableOpacity>
117	          <TouchableOpacity>
118	            <View style={Styles.login_social_button}>
119	              <Image
120	                style={Styles.login_social_icon}
121	                source={require('./assets/icon-google.png')}
122	              />
123	            </View>
124	          </TouchableOpacity>
125	          <TouchableOpacity>
126	            <View style={Styles.login_social_button}>
127	              <Image
128	                style={Styles.login_social_icon}
129	                source={require('./assets/icon-apple.png')}
130	              />
131	            </View>
132	          </TouchableOpacity>
133	        </View>
134	      </View>
135	      <>
136	        <TouchableOpacity onPress={() => navigation.navigate('Login')}>
137	          <Text style={Styles.login_footer_text}>
138	            {'Already have an account? '}
139	            <Text style={Styles.login_footer_link}>{'Log In'}</Text>
140	          </Text>
141	        </TouchableOpacity>
142	      </>
143	    </View>
144	  );
145	};
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  Text,
6	  TextInput,
7	  TouchableOpacity,
8	  View,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import {useNavigation} from '@react-navigation/native';
12	import {StackActions} from '@react-navigation/native';
13	import Styles from './Styles';
14	
15	export const UserRegistration: FC<{}> = ({}): ReactElement => {
16	  const navigation = useNavigation();
17	
18	  const [username, setUsername] = useState('');
19	  const [password, setPassword] = useState('');
20	  const [email, setEmail] = useState('');
21	  const [phoneNumber, setPhoneNumber] = useState('');
22	
23	  const doUserSignUp = async function (): Promise<boolean> {
24	    // Note that this values come from state variables that we've declared before
25	    const usernameValue: string = username;
26	    const passwordValue: string = password;
27	    const emailValue: string = email;
28	    const phoneNumberValue: string = phoneNumber;
29	    try {
30	      // Since the signUp method returns a Promise, we need to call it using await
31	      // Note that now you are setting the user email value as well
32	      let createdUser = await Parse.User.signUp(usernameValue, passwordValue, {
33	        email: emailValue,
34	        phoneNumber: phoneNumberValue,
35	      });
36	
37	      // Parse.User.signUp returns the already created ParseUser object if successful
38	      Alert.alert(
39	        'Success!',
40	        `User ${createdUser.get(
41	          'username',
42	        )} was successfully created! Verify your email to login`,
43	      );
44	      // Since email verification is now required, make sure to log out
45	      // the new user, so any Session created is cleared and the user can
46	      // safely log in again after verifying
47	      await Parse.User.logOut();
48	      // Go back to the login page
49	      navigation.dispatch(StackActions.popToTop());
50	      return true;
51	    } catch (error: object) {
52	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
53	      Alert.alert('Error!', error.message);
54	      return false;
55	    }
56	  };
57	
58	  return (
59	    <View style={Styles.login_wrapper}>
60	      <View style={Styles.form}>
61	        <TextInput
62	          style={Styles.form_input}
63	          value={username}
64	          placeholder={'Username'}
65	          onChangeText={(text) => setUsername(text)}
66	          autoCapitalize={'none'}
67	          keyboardType={'email-address'}
68	        />
69	        <TextInput
70	          style={Styles.form_input}
71	          value={email}
72	          placeholder={'Email'}
73	          onChangeText={(text) => setEmail(text)}
74	          autoCapitalize={'none'}
75	          keyboardType={'email-address'}
76	        />
77	        <TextInput
78	          style={Styles.form_input}
79	          value={phoneNumber}
80	          placeholder={'Phone (international format +15017122661)'}
81	          onChangeText={(text) => setPhoneNumber(text)}
82	          autoCapitalize={'none'}
83	          keyboardType={'phone-pad'}
84	        />
85	        <TextInput
86	          style={Styles.form_input}
87	          value={password}
88	          placeholder={'Password'}
89	          secureTextEntry
90	          onChangeText={(text) => setPassword(text)}
91	        />
92	        <TouchableOpacity onPress={() => doUserSignUp()}>
93	          <View style={Styles.button}>
94	            <Text style={Styles.button_label}>{'Sign Up'}</Text>
95	          </View>
96	        </TouchableOpacity>
97	      </View>
98	      <View style={Styles.login_social}>
99	        <View style={Styles.login_social_separator}>
100	          <View style={Styles.login_social_separator_line} />
101	          <Text style={Styles.login_social_separator_text}>{'or'}</Text>
102	          <View style={Styles.login_social_separator_line} />
103	        </View>
104	        <View style={Styles.login_social_buttons}>
105	          <TouchableOpacity>
106	            <View
107	              style={[
108	                Styles.login_social_button,
109	                Styles.login_social_facebook,
110	              ]}>
111	              <Image
112	                style={Styles.login_social_icon}
113	                source={require('./assets/icon-facebook.png')}
114	              />
115	            </View>
116	          </TouchableOpacity>
117	          <TouchableOpacity>
118	            <View style={Styles.login_social_button}>
119	              <Image
120	                style={Styles.login_social_icon}
121	                source={require('./assets/icon-google.png')}
122	              />
123	            </View>
124	          </TouchableOpacity>
125	          <TouchableOpacity>
126	            <View style={Styles.login_social_button}>
127	              <Image
128	                style={Styles.login_social_icon}
129	                source={require('./assets/icon-apple.png')}
130	              />
131	            </View>
132	          </TouchableOpacity>
133	        </View>
134	      </View>
135	      <>
136	        <TouchableOpacity onPress={() => navigation.navigate('Login')}>
137	          <Text style={Styles.login_footer_text}>
138	            {'Already have an account? '}
139	            <Text style={Styles.login_footer_link}>{'Log In'}</Text>
140	          </Text>
141	        </TouchableOpacity>
142	      </>
143	    </View>
144	  );
145	};
```
:::

Let’s now create a new file containing the new UserOTP screen, which will handle all the OTP processes. The screen will have two input fields, being the first one for your user to provide the means to get the OTP (email address or phone number). The other input field, hidden before submitting the OTP request, will contain the user received token. Here is the full UserOTP.js (or UserOTP.tsx) code:

:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {Alert, Text, TextInput, TouchableOpacity, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {useNavigation} from '@react-navigation/native';
5	import Styles from './Styles';
6	
7	export const UserOTP = () => {
8	  const navigation = useNavigation();
9	
10	  const [userData, setUserData] = useState('');
11	  const [userToken, setUserToken] = useState('');
12	  const [tokenRequested, setTokenRequested] = useState(false);
13	
14	  const requestOTP = async function () {
15	    // Note that this values come from state variables that we've declared before
16	    const userDataValue = userData;
17	    // Check if value is an email if it contains @. Note that in a real
18	    // app you need a much better validator for this field
19	    const verificationType =
20	      userDataValue.includes('@') === true ? 'email' : 'sms';
21	    // We need to call it using await
22	    try {
23	      await Parse.Cloud.run('requestOTP', {
24	        userData: userDataValue,
25	        verificationType: verificationType,
26	      });
27	      // Show token input field
28	      setTokenRequested(true);
29	      Alert.alert('Success!', `Token requested via ${verificationType}!`);
30	      return true;
31	    } catch (error) {
32	      Alert.alert('Error!', error.message);
33	      return false;
34	    }
35	  };
36	
37	  const verifyOTP = async function () {
38	    // Note that this values come from state variables that we've declared before
39	    const userDataValue = userData;
40	    const userTokenValue = userToken;
41	    // Check if value is an email if it contains @. Note that in a real
42	    // app you need a much better validator for this field
43	    const verificationType =
44	      userDataValue.includes('@') === true ? 'email' : 'sms';
45	    // We need the installation id to allow cloud code to create
46	    // a new session and login user without password
47	    const parseInstallationId = await Parse._getInstallationId();
48	    // We need to call it using await
49	    try {
50	      // Verify OTP, if successful, returns a sessionId
51	      let response = await Parse.Cloud.run('verifyOTP', {
52	        userData: userDataValue,
53	        verificationType: verificationType,
54	        userToken: userTokenValue,
55	        parseInstallationId: parseInstallationId,
56	      });
57	      if (response.sessionId !== undefined) {
58	        // Use generated sessionId to become a user,
59	        // logging in without needing to inform password and username
60	        await Parse.User.become(response.sessionId);
61	        const loggedInUser= await Parse.User.currentAsync();
62	        Alert.alert(
63	          'Success!',
64	          `User ${loggedInUser.get('username')} has successfully signed in!`,
65	        );
66	        // Navigation.navigate takes the user to the home screen
67	        navigation.navigate('Home');
68	        return true;
69	      } else {
70	        throw response;
71	      }
72	    } catch (error) {
73	      Alert.alert('Error!', error.message);
74	      return false;
75	    }
76	  };
77	
78	  return (
79	    <View style={Styles.login_wrapper}>
80	      {tokenRequested === false ? (
81	        <View style={Styles.form}>
82	          <TextInput
83	            style={Styles.form_input}
84	            value={userData}
85	            placeholder={'Email or mobile phone number'}
86	            onChangeText={(text) => setUserData(text)}
87	            autoCapitalize={'none'}
88	            keyboardType={'email-address'}
89	          />
90	          <TouchableOpacity onPress={() => requestOTP()}>
91	            <View style={Styles.button}>
92	              <Text style={Styles.button_label}>{'Request OTP'}</Text>
93	            </View>
94	          </TouchableOpacity>
95	        </View>
96	      ) : (
97	        <View style={Styles.form}>
98	          <Text>{'Inform the received token to proceed'}</Text>
99	          <TextInput
100	            style={Styles.form_input}
101	            value={userToken}
102	            placeholder={'Token (6 digits)'}
103	            onChangeText={(text) => setUserToken(text)}
104	            autoCapitalize={'none'}
105	            keyboardType={'default'}
106	          />
107	          <TouchableOpacity onPress={() => verifyOTP()}>
108	            <View style={Styles.button}>
109	              <Text style={Styles.button_label}>{'Verify'}</Text>
110	            </View>
111	          </TouchableOpacity>
112	          <TouchableOpacity onPress={() => requestOTP()}>
113	            <View style={Styles.button}>
114	              <Text style={Styles.button_label}>{'Resend token'}</Text>
115	            </View>
116	          </TouchableOpacity>
117	        </View>
118	      )}
119	    </View>
120	  );
121	};
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Text, TextInput, TouchableOpacity, View} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {useNavigation} from '@react-navigation/native';
5	import Styles from './Styles';
6	
7	export const UserOTP: FC<{}> = ({}): ReactElement => {
8	  const navigation = useNavigation();
9	
10	  const [userData, setUserData] = useState('');
11	  const [userToken, setUserToken] = useState('');
12	  const [tokenRequested, setTokenRequested] = useState(false);
13	
14	  const requestOTP = async function (): Promise<boolean> {
15	    // Note that this values come from state variables that we've declared before
16	    const userDataValue: string = userData;
17	    // Check if value is an email if it contains @. Note that in a real
18	    // app you need a much better validator for this field
19	    const verificationType: string =
20	      userDataValue.includes('@') === true ? 'email' : 'sms';
21	    // We need to call it using await
22	    try {
23	      await Parse.Cloud.run('requestOTP', {
24	        userData: userDataValue,
25	        verificationType: verificationType,
26	      });
27	      // Show token input field
28	      setTokenRequested(true);
29	      Alert.alert('Success!', `Token requested via ${verificationType}!`);
30	      return true;
31	    } catch (error) {
32	      Alert.alert('Error!', error.message);
33	      return false;
34	    }
35	  };
36	
37	  const verifyOTP = async function (): Promise<Boolean> {
38	    // Note that this values come from state variables that we've declared before
39	    const userDataValue: string = userData;
40	    const userTokenValue: string = userToken;
41	    // Check if value is an email if it contains @. Note that in a real
42	    // app you need a much better validator for this field
43	    const verificationType: string =
44	      userDataValue.includes('@') === true ? 'email' : 'sms';
45	    // We need the installation id to allow cloud code to create
46	    // a new session and login user without password; this is obtained
47	    // using a static method from Parse
48	    const parseInstallationId: string = await Parse._getInstallationId();
49	    // We need to call it using await
50	    try {
51	      // Verify OTP, if successful, returns a sessionId
52	      let response: object = await Parse.Cloud.run('verifyOTP', {
53	        userData: userDataValue,
54	        verificationType: verificationType,
55	        userToken: userTokenValue,
56	        parseInstallationId: parseInstallationId,
57	      });
58	      if (response.sessionId !== undefined) {
59	        // Use generated sessionId to become a user,
60	        // logging in without needing to inform password and username
61	        await Parse.User.become(response.sessionId);
62	        const loggedInUser: Parse.User = await Parse.User.currentAsync();
63	        Alert.alert(
64	          'Success!',
65	          `User ${loggedInUser.get('username')} has successfully signed in!`,
66	        );
67	        // Navigation.navigate takes the user to the home screen
68	        navigation.navigate('Home');
69	        return true;
70	      } else {
71	        throw response;
72	      }
73	    } catch (error) {
74	      Alert.alert('Error!', error.message);
75	      return false;
76	    }
77	  };
78	
79	  return (
80	    <View style={Styles.login_wrapper}>
81	      {tokenRequested === false ? (
82	        <View style={Styles.form}>
83	          <TextInput
84	            style={Styles.form_input}
85	            value={userData}
86	            placeholder={'Email or mobile phone number'}
87	            onChangeText={(text) => setUserData(text)}
88	            autoCapitalize={'none'}
89	            keyboardType={'email-address'}
90	          />
91	          <TouchableOpacity onPress={() => requestOTP()}>
92	            <View style={Styles.button}>
93	              <Text style={Styles.button_label}>{'Request OTP'}</Text>
94	            </View>
95	          </TouchableOpacity>
96	        </View>
97	      ) : (
98	        <View style={Styles.form}>
99	          <Text>{'Inform the received token to proceed'}</Text>
100	          <TextInput
101	            style={Styles.form_input}
102	            value={userToken}
103	            placeholder={'Token (6 digits)'}
104	            onChangeText={(text) => setUserToken(text)}
105	            autoCapitalize={'none'}
106	            keyboardType={'default'}
107	          />
108	          <TouchableOpacity onPress={() => verifyOTP()}>
109	            <View style={Styles.button}>
110	              <Text style={Styles.button_label}>{'Verify'}</Text>
111	            </View>
112	          </TouchableOpacity>
113	          <TouchableOpacity onPress={() => requestOTP()}>
114	            <View style={Styles.button}>
115	              <Text style={Styles.button_label}>{'Resend token'}</Text>
116	            </View>
117	          </TouchableOpacity>
118	        </View>
119	      )}
120	    </View>
121	  );
122	};
```
:::

Take a closer look at the requestOTP and verifyOTP functions, which are responsible for calling the respective Cloud Code functions and validating their response. More detail on how they work can be inspected in the code comments.

After creating the new screen, import and declare it in your App.js (or App.tsx). After that, add a new button in your UserLogin.js (or UserLogin.tsx) file, enabling your user to navigate to the OTP screen.

## 4 - Testing the New OTP Feature

Let’s now test our changes to the app. First, register a new user containing a valid email and phone number. Make sure to use the international notation (E.164) format in the phone number (e.g.: +14155552671).

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ItGPJroL7lUbXlVHS22O-_image.png" signedSrc size="50" width="355" height="751" position="center" caption}

Now, navigate to the OTP screen from the login screen and inform the same email or phone number as before. Click on the request button and you should get a message like this, changing the active input on your screen.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-Y28GHDGf3Y_1BcFLh1mX_image.png" signedSrc size="50" width="355" height="748" position="center" caption}

If you informed an email address, you should receive an email containing the OTP token; if a phone number was passed, you will get an SMS text message on your mobile phone. The email should contain a message like this, depending on how you set up the SendGrid template.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ymagmPxoqZJLwmTFpjSlJ_image.png)



Inform the OTP token and click on verify. If everything went well, you should now be at the home screen with the following message:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7TtHoyH3jZRy0cqVAfFF3_image.png" signedSrc size="50" width="360" height="751" position="center" caption}

## Conclusion

At the end of this guide, you learned how to use Parse Cloud Code functions to integrate third-party services in your React Native application. In the next guide, you will learn how to work with Users in Parse.

[title] Sign Up With Apple
[path] Platform/

# Sign In with Apple Tutorial

## Introduction

Sign In with Apple enables users to sign in to Apps using their Apple ID.
This feature is available on iOS 13 and later, and Parse 3.5 and later.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create a subdomain in Back4App.
- An <a href="https://developer.apple.com/" target="_blank">Apple Developer account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Add the Sign In with Apple capability to your XCode project

In your XCode Project, click on the Target (1) and go to the Signing & Capabilities tab (2).
Click the + Capability button (3) and add the Sign In with Apple capability (4).
While there, choose your Bundle Identifier (5) and hold that information because we will need it later.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OaqDtMwyMrhMjvpsgMqCP_image.png)

## 3 - Create a new Service ID

Log into your [Apple Developer account](https://developer.apple.com/) and go to the Identifiers section. Check if your created Bundle Identifier is there

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SJ71g8F23N98KDnIgRlTS_image.png)

Click the Bundle Identifier and scroll down. Check if the Sign In with Apple is selected

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Of5puKJb4HSnxpf7_7laa_image.png)

Click Edit and make sure the Enable as a primary App ID is selected

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aYO-XTOt-T54DIE3nGHtZ_image.png)

If everything is right, save and exit.

## 4 - Set up Parse Auth for Apple

Go to the Back4App website, log in, and then find your app. After that, click on Server Settings search for the Apple Login block, and select Settings.

The Apple Login section looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/g6VC7nVrqvf_yC3KBw5uJ_image.png" signedSrc size="40" width="255" height="314" position="center" caption}

Now, you just need to paste your Bundle ID in the field below and click on the button to save.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tSyAtP9dJ7v4L_9pWfb9k_image.png" signedSrc size="70" width="599" height="454" position="center" caption}

In case you face any trouble while integrating Apple Login, please contact our team via chat!

## 5 - Option 1 - Download our Template

There is some coding involved in making Sign in With Apple work, so we created [this template](https://github.com/templates-back4app/ParseSignInWithApple) that you can download, and change the Bundle Identifier, the App Id, and Client Key.

The code is fully documented so it is a good starting point.

If you prefer to read through this doc, please go on to the next step.

## 6 - Option 2 - Manually write code

Inside your view, add the AuthenticationServices framework and create the AuthDelegate that will handle the PFUserAuthenticationDelegate:

```swift
1   import AuthenticationServices
2
3   class AuthDelegate:NSObject, PFUserAuthenticationDelegate {
4       func restoreAuthentication(withAuthData authData: [String : String]?) -> Bool {
5           return true
6       }
7    
8       func restoreAuthenticationWithAuthData(authData: [String : String]?) -> Bool {
9           return true
10      }
11  }
```

## 7 - Implement your Delegates for the ViewController

Implement the ASAuthorizationControllerDelegate and ASAuthorizationControllerPresentationContextProviding for the ViewController:

```swift
1   class ViewController: UIViewController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding
```

## 8 - Add the Sign In with Apple button

The ViewDidAppear is a good place for it. If you choose other places, remember to call it just once:

```swift
1   override func viewDidAppear(_ animated: Bool) {
2           super.viewDidAppear(animated)
3        
4           // Sign In with Apple button
5           let signInWithAppleButton = ASAuthorizationAppleIDButton()
6
7           // set this so the button will use auto layout constraint
8           signInWithAppleButton.translatesAutoresizingMaskIntoConstraints = false
9
10          // add the button to the view controller root view
11          self.view.addSubview(signInWithAppleButton)
12
13          // set constraint
14          NSLayoutConstraint.activate([
15              signInWithAppleButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 50.0),
16              signInWithAppleButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -50.0),
17              signInWithAppleButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -70.0),
18              signInWithAppleButton.heightAnchor.constraint(equalToConstant: 50.0)
19          ])
20
21          // the function that will be executed when user tap the button
22          signInWithAppleButton.addTarget(self, action: #selector(appleSignInTapped), for: .touchUpInside)
23      }
```

The appleSignInTapped in the last line must also be defined inside the ViewController class:

```swift
1  // This is the function that will be executed when user taps the button
2       @objc func appleSignInTapped() {
3           let provider = ASAuthorizationAppleIDProvider()
4           let request = provider.createRequest()
5           // request full name and email from the user's Apple ID
6           request.requestedScopes = [.fullName, .email]
7
8           // pass the request to the initializer of the controller
9           let authController = ASAuthorizationController(authorizationRequests: [request])
10        
11          // similar to delegate, this will ask the view controller
12          // which window to present the ASAuthorizationController
13          authController.presentationContextProvider = self
14        
15          // delegate functions will be called when user data is
16          // successfully retrieved or error occured
17          authController.delegate = self
18          
19          // show the Sign-in with Apple dialog
20          authController.performRequests()
21      }
```

## 9 - The presentationContextProvider

The presentationContextProvider (ASAuthorizationControllerPresentationContextProviding) will ask for which window should display the Authorization dialog. As we are going to display it in the same window, we must return self.view\.window:

```swift
1   func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
2           // return the current view window
3           return self.view.window!
4       }
```

## 10 - Handling the delegate ASAuthorizationControllerDelegate

There are a few options that we must handle when the delegate is called, so let’s add some code to handle those options distinctly:

```swift
1   func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
2           print("authorization error")
3           guard let error = error as? ASAuthorizationError else {
4               return
5           }
6
7           switch error.code {
8           case .canceled:
9               // user press "cancel" during the login prompt
10              print("Canceled")
11          case .unknown:
12              // user didn't login their Apple ID on the device
13             print("Unknown")
14          case .invalidResponse:
15              // invalid response received from the login
16              print("Invalid Respone")
17          case .notHandled:
18              // authorization request not handled, maybe internet failure during login
19              print("Not handled")
20          case .failed:
21              // authorization failed
22              print("Failed")
23          @unknown default:
24              print("Default")
25          }
26      }
```

## 11 - Handling the delegate for didCompleteWithAuthorization

When we successfully authenticate, we can retrieve the authorized information:

```swift
1      func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
2        
3              if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
4                  // unique ID for each user, this uniqueID will always be returned
5                  let userID = appleIDCredential.user
6                  print("UserID: " + userID)
7            
8                  // if needed, save it to user defaults by uncommenting the line below
9                  //UserDefaults.standard.set(appleIDCredential.user, forKey: "userID")
10            
11                 // optional, might be nil
12                 let email = appleIDCredential.email
13                 print("Email: " + (email ?? "no email") )
14            
15                 // optional, might be nil
16                 let givenName = appleIDCredential.fullName?.givenName
17                 print("Given Name: " + (givenName ?? "no given name") )
18            
19                 // optional, might be nil
20                 let familyName = appleIDCredential.fullName?.familyName
21                 print("Family Name: " + (familyName ?? "no family name") )
22            
23                 // optional, might be nil
24                 let nickName = appleIDCredential.fullName?.nickname
25                 print("Nick Name: " + (nickName ?? "no nick name") )
26                 /*
27                     useful for server side, the app can send identityToken and authorizationCode
28                     to the server for verification purpose
29                 */
30                 var identityToken : String?
31                 if let token = appleIDCredential.identityToken {
32                     identityToken = String(bytes: token, encoding: .utf8)
33                     print("Identity Token: " + (identityToken ?? "no identity token"))
34                 }
35
36                 var authorizationCode : String?
37                 if let code = appleIDCredential.authorizationCode {
38                     authorizationCode = String(bytes: code, encoding: .utf8)
39                     print("Authorization Code: " + (authorizationCode ?? "no auth code") )
40                 }
41
42                 // do what you want with the data here
43            
44           }    
45       }
```

That’s the place where we can also add code for logging in Parse. So right after the do what you want with the data here comment, let’s add:

```swift
1   PFUser.logInWithAuthType(inBackground: "apple", authData: ["token": String(identityToken!), "id": userID]).continueWith { task -> Any? in
2                   if ((task.error) != nil){
3                       //DispatchQueue.main.async {
4                           print("Could not login.\nPlease try again.")
5                           print("Error with parse login after SIWA: \(task.error!.localizedDescription)")
6                       //}
7                       return task
8                   }
9                   print("Successfuly signed in with Apple")
10                  return nil
11              }
```

And of course, add the Parse framework:

```swift
1   import Parse
```


[title] Parse Server Version
[path] Platform/

## Introduction

In this guide, you will learn how to change your Parse Server version.

## Goal

- Change the Parse Server version.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read this page, however, to change it, you should be the app owner.**
:::

## Parse Server

<a href="https://blog.back4app.com/2020/03/17/managed-parse-server/" target="_blank">Parse Server</a> is an open-source framework that powers an application backend and it speeds up the time for developers by simplifying complicated programming tasks.

The Parse Server community is very active and often new versions <a href="https://github.com/parse-community/parse-server/releases" target="_blank">are released</a>. By changing the version of your app, you will be guaranteed to update to the latest version of Parse Server. All it takes is a single click on the Change Version option to upgrade or downgrade server versions.

It’s very simple, all you have to do is go to Manage Parse Server available at Server Settings, this block looks like below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/JomXSrOdmsadkeBVMK2Az_image.png" signedSrc size="40" width="244" height="304" position="center" caption}

Now, you can select the version you’d like to have and click on the SAVE button.

## Breaking Changes

Before upgrading to a newer version of Parse Server, it is highly recommended that you keep a [development app](https://www.back4app.com/docs/platform/app-settings#clone-app) to apply this change.

Also, in this topic, you can check the most common errors to not stumble upon a breaking change. See:

### Parse Server 6.2.0&#x20;

In this version, there is a security implementation in terms of ACL for Users that are not set as public read anymore.&#x20;

However, in case you'd like to bypass it (not recommended, as this allows other users and unauthenticated users to read data such as `email`), you need to add the following configuration in your [custom parse options](https://www.back4app.com/docs/platform/custom-parse-options):

```json
{
    "enforcePrivateUsers": false
}
```

### Parse Server 5.2.3&#x20;

In this version, there is a security implementation in terms of uploading files to your app.

It’s required to add the following configuration in your [custom parse options](https://www.back4app.com/docs/platform/custom-parse-options) to make it work.

```json
{
    "fileUpload": {
        "enableForPublic": true,
        "enableForAnonymousUser": true,
        "enableForAuthenticatedUser": true
    }
}
```

- **enableForPublic**: Is true if file upload should be allowed for anyone, regardless of user authentication.
- **enableForAnonymousUser**: Is true if file upload should be allowed for anonymous users.
- **enableForAuthenticatedUser:** Is true if file upload should be allowed for authenticated users.

**Deprecation: Database HUB - Connection**
This version no longer supports connection with datasets from <a href="https://back4app.com/database" target="_blank">Database Hub</a>. Clone is still available.

### **Parse Server 3.7.2**

Before upgrading to a Parse Server version equal to or higher than 3.7.2, note that word id turned into a reserved field and it is related to GraphQL implementations. Note that you might not be able to manage your objects with Create, Read, Update, or Delete via REST API or SDK.

### **Parse Server 3.1.1**

&#x20;Two problems might happen:

1. There’s a breaking change through the upgrade from 2.x to 3.x related to the cloud code, and you can read more about the changes <a href="https://www.back4app.com/docs/advanced-guides/parse-server-3" target="_blank">here</a>. In other words, this update has cleaned up Cloud Code syntax.
2. Before you change to this version, there is a possibility provided by Parse Server which allows the user to save the audience for tracking and sending a Push notification, so you need to remove this class to prevent problems with the dashboard and database.

### Parse Server 2.6.5

**Before** upgrading to this version, you need to make sure that you don’t have any expired certificates added to your app, please go to Server Settings > iOS Push notification > Settings and check if there are any expired certificates.

To renew the certificate, you can upload a new one following [this documentation](https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service#step-1---create-your-push-certificates-in-the-apple-developer-center) about generating an iOS certificate.

### **Parse Server 2.2.14**

Revocable sessions were introduced in the middle of 2015 and it helped to improve the security for users. So, if it is your current version, you must be aware of a very common problem related to Legacy Token.

At this moment, if you are using Legacy Token, it’s necessary to change to Revocable Token before selecting a newer version. This link can help you with it:

<a href="https://web.archive.org/web/20170101001730/https://parse.com/tutorials/session-migration-tutorial" target="_blank">Revocable Token migration tutorial</a>&#x20;

:::hint{type="info"}
If you got some error and the solutions above don’t work for you, please contact our support team via <a href="https://www.back4app.com/docs/platform/parse-server-version" target="_blank">App Id</a> chat.
:::

## Conclusion

At this point, you have learned how to upgrade or downgrade your current Parse Server version and possible breaking changes based on the most common errors.

[title] Debug Cloud Functions
[path] Local Development/

After creating and developing your application using Parse Cloud Code functions, there’s always room for improvement when it comes to testing and debugging. This guide will show you how to integrate your code editor with Node.js to debug your functions using a local Parse Server instance, simulating the Back4app environment.

## Goal

Enable you to debug your Parse Cloud Code locally in your preferred code editor.

## 1 - Preparing Your Project Files

If you are already hosting your application on Back4app or have set up Cloud Code via the dashboard, your project should follow this structure:

- `cloud`** Directory**: Contains the `main.js` file where your Cloud Code functions are defined.
- `public`** Directory**: Holds your static content such as HTML and JavaScript files, typically including an index.html file.

If your app is new or not yet deployed, replicate this structure to ensure the local Parse Server runs correctly.

## 2 - Running Your Parse Server Locally

To start a local instance of the Parse Server:

1. Navigate to your project directory in the terminal.
2. Run the following command to launch the server with a test database and your Cloud Code:

> parse-server --appId YOUR_APP_ID --clientKey YOUR_CLIENT_KEY --masterKey YOUR_MASTER_KEY --databaseURI mongodb://localhost/test --cloud ./cloud/main.js --verbose

- Replace the placeholder values (`YOUR_APP_ID`, etc.) with random values. Avoid using your production keys.

1. Verify that the server is running by opening `http://localhost:1337/parse` in your browser. An "unauthorized" error means the server is running but the request lacks authentication keys.

## 3 - Setting Up and Testing Cloud Code

Ensure all your Cloud Code functions are located in the `cloud/main.js` file. For example:

:::CodeblockTabs
main.js

```javascript
Parse.Cloud.define("debugTest", (request) => {
  return "Testing!";
});
```
:::

Restart the Parse Server to load the new function:

> CTRL+C # to stop the server
> parse-server --appId ... # rerun the command

Now, test the function using cURL in the terminal:

```curl
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-Client-Key: YOUR_CLIENT_KEY" \
http://localhost:1337/parse/functions/debugTest
```

If configured correctly, the terminal will display the response `"Testing!"`.

## 4- Debugging the Code with Node.js

You can use Node.js's debugging features, integrated with Visual Studio Code (or a similar IDE), to debug your functions step by step.

### Setting up VS Code

1. Open the **Run and Debug** panel on the left sidebar and click **Create a launch.json file**.
2. Choose **Node.js** as the environment.

This creates a basic debug configuration. To enhance it:

1. Click **Add Configuration...** and select **Node.js: Attach to Process**.
2. Choose the **Attach by Process ID** action and attach it to the Parse Server's node process.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OcGhOu8TVvhUkKu44bWbg_image.png)

### Debugging the Code

1. Open main.js and set a **breakpoint** on the line return "Testing!"; by clicking to the left of the line number.
2. Run the same cURL command as before. The debugger will pause execution at the breakpoint.
3. While paused, inspect environment variable values and the call stack in the debugger panel.

This approach lets you analyze your code’s behavior in detail.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fzlOtXOTnUjcT0VujaAN__image.png)

## Conclusion

By following this guide, you’ll be able to debug all aspects of your Parse integration and Cloud Code functions locally, improving your development workflow with Back4app.

[title] Aqui podem fazer o teste
[path] /

# Quickstart

::embed[/]{url="https://youtu.be/vLfIDscFgQI?list=PL_lJrbgUtzded_bF8KVl_puWZ-zDCLw7R"}

Back4App is an open source (based on <a href="https://parseplatform.org/" target="_blank">Parse Platform</a>), low code platform that helps you to develop modern apps. On Back4App, you will find the following features:

- Data Storage (Relational)
- Cloud Code Functions
- APIs (GraphQL and REST)
- File Storage
- Authentication
- Push Notifications

## 5-minute quick start

After creating your Back4App account and your first App, go to your App Dashboard and get your App Keys under App Settings-> Security & Keys(check the image below). Note that you will always need two keys to connect with Back4App, the Application ID, and another key according to the SDKyou will use.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9v0KLOCetJLpPPazjI2HE_image.png)

::::::Tabs
:::::Tab{title="JavaScript"}
Parse JavaScript SDK can be used in a large amount of platforms and frameworks.

:::ExpandableHeading
**React Native**

**1. Install Parse SDK**

> $ npm install parse @react-native-async-storage/async-storage --save

Use CocoaPods to add the native RNAsyncStorage to your project:

> cd ios & pod-install

**2. Initialize the SDK using your Application ID and JavaScript Key**

```javascript
1   //On your App.js file
2    import AsyncStorage from '@react-native-async-storage/async-storage';
3    import Parse from 'parse/react-native';
4
5    //Before using the SDK...
6    Parse.setAsyncStorage(AsyncStorage);
7    //Paste below the Back4App Application ID AND the JavaScript KEY
8    Parse.initialize('YOUR_APPLICATION_ID_HERE', 'YOUR_JAVASCRIPT_KEY_HERE');
9    //Point to Back4App Parse API address 
10   Parse.serverURL = 'https://parseapi.back4app.com/'
```

**3. Save your first data on Back4App**

Call the function `saveNewPlayer` to save your first data on Back4App. After calling the function go to your `App Dashboard` and check the data you’ve just saved.
:::

:::ExpandableHeading
**React**

**1. Install the dependency of Parse JavaScript SDK**

> $ yarn add parse

**2. Install the SDK**

```javascript
1  //Import Parse minified version
2  import Parse from 'parse/dist/parse.min.js';
```

**3. Save your first data on Back4App**

Call the function saveNewPlayerto save your first data on Back4App. After calling the function go to your App Dashboard and check the data you’ve just saved.

```javascript
1   async function saveNewPlayer() {
2     //Create your Parse Object
3     const soccerPlayer = new Parse.Object('SoccerPlayer');
4     //Define its attributes
5     soccerPlayer.set('playerName', 'A. Wed');
6     soccerPlayer.set('yearOfBirth', 1997);
7     soccerPlayer.set('emailContact', 'a.wed@email.io');
8     soccerPlayer.set('attributes', ['fast', 'good conditioning']);
9     try {
10      //Save the Object
11      const result = await soccerPlayer.save();
12      alert('New object created with objectId: ' + result.id);
13    } catch (error) {
14      alert('Failed to create new object: ' + error.message);
15    }
16  }
```
:::

:::ExpandableHeading
**Node.js**

**1. Install the NPM module**

> $ npm install parse --save

**2. Install the SDK**

```nodejs
1  var Parse = require('parse/node');
2
3  Parse.initialize("APP_ID","JS_KEY"); //PASTE HERE YOUR Back4App APPLICATION ID AND YOUR JavaScript KEY
4  Parse.serverURL = 'https://parseapi.back4app.com/'
```

**3. Save your first data on Back4App**

Call the function `saveNewPlayer` to save your first data on Back4App. After calling the function go to your `App Dashboard `and check the data you’ve just saved.

```javascript
1   async function saveNewPlayer() {
2     //Create your Parse Object
3     const soccerPlayer = new Parse.Object('SoccerPlayer');
4     //Define its attributes
5     soccerPlayer.set('playerName', 'A. Wed');
6     soccerPlayer.set('yearOfBirth', 1997);
7     soccerPlayer.set('emailContact', 'a.wed@email.io');
8     soccerPlayer.set('attributes', ['fast', 'good conditioning']);
9     try {
10      //Save the Object
11      const result = await soccerPlayer.save();
12      alert('New object created with objectId: ' + result.id);
13    } catch (error) {
14      alert('Failed to create new object: ' + error.message);
15    }
16  }
```
:::

::::ExpandableHeading
**Angular**

**1. Install the NPM module**

> $ npm install parse --save

**2. Install the SDK**

:::CodeblockTabs
Angular

```javascript
1   import * as Parse from 'parse';
2
3   @Component({ ... })
4
5   export class AppComponent {
6     ...
7
8     constructor() {
9       // Initialize your Parse App Keys
10      Parse.initialize("APP_ID","JS_KEY"); //PASTE HERE YOUR Back4App 11APPLICATION ID AND YOUR JavaScript KEY
12      Parse.serverURL = 'https://parseapi.back4app.com/'
13    }  
14  }
```
:::

**3. Save your first data on Back4App**

Call the function `saveNewPlayer `to save your first data on Back4App. After calling the function go to your `App Dashboard` and check the data you’ve just saved.

```javascript
1   async function saveNewPlayer() {
2     //Create your Parse Object
3     const soccerPlayer = new Parse.Object('SoccerPlayer');
4     //Define its attributes
5     soccerPlayer.set('playerName', 'A. Wed');
6     soccerPlayer.set('yearOfBirth', 1997);
7     soccerPlayer.set('emailContact', 'a.wed@email.io');
8     soccerPlayer.set('attributes', ['fast', 'good conditioning']);
9     try {
10      //Save the Object
11      const result = await soccerPlayer.save();
12      alert('New object created with objectId: ' + result.id);
13    } catch (error) {
14      alert('Failed to create new object: ' + error.message);
15    }
16  }
```
::::

:::ExpandableHeading
**Browser based application**

After creating your` index.html`, install Parse SDK direct on it, inside the `<head>` tag, using the code below.

```html
1  <!DOCTYPE html>
2  <html>
3  <head>
4    <title>My First Application</title>
5    <script type="text/javascript" src="https://npmcdn.com/parse/dist/parse.min.js"></script>
6    <script type="text/javascript" type="text/javascript">
7      Parse.initialize("APP_ID","JS_KEY"); //PASTE HERE YOUR Back4App APPLICATION ID AND YOUR JavaScript KEY
8      Parse.serverURL = 'https://parseapi.back4app.com/'
9    </script>
10  </head>
11  <body>
12    <!--
13      Your code
14    -->
15  </body>
16  </html>
```

**2. Save your first data on Back4App**

Call the function `saveNewPlayer `to save your first data on Back4App. After calling the function go to your `App Dashboard` and check the data you’ve just saved.

```javascript
1   async function saveNewPlayer() {
2     //Create your Parse Object
3     const soccerPlayer = new Parse.Object('SoccerPlayer');
4     //Define its attributes
5     soccerPlayer.set('playerName', 'A. Wed');
6     soccerPlayer.set('yearOfBirth', 1997);
7     soccerPlayer.set('emailContact', 'a.wed@email.io');
8     soccerPlayer.set('attributes', ['fast', 'good conditioning']);
9     try {
10      //Save the Object
11      const result = await soccerPlayer.save();
12      alert('New object created with objectId: ' + result.id);
13    } catch (error) {
14      alert('Failed to create new object: ' + error.message);
15    }
16  }
```
:::
:::::

::::Tab{title="Flutter"}
Install the latest <a href="https://github.com/parse-community/Parse-SDK-Flutter/releases" target="_blank">Parse Flutter SDK</a> in your application. Then, create a Flutter Project:

**Create a Flutter Project**

> flutter create flutter_parse

Check if everything is OK running the application:

> cd flutter_parse
> flutter run

**Installing Flutter plugin for Parse Server**

[Add](https://pub.dev/packages/parse_server_sdk_flutter) Parse to the project dependencies to `pubspec.yaml `file:

> dependencies:
>   parse_server_sdk_flutter: ^latest_version

**1. Setting up Parse SDK**

In your Dart Code, clean all main.dart code and start a new one importing the Parse SDK.

:::CodeblockTabs
main.dart

```dart
1 import 'package:flutter/material.dart';
2 import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';

```
:::

**2. Initialize the SDK using your Application ID and Client Key**

:::CodeblockTabs
main.dart

```dart
1 void main() async {
2  final keyApplicationId = 'YOUR_APPLICATION_ID_HERE';
3  final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
4  final keyParseServerUrl = 'https://parseapi.back4app.com';
5
6  await Parse().initialize(keyApplicationId, keyParseServerUrl,
7      clientKey: keyClientKey, debug: true);
8  }
```
:::

**3. Save your first data on Back4App**

```dart
1  void main() async {
2    Future<String> saveNewPlayer() async {
3      final soccerPlayer = ParseObject('SoccerPlayer')
4	      ..set('playerName', 'A. Wed')
5	      ..set('yearOfBirth', 1997)
6	      ..set('emailContact', 'a.wed@email.io')
7	      ..set('attributes', ['fast', 'good conditioning'])
8	      await soccerPlayer.save();
9	      return soccerPlayer.objectId;
10   }
```

:::hint{type="info"}
Facing any trouble? Feel free to check the complete<a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank"> Install Parse SDK guide to Flutter projects</a>. Also, feel free to check the official Parse Documentation regarding <a href="https://github.com/parse-community/Parse-SDK-Flutter" target="_blank">Parse SDK for Flutter</a>.
:::
::::

::::Tab{title="Android"}
**1. Parse Android SDK Installation**

To install the latest <a href="https://github.com/parse-community/Parse-SDK-Android/releases" target="_blank">Parse Android SDK</a> in your application, go to the build.gradle(Module\:app) file and insert the code snippet inside the dependencies\{} tag

```xml
1   // ... code
2   android {...}
3
4   dependencies {
5     // code...
6     // Don't forget to change the line below with the latest version of Parse SDK for Android
7     implementation "com.github.parse-community.Parse-SDK-Android:parse:latest.version.here"
8   } 
9
10  repositories {
11    mavenCentral()
12    jcenter()
13    maven { url 'https://jitpack.io' }
14  } 

```

:::hint{type="info"}
**You can find out which is the latest version of Parse SDK for Android at **<a href="https://jitpack.io/" target="_blank">**Jitpack Website**</a>**.**
:::

:::hint{type="warning"}
**Facing any trouble? Feel free to check the complete**<a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">** Install Parse SDK guide to Android projects**</a>**. Also, feel free to check the official Parse Documentation regarding **<a href="https://docs.parseplatform.org/android/guide/#installation" target="_blank">**Parse SDK for android.**</a>
:::

**2. Initialize the SDK using your Application ID and Client Key**

Inside the strings.xml file, insert the following lines, with your application keys.

:::CodeblockTabs
./app/src/main/res/values/strings.xml

```xml
1   <resources>
2    <string name="back4app_server_url">https://parseapi.back4app.com/</string>
3    <!-- Change the following strings as required -->
4    <string name="back4app_app_id">APP_ID</string>
5    <string name="back4app_client_key">CLIENT_KEY</string>
6  </resources>
```
:::

**3. Give Permissions and set up your App**

You need to grant permission for your Android app to access the internet network. Add the following code snippet to your AndroidManifest.xml file right after the application tag.

:::CodeblockTabs
./app/src/main/AndroidManifest.xml

```xml
1  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
2  <uses-permission android:name="android.permission.INTERNET" />
```
:::

Also, inside the applicationtag, add the following:

:::CodeblockTabs
./app/src/main/AndroidManifest.xml

```xml
1   <meta-data
2     android:name="com.parse.SERVER_URL"
3     android:value="@string/back4app_server_url" />
4   <meta-data
5     android:name="com.parse.APPLICATION_ID"
6     android:value="@string/back4app_app_id" />
7   <meta-data
8     android:name="com.parse.CLIENT_KEY"
9     android:value="@string/back4app_client_key" />
```
:::

**4. Initialize Parse SDK**

Create a Java file calledApp that extends Application. Inside App.java onCreate method, right after super.onCreate() call the following code:

```java
1  import com.parse.Parse;
2
3  Parse.initialize(new Parse.Configuration.Builder(this)
4    .applicationId(getString(R.string.back4app_app_id))
5    // if defined
6    .clientKey(getString(R.string.back4app_client_key))
7    .server(getString(R.string.back4app_server_url))
8    .build()
9  );
```

:::hint{type="info"}
Don’t forget to define this file in the** **AndroidManifest.xml**.**To do so, go to the AndroidManifest.xmlfile and add the following line of code inside the application** **tag:&#x20;

> 1 android:name=''.App''

&#x20;If the name of the java file that extends Application that you created on the previous step isn’t “App”, don’t forget that the code above should have the correct name of the file  (android\:name=".name\_of\_the\_file").
:::

:::hint{type="warning"}
**Facing any trouble? Feel free to check the complete**<a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">** Install Parse SDK guide to Android projects**</a>**. Also, feel free to check the official Parse Documentation regarding **<a href="https://docs.parseplatform.org/android/guide/#installation" target="_blank">**Parse SDK for android.**</a>
:::

**5. Save your first data on Back4App**

Call the function saveNewPlayer. Then, go to your App Dashboard and check the data you’ve just saved.

```javascript
1  private void saveNewPlayer() {
2    ParseObject soccerPlayer = new ParseObject("SoccerPlayer");
3    soccerPlayer.put("playerName", "A. Wed");
4    soccerPlayer.put("yearOfBirth", 1997);
5    soccerPlayer.put("emailContact", "a.wed@email.io");
6    soccerPlayer.put("attributes", ["fast", "good conditioning"]);
7    soccerPlayer.saveInBackground();
8  }
```



&#x20;







&#x20;

&#x20;
::::

::::Tab{title="iOS"}
**Install CocoaPods and Import Parse**

Xcode can use <a href="https://cocoapods.org/" target="_blank">CocoaPods</a> as dependency manager for Swift Cocoa projects. To install CocoaPods, copy the following code snippet and paste it into your terminal and hit return.

> $ sudo gem install cocoapod
>

:::hint{type="warning"}
You can refer to<a href="https://guides.cocoapods.org/using/getting-started.html" target="_blank"> CocoaPods Getting Started Guide</a> for additional details.
:::

**Connect your Parse App**

Open your project’s AppDelegate file to set up app’s credentials. Parse SDK for iOS uses these settings to connect to the Back4App servers. At the top of the file you should see a function called didFinishLaunchingWithOptions. Paste the following code snippet inside this function, and make sure it is above the line that says return true.

At the top of your AppDelegate.swift file make sure to include Parse as a module by using the following code snippet right below import UIKit.

:::CodeblockTabs
AppDelegate.swift

```swift
1     import Parse
```
:::

:::hint{type="warning"}
Facing any trouble? Feel free to check the complete Install Parse SDK guide to <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">iOS Swift </a>projects. Also, feel free to check the official Parse Documentation regarding <a href="https://docs.parseplatform.org/ios/guide/#getting-started" target="_blank">Parse SDK for iOS</a>.
:::

**2. Initialize the SDK using your Application ID and Client Key**

:::CodeblockTabs
AppDelegate.swift

```swift
1  let configuration = ParseClientConfiguration {
2    $0.applicationId = "PASTE_YOUR_APPLICATION_ID_HERE"
3    $0.clientKey = "PASTE_YOUR_CLIENT_ID_HERE"
4    $0.server = "https://parseapi.back4app.com"
5  }
6  Parse.initialize(with: configuration)
```
:::

**3. Save your first data on Back4App**

```swift
1   var soccerPlayer = PFObject(className:"SoccerPlayer")
2   person["playerName"] = "A. Wed"
3   person["yearOfBirth"] = 1997
4   person["emailContact"] = "a.wed@email.io"
5   person["attributes"] = ["fast", "good conditioning"]
6   person.saveInBackground {
7     (success: Bool, error: Error?) in
8     if (success) {
9       // The object has been saved.
10    } else {
11      // There was a problem, check error.description
12    }
13  }
```





&#x20;
::::

::::Tab{title="PHP"}
**1. Parse PHP SDK Installation**

**1.1 With composer**

Create a composer.json file in your projects root folder, containing the following.

> \{
>     "require": {
>         "parse/php-sdk" : "1.6.*"
>     }
> }

Run the “composer install” to download and set up the autoloader. After that, you can require it from your PHP script, using the code below.

```php
1   require 'vendor/autoload.php';
```

**1.2 With Git**

Go to <a href="https://github.com/parse-community/parse-php-sdk" target="_blank">Parse SDK for PHP page</a> on GitHub and clone it.

> $ git clone https://github.com/parse-community/parse-php-sdk.git

Inside your PHP file, include the autoload.php to automatically load the Parse SDK classes.

**2. Initialize the SDK using your Application ID, Rest Key and Master Key.**

```php
1   ParseClient::initialize( $app_id, $rest_key, $master_key);
2   ParseClient::setServerURL('https://parseapi.back4app.com','/');
```

**3. Save your first data on Back4App**

```php
1   $soccerPlayer = new ParseObject("SoccerPlayer");
2   $soccerPlayer->set("name", "A. Wed");
3   $soccerPlayer->set("yearOfBirth", 1997);
4   $soccerPlayer->set('emailContact', 'a.wed@email.io');
5   $soccerPlayer->setArray('attributes', ['fast', 'good conditioning']);
6
7   try {
8	        $soccerPlayer->save();
9	        echo 'New object created with objectId: ' . $soccerPlayer->getObjectId();
10  } catch (ParseException $ex) {  
11	        echo 'Failed to create new object, with error message: ' . $ex->getMessage();
12  }
```

:::hint{type="warning"}
Facing any trouble? Feel free to check the official Parse Documentation regarding <a href="https://docs.parseplatform.org/php/guide/#installation" target="_blank">Parse SDK for PHP</a>.
:::



****
::::

::::Tab{title=".NET"}
We need to add some libraries to set up Parse SDK for .NET. We will get them through Nuget Packages. To do so, go to Microsoft Visual Studio and at the Solution Explorer, click on your app’s name and go toManage NuGet Packages....

Then, click onBrowse to search and install the packages parse by Parse and Xamarin.Android.Support.v7.AppCompat by Xamarin Inc.

**Save your first data on Back4App**

```json
1  ParseObject soccerPlayer = new ParseObject("SoccerPlayer");
2  soccerPlayer["name"] = "A. Wed";
3  soccerPlayer["yearOfBirth"] = 1997;
4  soccerPlayer["emailContact"] = "a.wed@email.io";
5  soccerPlayer["attributes"] = new List<object>{"fast", "good conditioning"};
6  await soccerPlayer.SaveAsync();
```

:::hint{type="info"}
Facing any trouble? Feel free to check the official Parse Documentation regarding <a href="https://docs.parseplatform.org/dotnet/guide/#getting-started" target="_blank">Parse SDK for .NET</a>.
:::


::::

:::Tab{title="REST"}
**1. Save your first data on Back4App**

Open your terminal, and run the following command:

> curl -X POST \
> -H "X-Parse-Application-Id: APPLICATION_ID" \
> -H "X-Parse-REST-API-Key: REST_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{"name":"A. Wed","yearOfBirth":1997, "emailContact":"a.wed@email.io", "attributes":["fast", "good conditioning"]}' \
> https://parseapi.back4app.com/classes/SoccerPlayer
>
>
:::
::::::

## What can you build with Back4App?

There are many example apps and starter projects to get going

<a href="https://github.com/templates-back4app/react-js-slack-clone" target="_blank">ReactJS Slack Clone</a> - A React template using real-time, relational queries and authentication.

<a href="https://github.com/templates-back4app/flutter-user-signup" target="_blank">Flutter User Login/SignUp</a> - A user sign-up/login flutter template using Parse.User class.

<a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">React Native Associations</a>  - A template on React Native digging deeper into associations and relational queries using Pointers andRelations.

<a href="https://www.back4app.com/docs/flutter/parse-sdk/flutter-save-file" target="_blank">Flutter File Storage</a>  - Saving files from a Flutter app.

<a href="https://github.com/templates-back4app/AndroidGeoLocationKotlin" target="_blank">GeoPointers on Kotlin-Android</a>  - Exploring GeoPointers in Android.

<a href="https://github.com/templates-back4app/ios-template-todo-list" target="_blank">ToDo List example is Swift-iOS</a>  - A ToDo List example in Swift.

## &#x20;What to do Next?

After a quick start, we recommend keeping exploring the Back4App main features by checking out the guides below. You will find how to store and query relational data, use cloud functions to add business logic to your backend, use real-time subscriptions to keep your users up-to-date, store files, send push notifications, use the authentication mechanisms available on Back4App, and more. Choose the technology that suits you and enjoy the journey.

::::LinkArray
:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pEkmfq5PdkxC3lmfRzYsE_1.png"}
&#x20;      ** **<a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">**React Nactive**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tPONkoW1aPPhiAdTvbvrx_sem-nome-rectangle-sticker-landscape.png"}
<a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">**Flutter**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vqJJw7ljIhPJ1Yfz7mD9n_3.png"}
<a href="https://www.back4app.com/docs/android/android-project-with-source-code-download" target="_blank">Android </a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rzkB1YU-Feimt_SWfZUuC_4.png"}
<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank">iOS</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GsQE6ru-jj2rRbUt7P3Tr_5.png"}
****<a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">**Javascript**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/35IqTes9QhdmijlReYBYZ_6.png"}
<a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">GraphQL</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/D4RecbrtvjfbKUfb4yRbP_7.png"}
<a href="https://www.back4app.com/docs/js-framework/ionic/ionic-template" target="_blank">Ionic</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yOia2J8LzE-r1WSHQ7ZSQ_8.png"}
<a href="https://www.back4app.com/docs/xamarin/xamarin-templates" target="_blank">Xamarin</a>
:::
::::


[title] Untitled
[path] /


[title] Sign Up With Instagram
[path] Platform/

# Instagram OAuth Tutorial

## Introduction

Instagram OAuth enables users to sign in to Apps using their Instagram account through OAuth.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create a subdomain in Back4App.
- An <a href="https://developers.facebook.com/docs/instagram" target="_blank">Instagram Developer account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a new Instagram App Client

Log into your [Instagram Developer account](https://www.instagram.com/developer/) and sign up as a Developer.
Enter your website, telephone, and a description for your App. Accept the terms to proceed.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aIWmarBXNbPdz-1h9hO6v_image.png)

Go to Overview. Click on Register Your Application

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TDggEpHxpjBU33oNixvMs_image.png)

Click on Register a New Client

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dptSWcpQlFihQbHFfKBP__image.png)

Fill up the Application Name, Description, Company Name, Website URL, Redirect URIs, Privacy Policy URL, and Contact email.

For the Valid redirect URIs, if you are only trying to retrieve your access token, you can leave it as

> http://localhost

Otherwise, you should use the production URI for redirection

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gVVpnnKcOyxplyY56_Ukl_image.png)

At this point, you should have a Client like the image below

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XkrJesJQlIIZh-rmdtKrc_image.png)

Click on Manage and under the Security tab, uncheck the Disable Implicit OAuth

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/t_7wOB9BSdgKKzkJLaPhd_image.png)

## 3 - Retrieve your Token

If you left your Redirect URIs as localhost, there are two ways you can retrieve your token.
The first one is using your Browser of choice, and going to the following URL:

[https://api.instagram.com/oauth/authorize/?client\_id=CLIENT-ID\&redirect\_uri=REDIRECT-URI\&response\_type=code](https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID\&redirect_uri=REDIRECT-URI\&response_type=code)

just change the CLIENT-ID and REDIRECT-URI using the values you got from your newly created Client.
This will redirect you to an invalid page, but show you the access token in the URL:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2nRBVpsrA9fmD2Lh62jn6_image.png)

The other way to retrieve such a token is to run the following CURL command, replacing the CLIENT-ID, CLIENT-SECRET, and REDIRECT-URI for your values:

```curl
1   curl \-F 'client_id=CLIENT-ID' \
2       -F 'client_secret=CLIENT-SECRET' \
3       -F 'grant_type=authorization_code' \
4       -F 'redirect_uri=REDIRECT-URI' \
5       -F 'code=CODE' \
6       https://api.instagram.com/oauth/access_token
```

That command will also output your Access Token.

## 4 - Start the development

Now that the Sign In with Instagram is configured, you can start the development process passing the Access Token you retrieved for authentication.
The format for AUTHDATA is:

```json
1   {
2     "instagram": {
3       "id": "user's Instagram id (string)",
4       "access_token": "an authorized Instagram access token for the user"
5     }
6   }
```

Here is the method for the iOS SDK:

```swift
1   PFUser.logInWithAuthType(inBackground: "instagram", authData: ["access_token":tokenString, "id": user]).continueWith { task -> Any? in
2    
3   }
```

And here for the Android SDK:

```java
1   Map<string, string> authData = new HashMap<string, string>(); 
2   authData.put("access_token", tokenString);
3   authData.put("id", user);
4   ParseUser.logInWithInBackground("instagram", authData){
5
6   }
```


[title] Geoqueries
[path] React Native/Parse SDK (REST)/Data objects/

# Using React Native geolocation to perform geoqueries using Parse

## Introduction

In this guide, you will perform geoqueries in Parse using the React Native geolocation. You will implement a React Native component using these queries and learn how to set up and query realistic data using Back4App and React Native.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a> and also react-native-geolocation-service.
:::

## Goal

Perform Geoqueries using geopoints stored on Back4App and React Native geolocation.

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

```javascript
1   // This will create your query
2   let parseQuery = new Parse.Query("Profile");
3   // The query will resolve only after calling this method
4   let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Save some data on Back4App

Let’s create a City 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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zWAvl3tCT22AsXFJx2QD8_image.png)

Go ahead and create the City class with the following example content:

```javascript
1	// Add City objects and create table
2	// Note how GeoPoints are created, passing latitude and longitude as arguments
3	// Montevideo
4	City = new Parse.Object('City');
5	City.set('name', 'Montevideo - Uruguay');
6	City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
7	await City.save();
8	
9	// Brasília
10	City = new Parse.Object('City');
11	City.set('name', 'Brasília - Brazil');
12	City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
13	await City.save();
14	
15	// Bogotá
16	City = new Parse.Object('City');
17	City.set('name', 'Bogotá - Colombia');
18	City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
19	await City.save();
20	
21	// Mexico City
22	City = new Parse.Object('City');
23	City.set('name', 'Mexico City - Mexico');
24	City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
25	await City.save();
26	
27	// Washington, D.C.
28	City = new Parse.Object('City');
29	City.set('name', 'Washington, D.C. - USA');
30	City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
31	await City.save();
32	
33	// Ottawa
34	City = new Parse.Object('City');
35	City.set('name', 'Ottawa - Canada');
36	City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
37	await City.save();
38	
39	console.log('Success!');
```

## 3 - Query the data

Now that you have a populated class, we can now perform some GeoPoint queries in it. Let’s begin by ordering City results by the nearest from Kingston in Jamaica (latitude 18.01808695059913 and longitude -76.79894232253473), using the Parse.Query.near method:

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('City');
3	
4	// Create our GeoPoint for the query
5	let kingstonGeoPoint = new Parse.GeoPoint(18.018086950599134, -76.79894232253473);
6	
7	// `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
8	parseQuery.near('location', kingstonGeoPoint);
9	
10	// The query will resolve only after calling this method, retrieving
11	// an array of `Parse.Objects`
12	let queryResults = await parseQuery.find();
13	
14	// Let's show the results
15	for (let result of queryResults) {
16	  // You access `Parse.Objects` attributes by using `.get`
17	  console.log(result.get('name'));
18	};
```

Let’s now query using the methodParse.Query.withinKilometers, which will retrieve all results whose GeoPoint field is located within the max distance. Kingston will be used once again as a reference and the distance limit will be 3000 km.

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('City');
3	
4	// Create our GeoPoint for the query
5	let kingstonGeoPoint = new Parse.GeoPoint(18.018086950599134, -76.79894232253473);
6	
7	// You can also use `withinMiles` and `withinRadians` the same way,
8	// but with different measuring unities
9	parseQuery.withinKilometers('location', kingstonGeoPoint, 3000);
10	
11	// The query will resolve only after calling this method, retrieving
12	// an array of `Parse.Objects`
13	let queryResults = await parseQuery.find();
14	
15	// Let's show the results
16	for (let result of queryResults) {
17	  // You access `Parse.Objects` attributes by using `.get`
18	  console.log(result.get('name'));
19	};
```

Another useful query method isParse.Query.withinPolygon, which will query results whose GeoPoint field value is within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.

For this example, you will be using a simple polygon that roughly contains the South American continent, composed of 5 distant GeoPoints in the ocean.

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('City');
3	
4	// Create our GeoPoint polygon for the query
5	let geoPoint1 = new Parse.GeoPoint(15.822238344514378, -72.42845934415942);
6	let geoPoint2 = new Parse.GeoPoint(-0.7433770196268968, -97.44765968406668);
7	let geoPoint3 = new Parse.GeoPoint(-59.997149373299166, -76.52969196322749);
8	let geoPoint4 = new Parse.GeoPoint(-9.488786415007201, -18.346101586021952);
9	let geoPoint5 = new Parse.GeoPoint(15.414859532811047, -60.00625459569375);
10	
11	// Note that the polygon is merely an array of GeoPoint objects and that the first and last are not connected, so Parse connects them for you
12	parseQuery.withinPolygon('location', [geoPoint1, geoPoint2, geoPoint3, geoPoint4, geoPoint5]);
13	
14	// The query will resolve only after calling this method, retrieving
15	// an array of `Parse.Objects`
16	let queryResults = await parseQuery.find();
17	
18	// Let's show the results
19	for (let result of queryResults) {
20	  // You access `Parse.Objects` attributes by using `.get`
21	  console.log(result.get('name'));
22	};
```

## 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 3 buttons for calling the queries. The component also retrieves the device’s current location using react-native-geolocation-service, so the queries will be using real data.

This is how the component code is laid out, note the doQuery functions, containing the example code form before.

:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  View,
6	  PermissionsAndroid,
7	  Platform,
8	  ScrollView,
9	  StyleSheet,
10	} from 'react-native';
11	import Parse from 'parse/react-native';
12	import {
13	  List,
14	  Title,
15	  Button as PaperButton,
16	  Text as PaperText,
17	} from 'react-native-paper';
18	import Geolocation from 'react-native-geolocation-service';
19	
20	export const QueryList = () => {
21	  // State variable
22	  const [queryResults, setQueryResults] = useState(null);
23	
24	  // This function asks for location permission on iOS and Android
25	  const requestLocationPermissions = async () => {
26	    if (Platform.OS === 'ios') {
27	      // iOS can be asked always, since the OS handles if user already gave permission
28	      await Geolocation.requestAuthorization('whenInUse');
29	    } else if (Platform.OS === 'android') {
30	      let permissionCheck = await PermissionsAndroid.check(
31	        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
32	      );
33	      // Only asks for permission on Android if not given before
34	      if (permissionCheck !== true) {
35	        await PermissionsAndroid.request(
36	          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
37	          {
38	            title: 'Location Permission Request',
39	            message:
40	              'This app needs you permission for using your location for querying GeoPoints in Parse!',
41	            buttonPositive: 'OK',
42	          },
43	        );
44	      }
45	    }
46	  };
47	
48	  const doQueryNear = async function () {
49	    // Request location permissions
50	    await requestLocationPermissions();
51	    // Get current location and create the GeoPoint for the query
52	    Geolocation.getCurrentPosition(
53	      async (currentPosition) => {
54	        // Create our GeoPoint
55	        let currentLocationGeoPoint = new Parse.GeoPoint(
56	          currentPosition.coords.latitude,
57	          currentPosition.coords.longitude,
58	        );
59	
60	        // Create our query
61	        let parseQuery = new Parse.Query('City');
62	
63	        // `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
64	        parseQuery.near('location', currentLocationGeoPoint);
65	
66	        try {
67	          let results = await parseQuery.find();
68	          // Set query results to state variable
69	          setQueryResults(results);
70	        } catch (error) {
71	          // Error can be caused by lack of Internet connection
72	          Alert.alert('Error!', error.message);
73	        }
74	      },
75	      _error => {
76	        Alert.alert(
77	          'Error!',
78	          'This app needs your location permission to query this!',
79	        );
80	      },
81	      {enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
82	    );
83	    return true;
84	  };
85	
86	  const doQueryWithinKilometers = async function () {
87	    // Request location permissions
88	    await requestLocationPermissions();
89	    // Get current location and create the GeoPoint for the query
90	    Geolocation.getCurrentPosition(
91	      async (currentPosition) => {
92	        // Create our GeoPoint
93	        let currentLocationGeoPoint = new Parse.GeoPoint(
94	          currentPosition.coords.latitude,
95	          currentPosition.coords.longitude,
96	        );
97	
98	        // Create our query
99	        let parseQuery = new Parse.Query('City');
100	
101	        // You can also use `withinMiles` and `withinRadians` the same way,
102	        // but with different measuring unities
103	        parseQuery.withinKilometers('location', currentLocationGeoPoint, 3000);
104	
105	        try {
106	          let results = await parseQuery.find();
107	          // Set query results to state variable
108	          setQueryResults(results);
109	        } catch (error) {
110	          // Error can be caused by lack of Internet connection
111	          Alert.alert('Error!', error.message);
112	        }
113	      },
114	      _error => {
115	        Alert.alert(
116	          'Error!',
117	          'This app needs your location permission to query this!',
118	        );
119	      },
120	      {enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
121	    );
122	    return true;
123	  };
124	
125	  const doQueryWithinPolygon = async function () {
126	    // Create our GeoPoint polygon points
127	    let geoPoint1 = new Parse.GeoPoint(15.822238344514378, -72.42845934415942);
128	    let geoPoint2 = new Parse.GeoPoint(-0.7433770196268968, -97.44765968406668);
129	    let geoPoint3 = new Parse.GeoPoint(-59.997149373299166, -76.52969196322749);
130	    let geoPoint4 = new Parse.GeoPoint(-9.488786415007201, -18.346101586021952);
131	    let geoPoint5 = new Parse.GeoPoint(15.414859532811047, -60.00625459569375);
132	
133	    // Create our query
134	    let parseQuery = new Parse.Query('City');
135	
136	    // Note that the polygon is merely an array of GeoPoint objects and that the first and last are not connected, so Parse connects them for you
137	    parseQuery.withinPolygon('location', [
138	      geoPoint1,
139	      geoPoint2,
140	      geoPoint3,
141	      geoPoint4,
142	      geoPoint5,
143	    ]);
144	
145	    try {
146	      let results = await parseQuery.find();
147	      // Set query results to state variable
148	      setQueryResults(results);
149	    } catch (error) {
150	      // Error can be caused by lack of Internet connection
151	      Alert.alert('Error!', error.message);
152	    }
153	  };
154	
155	  const clearQueryResults = async function () {
156	    setQueryResults(null);
157	    return true;
158	  };
159	
160	  return (
161	    <>
162	      <View style={Styles.header}>
163	        <Image
164	          style={Styles.header_logo}
165	          source={ {
166	            uri:
167	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
168	          } }
169	        />
170	        <PaperText style={Styles.header_text}>
171	          <PaperText style={Styles.header_text_bold}>
172	            {'React Native on Back4App - '}
173	          </PaperText>
174	          {' GeoPoint Queries'}
175	        </PaperText>
176	      </View>
177	      <ScrollView style={Styles.wrapper}>
178	        <View>
179	          <Title>{'Result List'}</Title>
180	          {/* Query list */}
181	          {queryResults !== null &&
182	            queryResults !== undefined &&
183	            queryResults.map((result) => (
184	              <List.Item
185	                key={result.id}
186	                title={result.get('name')}
187	                titleStyle={Styles.list_text}
188	                style={Styles.list_item}
189	              />
190	            ))}
191	          {queryResults === null ||
192	          queryResults === undefined ||
193	          (queryResults !== null &&
194	            queryResults !== undefined &&
195	            queryResults.length <= 0) ? (
196	            <PaperText>{'No results here!'}</PaperText>
197	          ) : null}
198	        </View>
199	        <View>
200	          <Title>{'Query buttons'}</Title>
201	          <PaperButton
202	            onPress={() => doQueryNear()}
203	            mode="contained"
204	            icon="search-web"
205	            color={'#208AEC'}
206	            style={Styles.list_button}>
207	            {'Query Near'}
208	          </PaperButton>
209	          <PaperButton
210	            onPress={() => doQueryWithinKilometers()}
211	            mode="contained"
212	            icon="search-web"
213	            color={'#208AEC'}
214	            style={Styles.list_button}>
215	            {'Query Within KM'}
216	          </PaperButton>
217	          <PaperButton
218	            onPress={() => doQueryWithinPolygon()}
219	            mode="contained"
220	            icon="search-web"
221	            color={'#208AEC'}
222	            style={Styles.list_button}>
223	            {'Query Within Polygon'}
224	          </PaperButton>
225	          <PaperButton
226	            onPress={() => clearQueryResults()}
227	            mode="contained"
228	            icon="delete"
229	            color={'#208AEC'}
230	            style={Styles.list_button}>
231	            {'Clear Results'}
232	          </PaperButton>
233	        </View>
234	      </ScrollView>
235	    </>
236	  );
237	};
238	
239	// These define the screen component styles
240	const Styles = StyleSheet.create({
241	  header: {
242	    alignItems: 'center',
243	    paddingTop: 30,
244	    paddingBottom: 50,
245	    backgroundColor: '#208AEC',
246	  },
247	  header_logo: {
248	    height: 50,
249	    width: 220,
250	    resizeMode: 'contain',
251	  },
252	  header_text: {
253	    marginTop: 15,
254	    color: '#f0f0f0',
255	    fontSize: 16,
256	  },
257	  header_text_bold: {
258	    color: '#fff',
259	    fontWeight: 'bold',
260	  },
261	  wrapper: {
262	    width: '90%',
263	    alignSelf: 'center',
264	  },
265	  list_button: {
266	    marginTop: 6,
267	    marginLeft: 15,
268	    height: 40,
269	  },
270	  list_item: {
271	    borderBottomWidth: 1,
272	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
273	  },
274	  list_text: {
275	    fontSize: 15,
276	  },
277	});
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  View,
6	  PermissionsAndroid,
7	  Platform,
8	  ScrollView,
9	  StyleSheet,
10	} from 'react-native';
11	import Parse from 'parse/react-native';
12	import {
13	  List,
14	  Title,
15	  Button as PaperButton,
16	  Text as PaperText,
17	} from 'react-native-paper';
18	import Geolocation from 'react-native-geolocation-service';
19	
20	export const QueryList: FC<{}> = ({}): ReactElement => {
21	  // State variable
22	  const [queryResults, setQueryResults] = useState(null);
23	
24	  // This function asks for location permission on iOS and Android
25	  const requestLocationPermissions = async () => {
26	    if (Platform.OS === 'ios') {
27	      // iOS can be asked always, since the OS handles if user already gave permission
28	      await Geolocation.requestAuthorization('whenInUse');
29	    } else if (Platform.OS === 'android') {
30	      let permissionCheck: boolean = await PermissionsAndroid.check(
31	        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
32	      );
33	      // Only asks for permission on Android if not given before
34	      if (permissionCheck !== true) {
35	        await PermissionsAndroid.request(
36	          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
37	          {
38	            title: 'Location Permission Request',
39	            message:
40	              'This app needs you permission for using your location for querying GeoPoints in Parse!',
41	            buttonPositive: 'OK',
42	          },
43	        );
44	      }
45	    }
46	  };
47	
48	  const doQueryNear = async function (): Promise<boolean> {
49	    // Request location permissions
50	    await requestLocationPermissions();
51	    // Get current location and create the GeoPoint for the query
52	    Geolocation.getCurrentPosition(
53	      async (currentPosition: {
54	        coords: {latitude: number; longitude: number};
55	      }) => {
56	        // Create our GeoPoint
57	        let currentLocationGeoPoint: Parse.GeoPoint = new Parse.GeoPoint(
58	          currentPosition.coords.latitude,
59	          currentPosition.coords.longitude,
60	        );
61	
62	        // Create our query
63	        let parseQuery: Parse.Query = new Parse.Query('City');
64	
65	        // `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
66	        parseQuery.near('location', currentLocationGeoPoint);
67	
68	        try {
69	          let results: [Parse.Object] = await parseQuery.find();
70	          // Set query results to state variable
71	          setQueryResults(results);
72	        } catch (error) {
73	          // Error can be caused by lack of Internet connection
74	          Alert.alert('Error!', error.message);
75	        }
76	      },
77	      _error => {
78	        Alert.alert(
79	          'Error!',
80	          'This app needs your location permission to query this!',
81	        );
82	      },
83	      {enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
84	    );
85	    return true;
86	  };
87	
88	  const doQueryWithinKilometers = async function (): Promise<boolean> {
89	    // Request location permissions
90	    await requestLocationPermissions();
91	    // Get current location and create the GeoPoint for the query
92	    Geolocation.getCurrentPosition(
93	      async (currentPosition: {
94	        coords: {latitude: number; longitude: number};
95	      }) => {
96	        // Create our GeoPoint
97	        let currentLocationGeoPoint: Parse.GeoPoint = new Parse.GeoPoint(
98	          currentPosition.coords.latitude,
99	          currentPosition.coords.longitude,
100	        );
101	
102	        // Create our query
103	        let parseQuery: Parse.Query = new Parse.Query('City');
104	
105	        // You can also use `withinMiles` and `withinRadians` the same way,
106	        // but with different measuring unities
107	        parseQuery.withinKilometers('location', currentLocationGeoPoint, 3000);
108	
109	        try {
110	          let results: [Parse.Object] = await parseQuery.find();
111	          // Set query results to state variable
112	          setQueryResults(results);
113	        } catch (error) {
114	          // Error can be caused by lack of Internet connection
115	          Alert.alert('Error!', error.message);
116	        }
117	      },
118	      _error => {
119	        Alert.alert(
120	          'Error!',
121	          'This app needs your location permission to query this!',
122	        );
123	      },
124	      {enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
125	    );
126	    return true;
127	  };
128	
129	  const doQueryWithinPolygon = async function (): Promise<boolean> {
130	    // Create our GeoPoint polygon points
131	    let geoPoint1: Parse.GeoPoint = new Parse.GeoPoint(
132	15.822.238.344.514.300,00
133	-7.242.845.934.415.940,00
134	    );
135	    let geoPoint2: Parse.GeoPoint = new Parse.GeoPoint(
136	      -0.7433770196268968,
137	-9.744.765.968.406.660,00
138	    );
139	    let geoPoint3: Parse.GeoPoint = new Parse.GeoPoint(
140	-59.997.149.373.299.100,00
141	-7.652.969.196.322.740,00
142	    );
143	    let geoPoint4: Parse.GeoPoint = new Parse.GeoPoint(
144	-9.488.786.415.007.200,00
145	-18.346.101.586.021.900,00
146	    );
147	    let geoPoint5: Parse.GeoPoint = new Parse.GeoPoint(
148	15.414.859.532.811.000,00
149	-6.000.625.459.569.370,00
150	    );
151	
152	    // Create our query
153	    let parseQuery: Parse.Query = new Parse.Query('City');
154	
155	    // Note that the polygon is merely an array of GeoPoint objects and that the first and last are not connected, so Parse connects them for you
156	    parseQuery.withinPolygon('location', [
157	      geoPoint1,
158	      geoPoint2,
159	      geoPoint3,
160	      geoPoint4,
161	      geoPoint5,
162	    ]);
163	
164	    try {
165	      let results: [Parse.Object] = await parseQuery.find();
166	      // Set query results to state variable
167	      setQueryResults(results);
168	    } catch (error) {
169	      // Error can be caused by lack of Internet connection
170	      Alert.alert('Error!', error.message);
171	    }
172	  };
173	
174	  const clearQueryResults = async function (): Promise<boolean> {
175	    setQueryResults(null);
176	    return true;
177	  };
178	
179	  return (
180	    <>
181	      <View style={Styles.header}>
182	        <Image
183	          style={Styles.header_logo}
184	          source={ {
185	            uri:
186	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
187	          } }
188	        />
189	        <PaperText style={Styles.header_text}>
190	          <PaperText style={Styles.header_text_bold}>
191	            {'React Native on Back4App - '}
192	          </PaperText>
193	          {' GeoPoint Queries'}
194	        </PaperText>
195	      </View>
196	      <ScrollView style={Styles.wrapper}>
197	        <View>
198	          <Title>{'Result List'}</Title>
199	          {/* Query list */}
200	          {queryResults !== null &&
201	            queryResults !== undefined &&
202	            queryResults.map((result: Parse.Object) => (
203	              <List.Item
204	                key={result.id}
205	                title={result.get('name')}
206	                titleStyle={Styles.list_text}
207	                style={Styles.list_item}
208	              />
209	            ))}
210	          {queryResults === null ||
211	          queryResults === undefined ||
212	          (queryResults !== null &&
213	            queryResults !== undefined &&
214	            queryResults.length <= 0) ? (
215	            <PaperText>{'No results here!'}</PaperText>
216	          ) : null}
217	        </View>
218	        <View>
219	          <Title>{'Query buttons'}</Title>
220	          <PaperButton
221	            onPress={() => doQueryNear()}
222	            mode="contained"
223	            icon="search-web"
224	            color={'#208AEC'}
225	            style={Styles.list_button}>
226	            {'Query Near'}
227	          </PaperButton>
228	          <PaperButton
229	            onPress={() => doQueryWithinKilometers()}
230	            mode="contained"
231	            icon="search-web"
232	            color={'#208AEC'}
233	            style={Styles.list_button}>
234	            {'Query Within KM'}
235	          </PaperButton>
236	          <PaperButton
237	            onPress={() => doQueryWithinPolygon()}
238	            mode="contained"
239	            icon="search-web"
240	            color={'#208AEC'}
241	            style={Styles.list_button}>
242	            {'Query Within Polygon'}
243	          </PaperButton>
244	          <PaperButton
245	            onPress={() => clearQueryResults()}
246	            mode="contained"
247	            icon="delete"
248	            color={'#208AEC'}
249	            style={Styles.list_button}>
250	            {'Clear Results'}
251	          </PaperButton>
252	        </View>
253	      </ScrollView>
254	    </>
255	  );
256	};
257	
258	// These define the screen component styles
259	const Styles = StyleSheet.create({
260	  header: {
261	    alignItems: 'center',
262	    paddingTop: 30,
263	    paddingBottom: 50,
264	    backgroundColor: '#208AEC',
265	  },
266	  header_logo: {
267	    height: 50,
268	    width: 220,
269	    resizeMode: 'contain',
270	  },
271	  header_text: {
272	    marginTop: 15,
273	    color: '#f0f0f0',
274	    fontSize: 16,
275	  },
276	  header_text_bold: {
277	    color: '#fff',
278	    fontWeight: 'bold',
279	  },
280	  wrapper: {
281	    width: '90%',
282	    alignSelf: 'center',
283	  },
284	  list_button: {
285	    marginTop: 6,
286	    marginLeft: 15,
287	    height: 40,
288	  },
289	  list_item: {
290	    borderBottomWidth: 1,
291	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
292	  },
293	  list_text: {
294	    fontSize: 15,
295	  },
296	});
```
:::

This is how the component should look like after rendering and querying one of the query functions:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Xea6zYcy8-Pe1W1Tob3J3_image.png" signedSrc size="50" width="355" height="750" position="center" caption}

## Conclusion

At the end of this guide, you learned how GeoPoint data queries work on Parse and how to perform them on Back4App from a React Native App. In the next guide, you will check how to create and manage users in Parse.

[title] Cloud Code functions
[path] Get started/

**Cloud Code** is a powerful tool that enables you to execute JavaScript functions directly on the server, adding advanced features to your application without the need to manage your own servers. Running in Back4app’s environment ensures scalability and simplicity.

With Cloud Code, you can:

- Automate database actions in response to events.
- Create custom validations for requests.
- Integrate your application with external services using npm libraries.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need an app created at Back4app.
&#x20;Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create a new App tutorial</a> to learn how to create an app at Back4app.
:::

## Goal

- To deploy and execute a cloud function from your App

## 1 - Access your cloud code

Go to the **Cloud Code** section in your Back4app dashboard. You’ll find two main folders: `cloud` and `public`.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QHCbDWjHiW7Wo3QrIj64o_screenshot-2024-11-15-at-152832.png)



## **2 - Edit the main.js file**

The main.js file is where your Cloud Code functions are defined.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8ppF7oBa6LFWR4-FpSEFd_screenshot-2024-11-15-at-153229.png)

If needed, you can import functions from other files using:

:::CodeblockTabs
main.js

```javascript
require('./fileName.js');
```
:::

## 3 - Create your first Cloud Code Function

Some basic function examples include:

- A simple greeting function:

:::CodeblockTabs
main.js

```javascript
Parse.Cloud.define("hello", async (request) => {
    console.log("Hello from Cloud Code!");
    return "Hello from Cloud Code!";
});
```
:::

- A function to sum two numbers:

```javascript
Parse.Cloud.define("sumNumbers", async (request) => {
    return request.params.number1 + request.params.number2;
});
```

## 4- Deploy your code to the server

Once your functions are ready, click the **Deploy** button to publish them to the Back4app environment.&#x20;

## 5 - Test your cloud code function

You can test your functions directly via the API using tools like cURL or any preferred SDK. Below is an example for calling the `hello` function:

:::CodeblockTabs
```javascript
Parse.Cloud.run('hello')
  .then((result) => {
    console.log(result); // Output: "Hello from Cloud Code!"
  })
  .catch((error) => {
    console.error('Error:', error);
  });
```

Flutter

```dart
ParseCloudFunction function = ParseCloudFunction('hello');
ParseResponse response = await function.execute();

if (response.success) {
  print(response.result); // Output: "Hello from Cloud Code!"
} else {
  print('Error: ${response.error.message}');
}
```

Android

```java
ParseCloud.callFunctionInBackground("hello", new HashMap<>(), new FunctionCallback<Object>() {
    @Override
    public void done(Object result, ParseException e) {
        if (e == null) {
            Log.d("Cloud Code", result.toString()); // Output: "Hello from Cloud Code!"
        } else {
            Log.e("Cloud Code Error", e.getMessage());
        }
    }
});
```

iOS

```swift
ParseCloud.callFunction("hello", parameters: nil) { result in
    switch result {
    case .success(let response):
        print("Response: \(response)") // Output: "Hello from Cloud Code!"
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}
```

.NET

```csharp
var result = await ParseCloud.CallFunctionAsync<string>("hello", null);
Console.WriteLine(result); // Output: "Hello from Cloud Code!"
```

```php
use Parse\ParseCloud;

try {
    $result = ParseCloud::run("hello");
    echo $result; // Output: "Hello from Cloud Code!"
} catch (Exception $ex) {
    echo "Error: " . $ex->getMessage();
}
```

REST API

```curl
curl -X POST \
-H "X-Parse-Application-Id: APPLICATION-ID" \
-H "X-Parse-REST-API-Key: REST-API-KEY" \
--data-urlencode "" \
https://parseapi.back4app.com/functions/hello
```
:::

## 6 - Additional Features

- **Data Manipulation**: Create, edit, or retrieve objects in your database with specific functions, such as this example for creating a ToDo item:

:::CodeblockTabs
main.js

```javascript
Parse.Cloud.define("createToDo", async (request) => {
    const todo = new Parse.Object('ToDo');
    todo.set('title', request.params.title);
    todo.set('done', request.params.done);
    return await todo.save();
});
```
:::

- **Advanced Queries**: Retrieve information directly from the database:

:::CodeblockTabs
main.js

```javascript
Parse.Cloud.define("getListToDo", async (request) => {
    const query = new Parse.Query("ToDo");
    query.equalTo("done", true);
    query.descending("title");
    return await query.find();
});
```
:::

## Conclusion

With Cloud Code, you can effortlessly build robust and customized solutions. It’s ideal for automation, integrations, and validations and works seamlessly with any technology, such as Flutter, React Native, or REST API.

If you encounter any issues, the <a href="https://www.back4app.com/support" target="_blank">Back4app support team</a> is available to assist you.

[title] Logging out
[path] GraphQL Cookbook/

# Logging out a logged user through the Parse GraphQL API

## Problem

You want to log out a logged user in your backend through the Parse GraphQL API.

## Solution

Using the Parse GraphQL API, you can log out a logged user just by sending the user’s sessionToken through the X-Parse-Session-Token header (as described in the [authenticating a user](https://www.back4app.com/docs/parse-graphql/graphql-user-authentication) recipe) and calling the logOut mutation. Parse Server will destroy the sessionToken and it will not be accepted for any other future request.

## Version Information

Depending on the version of Parse you choose to run, the GraphQL queries, mutations and results will be slightly different.
Please choose the correct example along with the Parse version you are running.

## **Parse Server 4.4.0 and later**

:::CodeblockTabs
Request

```graphql
//The headers for this operation are X-Parse-Application-Id, X-Parse-Client-Key and X-Parse-Session-Token
1   mutation logOutButton {
2	   logOut(input: { clientMutationId: "9vc3NljYHP" }) {
3		   clientMutationId
4	   }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "logOut": {
4         "clientMutationId": "9vc3NljYHP"
5       }
6     }
7   }
8
```
:::

## Older Parse Server Versions

::::ExpandableHeading
**Parse Server 3.10.0 and 4.2.0**

:::CodeblockTabs
Request

```graphql
//With Parse 3.10.0 and 4.2.0 you must set a header called X-Parse-Session-Token containing the Session Token for the authenticated user. Once it is set, you can call:
1   mutation{
2     logOut(input: { clientMutationId: "sampleId"}){
3       viewer{
4         user{
5           id
6         }
7       }
8     }
9   }
```

Response

```graphql
1   {
2     "data": {
3       "logOut": {
4         "viewer": {
5           "user": {
6             "id": "X1VzZXI6UHNOUkJ3Y1YyRQ=="
7           }
8         }
9       }
10    }
11  }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.9.0**

:::CodeblockTabs
Request

```graphql
//With Parse 3.9.0 you must set a header called X-Parse-Session-Token containing the Session Token for the authenticated user. Once it is set, you can call:
1   mutation{
2     logOut{
3       id
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "logOut": {
4         "id": "Gx2zW7yEnY"
5       }
6     }
7   }
```
:::


::::

::::ExpandableHeading
**Parse Server 3.8.0**

:::CodeblockTabs
Request

```graphql
1   mutation{
2     logOut{
3       objectId
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "logOut": {
4         "objectId": "KTznKVzto2"
5       }
6     }
7   }
```
:::
::::

::::ExpandableHeading
**Parse Server 3.7.2**

:::CodeblockTabs
Request

```graphql
1   mutation LogOut {
2     users {
3       logOut
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "users": {
4         "logOut": true
5       }
6     }
7   }
```
:::


::::


[title] Untitled
[path] React Native/Parse SDK (REST)/


[title] Custom Parse Options
[path] Platform/

## Introduction

In this guide, you will learn how to add and edit your Custom Parse Options.

## Goal

- Configure the Parse Server Options.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read or edit this page.**
:::

## Parse Server Options

When you create a new application at Back4App, we will create all your application backend structure and it builds your database structure, your application layer, and your APIs. We do it all, thinking about scalability and security.

While your app is being created, a file called config.json will be generated with the options that contain the configuration like keys to starting the app in the JSON format.

This block looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NC3D9GCY83SL7CKJok3ZA_image.png" signedSrc size="40" width="239" height="305" position="center" caption}

## How to use it?

Now, we will show you some examples of properties that can be easily changed at this section.

:::hint{type="danger"}
Note that this is a **DANGER ZONE**. Your app can stop working if you do something wrong. If you are not sure, ask for support.
:::

Please, check the following topics about how to use each property below:

### **Property: allowCustomObjectId**

Enable (or disable) custom objectId.

```json
{
    "allowCustomObjectId": true
}
```

### **Property: customPages**

With this property, you will be able to add custom pages for password validation and reset.

**1 - Enable your Webhosting**

The first step that you need to take is to enable your web hosting following [this guide](https://www.back4app.com/docs/platform/parse-web-hosting).

**2 - Upload the HTML files**

At this step, You only need to deploy these static HTML pages in your “public” folder on cloud code. Please, download the following templates to edit them:

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aNXpCyDvMy8bdzXoB9pXY_choose-password.html)

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/iyI0RtmQH0_LW5swCCuRw_invalid-link.html)

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_x0rDq9PPlCurkL2kOcZY_invalid-verification-link.html)

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Q6viDQiA5zA7m5XfkRUcM_link-send-fail.html)

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LY_amC4Mu6O5ilSOY9TP2_link-send-success.html)

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OJ8hRCzPvXJcfpF-nFCKU_password-reset-success.html)

[](https://archbee-doc-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2VtVz1ZbmW4pBoVoUWG_z_verify-email-success.html)

:::hint{type="danger"}
Before uploading these files, please make sure that your file name doesn’t have spaces.
:::

**3 - Configuring the custom pages**

The configuration will look like something below:

Example:

```json
{
    "customPages": {
        "invalidLink": "https://<subdomain>.b4a.app/invalid_link.html",
        "verifyEmailSuccess": "https://<subdomain>.b4a.app/verify_email_success.html",
        "choosePassword": "https://<subdomain>.b4a.app/choose_password.html",
        "passwordResetSuccess": "https://<subdomain>.b4a.app/password_reset_success.html",
        "invalidVerificationLink": "https://<subdomain>.b4a.app/invalid_verification_link.html",
        "linkSendFail": "https://<subdomain>.b4a.app/link_send_fail.html",
        "linkSendSuccess": "https://<subdomain>.b4a.app/link_send_success.html"
    }
}
```

Check how to [create your subdomain here](https://www.back4app.com/docs/platform/activating-web-hosting)

### **Property: sessionLength**

This property configures the expiration date of your sessions, in seconds (defaults to 1 year).

Example:

```json
{
    "sessionLength": 31622400
}
```

### &#x20;**Property: emailVerifyTokenValidityDuration**

This property configures the email verification token validity duration, in seconds.

Example:

```json
{
    "emailVerifyTokenValidityDuration": Number
}
```

### **Property: enableAnonymousUsers**

With this property, you will be able to enable (or disable) anon users, defaults to true.

&#x20;Example:

```json
{
    "enableAnonymousUsers": false
}
```

### **Property: enableSingleSchemaCache**

Use a single schema cache shared across requests. Reduces the number of queries made to \_SCHEMA, defaults to false, i.e. unique schema cache per request.

Example:

```json
{
    "enableSingleSchemaCache": true
}
```

### **Property: expireInactiveSessions**

Sets whether we should expire the inactive sessions, defaults to true.

Example:

```json
{
    "expireInactiveSessions": false
}
```

### **Property: objectIdSize**

Sets the number of characters in generated object IDs, default 10.

Example:

```json
{
    "objectIdSize": Number
}
```

### **Property: preserveFileName**

Enable (or disable) the addition of a unique hash to the file names.

Note that it is recommended to keep it as false to prevent errors while trying to delete the unused files!

Example:

```json
{
    "preserveFileName": Boolean
}
```

## Conclusion

At this point, you have learned how to customize your Parse Server Options.

[title] Publish on Hub
[path] Parse Dashboard/

# Database Publishing

## Introduction

In the *Publish On Hub* section page, you can share your database (including files and cloud functions/triggers) and make it public for other developers clone or connect.

Publishing a Database allows you to be an active part of the Back4app community and share data that you think might be useful to others.

In this guide, you will learn how to publish an app on Hub. The complete guide about Database Hub can be found <a href="https://www.back4app.com/docs/database-hub/clone" target="_blank">here</a> .

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read or edit this page.**
:::

## Database Browser

In this video, you can see the steps on how to Publish an App on Hub.

::embed[]{url="https://www.youtube.com/watch?embeds_referring_euri=https%3A%2F%2Fwww.back4app.com%2F&source_ve_path=Mjg2NjQsMTY0NTAz&feature=emb_share&v=pFEXCNCJO5k"}


[title] Working with Users
[path] React Native/Relay (GraphQL)/Users/

# Working with Users

## Introduction

At the core of many apps, user accounts have a notion that lets users securely access their information. At Back4App/Parse provides a specialized user class called Parse.User that automatically handles much of the functionality required for user account management.

We will better explain how this class works by giving you a practical guide on making a user sign up, a user log-in, and a user logout. In the next tutorials, let’s understand how to implement each one in a React Native application using GraphQL and Relay.

## Goal

Explain the Parse.User class and how Relay will handle with this class.

## Parse User Class

Parse.User is a subclass of Parse.Object, and has all the same features, such as flexible schema, automatic persistence, and a key-value interface. All the methods on Parse.Object also exist in Parse.User. The difference is that Parse.User has some special additions specific to user accounts.

## Parse.User Properties

Parse.User has several values that set it apart from Parse.Object:

- username: The username for the user (required).
- password: The password for the user (required on signup).
- email: The email address for the user (optional).

We’ll go through each of these in detail as we run through the various use cases for users.

## Relay Mutation

A way to handle the User class on a front end with Relay in React Native is using Mutations. Mutations are responsible for creating functions, executing them, sending the data to the backend, and expecting a return. Any Mutation function will prepare the data to send it. When returned from the backend, handle the success or error scenario. In both cases, the application can control the next state and decide what will happen after.

This guide is using Relay Modern on the frontend to consume GraphQL. The flow to create a mutation is similar to create a query or fragment.

The Relay Mutation needs to be equal to the backend specification. When creating a new Mutation, the Relay Compiler will check if the backend structure is identical to the application/frontend source of truth, the schema.graphql.

Every Relay Mutation will have a principal function called commitMutation. This function handles the GraphQL fragment, the input variables, the completed, and the error callback. The Relay Mutation can have other arguments, but in the next tutorials won’t be used.

## commitMutation

commitMutation is the default function to create and execute mutations in your GraphQL on the client-side. Similar to QueryRenderer, the commitMutation will receive props. These props, combine in themselves, will prepare the fetch, call the server, and handle the return.

There so many props to handle your application on each case that it needs. But, in the next tutorials, it will use only the next one:

- environment: The environment is responsible for the store and network of applications.
- input is an object that contains the variables necessary to resolve the mutation.
- onCompleted and onError: are functions, as the name says, called when the mutation is complete. The onCompleted is for success and onError for error.

Example of commitMutation:

```javascript
1	function commit({environment, input, onCompleted, onError}) {
2	  const variables = {input};
3	
4	  commitMutation(environment, {
5	    mutation,
6	    variables,
7	    onCompleted,
8	    onError,
9	  });
10	}
11	
12	export default {
13	  commit,
```

:::hint{type="info"}
For more info about Relay Mutation go to the <a href="https://relay.dev/docs/en/mutations" target="_blank">official docs</a>.
:::

## Conclusion

Now, the mutation concept is clear and explained. In the next tutorial, it will handle the Sign Up flow on Back4App. There it will be specified how to implement a simple mutation to register a new user and return a session token.

[title] Quickstart
[path] React Native/Parse SDK (REST)/

## Introduction

This guide will help you set up and use Back4App with a new or existing project using the **React Native CLI**. You’ll install the `Parse SDK`, initialize it with your app keys, and create your first API call to save and retrieve data from Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">app created</a> on Back4App.
- [Npm ](https://www.npmjs.com/get-npm?utm_source=house\&utm_medium=homepage\&utm_campaign=free%20orgs\&utm_term=Install%20npm)or yarn installed.
- [Npx ](https://www.npmjs.com/package/npx)package runner installed.
:::

## 1 - Create your React Native project

There are two main ways to create a React Native project: **React Native CLI **and **Expo**. Choose based on your development environment and target platform (iOS or Android)

:::ExpandableHeading
**React Native CLI**

The instructions depend on your development operating system, and whether you want to start developing for iOS or Android. For more information, check the official documentation <a href="https://reactnative.dev/docs/environment-setup" target="_blank">here</a>.


:::

:::ExpandableHeading
**Expo**

**Install Expo CLI globally**

> npm install -g expo-cli

**Create a new React Native project**

> expo init B4aProject
>
> cd B4aProject
> expo start

>
:::

## 2 - Install dependencies

In your React Native project, install `Parse Javascript SDK` and `AsyncStorage` by running:

> npm install parse @react-native-async-storage/async-storage --save

- **Parse Javascript SDK **- To integrate your App with Back4app servers.
- **React Native Async Storage **- To use Parse SDK, an AsyncStorage handler is required.

For iOS, also add native AsyncStorage support:

> cd ios & pod install

## 3 - Get your App Keys

After creating your app on Back4App, find your App Keys under **App Settings > Security & Keys**. You’ll need both the **Application ID** and **JavaScript KEY** to connect.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IZ0wzNH_Parph4S2-2Nwe_rn-quickstart-1.png)

## 4 - Initialize Parse and connect to Back4App

Open `index.tsx` and initialize Parse with your `Application ID` and `JavaScript KEY`:

:::CodeblockTabs
index.tsx

```typescript
import Parse from 'parse/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Initialize Parse only once
Parse.setAsyncStorage(AsyncStorage);
Parse.initialize('YOUR_APPLICATION_ID', 'YOUR_JAVASCRIPT_KEY');
Parse.serverURL = 'https://parseapi.back4app.com/';

```
:::

## 5 - Save and Retrieve Data

With Parse initialized, create two functions in `index.tsx` to save and fetch data from Back4App.

```typescript
  // Function to create a new Person 
  async function createPerson() {
    setLoading(true);
    setError(null);
    
    try {
      const PersonObject = Parse.Object.extend("Person");
      const personObject = new PersonObject();
      
      personObject.set("name", "Back4App User");
      
      const result = await personObject.save();
      setResult(`Object created with ID: ${result.id}`);
      
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  }

  async function fetchPeople() {
    setLoading(true);
    setError(null);
    
    try {
      const PersonObject = Parse.Object.extend("Person");
      const query = new Parse.Query(PersonObject);
  
      const results = await query.find();
      const names = results.map(result => ({
        objectId: result.id,
        name: result.get("name"),
      }));
      
      setResult(`Fetched names: ${JSON.stringify(names, null, 2)}`);
      
    } catch (error) {
      setError(error instanceof Error ? error.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  }
```

## 6 - Test your App

1. Open your project’s terminal.
2. Run the project.

:::ExpandableHeading
**React Native CLI**

Run npx react-native run-android or npx react-native run-ios to open the application on your target platform.
:::

:::ExpandableHeading
**Expo**

Run expo start, and follow the instructions to view the app in your browser or device.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/DJc6gXLraAX2iWP7w9DJ5_screenshot-2024-10-31-at-134200.png)
:::

## Troubleshooting

Some common issues and solutions:

### **Metro has encountered an error: while trying to resolve module “idb-keyval’ from file**

Solution: Go to the metro.conf.js file and change it to this one:

```javascript
1   const { getDefaultConfig } = require("@expo/metro-config");
2   const defaultConfig = getDefaultConfig(__dirname);
3   defaultConfig.resolver.assetExts.push("cjs");
4   module.exports = defaultConfig;
```

### **Unable to resolve module ‘EventEmitter’**

Solution: Go to the file: node\_modules\parse\lib\react-native\EventEmitter.js and change this row:

>  var EventEmitter = require('../../../react-native/Libraries/vendor/emitter/EventEmitter');

to this:

>  import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';

In the same file EventEmitter.js, change the following row:

> module.exports = EventEmitter;

to this:

> export default EventEmitter;

### Issues with babel

In case you face any issues with babel, consider updating your `babel.config.js` to the following:

> module.exports = function (api) {
>   api.cache(true);
>   return {
>     presets: ['babel-preset-expo'],
>     plugins: [
>       '@babel/plugin-proposal-export-namespace-from',
>       'react-native-reanimated/plugin',
>     ],
>   };
> };

## Next Steps

This guide covers the basic setup and data storage with Back4App. Explore Parse features, including data storage, real-time capabilities, local data storage, cloud functions, authentication, and file storage.


[title] Usage
[path] React Native/Parse SDK (REST)/Real Time/

# Using the useParseQuery hook to build a real time React Native App

## Introduction

In this guide, you will explore the main features of @parse/react-native lib using a ToDO React Native app example. You will use the useParseQuery hook to query tasks in real-time and store results locally on this App. Using various Parse Queries, you will discover how to use the new Parse Lib on your project.

:::hint{type="danger"}
Parse React Native is currently on the Alpha version. The lib is under testing, so we recommend to proceed with caution. Your feedback is very appreciated, so feel free to use the lib and send us your questions and first impressions by dropping an email to community\@back4app.com
:::

## Goal

Explore the main use cases for the Parse React Native lib by creating a ToDo App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created on Back4App.
- Follow the <a href="https://www.back4app.com/docs/platform/parse-live-query" target="_blank">Enable Live Query</a> tutorial.
- **Note: **Follow the <a href="https://www.back4app.com/docs/react-native/parse-sdk/real-time/react-hook-real-time" target="_blank">Parse React Native tutorial</a> to learn how to get started with @parse/react-native
:::

## 1 - Setup the initial project

Before getting started, you will need to get the bootstrap React Native project that we have setup as a starting point for this tutorial. It is a simple react-native init project with all dependencies and styles pre-defined for you to focus on exploring @parse/react-native features. You may [download de Zip](https://github.com/templates-back4app/react-native-todo-app/archive/main.zip) or clone the project.

> git clone https:
>
> *//github.com/templates-back4app/react-native-todo-app.git*

Next, install the project dependencies:

> cd react-native-todo-app
>
>  \# Using yarn
> yarn install
>
>   \# Using npm
> npm install

For iOS, install pods:

> cd ios 
>
> **&&**
>
>  npx pod
>
> **-**
>
> install

In the previous guide, [Getting started](https://www.back4app.com/docs/react-native/parse-react-native-sdk/getting-started), you learned how to use initializeParse to enable connection with Back4app servers. Set up your App Id and JavaScriptKey in the entry point component located at src/index.js:&#x20;

```javascript
1   // src/index.js
2   initializeParse(
3   'https://parseapi.back4app.com/',
4   'APPLICATION_ID',
5   'JAVASCRIPT_KEY'
6   );
```

Go ahead and run the project:

> \# For iOS
> npx react-native run-ios
>
> \# For Android
> npx react-native run-android

After that you will have successfully setup the starter project and the App will look like the following:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ARgyL376qWC8HXTY40ure_image.png" signedSrc size="50" width="306" height="645" position="center" caption}

The project’s directory structure:

> react-native-todo-app
> ├── src/
> │   ├── config/
> │   │   └── ReactotronConfig.js
> │   ├── images/
> │   │   ├── back4app_logo.png
> │   │   └── bg.jpg
> │   ├── components/
> │   │   └── CardTodoButton/
> │   │       └── index.js // card button item component
> │   │   └── Menu/
> │   │       └── index.js // menu with card buttons
> │   │   └── TaskList/
> │   │       └── index.js // List of tasks component
> │   ├── pages/
> │   │   └── AddTodo/
> │   │       └── index.js // Add todo page  
> │   │   └── Learning/
> │   │       └── index.js // learning page  
> │   │   └── Main/
> │   │       └── index.js // main page
> │   │   └── Shopping/
> │   │       └── index.js // shopping page
> │   │   └── Work/
> │   │       └── index.js // work page
> │   ├── services/
> │   │   └── api.js
> │   ├── index.js // App entrypoint
> │   └── routes.js // navigation routes config
> ├── .editorconfig
> ├── .eslintrc.json
> ├── .gitignore
> ├── babel.config.js
> ├── dependencies.json
> ├── devDependencies.json
> ├── index.js
> ├── jsconfig.js
> ├── LICENSE
> ├── package.json
> └── README.md

The initial project have main 4 pages:

- **Learning Page**: shows tasks that belongs to the learning category
- **Shopping Page**: shows tasks that belongs to the shopping category
- **Work Page**: shows tasks that belongs to the work category
- **AddTodo Page**: basic form to create a new task

## 2 - Creating a new Task

A commom feature in a ToDo app is allowing users to create new tasks. For that, the create task function will use the Parse Javascript SDK to create a new Parse Object and save it on Back4app. On the AddTodo page from the starter project, you will have a basic form with an input to insert the task description, some check boxes to select the task category and a submit button. In this tutorial, you will create Parse.Object for the tasks which will have the following attributes:

:::hint{type="info"}
Look at the Parse Javascript SDK <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-save-data" target="_blank">Save Data</a> guide for more info on creating Parse objects.&#x20;
:::

```javascript
1	{
2	  description: 'simple string of task descrition',
3	  author: 'person creating the task',
4	  completed: false, // or true
5	  createdAt: Date, // automatically created by back4app
6	}
```

Now implement the method to create a task when the user clicks on submit. At thepages/AddTodo/index.jscomponent, let’s implement thehandleSubmitmethod:

```javascript
1	  async function handleSubmit() {
2	    try {
3	      const Task = new Parse.Object.extend('Task');
4	      // Create a new instance of that class.
5	      const task = new Task();
6	      task.set('description', description);
7	      task.set('category', category);
8	      task.set('author', 'Anonymous');
9	      task.set('completed', false);
10	      await task.save();
11	
12	      Alert.alert('New Task Created.');
13	    } catch (error) {
14	      console.log('Error while creating task: ', error);
15	    }
16	  }
```

After that, you will now be able to create some tasks. Feel free to create as many tasks as you want. In the next steps you will be querying them.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0QEgLvOn78QvuJGwDzcBF_image.png" signedSrc size="50" width="301" height="647" position="center" caption}

## 3 - Querying & Filtering Tasks

Now that you have created some tasks, it is time to use Parse React Native Lib. You will write some queries and pass them to useParseQuery hook. The queries will list all the non-completed tasks in the learning category. This is the first use case of the hook, you will build a one-time fetch query, by setting enableLiveQuery\:false, that runs when the learning page component renders. The enableLiveQuery is true by default, and changing to false will disable the real-time changes subscription.

On the pages/Learning/index.js component, let’s write our Parse.query:

```javascript
1     const Task = new Parse.Object.extend('Task');
2     const query = new Parse.Query(Task);
3     query.equalTo('completed', false);
4     query.equalTo('category', 'learning');
```

Pass the query as argument to theuseParseQueryhook:

> const {results} = useParseQuery(query, {enableLiveQuery: false});

The above code shows a basic use for the Parse hook. The useParseQuery hook is a new resource that you can use with any Parse.Query. Use [all Parse.Query capabitilies](https://docs.parseplatform.org/js/guide/#queries) to retrieve your data objects and the hook will make this experience even better. After getting the results, pass them down to the TaskList component to display tasks on the App:

```javascript
1	//Learning/index.js
2	import React, {useEffect} from 'react';
3	import {ActivityIndicator} from 'react-native';
4	import TaskList from '../../components/TaskList';
5	import Parse from 'parse/react-native.js';
6	import {useParseQuery} from '@parse/react-native';
7	
8	const Learning = () => {
9	  const Task = new Parse.Object.extend('Task');
10	  const query = new Parse.Query(Task);
11	  query.equalTo('completed', false);
12	  query.equalTo('category', 'learning');
13	
14	  const {results, isLoading} = useParseQuery(query, {enableLiveQuery: false});
15	
16	  if (isLoading) {
17	    return <ActivityIndicator/>;
18	  }
19	
20	  return <TaskList todos={results} />;
21	};
22	
23	export default Learning;
```

Your App should successfuly show the list of tasks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ElXKoPQfM6X0CAuZR10JY_image.png" signedSrc size="50" width="308" height="647" position="center" caption}

## 4 - Realtime changes

The second usage you are going to explore is real-time updates. The useParseQuery hook encapsulates the Parse Live Query and provides out of the box support for real-time changes. When passing a query to the hook, it creates a WebSocket connection to communicate with the Back4app LiveQuery server, which synchronizes automatically. You will add this feature to the tasks in the shopping category.

It is important to note that Live Query and Back4App subdomain must be enabled on your Back4app Dashboard App. Once you do that, add your subdomain url to initializeParse and the results from the Parse React Native hook will always have updated data. If you do not configure the subdomain, useParseQuery hook will not be able to retrieve data in real-time.

```javascript
1	// src/index.js
2	initializeParse(
3	'<yoursubdomain>.b4a.io',
4	'APPLICATION_ID',
5	'JAVASCRIPT_KEY'
6	);
```

On thepages/Shopping/index.jscomponent, let’s write ourParse.query:

```javascript
1     const Task = new Parse.Object.extend('Task');
2     const query = new Parse.Query(Task);
3     query.equalTo('completed', false);
4     query.equalTo('category', 'shopping');
```

Then, pass the query as argument to theuseParseQueryhook:

> const {results, isLoading, isSyncing} = useParseQuery(query);

Note that there is no need for extra parameters since the real-time changes is enabled by default. After getting the results, pass them down to theTaskListcomponent to display tasks on the App:

```javascript
1	import React from 'react';
2	import {ActivityIndicator} from 'react-native';
3	import TaskList from '../../components/TaskList';
4	import Parse from 'parse/react-native.js';
5	import {useParseQuery} from '@parse/react-native';
6	
7	const Shopping = () => {
8	  const Task = new Parse.Object.extend('Task');
9	  const query = new Parse.Query(Task);
10	  query.equalTo('completed', false);
11	  query.equalTo('category', 'shopping');
12	
13	  const {results, isLoading, isSyncing} = useParseQuery(query);
14	
15	  if (isLoading || isSyncing) {
16	    return <ActivityIndicator />;
17	  }
18	  return <TaskList todos={results || []} />;
19	};
20	
21	export default Shopping;
```

## 5 - Offline Support

The third use case for @parse/react-native is using offline caching of query results. This is useful in case your React Native App needs to work when users have network latency or Internet connectivity issues. Offline support improves your React Native Apps responsiveness and user expirience. The great news is that no extra steps are required! The offline-first approach and real-time subscriptions are enabled by default.

In short, simply using useParseQuery hook assures that your app will be caching query results for offline support combined with Live Query subscriptions for when your user goes back online.

## 6 - Limiting & Sorting queries

Suppose that the task list from the work category is too much for a person to handle and you want to show only a subset of them for the day. Also, order by date of creation.

On the pages/Shopping/index.js component, let’s write our Parse.query:

```javascript
1     const Task = new Parse.Object.extend('Task');
2     const query = new Parse.Query(Task);
3     query.equalTo('completed', false);
4     query.equalTo('category', 'work');
5     query.ascending('createdAt'); // order by creation date
6     query.limit(5); // limit to 5 tasks
```

Then, pass the query as argument to the useParseQuery hook and pass them down to the TaskList component to display tasks on the App:

```javascript
1	import React from 'react';
2	import {ActivityIndicator} from 'react-native';
3	import Parse from 'parse/react-native.js';
4	import {useParseQuery} from '@parse/react-native';
5	import TaskList from '../../components/TaskList';
6	// import { Container } from './styles';
7	
8	const Work = () => {
9	  const Task = new Parse.Object.extend('Task');
10	  const query = new Parse.Query(Task);
11	  query.equalTo('completed', false);
12	  query.equalTo('category', 'work');
13	  query.ascending('createdAt');
14	  query.limit(5);
15	
16	  const {results, isLoading} = useParseQuery(query, {
17	    enableLiveQuery: false,
18	  });
19	
20	  if (isLoading) {
21	    return <ActivityIndicator />;
22	  }
23	  return <TaskList todos={results} />;
24	};
25	
26	export default Work;
```

## 7 - Specifying useParseQuery arguments

You used @parse/react-native to retrive data from Back4app with features such Live Query in the previous steps. Therefore, an explanation of the interface exported is required. The useParseQuery hook accepts a Parse.Query and an UseParseQueryOptions object as its arguments. The default optional configuration object is the following:

```javascript
1   {
2     enableLiveQuery: true,
3     enableLocalDatastore: true,
4     initialLoad: []
5   }
```

- **enableLiveQuery**: Realtime Live Query feature is enabled by default
- **enableLocalDatastore**: Enables local caching of data results, default is true but you can turn off by setting to false
- **initialLoad**: If you already have some data loaded in memory then you can set them to show a preview of the data to users.

## It’s Done!

At this point, you’ve learned how to use the @parse/react-native lib by creating a React Native todo App on Back4App.

[title] Checking the API health
[path] GraphQL Cookbook/

# Checking the Parse GraphQL API health

## Problem

You want to check the Parse Server backend health through the GraphQL API.

## Solution

Using the Parse GraphQL API, you can check the backend health by calling the health query.

Example:

> **   query**
>
>  Health {
>    health
>    }

Result:

>    \{
>    "data": {
>    "health": 
>
> **true**
>
>
>    }
>    }


[title] Model Context Protocol (MCP)
[path] Local Development/

The Model Context Protocol (MCP) is a standard for connecting Large Language Models (LLMs) to platforms like Back4App. This guide covers how to connect Back4App to the following AI tools using MCP:

- Cursor
- Windsurf (Codium)
- Visual Studio Code (Copilot)
- Cline (VS Code extension)
- Claude desktop
- Claude code

Once connected, your AI assistants can interact with and query your Back4App projects on your behalf.

:::hint{type="warning"}
AI agents with MCP are configured to have full access to your Back4App apps, which includes the ability to create, modify, and delete resources. We strongly recommend first trying on a test account and app to understand its capabilities and potential impact before using it with production environments.
:::

## 1 - Create a access token (account key)

First, go to your Back4App dashboard and create a personal access token:

1. Log into your Back4App account
2. Hover over the "Hello, \[username]" menu
3. Go to **Account Keys** and create a new key
4. Give it a name that describes its purpose, like "AI Assistant MCP"
5. Copy the token securely - you won't be able to see it again

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Vtf9h_neBfwWz0MOTYGb8_screenshot-2024-11-28-at-164118.png)

This token will be used to authenticate the MCP server with your Back4app account.

## 2 - Configure in your AI tool

MCP compatible tools can connect to Back4App using the Back4App MCP server. Below are instructions for connecting to this server using popular AI tools:

### Cursor

1. Open Cursor and create a `.cursor` directory in your project root if it doesn't exist
2. Create a `.cursor/mcp.json` file if it doesn't exist and open it
3. Add the following configuration:

### macOS / Linux

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

### Windows

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx.cmd",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

1. Replace `<personal-access-token>` with your personal access token
2. Save the configuration file
3. Open Cursor and navigate to Settings/MCP. You should see a green active status after the server is successfully connected

### Windsurf

1. Open Windsurf and navigate to the Cascade assistant
2. Tap on the hammer (MCP) icon, then Configure to open the configuration file
3. Add the following configuration:

### macOS / Linux

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

### Windows

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx.cmd",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

1. Replace `<personal-access-token>` with your personal access token
2. Save the configuration file and reload by tapping Refresh in the Cascade assistant
3. You should see a green active status after the server is successfully connected

### Visual Studio Code (Copilot)

1. Open VS Code and create a `.vscode` directory in your project root if it doesn't exist
2. Create a `.vscode/mcp.json` file if it doesn't exist and open it
3. Add the following configuration:

### macOS / Linux

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "back4app-access-token",
      "description": "Back4App personal access token",
      "password": true
    }
  ],
  "servers": {
    "back4app": {
      "command": "npx",
      "args": ["-y", "@back4app/mcp-server-back4app@latest"],
      "env": {
        "BACK4APP_ACCESS_TOKEN": "${input:back4app-access-token}"
      }
    }
  }
}
```

### Windows

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "back4app-access-token",
      "description": "Back4App personal access token",
      "password": true
    }
  ],
  "servers": {
    "back4app": {
      "command": "npx.cmd",
      "args": ["-y", "@back4app/mcp-server-back4app@latest"],
      "env": {
        "BACK4APP_ACCESS_TOKEN": "${input:back4app-access-token}"
      }
    }
  }
}
```

1. Save the configuration file
2. Open Copilot chat and switch to "Agent" mode. You should see a tool icon that you can tap to confirm the MCP tools are available
3. Once you begin using the server, you will be prompted to enter your personal access token. Enter the token that you created earlier

For more info on using MCP in VS Code, see the Copilot documentation.

### Cline

1. Open the Cline extension in VS Code and tap the MCP Servers icon
2. Tap Configure MCP Servers to open the configuration file
3. Add the following configuration:

### macOS / Linux

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

### Windows

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx.cmd",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

1. Replace `<personal-access-token>` with your personal access token
2. Save the configuration file. Cline should automatically reload the configuration
3. You should see a green active status after the server is successfully connected

### Claude desktop

1. Open Claude desktop and navigate to Settings
2. Under the Developer tab, tap Edit Config to open the configuration file
3. Add the following configuration:

### macOS / Linux

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

### Windows

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx.cmd",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

1. Replace `<personal-access-token>` with your personal access token
2. Save the configuration file and restart Claude desktop
3. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available

### Claude code

1. Create a `.mcp.json` file in your project root if it doesn't exist
2. Add the following configuration:

### macOS / Linux

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

### Windows

```json
{
  "mcpServers": {
    "back4app": {
      "command": "npx.cmd",
      "args": [
        "-y",
        "@back4app/mcp-server-back4app@latest",
        "--access-token",
        "<account-key>"
      ]
    }
  }
}
```

1. Replace `<personal-access-token>` with your personal access token
2. Save the configuration file
3. Restart Claude code to apply the new configuration

## 3 - Available Tools

Once connected, your AI assistant can perform a wide range of tasks on your Back4App account. Here are some of the available tools:

### App Management

- **create\_parse\_app** - Create a new Parse app
- **get\_parse\_apps** - Get a list of all your Parse apps
- **get\_parse\_app** - Get details for a specific Parse app
- **set\_default\_app** - Set a default app for subsequent operations
- **get\_default\_app** - Get the currently set default app

### Direct API Access (Parse REST API)

- **call\_parse\_api** - Calls the Parse Server REST API endpoints to fully manage your app.&#x20;

**Database Operations**
&#x20;– Create, read, update, delete objects in any class
&#x20;– Rich querying (filters, sorting, pagination, count, aggregate, distinct)

**User Management & Security**
&#x20;– Sign up / log in / log out / password reset
&#x20;– Roles, ACLs & CLPs to lock down data at object- and class-level

**Real-Time & Push**
&#x20;– LiveQuery over WebSockets (subscribe to creates/updates/deletes)
&#x20;– Push notifications & installation records

**Call Cloud Code functions**
&#x20;– Cloud Functions and Scheduled Jobs
&#x20;– Before/after triggers, custom webhooks

**Files, Analytics & Extensions**
&#x20;– File upload/download


### Cloud Code and Web Hosting on Parse App

- **list\_cloud\_code\_and\_web\_hosting\_files** - List all cloud code and web hosting files
- **get\_file\_content** - View the content of specific files
- **deploy\_cloud\_code\_files** - Deploy cloud code files to your app
- **deploy\_web\_hosting\_files** - Deploy web hosting files to your app

## 4 - Security Considerations

:::hint{type="danger"}
The MCP server has full access to your Back4App account with the permissions of your personal access token. This means AI tools can create, modify, and delete your apps and data. Always take the following precautions:
:::

1. **Use a dedicated test account** for initial experimentation
2. **Create a separate access token** specifically for MCP use
3. **Never share your configuration files** containing access tokens
4. **Review all code and API Calls** generated or modified by AI before deploying to production
5. **Revoke access tokens** immediately if you suspect any unauthorized use

## 5 - Troubleshooting

### Common Issues

- **Connection failures**: Ensure your personal access token is valid and correctly entered
- **Server not responding**: Check that you have Node.js installed and that npx is working correctly
- **Permission errors**: Verify that your access token has the necessary permissions
- **Updates not appearing**: Some MCP clients require a restart after configuration changes



## 6 - Next Steps

Your AI tool is now connected to Back4App using MCP. Try asking your AI assistant to create a new app, deploy some cloud code, or manage your data using natural language.

If you experience any issues or have feedback, please send us a message at: <a href="mailto:community@back4app.com" target="_blank">community\@back4app.com</a>

## Conclusion

With Back4App's MCP integration, you can leverage the power of AI Agents to accelerate your development workflow. From creating and configuring apps to deploying code and managing data, your AI assistant can now seamlessly interact with the Back4App platform on your behalf.

[title] Password reset
[path] GraphQL Cookbook/

# Request Password Reset with Parse GraphQL API&#x20;

## Problem

You want to request a reset password to a user in your database through the Parse GraphQL API.

## Version Information

It is only available for Parse Server 3.10.0 and later.

:::CodeblockTabs
Request

```graphql
1   mutation resetPassword {
2     resetPassword(input: { email: "example@email.com" }) {
3       ok
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "resetPassword": {
4         "ok": true
5       }
6     }
7   }
```
:::


[title] Authenticating a user
[path] GraphQL Cookbook/

# Authenticating a logged user through the Parse GraphQL API

## Problem

You want to authenticate a logged user in your backend through the Parse GraphQL API.

## Solution

Using the Parse GraphQL API, you can authenticate a logged user just by sending the user’s sessionToken (that you may have acquired by [signing up](https://www.back4app.com/docs/parse-graphql/graphql-sign-up) or [logging in](https://www.back4app.com/docs/parse-graphql/graphql-login) this user) through the X-Parse-Session-Token header. This header can be sent in any operation and it will make this operation to be run in the behavior of the authenticated user.

The X-Parse-Session-Token can be easily set in the Parse Dashboard GraphQL Playground through the HTTP HEADERS option in the bottom left side of this tool.



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zJ-w--S3pZaInDW9En-qZ_image.png)


[title] Email verification
[path] GraphQL Cookbook/

# Request Password Reset with Parse GraphQL API

## **Problem**

You want to send an email verification to a user in your database through the Parse GraphQL API.

## **Version Information**

It is only available for Parse Server 3.10.0 and later.&#x20;

:::CodeblockTabs
Request

```graphql
1   mutation sendVerificationEmail {
2     sendVerificationEmail(input: { email: "example@email.com" }) {
3       ok
4     }
5   }
```

Response

```graphql
1   {
2     "data": {
3       "sendVerificationEmail": {
4         "ok": true
5       }
6     }
7   }
```
:::


[title] Slow query report
[path] Parse Dashboard/

# Eliminate app lag with our Slow Query Tool

## Introduction

This document defines our generic analytic **Slow requests report** that allows users to focus on response time and monitor the performance of their app requests. This report is designed to provide you with the important details about the problematic and slowest requests that have occurred in your application starting from 00:00 UTC of the current day. In this manner, you get a clear idea of the exact moment the unhandled request occurred. You can also ascertain parameters associated with the request as method and path, response status and response time (in milliseconds).

This report is exceptionally advantageous for developers as it helps them understand the precise cause of slow requests and eliminate the bottlenecks by identifying inefficient and expensive queries, thereby enabling them to improve app performance.

:::hint{type="danger"}
**This Analytic Report will work only if the Parse Server version of your app is equal to or greater than 2.8.4.**

**If you want to manage your Parse Server Version for 2.8.4, follow **<a href="https://help.back4app.com/hc/en-us/articles/360016081032-Manage-Parse-Server-Version" target="_blank">**these instructions**</a>**.**
:::

## Getting started

To start using the **Slow requests report** of your app, you need to:

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a> and click on My Apps. Find your app and then click on DASHBOARD.2.  Select More > Analytics section and click on Slow Requests section under the Analytics tab.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/oR4bvfKpLt4rPu8cddE4S_image.png)

&#x20;    2\.  Select More > Analytics section and click on Slow Requests section under the Analytics tab.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/1ULpeW2XjtxORbGeAd5vB_image.png)

By now, you should be seeing a list on the right side of the Dashboard containing the slowest requests that have occurred in your app, starting from 00:00 UTC of the current day. This list will show all the vital information about the slow requests, including the exact date and time the request has occurred, its method and path, parameters associated with it, its response status, and also response time in milliseconds.

:::hint{type="info"}
**The list comprising slow requests is arranged from the request with the longest response time to the one with the shortest response time.**
:::

:::hint{type="info"}
**To see all the entries of the list, scroll down a little bit.**
:::

## Exploring the report

To explore the **Slow requests report** of your app, follow these steps:

1. If you don’t want to see all slow requests in the list at once, you can filter the necessary queries that are required in the report. To do so, you need to click on the Filterbutton located at the top right corner.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TG90MqQdPFSZrSGpxGSbd_image.png)

&#x20;    2\. Select the filter you want to apply: Method, Path, or Response Status of your slow requests. On selecting one of the filters, you can already run a query. You can also pick multiple filter items from the options . After choosing the filter (or filters) you want, go to the bottom right corner of your Dashboard and click on the Run query button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/b2aVagKHVWuR8NhZmu2JW_image.png)

:::hint{type="danger"}
Be aware that if you select a new filter, you can’t undo this selection, except if you click on Clear all. This option will automatically clear all your filter selections.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9rUAgGfDAFzS70kP7mra0_image.png)
:::

&#x20;    3\. Now, your filtered query is done, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ih4vSan8jN8vEnmaTp41N_image.png)

&#x20;    4\. If you want to download the report data, click on the Download button placed on the top right of the screen.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-TeeRpTM5GAniUerqrW-c_image.png)

## It’s done!

Congratulations! Now, you can explore the **Slow requests report** of your app through Back4App and identify as well as optimize slow requests!

[title] N:N Relationship
[path] React Native/Parse SDK (REST)/Data objects/

# Many to many Relationship

## Introduction

At the core of many backends, you will find the ability to store data. Using Parse, you can store data objects establishing relations between them. Data relations standardize how each data object is related or associated with other ones. That can give you extra power when building and running complex queries. There are three main relation types:

- one-to-many, where one object can be related to many other objects;
- one-to-one, establishing direct relations between two objects and only them;
- many-to-many, which can create many complex relations between many objects.

In this guide, we will focus on many-to-many relations. These relations are common in applications containing authored content, such as blogs and news feeds, because authors and category tags can be interchangeable and optimized for quick querying. Data storage backends usually will demand explicit declarations of these associations and even require that you create, on your own, relational tables for managing their joining. Parse makes it automatically for you, removing any possibility of errors while building your data relations and speeding up your application modeling process.

There are two ways to create a many-to-many relation in Parse. The first uses the Parse.Object relations, which is the fastest in creation and query time. The second is using arrays which can lead to slow query times depending on their size. Because of this performance issue, we will use only relation examples from now on.

In this guide, you will implement a React Native book registration application that contains the three main kinds of data associations. You will learn how to create and query many-to-many data relations using your Back4App and React Native.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">JavaScript Example Repository</a>
- <a href="https://github.com/templates-back4app/react-native-ts-associations" target="_blank">TypeScript Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

To perform and demonstrate many-to-many database relations in React Native using Parse in a realistic scenario.

## 1 - Understanding the Book class

Since in this guide we will be using a book registration application example, you need to first understand how the object relations are laid out in this database. The main object class that you’ll be using is the Book class, which will store each book entry in the registration. These are the other four object classes:

- Publisher: book publisher name, one-to-many relation withBook
- Genre: book genre, one-to-many relation withBook. Note that for this example we will consider that a book can only have one genre;
- Author: book author, many-to-many relation withBook, since a book can have more than one author and an author can have more than one book as well;
- ISDB: book ISDB identifying number, one-to-one relation withBook, since this number is unique for each book.

Here is a visual representation of these database tables:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Be0ga_pjJxkqOIWuIOrYf_image.png)

For simplicity, we will assume that each object class has only a string type name attribute (title for the Book), apart from any additional relational attribute.

## 2 - Creating one-to-many relations

Before going into this step we recommend you to clone and run the React Native Library app exmaple (<a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">JavaScript Example Repository</a>, <a href="https://github.com/templates-back4app/react-native-ts-associations" target="_blank">TypeScript Example Repository</a>). This application has two main screens: one responsible for listing the registered books and the other for creating new books. In the book registration form, there are direct links to the other related objects, so when a user picks a publisher or genre option, the form fields will hold the complete Parse.Object instance.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/z8yi0XlaVuUAdcjSX0KGK_image.png" signedSrc size="50" width="354" height="733" position="center" caption}

Let’s take a look at the book creation method that is called when submitting this form:

:::CodeblockTabs
JavaScript

```javascript
1	const createBook = async function () {
2	  try {
3	    // These values come from state variables linked to
4	    // the screen form fields, retrieving the user choices
5	    // as a complete Parse.Object, when applicable;
6	    const bookTitleValue = bookTitle;
7	    const bookISBDValue = bookISBD;
8	    // For example, bookPublisher holds the value from
9	    // RadioButton.Group field with its options being every
10	    // Publisher parse object instance saved on server, which is
11	    // queried on screen load via useEffect
12	    const bookPublisherObject = bookPublisher;
13	    const bookGenreObject = bookGenre;
14	    // bookAuthors can be an array of Parse.Objects, since the book
15	    // may have more than one Author
16	    const bookAuthorsObjects = bookAuthors;
17	
18	    // Creates a new parse object instance
19	    let Book = new Parse.Object('Book');
20	
21	    // Set data to parse object
22	    // Simple title field
23	    Book.set('title', bookTitleValue);
24	
25	    // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
26	    let isbdQuery = new Parse.Query('ISBD');
27	    isbdQuery.equalTo('name', bookISBDValue);
28	    let isbdQueryResult = await isbdQuery.first();
29	    if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
30	      // If first returns a valid object instance, it means that there
31	      // is at least one instance of ISBD with the informed value
32	      Alert.alert(
33	        'Error!',
34	        'There is already an ISBD instance with this value!',
35	      );
36	      return false;
37	    } else {
38	      // Create a new ISBD object instance to create a one-to-one relation on saving
39	      let ISBD = new Parse.Object('ISBD');
40	      ISBD.set('name', bookISBDValue);
41	      ISBD = await ISBD.save();
42	      // Set the new object to the new book object ISBD field
43	      Book.set('isbd', ISBD);
44	    }
45	
46	    // One-to-many relations can be set in two ways:
47	    // add direct object to field (Parse will convert to pointer on save)
48	    Book.set('publisher', bookPublisherObject);
49	    // or add pointer to field
50	    Book.set('genre', bookGenreObject.toPointer());
51	
52	    // many-to-many relation
53	    // Create a new relation so data can be added
54	    let authorsRelation = Book.relation('authors');
55	    // bookAuthorsObjects is an array of Parse.Objects,
56	    // you can add to relation by adding the whole array or object by object
57	    authorsRelation.add(bookAuthorsObjects);
58	
59	    // After setting the values, save it on the server
60	    try {
61	      await Book.save();
62	      // Success
63	      Alert.alert('Success!');
64	      navigation.goBack();
65	      return true;
66	    } catch (error) {
67	      // Error can be caused by lack of Internet connection
68	      Alert.alert('Error!', error.message);
69	      return false;
70	    }
71	  } catch (error) {
72	    // Error can be caused by lack of value selection
73	    Alert.alert(
74	      'Error!',
75	      'Make sure to select valid choices in Publisher, Genre and Author fields!',
76	    );
77	    return false;
78	  }
79	};
```

TodoList.tsx

```typescript
1	const createBook = async function (): Promise<boolean> {
2	  try {
3	    // These values come from state variables linked to
4	    // the screen form fields, retrieving the user choices
5	    // as a complete Parse.Object, when applicable;
6	    const bookTitleValue: string = bookTitle;
7	    const bookISBDValue: string = bookISBD;
8	    // For example, bookPublisher holds the value from
9	    // RadioButton.Group field with its options being every
10	    // Publisher parse object instance saved on server, which is
11	    // queried on screen load via useEffect
12	    const bookPublisherObject: Parse.Object = bookPublisher;
13	    const bookGenreObject: Parse.Object = bookGenre;
14	    // bookAuthors can be an array of Parse.Objects, since the book
15	    // may have more than one Author
16	    const bookAuthorsObjects: [Parse.Object] = bookAuthors;
17	
18	    // Creates a new parse object instance
19	    let Book: Parse.Object = new Parse.Object('Book');
20	
21	    // Set data to parse object
22	    // Simple title field
23	    Book.set('title', bookTitleValue);
24	
25	    // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
26	    let isbdQuery: Parse.Query = new Parse.Query('ISBD');
27	    isbdQuery.equalTo('name', bookISBDValue);
28	    let isbdQueryResult: Parse.Object = await isbdQuery.first();
29	    if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
30	      // If first returns a valid object instance, it means that there
31	      // is at least one instance of ISBD with the informed value
32	      Alert.alert(
33	        'Error!',
34	        'There is already an ISBD instance with this value!',
35	      );
36	      return false;
37	    } else {
38	      // Create a new ISBD object instance to create a one-to-one relation on saving
39	      let ISBD: Parse.Object = new Parse.Object('ISBD');
40	      ISBD.set('name', bookISBDValue);
41	      ISBD = await ISBD.save();
42	      // Set the new object to the new book object ISBD field
43	      Book.set('isbd', ISBD);
44	    }
45	
46	    // One-to-many relations can be set in two ways:
47	    // add direct object to field (Parse will convert to pointer on save)
48	    Book.set('publisher', bookPublisherObject);
49	    // or add pointer to field
50	    Book.set('genre', bookGenreObject.toPointer());
51	
52	    // many-to-many relation
53	    // Create a new relation so data can be added
54	    let authorsRelation = Book.relation('authors');
55	    // bookAuthorsObjects is an array of Parse.Objects,
56	    // you can add to relation by adding the whole array or object by object
57	    authorsRelation.add(bookAuthorsObjects);
58	
59	    // After setting the values, save it on the server
60	    try {
61	      await Book.save();
62	      // Success
63	      Alert.alert('Success!');
64	      navigation.goBack();
65	      return true;
66	    } catch (error) {
67	      // Error can be caused by lack of Internet connection
68	      Alert.alert('Error!', error.message);
69	      return false;
70	    }
71	  } catch (error) {
72	    // Error can be caused by lack of value selection
73	    Alert.alert(
74	      'Error!',
75	      'Make sure to select valid choices in Publisher, Genre and Author fields!',
76	    );
77	    return false;
78	  }
79	};
```
:::

Look how the bookAuthorsObjects are set to the new book Parse.Object instance. To create a many-to-many relation, you first need to create a new Parse.Object.relation and then add the related objects to it either one by one or by passing an array of Parse.Object using the Parse.Object.relation.add method. Parse will create a relation type column and also a relational table on your database. Parse will also create a link for easy access to this new table in the field column in the dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/hDZsC5yEccVK5C5ly5q-T_image.png)

Be aware that you can also manage objects and add new ones to your relation through the dashboard once it is created, so remember this shortcut when developing your application.

## 3 - Querying many-to-many relations

Retrieving results from many-to-many related objects queries need some additional steps comparing to regular ones. Take a look at the query function in the book registers list screen:

:::CodeblockTabs
JavaScript

```javascript
1	const queryBooks = async function () {
2	  // These values come from state variables linked to
3	  // the screen query RadioButton.Group fields, with its options being every
4	  // parse object instance saved on server from the referred class, which is
5	  // queried on screen load via useEffect; these variables retrievie the user choices
6	  // as a complete Parse.Object;
7	  const queryPublisherValue = queryPublisher;
8	  const queryGenreValue = queryGenre;
9	  const queryAuthorValue = queryAuthor;
10	  const queryIsbdValue = queryIsbd;
11	
12	  // Reading parse objects is done by using Parse.Query
13	  const parseQuery = new Parse.Query('Book');
14	
15	  // One-to-many queries
16	  if (queryPublisherValue !== '') {
17	    parseQuery.equalTo('publisher', queryPublisherValue);
18	  }
19	  if (queryGenreValue !== '') {
20	    parseQuery.equalTo('genre', queryGenreValue);
21	  }
22	
23	  // One-to-one query
24	  if (queryIsbdValue !== '') {
25	    parseQuery.equalTo('isbd', queryIsbdValue);
26	  }
27	
28	  // Many-to-many query
29	  // In this case, we need to retrieve books related to the chosen author
30	  if (queryAuthorValue !== '') {
31	    parseQuery.equalTo('authors', queryAuthorValue);
32	  }
33	
34	  try {
35	    let books = await parseQuery.find();
36	    // Many-to-many objects retrieval
37	    // In this example we need to get every related author Parse.Object
38	    // and add it to our query result objects
39	    for (let book of books) {
40	      // This query is done by creating a relation and querying it
41	      let bookAuthorsRelation = book.relation('authors');
42	      book.authorsObjects = await bookAuthorsRelation.query().find();
43	    }
44	    setQueriedBooks(books);
45	    return true;
46	  } catch (error) {
47	    // Error can be caused by lack of Internet connection
48	    Alert.alert('Error!', error.message);
49	    return false;
50	  }
51	};
```

TodoList.tsx

```typescript
1	const queryBooks = async function (): Promise<boolean> {
2	  // These values come from state variables linked to
3	  // the screen query RadioButton.Group fields, with its options being every
4	  // parse object instance saved on server from the referred class, which is
5	  // queried on screen load via useEffect; these variables retrievie the user choices
6	  // as a complete Parse.Object;
7	  const queryPublisherValue: Parse.Object = queryPublisher;
8	  const queryGenreValue: Parse.Object = queryGenre;
9	  const queryAuthorValue: Parse.Object = queryAuthor;
10	  const queryIsbdValue: Parse.Object = queryIsbd;
11	
12	  // Reading parse objects is done by using Parse.Query
13	  const parseQuery: Parse.Query = new Parse.Query('Book');
14	
15	  // One-to-many queries
16	  if (queryPublisherValue !== '') {
17	    parseQuery.equalTo('publisher', queryPublisherValue);
18	  }
19	  if (queryGenreValue !== '') {
20	    parseQuery.equalTo('genre', queryGenreValue);
21	  }
22	
23	  // One-to-one query
24	  if (queryIsbdValue !== '') {
25	    parseQuery.equalTo('isbd', queryIsbdValue);
26	  }
27	
28	  // Many-to-many query
29	  // In this case, we need to retrieve books related to the chosen author
30	  if (queryAuthorValue !== '') {
31	    parseQuery.equalTo('authors', queryAuthorValue);
32	  }
33	
34	  try {
35	    let books: [Parse.Object] = await parseQuery.find();
36	    // Many-to-many objects retrieval
37	    // In this example we need to get every related author Parse.Object
38	    // and add it to our query result objects
39	    for (let book of books) {
40	      // This query is done by creating a relation and querying it
41	      let bookAuthorsRelation = book.relation('authors');
42	      book.authorsObjects = await bookAuthorsRelation.query().find();
43	    }
44	    setQueriedBooks(books);
45	    return true;
46	  } catch (error) {
47	    // Error can be caused by lack of Internet connection
48	    Alert.alert('Error!', error.message);
49	    return false;
50	  }
51	};
```
:::

In this case, to query any books related to a specific author, you need to perform a Parse.Query.equalTo method passing the Parse.Object instance as the parameter. After querying, Parse will not store Parse.Object instances from many-to-many relational fields, only a reference to the related class name, such as \{"\_\_type": "Relation", "className": "Author"}. To retrieve and show data from these object instances, you need to create a relation and query it again, storing the results in an object array of your own. After that, you may iterate over the object array and use the Parse.Object.get method to retrieve the author’s name. Here is how the list screen looks like by using these procedures:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7tlBZW-KuCLFFpQv-2QqP_image.png" signedSrc size="50" width="351" height="734" position="center" caption}

## Conclusion

At the end of this guide, you learned how to create and query many-to-many relations in Parse on React Native. In the next guide, we will show you how to do one-to-one queries and relations.

[title] Untitled
[path] React Native/Relay (GraphQL)/


[title] New App
[path] Get started/

Back4app provides a complete solution for setting up and managing the backend of your application. In this guide, you will learn how to create your first backend app in just a few simple steps.

## Goal

This guide will walk you through creating an app on Back4app, highlighting the main features of the platform to simplify development and scalability.

## **Prerequisites**

:::hint{type="info"}
There are no prerequisites to create an App at Back4app and it is also free of charge.
:::

## 1 - Create your Back4app account

1. Visit the [Back4App website](https://www.back4app.com/).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_BBnEvBWXJ7KxPmOEPqqi_screenshot-2024-11-05-at-093908.png)

&#x20;   2\. Click **Sign up** in the top menu.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zozcNYKbCCTRanj4udEhw_screenshot-2024-11-05-at-093944.png)

&#x20;   3\. Fill out the registration form with your details and accept the [Terms of Service](https://www.back4app.com/terms-of-service.pdf) and [Privacy Policy](https://www.back4app.com/privacy.pdf).

:::hint{type="info"}
*Registration is quick and completely free.*
:::

## 2 - Create a new app

&#x20;   1\. Once registered, you will be taken to the "Launch Your New App" screen. In this guide, as we are talking about creating a Backend App, choose the former option. For the latter, check this guide.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ocZjAIDkte6Bggha-fSbk_screenshot-2024-11-05-at-094236.png)

&#x20;   2\. Enter a name for your app and click **Create**.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vsZdYCJ7QVl-M8W5UyqyG_screenshot-2024-11-05-at-094327.png)

&#x20;   3\. Wait as Back4app automatically sets up your app's backend structure, including features like:

&#x20;       1\.   Data and file storage.

&#x20;       2\.   Cloud functions.

&#x20;       3\.   User authentication.

&#x20;       4\.   Push notifications.

&#x20;       5\.   Automatic REST/GraphQL APIs.

:::hint{type="info"}
*This setup is built with security, performance, and scalability in mind.*
:::

## 3 - Navigating on the Back4app Dashboard

1. After creating your app, you will see the **Dashboard**, which is the management interface for your app.
2. Explore key sections such as:
   1. **Database Browser**: Manage your data and structures.
   2. **Cloud Code**: Create and manage cloud functions.
   3. **API:** Perform API operations through the dashboard. Go to **App Settings -> Security & Keys** to access your app credentials, which will be essential for connecting your front end.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GSgGw8T3u70n70xWjyELd_screenshot-2024-11-05-at-09-47-34.png)



:::hint{type="info"}
Navigate through Back4app’s onboarding flow to get a better understanding of the main dashboard sections.
:::

## Conclusion and Next Steps

In this guide, you’ve created a new App at Back4app in the next guide you will <a href="https://www.back4app.com/docs/get-started/parse-sdk" target="_blank">connect your App frontend to Back4app</a> and make your first API Call.


[title] Real-time database
[path] Platform/

# How to use Parse Server Live Query

## Introduction

Live queries are meant to be used in **real-time reactive applications**, where just using the traditional query paradigm could cause several problems, like increased response time and high network and server usage. Live queries should be used in cases where you need to continuously update a page with fresh data coming from the database, which often happens in (but is not limited to) online games, messaging clients and shared to-do lists.

This section explains how to use Back4App’s Live Query in a JavaScript environment through Back4App.

For this tutorial, as an example, you will build a **Todo Online List** using Live Queries, as shown below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kt59SWYYaqeQXSBhaNuwt_image.png" signedSrc size="80" width="680" height="740" position="center" caption}

:::hint{type="info"}
See the complete **Todo Online List** project at <a href="https://github.com/igor-ribeiiro/OnlineTodoList" target="_blank">Live Query Todo List Project</a>.
:::

:::hint{type="info"}
See more about Parse SDK at <a href="https://parseplatform.org/Parse-SDK-JS/api/4.3.1/" target="_blank">JavaScript SDK API Reference</a> and<a href="https://docs.parseplatform.org/js/guide/#live-queries" target="_blank"> Parse open source documentation for JavaScript SDK</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A basic JavaScript App connected with Back4App or the <a href="https://github.com/igor-ribeiiro/OnlineTodoList" target="_blank">Live Query Todo List Project</a>.
  - **Note: **You can use the app created in our <a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">JavaScript Install Parse SDK</a> tutorial or the <a href="https://github.com/igor-ribeiiro/OnlineTodoList" target="_blank">Live Query Todo List Project</a>.
- Sufficient knowledge of <a href="https://docs.parseplatform.org/js/guide/#queries" target="_blank">JavaScript Parse Queries</a> (but not mandatory).
:::

## 1 - Enable Live Query

Before you start coding, it’s necessary to have a class in your database to enable Live Query. To do that, simply find your app at the <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, and click on Dashboard > Create a class, as shown below:&#x20;

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EZk9j8BkQLq6JNVtTh7Yj_image.png)

:::hint{type="info"}
**Note: **Here, this class will be called Message.
:::

Now, to enable the Live Query feature, log in to your account at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, find your app, and click on App Settings > Server Settings. Look for the “Server URL and Live Query” block and click on SETTINGS.
The “Server URL and Live Query” block looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ujWY8spojft4dh-Vay9Yh_image.png" signedSrc size="50" width="308" height="358" position="center" caption}

Then, you will arrive at a page like the one below. On this page you will need to check the Activate your Back4App subdomain option, the Activate Live Query option, and all the classes on which you want Live Query to be activated, as shown below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/oBKSK5CCjDiQV-Wstg9zy_image.png" signedSrc size="60" width="461" height="514" position="center" caption}

:::hint{type="info"}
It’s necessary to activate your subdomain to use Live Queries because it will work as the live server.
:::

## 2 - Subscribe to your Query

To start using LiveQueries, you first need to create a LiveQueryClient that will manage the
WebSocket connections for you. To do this, you will have to provide the application ID, its JavaScript Key for verifying purposes, and also a server URL that should be the domain with which you did the setup in the previous step.

Here’s the code for LiveQueryClient:

:::CodeblockTabs
liveQueryClient.js

```javascript
1   var client = new Parse.LiveQueryClient({
2       applicationId: 'Your app Id here',
3       serverURL: 'wss://' + 'Your domain here', // Example: 'wss://livequerytutorial.back4app.io'
4       javascriptKey: 'Your JavaScript key here'
5   });
6   client.open();
```
:::

After following the above-mentioned steps, you should create a query for the type of object you want to subscribe. A subscription is an
event emitter, which will fire events when changes happen to an object that satisfies your query.
In this example, you will make a basic query and will subscribe all changes done to
the Todo object.

:::hint{type="info"}
See more about queries at <a href="https://docs.parseplatform.org/js/guide/#queries" target="_blank">Parse Official Queries Documentation</a>.
:::

Below is the code for querySubscribe:

:::CodeblockTabs
querySubscribe.js

```javascript
1   var query = new Parse.Query('Todo');
2   query.ascending('createdAt').limit(5);
3   var subscription = client.subscribe(query);
```
:::

## 3 - Listen to events

With the setup ready, it’s necessary to code what your app will do when an event fires.
In this part, we are going to show how to listen to these events in a practical example.

The **Todo Online List** example will serve as a guideline for your project because there is little to no boilerplate code used.

The two main events that you are going to use here are the create event and the delete event. The complete list of events can be found <a href="http://docs.parseplatform.org/js/guide/#event-handling" target="_blank">here</a>.

### **3.1 - The Create Event**

The createEvent is fired every time a ParseObject is created and fulfills the query constraints you’ve inserted. This event returns the created object.
In the **Todo Online List** example, the array of activities is stored in the this.todos variable and we will add the new objects of our database in this array, when a create event happens.

The code for createEvent is shown below:

:::CodeblockTabs
createEvent.js

```javascript
1   subscription.on('create', todo => {
2       this.todos.add(todo);
3       console.log('On create event');
4   });
```
:::

### **3.2 - The Delete Event**

Whenever an existing ParseObject that fulfills your query constraints is deleted from the database, you’ll get this event, which returns the deleted object.
In the **Todo Online List** example, you have to delete an object from the list every time it is deleted from the database. Look for matching IDs between the server and the code, to identify the deleted objects.

The code for deleteEvent is the following:

:::CodeblockTabs
deleteEvent.js

```javascript
1   subscription.on('delete', todo => {
2       this.todos.forEach(t => {
3           if (t.id === todo.id) {
4               console.log('On delete event');
5               this.todos.delete(t);
6           }
7       });
8   });
```
:::

## It’s done!

At this point, you know how to use Live Queries to make real-time reactive applications.
You also know how to do the correct Live Query setup using Back4App and can start by implementing it in your app.

[title] Getting Started
[path] React Native/Relay (GraphQL)/

# Get Started with GraphQL Relay

## Introduction

In this guide you will learn what is Relay, how to work with schema and understand how to work with GraphQL on back4app

## Prerequisites

:::hint{type="info"}
**This is not a tutorial yet, but to feel free confortable reading it you will need:**

- Basic JavaScript knowledge.
- Basic understand about GraphQL. If you don’t have, the <a href="https://github.com/graphql/graphql-js" target="_blank">GraphQL.js</a> its a perfect place to start
:::

## Relay

Relay is a GraphQL client developed by Facebook Engineering Team and designed for performance when building data-driven applications. More precisely, Relay is a framework for declaratively fetching and managing GraphQL data on the client-side that uses strict conventions to help your application succeed. It was built with scalability in mind to power complex applications like Facebook. The ultimate goal of GraphQL and Relay is to deliver instant UI-response interactions.

One of the main advantages of using GraphQL is that you can fetch with a single query all the data needed to build an application page, for example. Off course that this is good(you can save round-trips to the server) but with that comes a problem. You may use different queries to the same component when reusing this component in different parts of your application. To avoid this kind of problem Relay uses an important concept: colocation.

## Colocation

When using Relay, components and their data requirements live together. Components data requirements are declared inside them. That means all the components declare the data that they need. Relay ensures that each component has the data it needs when renders.

The Relay structure behind the Colocation concept are the containers. The most common is the Relay Fragment Container. The container is the component that attempts to fulfill the data required when rendering each component. The containers declare their data dependency using GraphQL fragments.

Every component will have its own fragment container. The container doesn’t fetch the data directly; it only declares the specification for the data needed when rendering. The data will be fetched on the server-side. Relay will make sure that the data is available before the rendering occurs. Relay composes a tree of data with those containers, ignoring the redundancies, and fetching the data on the server.

Let’s illustrate the concept with an example. A fragment is a selection of fields on a GraphQL type. Relay works with fragment, pagination and refetch container. The most common is a fragment container. See below a fragment in GraphQL and after the same on Relay.

```graphql
1   query Health {
2     health
3   }
```

```typescript
1	type HomeProps = {
2	  query: Home_query;
3	};
4	
5	const Home = ({query}: HomeProps) => {
6	  return (
7	    <View>
8	      <Text>API Health: {query.health ? 'Health' : 'Not health' }</Text>
9	    </View>
10	  );
11	};
12	
13	const HomeFragment = createFragmentContainer(Home, {
14	  query: graphql`
15	    fragment Home_query on Query {
16	      health
17	    }
18	  `,
19	});
20	
21	export default createQueryRendererModern(HomeFragment, Home, {
22	  query: graphql`
23	    query HomeQuery {
24	      ...Home_query
25	    }
26	  `,
27	  hideSplash: true,
28	});
```

On the first code you can see the GraphQL version that allows us to split this query into reusable fragments. On the next code you can see the Relay one that is showing the fragments and the data together in the same component.
Colocation means that data definitions and view definitions live together. Collocation has some benefits. We just need to declare exactly the date that we need. That means it is hard to over fetch data which improves our application and is hard to under fetch data which prevent errors with missing data. Another important concept is Data Masking which means that components can only access data they asked for. Data Masking helps to prevent dependency errors. Also, components are only updated if the data they are using change.

## Query Renderer Modern

:::hint{type="info"}
We recommend to take a look at the [official Query Renderer docs](https://relay.dev/docs/en/query-renderer.html#docsNav) for a better understanding.
:::

Based on the fragment containers, Relay will read them and make the request to the server based on the data they have. But, how does this requisition happen? This is where Query Renderer Modern comes in.

The Query Renderer Modern is the component that reads the fragment containers, makes the request to the server and returns the data to the component.

Every root component will have a Query Renderer Modern making this request. In the example above what we have an abstraction so that we pass only the fragment that must be read and so all the rest of the work is done inside createQueryRendererModern.

:::hint{type="info"}
In the next step of the doc we will enter the createQueryRendererModern to understand the abstraction based on the pure example above
:::

Below is a pure example of Query Render Modern:

```typescript
1	export default function ArtistRenderer({artistID}) {
2	  return (
3	    <QueryRenderer
4	      environment={environment}
5	      query={graphql`
6	        query QueryRenderersArtistQuery($artistID: String!) {
7	          # The root field for the query
8	          artist(id: $artistID) {
9	            # A reference to your fragment container
10	            ...ArtistHeader_artist
11	          }
12	        }
13	      `}
14	      variables={ {artistID} }
15	      render={({error, props}) => {
16	        if (error) {
17	          return <div>{error.message}</div>;
18	        } else if (props) {
19	          return <Artist artist={props.artist} />;
20	        }
21	        return <div>Loading</div>;
22	      }}
23	    />
24	  );
25	}
```

We can see that the Query Renderer Modern is a <a href="https://reactjs.org/docs/higher-order-components.html" target="_blank">HOC component</a>. In other words, wrap the component that owns the container with the data to be requested, make the request with the Relay environment and return the data downwards passing to the component.

## Automatic Type Generation

Working with Relay we can build an application type safely. As we said every component will be the owner of their data. So, when we run the relay-compiler, it reads every fragment of data and makes a check with your graphql schema. If all checks of Relay compiler is ok, the types it will be generate in a folder called **generated**. This folder is create inside of the root components folders.

See the example below:

Component home checking the API health:

```graphql
1	const Home = ({query}: HomeProps) => {
2	  return (
3	    <View>
4	      <Text>API Health: {query.health ? 'Health' : 'Not health' }</Text>
5	    </View>
6	  );
7	};
8	
9	const HomeFragment = createFragmentContainer(Home, {
10	  query: graphql`
11	    fragment Home_query on Query {
12	      health
13	    }
14	  `,
15	});
16	
17	export default createQueryRendererModern(HomeFragment, Home, {
18	  query: graphql`
19	    query HomeQuery {
20	      ...Home_query
21	    }
22	  `,
23	  hideSplash: true,
24	});
```

Generated folder containing the types for Home component:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TjOKbbJomDUZHH3P56c9i_image.png" signedSrc size="50" width="218" height="66" position="center" caption}

And the types generated:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/P8873lGO8IRZ5DN0zRAhM_image.png" signedSrc size="80" width="416" height="566" position="center" caption}

After this we just need import the type on component and compose it:

```typescript
1   import {Home_query} from "./__generated__/Home_query.graphql";
2
3   type HomeProps = {
4     query: Home_query;
5   };
```

The type is generate in Flow, the core language for types from Facebook. But, with a library you can generate in Typescript. This is most common to work with React Native. Resuming, with Relay compiler we can check the data implementation before run time. This helps us to prevent bugs and then improve the application quality and development time.

On queries, we can avoid duplicate data in N different components. It check if some data is duplicate. If has it will remove the redundancies. This will reduce the payload size of queries and increase the performance of application even more.

## Developing

On queries, we can avoid duplicate data in N different components. It check if some data is duplicate. If has it will remove the redundancies. This will reduce the payload size of queries and increase the performance of application even more.

# On the server Side - Back4App

Back4App generates a ready-to-use Relay compliant GraphQL API to use on your project. What Back4App generates for you so you don’t have to build by yourself on server side:

## Infrastructure

On Back4app Dashboard you can create a complete data model with classes, types and everything else a database needs. You don’t have to worry about setup and maintain server by yourself. After create your data model Back4App we will generate everything that is necessary to consume it on the frontend side using GraphQL. Let’s take a look at the GraphQL API Schema.

## Schema

The schema file is the core of any GraphQL application. It describes the whole server available to be use on client side. Relay works using your schema file to confirm the queries
string and generated file to appear in ./**generated**/MyComponent.graphql, as we said on **Automatic Type Generation** topic.

To use the Relay Compiler, you need either a .graphql or .json GraphQL schema file describing your GraphQL server’s API. These files are local representations of a server source of truth and are not edited.

Every schema of GraphQL can be compose by query type, mutation type and subscription type. To work with a schema being consume by Relay on front end, you need some of core concepts on back-end: node interface and connection.

:::hint{type="success"}
We recommend reading about node interface and Relay connections to turn this reading more easier to abstract.
:::

## Node Interfaces

The node interface implements every type on GraphQL to help fetch data via id. This is an implementation to make more easy fetch data from server and updated. So each data of each type it will have a unique id, called as global unique id, being implemented by a node interface. With node, Relay avoid nested queries and make the fetch and re-fetch using it. This is hard to implement and needs a bit of work, but we already build this for you!

## Pagination

Build to be compose, as on front-end on back-end the Relay it will help us to compose our data to. For work with pagination we have connections. Those connections implements the node from the type we are fetching and have a standard model, facilitating the implementation of pagination on server side. Fortunately, again, we have this already build to use on frontend.

## Conclusion

In this guide we introduced what is Relay, its main concepts and how it works. Also we could see how Back4App automates the GraphQL server creation and delivers us a complete database with GraphQL API to work with.

Resuming, the whole backend is already built by back4app. You only need to create your types on the dashboard and start to consume based on schema generated. In the next section we will explain how to handle this schema on the front end and how to prepare your environment to use Relay. We also have a [GraphQL Cookbook](https://www.back4app.com/docs/parse-graphql/graphql-getting-started), you can use it to help you to understand more concepts on our dashboard.

[title] 1:1 Relationship
[path] React Native/Parse SDK (REST)/Data objects/

# One to one Relationship



## Introduction

At the core of many backends, you will find the ability to store data. Using Parse, you can store data objects establishing relations between them. Data relations standardize how each data object is related or associated with other ones. That can give you extra power when building and running complex queries. There are three main relation types:

- one-to-many, where one object can be related to many other objects;
- one-to-one, establishing direct relations between two objects and only them;
- many-to-many, which can create many complex relations between many objects.

In this guide, we will focus on one-to-one relations. These relations are common in applications involving sensible data and user management which require unique fields that need to be enforced, such as ID numbers and phone numbers. Data storage backends usually will demand explicit declarations of these associations and Parse does not have an automatic solution for achieving such associations.

However, there are ways to implement 1:1 relations in Parse by using Parse.Cloud functions on your server, enforcing that table relations remain unique before saving data. This is done by creating beforeSave functions in both related classes and prevent saving if the father class already possesses one instance in the child class, and vice-versa.

You can also treat these cases in your application Parse code, querying the server before saving and guaranteeing said relation. This will be the way shown in this guide, but note that using cloud functions is much cleaner and more advised.

In this guide, you will implement a React Native book registration application that contains the three main kinds of data associations. You will learn how to create and query one-to-one data relations using Back4App and React Native.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">JavaScript Example Repository</a>
- <a href="https://github.com/templates-back4app/react-native-ts-associations" target="_blank">TypeScript Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and connected to <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

To perform and demonstrate one-to-one database relations in React Native using Parse in a realistic scenario.

## 1 - Understanding the Book class

Since in this guide we will be using a book registration application example, you need to first understand how the object relations are laid out in this database. The main object class that you’ll be using is the Book class, which will store each book entry in the registration. These are the other four object classes:

- Publisher: book publisher name, one-to-many relation withBook
- Genre: book genre, one-to-many relation withBook. Note that for this example we will consider that a book can only have one genre;
- Author: book author, many-to-many relation withBook, since a book can have more than one author and an author can have more than one book as well;
- ISDB: book ISDB identifying number, one-to-one relation withBook, since this number is unique for each book.

Here is a visual representation of these database tables:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yLKySy88tgZLtiFkWN2PO_image.png)

For simplicity, we will assume that each object class has only a string type name attribute (title for the Book), apart from any additional relational attribute.

## 2 - Creating one-to-many relations

Before going into this step we recommend you to clone and run the React Native Library app exmaple (<a href="https://github.com/templates-back4app/react-native-js-associations" target="_blank">JavaScript Example Repository</a>, <a href="https://github.com/templates-back4app/react-native-ts-associations" target="_blank">TypeScript Example Repository</a>). This application has two main screens: one responsible for listing the registered books and the other for creating new books. In the book registration form, there are direct links to the other related objects and a TextInput field assigned to the book’s ISBD value, which will be used to create your one-to-one relation.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eS4Xh4BrAAX1IPI9GO_ID_image.png" signedSrc size="50" width="352" height="741" position="center" caption}

Let’s take a look at the book creation method that is called when submitting this form:

:::CodeblockTabs
JavaScript

```javascript
1	const createBook = async function () {
2	  try {
3	    // These values come from state variables linked to
4	    // the screen form fields, retrieving the user choices
5	    // as a complete Parse.Object, when applicable;
6	    const bookTitleValue = bookTitle;
7	    const bookISBDValue = bookISBD;
8	    // For example, bookPublisher holds the value from
9	    // RadioButton.Group field with its options being every
10	    // Publisher parse object instance saved on server, which is
11	    // queried on screen load via useEffect
12	    const bookPublisherObject = bookPublisher;
13	    const bookGenreObject = bookGenre;
14	    // bookAuthors can be an array of Parse.Objects, since the book
15	    // may have more than one Author
16	    const bookAuthorsObjects = bookAuthors;
17	
18	    // Creates a new parse object instance
19	    let Book = new Parse.Object('Book');
20	
21	    // Set data to parse object
22	    // Simple title field
23	    Book.set('title', bookTitleValue);
24	
25	    // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
26	    let isbdQuery = new Parse.Query('ISBD');
27	    isbdQuery.equalTo('name', bookISBDValue);
28	    let isbdQueryResult = await isbdQuery.first();
29	    if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
30	      // If first returns a valid object instance, it means that there
31	      // is at least one instance of ISBD with the informed value
32	      Alert.alert(
33	        'Error!',
34	        'There is already an ISBD instance with this value!',
35	      );
36	      return false;
37	    } else {
38	      // Create a new ISBD object instance to create a one-to-one relation on saving
39	      let ISBD = new Parse.Object('ISBD');
40	      ISBD.set('name', bookISBDValue);
41	      ISBD = await ISBD.save();
42	      // Set the new object to the new book object ISBD field
43	      Book.set('isbd', ISBD);
44	    }
45	
46	    // One-to-many relations can be set in two ways:
47	    // add direct object to field (Parse will convert to pointer on save)
48	    Book.set('publisher', bookPublisherObject);
49	    // or add pointer to field
50	    Book.set('genre', bookGenreObject.toPointer());
51	
52	    // many-to-many relation
53	    // Create a new relation so data can be added
54	    let authorsRelation = Book.relation('authors');
55	    // bookAuthorsObjects is an array of Parse.Objects,
56	    // you can add to relation by adding the whole array or object by object
57	    authorsRelation.add(bookAuthorsObjects);
58	
59	    // After setting the values, save it on the server
60	    try {
61	      await Book.save();
62	      // Success
63	      Alert.alert('Success!');
64	      navigation.goBack();
65	      return true;
66	    } catch (error) {
67	      // Error can be caused by lack of Internet connection
68	      Alert.alert('Error!', error.message);
69	      return false;
70	    }
71	  } catch (error) {
72	    // Error can be caused by lack of value selection
73	    Alert.alert(
74	      'Error!',
75	      'Make sure to select valid choices in Publisher, Genre and Author fields!',
76	    );
77	    return false;
78	  }
79	};
```

TodoList.tsx

```typescript
1	const createBook = async function (): Promise<boolean> {
2	  try {
3	    // These values come from state variables linked to
4	    // the screen form fields, retrieving the user choices
5	    // as a complete Parse.Object, when applicable;
6	    const bookTitleValue: string = bookTitle;
7	    const bookISBDValue: string = bookISBD;
8	    // For example, bookPublisher holds the value from
9	    // RadioButton.Group field with its options being every
10	    // Publisher parse object instance saved on server, which is
11	    // queried on screen load via useEffect
12	    const bookPublisherObject: Parse.Object = bookPublisher;
13	    const bookGenreObject: Parse.Object = bookGenre;
14	    // bookAuthors can be an array of Parse.Objects, since the book
15	    // may have more than one Author
16	    const bookAuthorsObjects: [Parse.Object] = bookAuthors;
17	
18	    // Creates a new parse object instance
19	    let Book: Parse.Object = new Parse.Object('Book');
20	
21	    // Set data to parse object
22	    // Simple title field
23	    Book.set('title', bookTitleValue);
24	
25	    // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
26	    let isbdQuery: Parse.Query = new Parse.Query('ISBD');
27	    isbdQuery.equalTo('name', bookISBDValue);
28	    let isbdQueryResult: Parse.Object = await isbdQuery.first();
29	    if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
30	      // If first returns a valid object instance, it means that there
31	      // is at least one instance of ISBD with the informed value
32	      Alert.alert(
33	        'Error!',
34	        'There is already an ISBD instance with this value!',
35	      );
36	      return false;
37	    } else {
38	      // Create a new ISBD object instance to create a one-to-one relation on saving
39	      let ISBD: Parse.Object = new Parse.Object('ISBD');
40	      ISBD.set('name', bookISBDValue);
41	      ISBD = await ISBD.save();
42	      // Set the new object to the new book object ISBD field
43	      Book.set('isbd', ISBD);
44	    }
45	
46	    // One-to-many relations can be set in two ways:
47	    // add direct object to field (Parse will convert to pointer on save)
48	    Book.set('publisher', bookPublisherObject);
49	    // or add pointer to field
50	    Book.set('genre', bookGenreObject.toPointer());
51	
52	    // many-to-many relation
53	    // Create a new relation so data can be added
54	    let authorsRelation = Book.relation('authors');
55	    // bookAuthorsObjects is an array of Parse.Objects,
56	    // you can add to relation by adding the whole array or object by object
57	    authorsRelation.add(bookAuthorsObjects);
58	
59	    // After setting the values, save it on the server
60	    try {
61	      await Book.save();
62	      // Success
63	      Alert.alert('Success!');
64	      navigation.goBack();
65	      return true;
66	    } catch (error) {
67	      // Error can be caused by lack of Internet connection
68	      Alert.alert('Error!', error.message);
69	      return false;
70	    }
71	  } catch (error) {
72	    // Error can be caused by lack of value selection
73	    Alert.alert(
74	      'Error!',
75	      'Make sure to select valid choices in Publisher, Genre and Author fields!',
76	    );
77	    return false;
78	  }
79	};
```
:::

Look how the bookISBDValue is set to the new book Parse.Object instance. Creating and saving one-to-one and one-to-many relations in Parse are similar processes, in which you pass as an argument the Parse.Object instance using the Parse.Object.set method, which takes two arguments: the field name and the value to be set.

The catch here is that, before saving, you need to enforce that there are no ISBD objects containing the informed ISBD ID string value in your database and that there are no Book objects already related to it as well. The second part will always be true in this case, since you are creating a new Book object every time. Enforcing the ISBD uniqueness can be achieved by using the following highlighted query:

:::CodeblockTabs
JavaScript

```javascript
1	let isbdQuery = new Parse.Query('ISBD');
2	isbdQuery.equalTo('name', bookISBDValue);
3	let isbdQueryResult = await isbdQuery.first();
4	if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
5	  // If first returns a valid object instance, it means that there
6	  // is at least one instance of ISBD with the informed value
7	  Alert.alert(
8	    'Error!',
9	    'There is already an ISBD instance with this value!',
10	  );
11	  return false;
12	}
```

TodoList.tsx

```typescript
1	let isbdQuery: Parse.Query = new Parse.Query('ISBD');
2	isbdQuery.equalTo('name', bookISBDValue);
3	let isbdQueryResult: Parse.Object = await isbdQuery.first();
4	if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
5	  // If first returns a valid object instance, it means that there
6	  // is at least one instance of ISBD with the informed value
7	  Alert.alert(
8	    'Error!',
9	    'There is already an ISBD instance with this value!',
10	  );
11	  return false;
12	}
```
:::

After successfully saving your objects, Parse will create a pointer data type column and a direct link on your dashboard for quick access under the hood.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/F5vFvtQwa5BWn9HGb2wkA_image.png)

## 3 - Querying one-to-one relations

Querying one-to-one related objects is pretty straightforward as much of it is handled by Parse. Take a look at the query function in the book registers list screen:

:::CodeblockTabs
JavaScript

```javascript
1	const queryBooks = async function () {
2	  // These values come from state variables linked to
3	  // the screen query RadioButton.Group fields, with its options being every
4	  // parse object instance saved on server from the referred class, which is
5	  // queried on screen load via useEffect; these variables retrievie the user choices
6	  // as a complete Parse.Object;
7	  const queryPublisherValue = queryPublisher;
8	  const queryGenreValue = queryGenre;
9	  const queryAuthorValue = queryAuthor;
10	  const queryIsbdValue = queryIsbd;
11	
12	  // Reading parse objects is done by using Parse.Query
13	  const parseQuery = new Parse.Query('Book');
14	
15	  // One-to-many queries
16	  if (queryPublisherValue !== '') {
17	    parseQuery.equalTo('publisher', queryPublisherValue);
18	  }
19	  if (queryGenreValue !== '') {
20	    parseQuery.equalTo('genre', queryGenreValue);
21	  }
22	
23	  // One-to-one query
24	  if (queryIsbdValue !== '') {
25	    parseQuery.equalTo('isbd', queryIsbdValue);
26	  }
27	
28	  // Many-to-many query
29	  // In this case, we need to retrieve books related to the chosen author
30	  if (queryAuthorValue !== '') {
31	    parseQuery.equalTo('authors', queryAuthorValue);
32	  }
33	
34	  try {
35	    let books = await parseQuery.find();
36	    // Many-to-many objects retrieval
37	    // In this example we need to get every related author Parse.Object
38	    // and add it to our query result objects
39	    for (let book of books) {
40	      // This query is done by creating a relation and querying it
41	      let bookAuthorsRelation = book.relation('authors');
42	      book.authorsObjects = await bookAuthorsRelation.query().find();
43	    }
44	    setQueriedBooks(books);
45	    return true;
46	  } catch (error) {
47	    // Error can be caused by lack of Internet connection
48	    Alert.alert('Error!', error.message);
49	    return false;
50	  }
51	};
```

TodoList.tsx

```typescript
1	const queryBooks = async function (): Promise<boolean> {
2	  // These values come from state variables linked to
3	  // the screen query RadioButton.Group fields, with its options being every
4	  // parse object instance saved on server from the referred class, which is
5	  // queried on screen load via useEffect; these variables retrievie the user choices
6	  // as a complete Parse.Object;
7	  const queryPublisherValue: Parse.Object = queryPublisher;
8	  const queryGenreValue: Parse.Object = queryGenre;
9	  const queryAuthorValue: Parse.Object = queryAuthor;
10	  const queryIsbdValue: Parse.Object = queryIsbd;
11	
12	  // Reading parse objects is done by using Parse.Query
13	  const parseQuery: Parse.Query = new Parse.Query('Book');
14	
15	  // One-to-many queries
16	  if (queryPublisherValue !== '') {
17	    parseQuery.equalTo('publisher', queryPublisherValue);
18	  }
19	  if (queryGenreValue !== '') {
20	    parseQuery.equalTo('genre', queryGenreValue);
21	  }
22	
23	  // One-to-one query
24	  if (queryIsbdValue !== '') {
25	    parseQuery.equalTo('isbd', queryIsbdValue);
26	  }
27	
28	  // Many-to-many query
29	  // In this case, we need to retrieve books related to the chosen author
30	  if (queryAuthorValue !== '') {
31	    parseQuery.equalTo('authors', queryAuthorValue);
32	  }
33	
34	  try {
35	    let books: [Parse.Object] = await parseQuery.find();
36	    // Many-to-many objects retrieval
37	    // In this example we need to get every related author Parse.Object
38	    // and add it to our query result objects
39	    for (let book of books) {
40	      // This query is done by creating a relation and querying it
41	      let bookAuthorsRelation = book.relation('authors');
42	      book.authorsObjects = await bookAuthorsRelation.query().find();
43	    }
44	    setQueriedBooks(books);
45	    return true;
46	  } catch (error) {
47	    // Error can be caused by lack of Internet connection
48	    Alert.alert('Error!', error.message);
49	    return false;
50	  }
51	};
```
:::

In this case, to query any books related to a specific ISBD, you need only to perform a Parse.Query.equalTo method passing the Parse.Object instance as the parameter. After querying, Parse will store inside the resulting objects the complete instances of any one-to-one relational fields. To retrieve and show data from these object instances, you can chain the Parse.Object.get method like this: bookParseObject.get(('isbd').get('name'). Here is how the list screen looks like by using these getters to retrieve the ISBD name from the list items:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0HJqpWWdXlfL-U8ZApFOP_image.png" signedSrc size="50" width="346" height="734" position="center" caption}

## Conclusion

At the end of this guide, you learned how to create and query one-to-one relations in Parse on React Native. In the next guide, we will show you how to register users.

[title] Triggers
[path] React Native/Parse SDK (REST)/Cloud Functions/

# Using Cloud Functions in a React Native App

## Introduction

In this guide, you will learn how to use the Parse Cloud Code Functions from a React Native App. You will see examples of triggers implemented using cloud functinos, and also check how to implement a React Native component using these functions and Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Understand how to <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">deploy a cloud function</a> on Back4App.
- If you want to run this guide’s example application, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

Run Parse Cloud Code on Back4App from a React Native App.

## 1 - Understanding Cloud Code Functions

In your applications, there will be times that you will need to perform certain data operations or heavy processing that shouldn’t be done on mobile devices. For these cases, you may use Cloud Code functions, that run directly on your Parse server and are called using Parse API. Note that this also enables changing some part of your app’s logic without needing to release a new version on the app stores, which can be useful in some cases.

There are two main types of Cloud Code functions, generic functions, using define and holding any type of code you want, and triggers, that are fired when certain events occur in your Parse server automatically.

In the next step, you will be presented with examples for most of these function types, which can be created and deployed directly in your Back4App dashboard or via CLI. You can check how to perform this in [our Cloud functions starter guide](https://www.back4app.com/docs/get-started/cloud-functions).

## 2 - Cloud Code Reference

### Cloud Functions

:::CodeblockTabs
Cloud Functions

```javascript
1	// This a generic cloud function that may contain any type of operation,
2	// these should be called via API in your app to be run
3	Parse.Cloud.define("getAverageMovieReviews", async (request) => {
4	  const query = new Parse.Query("Review");
5	  // Parameters can be passed in your request body and are accessed inside request.params
6	  query.equalTo("movie", request.params.movie);
7	  const results = await query.find();
8	  let sum = 0;
9	  for (let review of results) {
10	    sum += review.get("stars");
11	  }
12	  return {
13	    result: sum / results.length,
14	  }
15	});
```
:::

Response:

> 1   { "result": 3.5 }

### Save Triggers

:::CodeblockTabs
beforeSave

```javascript
1	// This will run every time an object of this class will be saved on the server
2	Parse.Cloud.beforeSave("Product", (request) => {
3	  // You can change any data before saving, can be useful for trimming text
4	  // or validating unique fields
5	  const name = request.object.get("description");
6	  if (description.length > 140) {
7	    // Truncate and add a ...
8	    request.object.set("description", comment.substring(0, 137) + "...");
9	  }
10	});
```

afterSave

```javascript
1	// This will always run after saving an object of this class
2	Parse.Cloud.afterSave("Comment", async (request) => {
3	  // You can use this method for changing other classes, like handling
4	  // a Comment counter for a Post
5	  const query = new Parse.Query("Post");
6	  try {
7	    let post = await query.get(request.object.get("post").id);
8	    post.increment("comments");
9	    return post.save();
10	  } catch (error) {
11	    console.error("Got an error " + error.code + " : " + error.message);
12	  }
13	});
```
:::

### Delete Triggers

:::CodeblockTabs
beforeDelete

```javascript
1	// This will be called before deleting an object, useful for checking
2	// for existing dependent conditions
3	Parse.Cloud.beforeDelete("Album", async (request) => {
4	  // This won't proceed deleting if the condition is not met, protection your
5	  // data
6	  const query = new Parse.Query("Photo");
7	  query.equalTo("album", request.object);
8	  const count = await query.count();
9	  if (count > 0) {
10	    throw "Can't delete album if it still has photos.";
11	  }
12	});
```

afterDelete

```javascript
1	// Called after deleting an object, useful for cascade deleting related
2	// objects
3	Parse.Cloud.afterDelete("Post", async (request) => {
4	  // Delete all post Comments after deleting the Post
5	  const query = new Parse.Query("Comment");
6	  query.equalTo("post", request.object);
7	  try {
8	    let results = await query.find();
9	    await Parse.Object.destroyAll(results);
10	  } catch (error) {
11	    console.error("Error finding related comments " + error.code + ": " + error.message);
12	  }
13	});
```
:::

### File Triggers

:::CodeblockTabs
beforeSaveFile

```javascript
1	// Can be called to change any file properties, like renaming it
2	Parse.Cloud.beforeSaveFile(async (request) => {
3	  const { file } = request;
4	  const fileData = await file.getData();
5	  // Note that the new file will be saved instead of the user submitted one
6	  const newFile = new Parse.File('a-new-file-name.txt', { base64: fileData });
7	  return newFile;
8	});
```

afterSaveFile

```javascript
1	// Can be called for managing file metadata and creating
2	// your file referencing object
3	Parse.Cloud.afterSaveFile(async (request) => {
4	  const { file, fileSize, user } = request;
5	  const fileObject = new Parse.Object('FileObject');
6	  fileObject.set('file', file);
7	  fileObject.set('fileSize', fileSize);
8	  fileObject.set('createdBy', user);
9	  const token = { sessionToken: user.getSessionToken() };
10	  await fileObject.save(null, token);
11	});
```

beforeDeleteFile

```javascript
1	// Useful for checking conditions in objects using the file to be deleted
2	Parse.Cloud.beforeDeleteFile(async (request) => {
3	  // Only deletes file if the object storing it was also deleted before
4	  const { file, user } = request;
5	  const query = new Parse.Query('FileObject');
6	  query.equalTo('fileName', file.name());
7	  const fileObjectCount = await query.count({ useMasterKey: true });
8	  if (fileObjectCount > 0) {
9	    throw 'The FileObject should be delete first!';
10	  }
11	});
```

afterDeleteFile

```javascript
1	// Useful for cleaning up objects related to the file
2	Parse.Cloud.afterDeleteFile(async (request) => {
3	  // Note that this example is the opposite of the beforeDeleteFile one,
4	  // this one will clean up FileObjects after deleting the file
5	  const { file } = request;
6	  const query = new Parse.Query('FileObject');
7	  query.equalTo('fileName', file.name());
8	  const fileObject = await query.first({ useMasterKey: true });
9	  await fileObject.destroy({ useMasterKey: true });
10	});
```
:::

### Find Triggers

:::CodeblockTabs
beforeFind

```javascript
1	// This will be called before any queries using this class
2	Parse.Cloud.beforeFind('MyObject', (request) => {
3	  // Useful for hard setting result limits or forcing
4	  // certain conditions
5	  let query = request.query;
6	  query.limit(5);
7	});
```

afterFind

```javascript
1	// This will be called after the query is done in the database
2	Parse.Cloud.afterFind('MyObject', (req) => {
3	  // Can be used in rare cases, like force reversing result ordering
4	  let results = req.objects;
5	  results = results.reverse();
6	  return req.objects;
7	});
```
:::

### Session Triggers

:::CodeblockTabs
beforeLogin

```javascript
1	// Can be used for checking for user permissions and state
2	Parse.Cloud.beforeLogin(async (request) => {
3	  // Doesn't allow banned users to login
4	  const { object: user } = request;
5	  if (user.get('isBanned') === true) {
6	    throw 'Access denied, you have been banned.';
7	  }
8	});
```

afterLogout

```javascript
1	// Can be used for cleaning up user actions after logging out
2	Parse.Cloud.afterLogout(async (request) => {
3	  // Sets custom online flag to false
4	  const { object: session } = request;
5	  const user = session.get('user');
6	  user.set('isOnline', false);
7	  await user.save(null, { useMasterKey: true } );
8	});
```
:::

## 3 - Using Cloud Code from a React Native component

Let’s now create an example Parse Cloud Code function and call it inside a component in React Native, with a simple interface having a label showing the function result and also a form for adding new objects.

Our cloud function will calculate the average of the ratings of every movie Review object in our app, which is a Parse.Object class composed of text, rate and movie fields. Here is the function code:

```javascript
1	Parse.Cloud.define("getMovieAverageRating", async (request) => {
2	  const query = new Parse.Query("Review");
3	  query.equalTo("movie", request.params.movie);
4	  const results = await query.find();
5	  let sum = 0;
6	  for (let review of results) {
7	    sum += review.get("rate");
8	  }
9	  return {
10	    result: sum / results.length,
11	  };
12	});
```

To call this cloud function in React Native, you need to use the Parse.Cloud.run method, passing as an argument the function name and also any necessary parameters inside an object.

:::CodeblockTabs
JavaScript

```javascript
1	const runGetMovieAverageRating = async function () {
2	  try {
3	    const params = {
4	      movie: 'Mission Very Possible',
5	    };
6	    let resultObject = await Parse.Cloud.run(
7	      'getMovieAverageRating',
8	      params,
9	    );
10	    // Set query results to state variable using state hook
11	    setRatingsAverage(resultObject.result.toFixed(1));
12	    return true;
13	  } catch (error) {
14	    // Error can be caused by lack of Internet connection
15	    // or by not having an valid Review object yet
16	    Alert.alert('Error!', error.message);
17	    return false;
18	  }
19	};
```

```typescript
1	const runGetMovieAverageRating = async function (): Promise<boolean> {
2	  try {
3	    const params: {movie: string} = {
4	      movie: 'Mission Very Possible',
5	    };
6	    let resultObject: {result: number} = await Parse.Cloud.run(
7	      'getMovieAverageRating',
8	      params,
9	    );
10	    // Set query results to state variable using state hook
11	    setRatingsAverage(resultObject.result.toFixed(1));
12	    return true;
13	  } catch (error) {
14	    // Error can be caused by lack of Internet connection
15	    // or by not having an valid Review object yet
16	    Alert.alert('Error!', error.message);
17	    return false;
18	  }
19	};
```
:::

We can also enforce that the reviews’ texts are kept short by using a beforeSave trigger function for the Review object. Here is the function code:

:::CodeblockTabs
JavaScript

```javascript
1	Parse.Cloud.beforeSave("Review", (request) => {
2	  const text = request.object.get("text");
3	  if (text.length > 20) {
4	    // Truncate and add a ...
5	    request.object.set("text", text.substring(0, 17) + "...");
6	  }
7	});
```
:::

This is how the full component code is laid out:

:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  View,
6	  Platform,
7	  ScrollView,
8	  StyleSheet,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import {
12	  List,
13	  Title,
14	  TextInput as PaperTextInput,
15	  Button as PaperButton,
16	  Text as PaperText,
17	} from 'react-native-paper';
18	
19	export const MovieRatings = () => {
20	  // State variable
21	  const [queryResults, setQueryResults] = useState(null);
22	  const [ratingsAverage, setRatingsAverage] = useState('');
23	  const [reviewText, setReviewText] = useState('');
24	  const [reviewRate, setReviewRate] = useState('');
25	
26	  const runGetMovieAverageRating = async function () {
27	    try {
28	      const params = {
29	        movie: 'Mission Very Possible',
30	      };
31	      let resultObject = await Parse.Cloud.run(
32	        'getMovieAverageRating',
33	        params,
34	      );
35	      // Set query results to state variable using state hook
36	      setRatingsAverage(resultObject.result.toFixed(1));
37	      return true;
38	    } catch (error) {
39	      // Error can be caused by lack of Internet connection
40	      // or by not having an valid Review object yet
41	      Alert.alert(
42	        'Error!',
43	        'Make sure that the cloud function is deployed and that the Review class table is created',
44	      );
45	      return false;
46	    }
47	  };
48	
49	  const doReviewQuery = async function () {
50	    // Create our query
51	    let parseQuery = new Parse.Query('Review');
52	    try {
53	      let results = await parseQuery.find();
54	      // Set query results to state variable
55	      setQueryResults(results);
56	      return true;
57	    } catch (error) {
58	      // Error can be caused by lack of Internet connection
59	      Alert.alert('Error!', error.message);
60	      return false;
61	    }
62	  };
63	
64	  const createReview = async function () {
65	    try {
66	      // This values come from state variables linked to
67	      // the screen form fields
68	      const reviewTextValue = reviewText;
69	      const reviewRateValue = Number(reviewRate);
70	
71	      // Creates a new parse object instance
72	      let Review = new Parse.Object('Review');
73	
74	      // Set data to parse object
75	      // Simple title field
76	      Review.set('text', reviewTextValue);
77	
78	      // Simple number field
79	      Review.set('rate', reviewRateValue);
80	
81	      // Set default movie
82	      Review.set('movie', 'Mission Very Possible');
83	
84	      // After setting the values, save it on the server
85	      try {
86	        await Review.save();
87	        // Success
88	        Alert.alert('Success!');
89	        // Updates query result list
90	        doReviewQuery();
91	        runGetMovieAverageRating();
92	        return true;
93	      } catch (error) {
94	        // Error can be caused by lack of Internet connection
95	        Alert.alert('Error!', error.message);
96	        return false;
97	      }
98	    } catch (error) {
99	      // Error can be caused by lack of field values
100	      Alert.alert('Error!', error.message);
101	      return false;
102	    }
103	  };
104	
105	  return (
106	    <>
107	      <View style={Styles.header}>
108	        <Image
109	          style={Styles.header_logo}
110	          source={ {
111	            uri:
112	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
113	          } }
114	        />
115	        <PaperText style={Styles.header_text}>
116	          <PaperText style={Styles.header_text_bold}>
117	            {'React Native on Back4App - '}
118	          </PaperText>
119	          {' Cloud Code Movie Ratings'}
120	        </PaperText>
121	      </View>
122	      <ScrollView style={Styles.wrapper}>
123	        <View>
124	          <Title>{'Mission Very Possible Reviews'}</Title>
125	          <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
126	          {/* Query list */}
127	          {queryResults !== null &&
128	            queryResults !== undefined &&
129	            queryResults.map((result) => (
130	              <List.Item
131	                key={result.id}
132	                title={`Review text: ${result.get('text')}`}
133	                description={`Rate: ${result.get('rate')}`}
134	                titleStyle={Styles.list_text}
135	                style={Styles.list_item}
136	              />
137	            ))}
138	          {queryResults === null ||
139	          queryResults === undefined ||
140	          (queryResults !== null &&
141	            queryResults !== undefined &&
142	            queryResults.length <= 0) ? (
143	            <PaperText>{'No results here!'}</PaperText>
144	          ) : null}
145	        </View>
146	        <View>
147	          <Title>Action Buttons</Title>
148	          <PaperButton
149	            onPress={() => runGetMovieAverageRating()}
150	            mode="contained"
151	            icon="search-web"
152	            color={'#208AEC'}
153	            style={Styles.list_button}>
154	            {'Calculate Review Average'}
155	          </PaperButton>
156	          <PaperButton
157	            onPress={() => doReviewQuery()}
158	            mode="contained"
159	            icon="search-web"
160	            color={'#208AEC'}
161	            style={Styles.list_button}>
162	            {'Query Reviews'}
163	          </PaperButton>
164	        </View>
165	        <View>
166	          <Title>Add new review</Title>
167	          <PaperTextInput
168	            value={reviewText}
169	            onChangeText={text => setReviewText(text)}
170	            label="Text"
171	            mode="outlined"
172	            style={Styles.form_input}
173	          />
174	          <PaperTextInput
175	            value={reviewRate}
176	            onChangeText={text => setReviewRate(text)}
177	            keyboardType={'number-pad'}
178	            label="Rate (1-5)"
179	            mode="outlined"
180	            style={Styles.form_input}
181	          />
182	          <PaperButton
183	            onPress={() => createReview()}
184	            mode="contained"
185	            icon="plus"
186	            style={Styles.submit_button}>
187	            {'Add'}
188	          </PaperButton>
189	        </View>
190	      </ScrollView>
191	    </>
192	  );
193	};
194	
195	// These define the screen component styles
196	const Styles = StyleSheet.create({
197	  header: {
198	    alignItems: 'center',
199	    paddingTop: 30,
200	    paddingBottom: 50,
201	    backgroundColor: '#208AEC',
202	  },
203	  header_logo: {
204	    height: 50,
205	    width: 220,
206	    resizeMode: 'contain',
207	  },
208	  header_text: {
209	    marginTop: 15,
210	    color: '#f0f0f0',
211	    fontSize: 16,
212	  },
213	  header_text_bold: {
214	    color: '#fff',
215	    fontWeight: 'bold',
216	  },
217	  wrapper: {
218	    width: '90%',
219	    alignSelf: 'center',
220	  },
221	  list_button: {
222	    marginTop: 6,
223	    marginLeft: 15,
224	    height: 40,
225	  },
226	  list_item: {
227	    borderBottomWidth: 1,
228	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
229	  },
230	  list_text: {
231	    fontSize: 15,
232	  },
233	  form_input: {
234	    height: 44,
235	    marginBottom: 16,
236	    backgroundColor: '#FFF',
237	    fontSize: 14,
238	  },
239	  submit_button: {
240	    width: '100%',
241	    maxHeight: 50,
242	    alignSelf: 'center',
243	    backgroundColor: '#208AEC',
244	  },
245	});
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {
3	  Alert,
4	  Image,
5	  View,
6	  Platform,
7	  ScrollView,
8	  StyleSheet,
9	} from 'react-native';
10	import Parse from 'parse/react-native';
11	import {
12	  List,
13	  Title,
14	  TextInput as PaperTextInput,
15	  Button as PaperButton,
16	  Text as PaperText,
17	} from 'react-native-paper';
18	
19	export const MovieRatings: FC<{}> = ({}): ReactElement => {
20	  // State variable
21	  const [queryResults, setQueryResults] = useState(null);
22	  const [ratingsAverage, setRatingsAverage] = useState('');
23	  const [reviewText, setReviewText] = useState('');
24	  const [reviewRate, setReviewRate] = useState('');
25	
26	  const runGetMovieAverageRating = async function (): Promise<boolean> {
27	    try {
28	      const params: {movie: string} = {
29	        movie: 'Mission Very Possible',
30	      };
31	      let resultObject: {result: number} = await Parse.Cloud.run(
32	        'getMovieAverageRating',
33	        params,
34	      );
35	      // Set query results to state variable using state hook
36	      setRatingsAverage(resultObject.result.toFixed(1));
37	      return true;
38	    } catch (error) {
39	      // Error can be caused by lack of Internet connection
40	      // or by not having an valid Review object yet
41	      Alert.alert(
42	        'Error!',
43	        'Make sure that the cloud function is deployed and that the Review class table is created',
44	      );
45	      return false;
46	    }
47	  };
48	
49	  const doReviewQuery = async function (): Promise<boolean> {
50	    // Create our query
51	    let parseQuery: Parse.Query = new Parse.Query('Review');
52	    try {
53	      let results: [Parse.Object] = await parseQuery.find();
54	      // Set query results to state variable
55	      setQueryResults(results);
56	      return true;
57	    } catch (error) {
58	      // Error can be caused by lack of Internet connection
59	      Alert.alert('Error!', error.message);
60	      return false;
61	    }
62	  };
63	
64	  const createReview = async function (): Promise<boolean> {
65	    try {
66	      // This values come from state variables linked to
67	      // the screen form fields
68	      const reviewTextValue: string = reviewText;
69	      const reviewRateValue: number = Number(reviewRate);
70	
71	      // Creates a new parse object instance
72	      let Review: Parse.Object = new Parse.Object('Review');
73	
74	      // Set data to parse object
75	      // Simple title field
76	      Review.set('text', reviewTextValue);
77	
78	      // Simple number field
79	      Review.set('rate', reviewRateValue);
80	
81	      // Set default movie
82	      Review.set('movie', 'Mission Very Possible');
83	
84	      // After setting the values, save it on the server
85	      try {
86	        await Review.save();
87	        // Success
88	        Alert.alert('Success!');
89	        // Updates query result list
90	        doReviewQuery();
91	        runGetMovieAverageRating();
92	        return true;
93	      } catch (error) {
94	        // Error can be caused by lack of Internet connection
95	        Alert.alert('Error!', error.message);
96	        return false;
97	      }
98	    } catch (error) {
99	      // Error can be caused by lack of field values
100	      Alert.alert('Error!', error.message);
101	      return false;
102	    }
103	  };
104	
105	  return (
106	    <>
107	      <View style={Styles.header}>
108	        <Image
109	          style={Styles.header_logo}
110	          source={ {
111	            uri:
112	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
113	          } }
114	        />
115	        <PaperText style={Styles.header_text}>
116	          <PaperText style={Styles.header_text_bold}>
117	            {'React Native on Back4App - '}
118	          </PaperText>
119	          {' Cloud Code Movie Ratings'}
120	        </PaperText>
121	      </View>
122	      <ScrollView style={Styles.wrapper}>
123	        <View>
124	          <Title>{'Mission Very Possible Reviews'}</Title>
125	          <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
126	          {/* Query list */}
127	          {queryResults !== null &&
128	            queryResults !== undefined &&
129	            queryResults.map((result: Parse.Object) => (
130	              <List.Item
131	                key={result.id}
132	                title={`Review text: ${result.get('text')}`}
133	                description={`Rate: ${result.get('rate')}`}
134	                titleStyle={Styles.list_text}
135	                style={Styles.list_item}
136	              />
137	            ))}
138	          {queryResults === null ||
139	          queryResults === undefined ||
140	          (queryResults !== null &&
141	            queryResults !== undefined &&
142	            queryResults.length <= 0) ? (
143	            <PaperText>{'No results here!'}</PaperText>
144	          ) : null}
145	        </View>
146	        <View>
147	          <Title>Action Buttons</Title>
148	          <PaperButton
149	            onPress={() => runGetMovieAverageRating()}
150	            mode="contained"
151	            icon="search-web"
152	            color={'#208AEC'}
153	            style={Styles.list_button}>
154	            {'Calculate Review Average'}
155	          </PaperButton>
156	          <PaperButton
157	            onPress={() => doReviewQuery()}
158	            mode="contained"
159	            icon="search-web"
160	            color={'#208AEC'}
161	            style={Styles.list_button}>
162	            {'Query Reviews'}
163	          </PaperButton>
164	        </View>
165	        <View>
166	          <Title>Add new review</Title>
167	          <PaperTextInput
168	            value={reviewText}
169	            onChangeText={text => setReviewText(text)}
170	            label="Text"
171	            mode="outlined"
172	            style={Styles.form_input}
173	          />
174	          <PaperTextInput
175	            value={reviewRate}
176	            onChangeText={text => setReviewRate(text)}
177	            keyboardType={'number-pad'}
178	            label="Rate (1-5)"
179	            mode="outlined"
180	            style={Styles.form_input}
181	          />
182	          <PaperButton
183	            onPress={() => createReview()}
184	            mode="contained"
185	            icon="plus"
186	            style={Styles.submit_button}>
187	            {'Add'}
188	          </PaperButton>
189	        </View>
190	      </ScrollView>
191	    </>
192	  );
193	};
194	
195	// These define the screen component styles
196	const Styles = StyleSheet.create({
197	  header: {
198	    alignItems: 'center',
199	    paddingTop: 30,
200	    paddingBottom: 50,
201	    backgroundColor: '#208AEC',
202	  },
203	  header_logo: {
204	    height: 50,
205	    width: 220,
206	    resizeMode: 'contain',
207	  },
208	  header_text: {
209	    marginTop: 15,
210	    color: '#f0f0f0',
211	    fontSize: 16,
212	  },
213	  header_text_bold: {
214	    color: '#fff',
215	    fontWeight: 'bold',
216	  },
217	  wrapper: {
218	    width: '90%',
219	    alignSelf: 'center',
220	  },
221	  list_button: {
222	    marginTop: 6,
223	    marginLeft: 15,
224	    height: 40,
225	  },
226	  list_item: {
227	    borderBottomWidth: 1,
228	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
229	  },
230	  list_text: {
231	    fontSize: 15,
232	  },
233	  form_input: {
234	    height: 44,
235	    marginBottom: 16,
236	    backgroundColor: '#FFF',
237	    fontSize: 14,
238	  },
239	  submit_button: {
240	    width: '100%',
241	    maxHeight: 50,
242	    alignSelf: 'center',
243	    backgroundColor: '#208AEC',
244	  },
245	});
```
:::

This is how the component should look like after rendering and querying by one of the query functions:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TLxZDfyAWUhR-mxNfBSvB_image.png" signedSrc size="50" width="354" height="751" position="center" caption}

## Conclusion

At the end of this guide, you learned how Parse Cloud Code functions work and how to perform them on Back4App from a React Native App. In the next guide, you will learn how to work with Users in Parse.

[title] Core Settings
[path] Platform/

# Manage the App Core Settings

## Introduction

Once your app is created on Back4app, you have a feature called Core Settings which allows to find/edit your keys, restart, transfer or delete the app.

This guide shows the main functionalities and what you can do at this page.

## Goal

- Know the page permits.

## Prerequisites

:::hint{type="info"}
**There are no pre-requisites to read this page, however, some functionalities are allowed only to the app’s owner**
:::

## 1 - Access your Core Settings and find your keys

Access your Back4App account, find your app and go to the Server Settings > Core Settings > Settings. This block looks like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VB5hamqzZtZZiOIKvYMU4_image.png" signedSrc size="30" width="243" height="304" position="center" caption}

On this page, you have access to your app details and keys to connect to it via SDK or REST.

## 2 - Page features

There are options like editing the details of your app, restarting, transferring, cloning, and deleting your app. All these functionalities are located at the end of the page:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5QxsNXpWgsImGO6MUOhYY_image.png" signedSrc size="40" width="407" height="267" position="center" caption}

## **2.1 - Edit details**

The first button can be used to edit the name, description, keys, enable/disable properties, or even your application’s Connection String.

### **2.1.1 - App name and description**

Right after you click on this button, you will be able to edit the name and the descriptions of your app. For both options, you can click on the blanks and write as you think is the best for your app.

### **2.1.2 - Keys**

You can also edit the Client Key, Javascript Key, .NET key, REST API Key, Webhook Key, File Key, and Master Key. To do this, search on the page what you want to change, click in the box that there is the old one, and generate a new one.

### **2.1.3 - Connection String**

Be careful if you want to change the *Connection String* because you will finish the past references that were linked with the last URI. To do this, you must click on the change database URI and sequentially click on OK.

### **2.1.4 - Allow class creation and push notifications**

1. **Allow class creation:** By enabling this option, the user will be able to create a new class in the database without the administrator’s permission. In this case, you have to be careful with this action.
2. **Allow push notifications from client:** By enabling this option, the user will be able to send notifications to other users. This operation is hazardous because you allow users to break into the privacy of others. Consequently, you turn your app vulnerable to security problems.

### **2.1.5 - Applying changes**

After all this process you click in the box SAVE button to apply all the new modifications.

## **2.2 - Restart App**

By clicking on this button, you are restarting your app activity including a kill of the current process, and switch on it sequentially.

When you click on it, a box will appear asking if you want to do this; if yes, click on OK, otherwise, click on CANCEL.

## **2.3 - Transfer App**

After clicking on this option, you will transfer the possession of the app to another user, to do this you click in the blank and write the e-mail of this person. So, you will be asked to accept the transfer.

## **2.4 - Clone App**

You use this option to create a new app with the same database as the app that you are working on. To do this, you click in the blank to give a name to your new app, click on the box to clone the database, and click on the CLONE button to confirm the action.

## **2.5 - Delete App**

When you click on this option, you lose, irreversibly, the power of being the administrator of the app. After clicking on this option, you must write the application name and click on the red button (DELETE) to complete this action.

[title] Connect to Back4app
[path] Get started/

Now that you've created your application on Back4app, you’re just a few steps away from using its backend features. This guide will walk you through connecting your app to Back4app's servers using the Parse SDK or REST APIs.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- **Create an Application**: [Create a new app on Back4app](https://www.back4app.com/docs/get-started/new-parse-app) if you haven't already.
- **Access Application Keys**: Go to **Server Settings > Core Settings > App ID and Keys** to find your keys. You will use these to authenticate with Back4app.
:::

## 1 - Install the Parse SDK

Based on the platform you’re working with, follow one of the installation methods below:

### JavaScript (Web or Node.js)

> \# Install the Parse SDK
> $ npm install parse --save

### React Native

> \# Install Parse and AsyncStorage for React Native
> $ npm install parse @react-native-async-storage/async-storage --save
>
> \# Run pod install for iOS
> cd ios && pod install

### Flutter

Add Parse SDK to `pubspec.yaml`

> dependencies:
>   parse_server_sdk_flutter: ^latest_version

:::hint{type="info"}
To check the latest version, you can check <a href="https://pub.dev/packages/parse_server_sdk_flutter" target="_blank">here.</a>
:::

### Android

Open your `build.gradle` file (Module: app) and add the following dependencies:

> repositories {
>     mavenCentral()
>     jcenter()
>     maven { url 'https://jitpack.io' }
> }
> dependencies {
>     implementation 'com.github.parse-community.Parse-SDK-Android:parse:latest_version'
> }

:::hint{type="info"}
To check the latest version, you can check <a href="https://github.com/parse-community/Parse-SDK-Android" target="_blank">here.</a>
:::

### iOS

1. Install CocoaPods

> sudo gem install cocoapods

&#x20;   2\.  Add Parse SDK to your Podfile:

> pod 'ParseSwift

:::hint{type="info"}
You can also use the Swift Package Manager (SPM) or Carthage to install ParseSwift. Click <a href="https://github.com/parse-community/Parse-Swift?tab=readme-ov-file#installation" target="_blank">here</a> to know more.
:::

### PHP

1. Create a `composer.json` file in your project root with the following content:

> \{
>   "require": {
>     "parse/php-sdk": "latest_version_here"
>   }
> }

Then run:

> composer install

&#x20;   2\.  Or clone from GitHub: &#x20;

> git clone https://github.com/parse-community/parse-php-sdk.git

:::hint{type="info"}
For more details, see <a href="https://docs.parseplatform.org/php/guide/#installation" target="_blank">this guide</a>.
:::

### .NET

1. Add Parse SDK from NuGet Packages: Open Visual Studio, go to Solution Explorer, right-click your project, and select `Manage NuGet Packages...`
2. Search for Parse and install it.

:::hint{type="info"}
For more details, see <a href="https://github.com/parse-community/Parse-SDK-dotNET" target="_blank">this guide</a>.
:::

## 2 - Initialize the Parse SDK&#x20;

Once the SDK is installed, initialize it in your app. Below are examples for each platform:

:::CodeblockTabs
```javascript
// Import Parse SDK
const Parse = require('parse/node');

// Initialize with your Back4app keys
Parse.initialize("YOUR_APP_ID", "YOUR_JS_KEY");  // Replace with your App ID and JS Key
Parse.serverURL = 'https://parseapi.back4app.com';
```

RN

```javascript
import Parse from 'parse/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Set AsyncStorage
Parse.setAsyncStorage(AsyncStorage);

// Initialize Parse
Parse.initialize("YOUR_APP_ID", "YOUR_JS_KEY");  // Replace with your App ID and JS Key
Parse.serverURL = 'https://parseapi.back4app.com';
```

Flutter

```dart
import 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';

void main() async {
  await Parse().initialize(
    'YOUR_APP_ID',    // Replace with your App ID
    'https://parseapi.back4app.com',
    clientKey: 'YOUR_CLIENT_KEY',  // Replace with your Client Key
  );
}
```

Android

```java
Parse.initialize(new Parse.Configuration.Builder(context)
    .applicationId("YOUR_APP_ID")  // Replace with your App ID
    .clientKey("YOUR_CLIENT_KEY")  // Replace with your Client Key
    .server("https://parseapi.back4app.com")
    .build()
);
```

iOS

```swift
import ParseSwift

ParseSwift.initialize(applicationId: "YOUR_APP_ID", 
                     clientKey: "YOUR_CLIENT_KEY", 
                     serverURL: URL(string: "https://parseapi.back4app.com")!)
```

```php
require 'vendor/autoload.php';

ParseClient::initialize('YOUR_APP_ID', 'YOUR_REST_KEY', 'YOUR_MASTER_KEY');
ParseClient::setServerURL('https://parseapi.back4app.com', '/');
```

.NET

```csharp
ParseClient.Initialize(new ParseClient.Configuration {
    ApplicationId = "YOUR_APP_ID", // Replace with your App ID
    Key = "YOUR_MASTER_KEY"
    ServerURI = "https://parseapi.back4app.com/"
});
```
:::

## 3 - Save and Read your first Data Object

To ensure your connection is set up correctly, save and retrieve a test object in your Back4app app. Here’s an example:

:::CodeblockTabs
```javascript
// Create and save a test object
const Person = new Parse.Object("Person");
Person.set("name", "Jon Snow");
Person.set("age", 30);

Person.save()
  .then(() => console.log("Successfully connected to Back4app!"))
  .catch((error) => console.error("Connection error:", error.message));
```

RN

```javascript
// Create and save a test object
const testConnection = async () => {
  const Person = new Parse.Object("Person");
  Person.set("name", "Jon Snow");
  Person.set("age", 30);

  try {
    await Person.save();
    console.log("Successfully connected to Back4app!");
  } catch (error) {
    console.error("Connection error:", error.message);
  }
};

testConnection();
```

Flutter

```dart
import 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';

// Test connection by saving an object
void testConnection() async {
  var person = ParseObject('Person')
    ..set('name', 'Jon Snow')
    ..set('age', 30);

  var response = await person.save();

  if (response.success) {
    print("Successfully connected to Back4app!");
  } else {
    print("Connection error: ${response.error?.message}");
  }
}

testConnection();
```

Android

```java
import com.parse.ParseObject;
import com.parse.SaveCallback;
import com.parse.ParseException;

// Test connection by saving an object
ParseObject person = new ParseObject("Person");
person.put("name", "Jon Snow");
person.put("age", 30);

person.saveInBackground(new SaveCallback() {
    @Override
    public void done(ParseException e) {
        if (e == null) {
            Log.i("ParseConnection", "Successfully connected to Back4app!");
        } else {
            Log.e("ParseConnection", "Connection error: " + e.getMessage());
        }
    }
});
```

iOS

```swift
import ParseSwift

// Test connection by saving an object
struct Person: ParseObject {
    var objectId: String?
    var name: String?
    var age: Int?

    // Required ParseObject properties
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    init() {
        // Default empty initializer
    }
}

func testConnection() {
    var person = Person()
    person.name = "Jon Snow"
    person.age = 30

    person.save { result in
        switch result {
        case .success:
            print("Successfully connected to Back4app!")
        case .failure(let error):
            print("Connection error: \(error.localizedDescription)")
        }
    }
}

testConnection()
```

```php
require 'vendor/autoload.php';

// Create and save a test object
$person = new ParseObject("Person");
$person->set("name", "Jon Snow");
$person->set("age", 30);

try {
    $person->save();
    echo "Successfully connected to Back4app!";
} catch (ParseException $error) {
    echo "Connection error: " . $error->getMessage();
}
```

.NET

```csharp
using Parse;

// Create and save a test object
var person = new ParseObject("Person");
person["name"] = "Jon Snow";
person["age"] = 30;

await person.SaveAsync().ContinueWith(t => {
    if (t.IsCompletedSuccessfully) {
        Console.WriteLine("Successfully connected to Back4app!");
    } else {
        Console.WriteLine("Connection error: " + t.Exception?.Message);
    }
});
```

REST API

```curl
//Saving your First Data Object on Back4App

curl -X POST \
-H "X-Parse-Application-Id: APPLICATION_ID" \
-H "X-Parse-REST-API-Key: REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"John Snow","age":27}' \
https://parseapi.back4app.com/classes/Person

//Reading your First Data Object from Back4app

curl -X GET \
-H "X-Parse-Application-Id: APPLICATION_ID" \
-H "X-Parse-REST-API-Key: REST_API_KEY" \
https://parseapi.back4app.com/classes/Person/OBJECT-ID-HERE
```
:::

:::hint{type="info"}
After running these code snippets, check your Back4app Dashboard to verify that the object was saved successfully. This confirms that your connection to Back4app is working.
:::

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3z1v07mHc4n6l-sslZStt_screenshot-2024-11-08-at-115652.png)

## What to do Next?

After a quick start, we recommend keeping exploring the Back4app main features by checking out the guides below.

::::LinkArray
:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pEkmfq5PdkxC3lmfRzYsE_1.png"}
&#x20;      **    **<a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">**React Native**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tPONkoW1aPPhiAdTvbvrx_sem-nome-rectangle-sticker-landscape.png"}
&#x20;               <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">**Flutter**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vqJJw7ljIhPJ1Yfz7mD9n_3.png"}
&#x20;            <a href="https://www.back4app.com/docs/android/android-project-with-source-code-download" target="_blank">Android </a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rzkB1YU-Feimt_SWfZUuC_4.png"}
&#x20;                  <a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank">iOS</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GsQE6ru-jj2rRbUt7P3Tr_5.png"}
&#x20;          <a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">**Javascript**</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/35IqTes9QhdmijlReYBYZ_6.png"}
&#x20;              <a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">GraphQL</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/D4RecbrtvjfbKUfb4yRbP_7.png"}
&#x20;                <a href="https://www.back4app.com/docs/js-framework/ionic/ionic-template" target="_blank">Ionic</a>
:::

:::LinkArrayItem{headerType="IMAGE" headerImage="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yOia2J8LzE-r1WSHQ7ZSQ_8.png"}
&#x20;             <a href="https://www.back4app.com/docs/xamarin/xamarin-templates" target="_blank">Xamarin</a>
:::
::::

&#x20;

[title] useParseQuery
[path] React Native/Parse SDK (REST)/Real Time/

# Getting started with the React hook for real time updates using Parse

## Introduction

Welcome to Parse React Native Lib! In this guide, you’ll learn what the Parse React Lib is, how to install and start to use it on your React Native project.

## Motivation

The easiest way to integrate Parse/Back4App into your Javascript-based project is through the [Parse Javascript SDK](https://www.npmjs.com/package/parse). This library works on multiple Javascript environments such as NodeJS, ReactJS, VueJS, AngularJS, React-Native, and gives you access to the Back4App features.

Parse React Native’s goal is to make this experience even better for React Native developers by delivering a light-weight and easy-to-use layer that provides minimal configuration, code re-usability, and native optimizations for Android and iOS.

Using this package will ensure that items like setting up credentials, HTTP requests, real-time synchronization, offline-first interaction are automatically available to your React Native App. The lib is written entirely in Typescript, on top of [Parse Javascript SDK](https://www.npmjs.com/package/parse), and is currently on the Alpha version.

In this initial guide, you will install and set up the @parse/react-native library on your React Native project.

:::hint{type="danger"}
Parse React Native is currently on the Alpha version. The lib is under testing, so we recommend to proceed with caution. Your feedback is very appreciated, so feel free to use the lib and send us your questions and first impressions by dropping an email to community\@back4app.com.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">app created</a> on Back4App:
- Follow the <a href="https://www.back4app.com/docs/platform/parse-live-query" target="_blank">Enable Live Query</a> tutorial.
- [Npm ](https://www.npmjs.com/get-npm?utm_source=house\&utm_medium=homepage\&utm_campaign=free%20orgs\&utm_term=Install%20npm)or yarn installed:
- [Npx ](https://www.npmjs.com/package/npx)package runner installed.
:::

## 1 - Installation

If you don’t have a React Native application, then go ahead and create a new project with npx package runner using the following command line:

> npx react-native init startWithParseRN

Install @parse/react-native and its peer dependency parse in your React Native application:

> \# Using yarn
> yarn add @parse/react-native parse
>
> \# Using npm
> npm install --save @parse/react-native parse

## 2 - Application Setup

To allow the App to connect to Back4App servers securely, you must provide Parse JavaScript SDK with App’s credentials.

```javascript
1	//In your App.js
2	import { initializeParse } from  '@parse/react-native';
3	
4	initializeParse(
5	  'https://YOUR-SUBDOMAIN.b4a.io/',
6	  'APPLICATION_ID',
7	  'JAVASCRIPT_KEY'
8	);
```

You can find yourApp Id and Javascript Key credentials by opening your app Dashboard at [Back4App Website](https://www.back4app.com/) and clicking on App Settings > Core Settings, under Server Settings.

Your subdomain must be enabled at App Settings > Core Settings > Server URL and Live Query. For more information, please check this guide [here](https://www.back4app.com/docs/platform/parse-live-query).

## 3 - Creating your first Query

Next, you will build your first Query and display it in your App. The @parse/react-native library exports a useParseQuery hook, so you don’t waste time looking into how to implement features like offline support, real-time changes, and so on.

It takes a Parse.Query and returns an object with some props that you can use to access data returned by queries:

```javascript
1	// In your App.js
2	
3	import React from 'react';
4	import { ActivityIndicator, FlatList, View,Text } from 'react-native';
5	import { initializeParse, useParseQuery } from  '@parse/react-native';
6	
7	// remember to call initializeParse with your credentials before using useParseQuery
8	initializeParse(
9	  'https://YOUR-SUBDOMAIN.b4a.io/',
10	  'APPLICATION_ID',
11	  'JAVASCRIPT_KEY'
12	);
13	
14	export default function App() {
15	 const parseQuery = new Parse.Query('Todo');
16	 const {
17	  isLive,
18	  isLoading,
19	  isSyncing,
20	  results,
21	  count,
22	  error,
23	  reload
24	 } = useParseQuery(parseQuery);
25	
26	if (isLoading) {
27	  return <ActivityIndicator/>;
28	}
29	
30	return (
31	<FlatList
32	   data={results}
33	   renderItem={({item}) => (
34	   <View
35	     style={ {
36	      height:  70,
37	      flex:  1,
38	      alignItems:  'center',
39	      justifyContent:  'center',
40	    } }>
41	      <Text>Task: {item.get('title')}</Text>
42	   </View>
43	)}
44	/>);
45	}
```

When passing a query to the hook, it will first look for cached results that it might have stored. Then, it creates a WebSocket connection to communicate with the Back4app LiveQuery server, which synchronizes automatically. In other words, the offline-first approach and real-time changes are enabled by default.

To check the query state use the props returned by the hook:

- IsLive: Iftrue, indicates the query have subscribed to realtime updates.
- isLoading\:Iftrue, the query is fetching the results.
- isSyncing\:Iftrue, the query is getting updated results from Back4app servers.
- results: This is the data returned from the query.
- count: Indicates the number of objects that matched the query
- error: When something goes wrong with the query it returns an error.
- reload: Reload your query results.

The useParseQuery accepts any Parse.Query and you can see the [full documentation](https://github.com/neon-bindings/examples/blob/master/thread-count/native/src/lib.rs) with examples about Parse queries.

## 4 - Test the App Hook

Now you should be able to run your React Native App and see the results.

> \# on Android
> npx react-native run-android
>
> \# on iOS
> npx react-native run-ios

Keep in mind that you should add some data to your Back4app project to see some items in your App.

## It’s done!

At this point, you have installed @parse/react-native on your project, configured the connections with Back4App, and written your first Query. In the next guide, you will see one of the main features of this lib how to use it. Let’s continue to [“Realtime Changes”](https://reactnavigation.org/docs/1.x/hello-react-navigation) to start writing some more queries.

[title] Enable Live Query
[path] Platform/

# Activating your Live Query

## Introduction

The Query is one of the key concepts for Parse Technology. It allows you to retrieve objects by specifying some conditions, making it easy to build apps such as social networks, dashboards, mobile commerce, and also amazing games. However, Parse.Query is based on a pull model, which is not suitable for apps that need real-time support.

Suppose you are building an app that needs real-time messages like a chat-app. Parse.Query would not be an ideal solution since a user app doesn’t know when another one updated a conversation with a new message. In this case, the database has a new update to inform the other to query from the server to get the updates.

**LiveQuery solves this problem.** This tool allows you to subscribe to a Parse.Query you are interested in. Once subscribed, the server will notify clients whenever a Parse.Object that matches the Parse.Query is created or updated, in real-time.

At this guide, we’re going to show you how to activate LiveQueries to your App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A verified account at Back4App.
- An app created in Back4App.
  - Check this <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## How can I use this feature?

When you enable the Live Query feature is a guarantee that you can use more functionalities in Back4App side, so, it has the many different purposes that are described below. You will also learn the process of setting up a subdomain later, in the following instructions.

## 1 - Locate the feature block

To locate and enable your Live Query feature, log in to your Back4App account go to the My Apps option, and then click on Server Settings. After that, search for the Server URL and Live Query and select Settings. The Server URL and Live Query section looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/s0-t_ufZcSOI82tmZgpZ0_image.png)

## **2 - Use Live Query in your App**

To enable client-LiveQuery server interaction, it’s necessary to activate the Live Query in the Web Hosting box and select the Class that will allow communication between the client and server. The section related to this is shown below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CES9K-MM9MUICVUYqU3xj_image.png" signedSrc size="50" width="846" height="899" position="center" caption}

## **3 - Change the default serverURL**

You can change the default URL [https://parseapi.back4app.com/](https://parseapi.back4app.com/) to your subdomain [\* .b4a.io/](https://www.back4app.com/docs/platform/*.b4a.io/).

With the guide described above, you’ll be able to set a custom API address with Back4App and enable as well with various features, including using Live Query and changing your ServerURL!

[title] Command Line Interface Setup
[path] Local Development/

The Back4app Command Line Interface (CLI) is a powerful tool for managing apps, deploying code, and handling cloud functions directly from your terminal. This guide will walk you through installation, configuration, and key features to get you started.

## **What You Can Do With Back4app CLI**

- Create and manage Back4app applications.
- Deploy Cloud Code Functions and other local files.
- Manage app versions and releases.
- Deploy web applications.
- Install and manage NPM modules.

## 1  - Installation

### Prerequisites

:::hint{type="info"}
- A Back4app account. [Sign up here](https://www.back4app.com).
- Python on your machine.
:::

### **For macOS and Linux**

**Option 1: Install via Script**

1. Open your terminal and run:

> curl https://raw.githubusercontent.com/back4app/parse-cli/back4app/installer.sh | sudo /bin/bash

&#x20;   2\.  The tool will be installed as `b4a` in `/usr/local/bin/b4a.`

**Option 2: Manual Installation**

1. Check your system's architecture by running:

> *uname -pm*

&#x20;   2\.  Download the correct executable for your system from [this repository](https://github.com/back4app/parse-cli/blob/back4app/installer.sh).

&#x20;   3\.  Move the downloaded file to `/usr/local/bin/`:

> mv <downloaded_file> /usr/local/bin/b4a
> chmod 755 /usr/local/bin/b4a

&#x20;   4\.  The CLI is now ready to use.

### **For Windows**

- Download the <a href="https://github.com/back4app/parse-cli/releases/download/release_3.3.1/b4a.exe" target="_blank">b4a.exe</a> file from the [releases page](https://github.com/back4app/parse-cli/releases).
- Move the file to `C:\Windows\System32` to make it accessible globally.2 - Configuration

### **Connect CLI to Your Back4app account**

1. Log into your Back4app account.
2. Generate an account key:
   - Hover over the "Hello, \[username]" menu.
   - Go to **Account Keys** and create a new key.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Vtf9h_neBfwWz0MOTYGb8_screenshot-2024-11-28-at-164118.png)

&#x20;   3\.  Configure the key in the CLI:

> b4a configure accountkey

Paste your account key when prompted.

:::hint{type="danger"}
This command will only work for the **first time **you do this setup on a specific computer. Otherwise, run b4a configure accountkey -d to overwrite your old key with a new one.
:::

The account keys you’ve used are stored in $\{HOME}/.back4app/netrc for Mac and Linux.

## 3 - Usage

### **Create a New App**

1. Open the terminal and run: `b4a new`
2. Select "(n)ew" to create a new app.
3. Follow the prompts to name and set up your app.

### **Deploy Cloud Functions**

1. Create your local Cloud Code file, e.g., `main.js`:

```javascript
Parse.Cloud.define("hello", (request) => {
  return "Hello, world!";
});
```

&#x20;   2\.  Deploy the code:

> b4a deploy

The CLI will upload your `cloud` and `public` folders to Back4app.

### **Other Useful Commands**

| Command      | Description                               |
| ------------ | ----------------------------------------- |
| b4a list     | List all apps connected to your account.  |
| b4a deploy   | Deploy Cloud Code and hosting files.      |
| b4a releases | Gets the releases for an app.             |
| b4a rollback | Rolls back the version for the given app. |

Run `b4a help` for a full list of commands.

## 4 -** Troubleshooting**

### **Common Issues**

- **Permission Errors**: Use `sudo` for Linux/Mac installations or ensure the Windows executable is in the correct directory.
- **Connection Problems**: Verify your account key configuration by re-running `b4a configure accountkey`.
- **Deployment Errors**: Check the `.parse.local` and `.parse.project` files for correct configuration.

## 5 -** Next Steps**

- Experiment with Cloud Code by writing custom functions.
- Explore the [local Parse Server environment](https://www.back4app.com/docs/local-development/parse-server-local) for offline development.

## **Conclusion**

With the Back4app CLI, you can efficiently manage your applications, deploy updates, and leverage powerful Cloud Code functions. Follow this guide to get started and explore the full potential of Back4app's CLI tool.

[title] Untitled
[path] /


[title] Sign Up With Google
[path] Platform/

# Sign In with Google Tutorial

## Introduction

Sign In with Google enables users to sign in to Apps using their Google accounts.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create an subdomain in Back4App.
- An <a href="https://developers.google.com/?hl=pt-br" target="_blank">Google Developer account</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a new Client Identifier

Log into your [Google Developer account](https://developers.google.com/) and go to Google API Console. Click Credentials and choose OAuth 2.0 Client IDs

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OmNe9xVnQaAqqIy1Ft3zB_image.png)

If you do not have a Consent Screen, Google will ask you to create one. Click on Configure consent Screen, you will be redirected to the following page:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ocXYNb6QjRzd8Y7kSfQrG_image.png)

Complete the screen consent configuration and hit Save

Pick the platform you will need. For this example, I am using Javascript (Web Application), but you should pick the one you will be using.

In Authorized JavaScript Origins, replace the URL with your subdomain.
In Authorized redirect URIs, insert your subdomain followed by /redirect. As shown in the image below:

:::hint{type="info"}
**Note**: If you do not have your subdomain enabled yet, please check the following guide to know how can you do this: <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Create your Subdomain</a>
:::

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fHWYzVUltAqVN7gjrjURG_image.png)

After that you should have your Client ID and Secret:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/RutkaartvQC1wEC9kSXp2_image.png" signedSrc size="70" width="498" height="427" position="center" caption}

## 3 - Retrieve your Code

Visit the following URL, changing the values for REDIRECT\_URI and CLIENT\_ID for the ones you created:

> https://accounts.google.com/o/oauth2/v2/auth?scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&access_type=offline&include_granted_scopes=true&response_type=code&state=state_parameter_passthrough_value&redirect_uri=REDIRECT-URL&client_id=CLIENT_ID

The scopes necessary to retrieve the auth\_token and later on the user\_id are:

https\://www\.googleapis.com/auth/userinfo.email
https\://www\.googleapis.com/auth/plus.me
https\://www\.googleapis.com/auth/userinfo.profile

Log in with your Google account and the redirected website will have your code in the URL:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Y9Z_gxHrgAdlx7qr3w3Y__image.png)

Copy the Code part of the URL only and run the following CURL command replacing the values YOUR\_CODE, CLIENT\_ID, CLIENT\_SECRET, and REDIRECT\_URI for the values of your application

```curl
1   curl -X POST \
2     https://oauth2.googleapis.com/token \
3     -F 'grant_type=authorization_code' \
4     -F 'code=YOUR_CODE' \
5     -F 'client_id=CLIENT_ID' \
6     -F 'client_secret=CLIENT_SECRET' \
7     -F 'redirect_uri=REDIRECT_URI'
```

Run it and you should retrieve your access token:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/E7bvJz86DXWcs42_9Zam7_image.png)

REMEMBER: the code can be used only once. If you get an error or don’t use your token, you must re-generate your Code to be able to run it again.

Now it is time to retrieve your Google's User ID. It is a numeric string that you will pass along as the id in step 4.
To do so, run the following command replacing the YOUR TOKEN string for the token you received in the previous command.

```curl
1   curl -X GET https://www.googleapis.com/userinfo/v2/me?access_token=YOUR_TOKEN
```

## 4 - Start the development

Now that the sign-in with Google is configured, you can start the development process.
The format for AUTHDATA is:

```json
1   {
2     "google": {
3       "id": "user's Google id (string)",
4       "id_token": "an authorized Google id_token for the user (use when not using access_token)",
5       "access_token": "an authorized Google access_token for the user (use when not using id_token)"
6     }
7   }
```

Here is the method for the iOS SDK:

```swift
1   PFUser.logInWithAuthType(inBackground: "google", authData: ["access_token":tokenString, "id": user]).continueWith { task -> Any? in
2    
3   }
```

And here for the Android SDK:

```java
1   Map<string, string> authData = new HashMap<string, string>(); 
2   authData.put("access_token", tokenString);
3   authData.put("id", user);
4   ParseUser.logInWithInBackground("google", authData){
5
6   }
```

Remember, this must be done at every login for every user.

[title] Untitled
[path] /


[title] SignIn with Google
[path] React Native/Parse SDK (REST)/Users/

# SignIn with Google for React Native

## Introduction

In the last tutorial, you built a User login/logout feature to your App using the Parse.User class. Now you will learn how to use Google Sign-in to retrieve user data from Google and log in, sign up or link existent users with it. You will also install and configure react-native-google-signin lib to achieve that.

The Parse.User.linkWith method is responsible for signing up and logging in users using any third-party authentication method, as long as you pass the right parameters requested by each different provider. After linking the user data to a new or existent Parse.User, Parse will store a valid user session in your device. Future calls to methods like currentAsyncwill successfully retrieve your User data, just like with regular logins.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-js-login-google" target="_blank">JavaScript Example Repository</a>
- <a href="https://github.com/templates-back4app/react-native-ts-login-google" target="_blank">TypeScript Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## Goal

To build a User LogIn feature using Google Sign-in on Parse for a React Native App.

## 1 - Installing dependencies

The most popular way to enable Google Sign-in on React Native is using react-native-google-signin to handle it. Since this library configuration depends on your development environment, target platform, and preferences, set it up following the [official docs](https://github.com/react-native-google-signin/google-signin).

After that, make sure that your app main file (App.js or App.tsx) is correctly initializing and configuring GoogleSignin like this:

:::CodeblockTabs
App.tsx/App.js

```typescript
1	// Other imports
2	import {GoogleSignin} from '@react-native-community/google-signin';
3	
4	// Parse initialization configuration goes here
5	// ...
6	
7	// GoogleSignIn initial configuration
8	// iosClientId is required for iOS platform development and
9	// webCLientId for Android. Use only what is suitable to you
10	GoogleSignin.configure({
11	  iosClientId:
12	    'GOOGLE_IOS_CLIENT_ID',
13	  webClientId:
14	    'GOOGLE_ANDROID_WEB_CLIENT_ID',
15	});
16	
17	// ...
```
:::

## 2 - Usign Google Sign-in with Parse

Let’s now create a new method inside the UserLogIn component calling Google Sign-in authentication modal with GoogleSignin.signIn. If the user signs in with Google, this call will retrieve the user data from Google and you need to store the id, idToken, and Google email for later.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogInGoogle = async function () {
2	  try {
3	    // Check if your user can sign in using Google on his phone
4	    await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
5	    // Retrieve user data from Google
6	    const userInfo = await GoogleSignin.signIn();
7	    const googleIdToken = userInfo.idToken;
8	    const googleUserId = userInfo.user.id;
9	    const googleEmail = userInfo.user.email;
10	  } catch (error) {
11	    Alert.alert('Error!', error.code);
12	    return false;
13	  }
14	};
```

```typescript
1	const doUserLogInGoogle = async function (): Promise<boolean> {
2	  try {
3	    // Check if your user can sign in using Google on his phone
4	    await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
5	    // Retrieve user data from Google
6	    const userInfo: object = await GoogleSignin.signIn();
7	    const googleIdToken: string = userInfo.idToken;
8	    const googleUserId: string = userInfo.user.id;
9	    const googleEmail: string = userInfo.user.email;
10	  } catch (error) {
11	    Alert.alert('Error!', error.code);
12	    return false;
13	  }
14	};
```
:::

After that, you can use Parse.User.linkWith on a new Parse.User object to register a new user and log in. Note that if your user had already signed up using this Google authentication, linkWith will log him in using the existent account.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogInGoogle = async function () {
2	  try {
3	    // Check if your user can sign in using Google on his phone
4	    await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
5	    // Retrieve user data from Google
6	    const userInfo = await GoogleSignin.signIn();
7	    const googleIdToken = userInfo.idToken;
8	    const googleUserId = userInfo.user.id;
9	    const googleEmail = userInfo.user.email;
10	    // Log in on Parse using this Google id token
11	    const userToLogin = new Parse.User();
12	    // Set username and email to match google email
13	    userToLogin.set('username', googleEmail);
14	    userToLogin.set('email', googleEmail);
15	    return await user
16	      .linkWith('google', {
17	        authData: {id: googleUserId, id_token: googleIdToken},
18	      })
19	      .then(async (loggedInUser) => {
20	        // logIn returns the corresponding ParseUser object
21	        Alert.alert(
22	          'Success!',
23	          `User ${loggedInUser.get('username')} has successfully signed in!`,
24	        );
25	        // To verify that this is in fact the current user, currentAsync can be used
26	        const currentUser = await Parse.User.currentAsync();
27	        console.log(loggedInUser === currentUser);
28	        // Navigation.navigate takes the user to the screen named after the one
29	        // passed as parameter
30	        navigation.navigate('Home');
31	        return true;
32	      })
33	      .catch(async (error) => {
34	        // Error can be caused by wrong parameters or lack of Internet connection
35	        Alert.alert('Error!', error.message);
36	        return false;
37	      });
38	  } catch (error) {
39	    Alert.alert('Error!', error.code);
40	    return false;
41	  }
42	};
```

```typescript
1	const doUserLogInGoogle = async function (): Promise<boolean> {
2	  try {
3	    // Check if your user can sign in using Google on his phone
4	    await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
5	    // Retrieve user data from Google
6	    const userInfo: object = await GoogleSignin.signIn();
7	    const googleIdToken: string = userInfo.idToken;
8	    const googleUserId: string = userInfo.user.id;
9	    const googleEmail: string = userInfo.user.email;
10	    // Log in on Parse using this Google id token
11	    const userToLogin: Parse.User = new Parse.User();
12	    // Set username and email to match google email
13	    userToLogin.set('username', googleEmail);
14	    userToLogin.set('email', googleEmail);
15	    return await user
16	      .linkWith('google', {
17	        authData: {id: googleUserId, id_token: googleIdToken},
18	      })
19	      .then(async (loggedInUser: Parse.User) => {
20	        // logIn returns the corresponding ParseUser object
21	        Alert.alert(
22	          'Success!',
23	          `User ${loggedInUser.get('username')} has successfully signed in!`,
24	        );
25	        // To verify that this is in fact the current user, currentAsync can be used
26	        const currentUser: Parse.User = await Parse.User.currentAsync();
27	        console.log(loggedInUser === currentUser);
28	        // Navigation.navigate takes the user to the screen named after the one
29	        // passed as parameter
30	        navigation.navigate('Home');
31	        return true;
32	      })
33	      .catch(async (error: object) => {
34	        // Error can be caused by wrong parameters or lack of Internet connection
35	        Alert.alert('Error!', error.message);
36	        return false;
37	      });
38	  } catch (error) {
39	    Alert.alert('Error!', error.code);
40	    return false;
41	  }
42	};
```
:::

Add this function to your UserSignIn component and assign it to your Google button onPress parameter. Go ahead and test your new function. Note that the user will be redirected to your home screen after successfully registering and/or signing in.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Og5gnStDG8tFDopB_hsuX_image.png" signedSrc size="50" width="371" height="731" position="center" caption}

## 3 - Verifying user sign in and session creation

To make sure that the Google sign-in worked, you can look at your Parse dashboard and see your new User (if your Google authentication data didn’t belong to another user), containing the Google authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pdP6OCXjAUqbJeFPot4Id_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to that User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/DFBImTO7hEaauAEIxGcmM_image.png)

## 4 - Linking an existing User to Google Sign-in

Another linkWith possible use is to link an existing user with another auth provider, in this case, Google. Add this function that calls linkWith the same way you did in UserLogIn to your HelloUser component or directly to your home screen. The only difference here is that instead of calling the method from an emptyParse.User, you will use it from the logged-in user object.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLinkGoogle = async function () {
2	  try {
3	    // Check if your user can sign in using Google on his phone
4	    await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
5	    // Retrieve user data from Google
6	    const userInfo = await GoogleSignin.signIn();
7	    const googleIdToken = userInfo.idToken;
8	    const googleUserId = userInfo.user.id;
9	    const authData = {
10	      id: googleUserId,
11	      id_token: googleIdToken,
12	    };
13	    let currentUser: Parse.User = await Parse.User.currentAsync();
14	    // Link user with his Google Credentials
15	    return await currentUser
16	      .linkWith('google', {
17	        authData: authData,
18	      })
19	      .then(async (loggedInUser) => {
20	        // logIn returns the corresponding ParseUser object
21	        Alert.alert(
22	          'Success!',
23	          `User ${loggedInUser.get(
24	            'username',
25	          )} has successfully linked his Google account!`,
26	        );
27	        // To verify that this is in fact the current user, currentAsync can be used
28	        currentUser = await Parse.User.currentAsync();
29	        console.log(loggedInUser === currentUser);
30	        return true;
31	      })
32	      .catch(async (error) => {
33	        // Error can be caused by wrong parameters or lack of Internet connection
34	        Alert.alert('Error!', error.message);
35	        return false;
36	      });
37	  } catch (error) {
38	    Alert.alert('Error!', error.code);
39	    return false;
40	  }
41	};
```

```typescript
1	const doUserLinkGoogle = async function (): Promise<boolean> {
2	  try {
3	    // Check if your user can sign in using Google on his phone
4	    await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
5	    // Retrieve user data from Google
6	    const userInfo: object = await GoogleSignin.signIn();
7	    const googleIdToken: string = userInfo.idToken;
8	    const googleUserId: string = userInfo.user.id;
9	    const authData: object = {
10	      id: googleUserId,
11	      id_token: googleIdToken,
12	    };
13	    let currentUser: Parse.User = await Parse.User.currentAsync();
14	    // Link user with his Google Credentials
15	    return await currentUser
16	      .linkWith('google', {
17	        authData: authData,
18	      })
19	      .then(async (loggedInUser: Parse.User) => {
20	        // logIn returns the corresponding ParseUser object
21	        Alert.alert(
22	          'Success!',
23	          `User ${loggedInUser.get(
24	            'username',
25	          )} has successfully linked his Google account!`,
26	        );
27	        // To verify that this is in fact the current user, currentAsync can be used
28	        currentUser = await Parse.User.currentAsync();
29	        console.log(loggedInUser === currentUser);
30	        return true;
31	      })
32	      .catch(async (error: object) => {
33	        // Error can be caused by wrong parameters or lack of Internet connection
34	        Alert.alert('Error!', error.message);
35	        return false;
36	      });
37	  } catch (error) {
38	    Alert.alert('Error!', error.code);
39	    return false;
40	  }
41	};
```
:::

Assign this function to a Google button onPress parameter on your home screen. Test your new function, noting that the Parse.User object authData value will be updated with the new auth provider data. Verify if the user has indeed updated in your Parse server dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Z91JJIP-EIX73rzOiINH-_image.png)

## Conclusion

At the end of this guide, you learned how to log in, sign up or link existing Parse users on React Native using Google Sign-in with react-native-google-signin. In the next guide, we will show you how to perform useful user queries.

[title] Validators
[path] React Native/Parse SDK (REST)/Cloud Functions/

# Using Cloud Function validators in a React Native App

## Introduction

In this guide, you will learn how to use the Parse Cloud Code function validators and context usage from a React Native App. You will see examples of validators implemented in cloud functions, how to use context between different functions, and also check how to implement a React Native component using these improved functions in Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Understand how to <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">deploy a cloud function</a> on Back4App.
- If you want to run this guide’s example application, you should set up thereact-native-paper<a href="https://github.com/callstack/react-native-paper" target="_blank">library</a>.
:::

## Goal

Run Parse Cloud Code functions with validators and context usage on Back4App from a React Native App.

## 1 - Understanding Cloud Code Functions Validators

Using Cloud Code functions in your application enables great flexibility in your code, making it possible to detach reusable methods from your app and to better control their behavior. You can check or review how to use them in [our Cloud functions starter guide](https://www.back4app.com/docs/get-started/cloud-functions).

Data sent to these cloud functions must be validated to avoid unexpected errors, and Parse Cloud Code (starting from version 4.4.0) offers complete integrated validators, that can be passed as an object or even another function. These tests are called before executing any code inside your function, so you can always assume that data will be valid if the validators accept it.

Take the following cloud function as an example, in which the average of a certain movie rating is calculated. The movie name is required(movie), so you can use the following object to ensure it is passed. If the movie parameter is not passed, the function will return an error containing the message “Validation failed. Please specify data for the movie.”.

```javascript
1	Parse.Cloud.define('getMovieAverageRating', async (request) => {
2	  const query = new Parse.Query("Review");
3	  query.equalTo("movie", request.params.movie);
4	  const results = await query.find();
5	  let sum = 0;
6	  for (let review of results) {
7	    sum += review.get("rate");
8	  }
9	  return {
10	    result: sum / results.length,
11	  };
12	},{
13	  fields : ['movie'],
14	});
```

You can also pass more advanced options to the validator, such as requiring that the user calling the function is authenticated using therequireUser flag. Another useful addition is to add a condition to validate a parameter value, such as the following, ensuring that the movie value is a string and at least 3 characters long.

```javascript
1	Parse.Cloud.define('getMovieAverageRating', async (request) => {
2	  const query = new Parse.Query("Review");
3	  query.equalTo("movie", request.params.movie);
4	  const results = await query.find();
5	  let sum = 0;
6	  for (let review of results) {
7	    sum += review.get("rate");
8	  }
9	  return {
10	    result: sum / results.length,
11	  };
12	},{
13	  fields : {
14	    movie : {
15	      required: true,
16	      type: String,
17	      options: val => {
18	        return val.length >= 3;
19	      },
20	      error: 'Movie must have at least 3 characters'
21	    }
22	  },
23	});
```

The complete list of validation options can be seen on the Parse [docs](https://docs.parseplatform.org/cloudcode/guide/#implementing-cloud-function-validation).

## 2 - Understanding Cloud Code Functions Context

When using Cloud Code (starting from version 4.3.0) save triggers in your application, you can pass a context dictionary in the Parse.Object.save method and also pass it from a beforeSave handler to an afterSave handler. This can be used to ensure data consistency or to perform any kind of asynchronous operation doable only after successfully saving your object.

Take the following cloud function trigger as an example, in which a Review object is saved and we want to differentiate whether the object save call was made from your application or the dashboard. This will be achieved by setting the context before saving the object in your app code like this:

```javascript
1	const Review = new Parse.Object('Review');
2	Review.set('text', 'Great movie!');
3	Review.set('rate', 5);
4	Review.set('movie', 'Mission Very Possible');
5	
6	const context = { fromApplication: true };
7	const savedReview = await Review.save(null, {context: context});
```

Here are the save trigger functions, note that the context object can be directly accessed from the request in both of them:

```javascript
1	Parse.Cloud.beforeSave('Review', async (request) => {
2	  // Try to get context
3	  try {
4	    const context = request.context;
5	    if (context.fromApplication === true) {
6	      // Set a flag in the object before saving
7	      request.object.set('fromApplication', true);
8	    }
9	  } catch(error){}
10	
11	  const text = request.object.get('text');
12	  if (text.length > 20) {
13	    // Truncate and add a ...
14	    request.object.set('text', text.substring(0, 17) + '...');
15	  }
16	});
17	
18	Parse.Cloud.afterSave('Review', async (request) => {
19	  // Try to get context
20	  try {
21	    const context = request.context;
22	    if (context.fromApplication === true) {
23	      // Do something when coming from application, like sending a notification or email
24	      console.log('Got context fromApplication when saving Review object');
25	    }
26	  } catch(error){}
27	});
```

## 3 - Using Validated Cloud Code from a React Native component

Let’s now use the same component example from the last [guide](https://www.back4app.com/docs/react-native/parse-sdk/cloud-functions/react-native-cloud-functions) as a base and add some changes to highlight the functions now using validators and context. Remember to deploy the cloud functions shown in Steps 1 and 2 and, after that, change the “Review” object creation function to the following, with the addition of the context argument in the save method:

:::CodeblockTabs
JavaScript

```javascript
1	const createReview = async function () {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue = reviewText;
6	    const reviewRateValue = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      // Add 
24	      const context = {fromApplication: true};
25	      await Review.save(null, {context: context});
26	      // Success
27	      Alert.alert('Success!');
28	      // Updates query result list
29	      doReviewQuery();
30	      runGetMovieAverageRating();
31	      return true;
32	    } catch (error) {
33	      // Error can be caused by lack of Internet connection
34	      Alert.alert('Error!', error.message);
35	      return false;
36	    }
37	  } catch (error) {
38	    // Error can be caused by lack of field values
39	    Alert.alert('Error!', error.message);
40	    return false;
41	  }
42	};
```

```typescript
1	const createReview = async function (): Promise<boolean> {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue: string = reviewText;
6	    const reviewRateValue: number = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review: Parse.Object = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      const context = {fromApplication: true};
24	      await Review.save(null, {context: context});
25	      // Success
26	      Alert.alert('Success!');
27	      // Updates query result list
28	      doReviewQuery();
29	      runGetMovieAverageRating();
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      Alert.alert('Error!', error.message);
34	      return false;
35	    }
36	  } catch (error) {
37	    // Error can be caused by lack of field values
38	    Alert.alert('Error!', error.message);
39	    return false;
40	  }
41	};
```
:::

To highlight the movie average calculation function validator, add a new function querying a movie called “Me” and add another button calling it. Remember that our validator will make the request fail because of the movie name length. Here is the new function code:

:::CodeblockTabs
JavaScript

```javascript
1	const createReview = async function () {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue = reviewText;
6	    const reviewRateValue = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      // Add 
24	      const context = {fromApplication: true};
25	      await Review.save(null, {context: context});
26	      // Success
27	      Alert.alert('Success!');
28	      // Updates query result list
29	      doReviewQuery();
30	      runGetMovieAverageRating();
31	      return true;
32	    } catch (error) {
33	      // Error can be caused by lack of Internet connection
34	      Alert.alert('Error!', error.message);
35	      return false;
36	    }
37	  } catch (error) {
38	    // Error can be caused by lack of field values
39	    Alert.alert('Error!', error.message);
40	    return false;
41	  }
42	};
```

```typescript
1	const createReview = async function (): Promise<boolean> {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue: string = reviewText;
6	    const reviewRateValue: number = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review: Parse.Object = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      const context = {fromApplication: true};
24	      await Review.save(null, {context: context});
25	      // Success
26	      Alert.alert('Success!');
27	      // Updates query result list
28	      doReviewQuery();
29	      runGetMovieAverageRating();
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      Alert.alert('Error!', error.message);
34	      return false;
35	    }
36	  } catch (error) {
37	    // Error can be caused by lack of field values
38	    Alert.alert('Error!', error.message);
39	    return false;
40	  }
41	};
```
:::

This is how the new full component code is laid out, note that there is a new line in the listing item’s title showing if the Review was created from the application or not:



:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  TextInput as PaperTextInput,
8	  Button as PaperButton,
9	  Text as PaperText,
10	} from 'react-native-paper';
11	
12	export const MovieRatings = () => {
13	  // State variable
14	  const [queryResults, setQueryResults] = useState(null);
15	  const [ratingsAverage, setRatingsAverage] = useState('');
16	  const [reviewText, setReviewText] = useState('');
17	  const [reviewRate, setReviewRate] = useState('');
18	
19	  const runGetMovieAverageRating = async function () {
20	    try {
21	      const params = {
22	        movie: 'Mission Very Possible',
23	      };
24	      let resultObject = await Parse.Cloud.run(
25	        'getMovieAverageRating',
26	        params,
27	      );
28	      // Set query results to state variable using state hook
29	      setRatingsAverage(resultObject.result.toFixed(1));
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      // or by not having an valid Review object yet
34	      Alert.alert(
35	        'Error!',
36	        'Make sure that the cloud function is deployed and that the Review class table is created',
37	      );
38	      return false;
39	    }
40	  };
41	
42	  const runGetMeMovieAverageRating = async function () {
43	    try {
44	      const params = {
45	        movie: 'Me',
46	      };
47	      let resultObject = await Parse.Cloud.run(
48	        'getMovieAverageRating',
49	        params,
50	      );
51	      return true;
52	    } catch (error) {
53	      // Error can be caused by lack of Internet connection
54	      // or by not having an valid Review object yet
55	      Alert.alert('Error!', JSON.stringify(error.message));
56	      return false;
57	    }
58	  };
59	
60	  const doReviewQuery = async function () {
61	    // Create our query
62	    let parseQuery = new Parse.Query('Review');
63	    try {
64	      let results = await parseQuery.find();
65	      // Set query results to state variable
66	      setQueryResults(results);
67	      return true;
68	    } catch (error) {
69	      // Error can be caused by lack of Internet connection
70	      Alert.alert('Error!', error.message);
71	      return false;
72	    }
73	  };
74	
75	  const createReview = async function () {
76	    try {
77	      // This values come from state variables linked to
78	      // the screen form fields
79	      const reviewTextValue = reviewText;
80	      const reviewRateValue = Number(reviewRate);
81	
82	      // Creates a new parse object instance
83	      let Review = new Parse.Object('Review');
84	
85	      // Set data to parse object
86	      // Simple title field
87	      Review.set('text', reviewTextValue);
88	
89	      // Simple number field
90	      Review.set('rate', reviewRateValue);
91	
92	      // Set default movie
93	      Review.set('movie', 'Mission Very Possible');
94	
95	      // After setting the values, save it on the server
96	      try {
97	        const context = {fromApplication: true};
98	        await Review.save(null, {context: context});
99	        // Success
100	        Alert.alert('Success!');
101	        // Updates query result list
102	        doReviewQuery();
103	        runGetMovieAverageRating();
104	        return true;
105	      } catch (error) {
106	        // Error can be caused by lack of Internet connection
107	        Alert.alert('Error!', error.message);
108	        return false;
109	      }
110	    } catch (error) {
111	      // Error can be caused by lack of field values
112	      Alert.alert('Error!', error.message);
113	      return false;
114	    }
115	  };
116	
117	  return (
118	    <>
119	      <View style={Styles.header}>
120	        <Image
121	          style={Styles.header_logo}
122	          source={ {uri: 'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',} }
123	        />
124	        <PaperText style={Styles.header_text}>
125	          <PaperText style={Styles.header_text_bold}>
126	            {'React Native on Back4App - '}
127	          </PaperText>
128	          {' Cloud Code Movie Ratings'}
129	        </PaperText>
130	      </View>
131	      <ScrollView style={Styles.wrapper}>
132	        <View>
133	          <Title>{'Mission Very Possible Reviews'}</Title>
134	          <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
135	          {/* Query list */}
136	          {queryResults !== null &&
137	            queryResults !== undefined &&
138	            queryResults.map((result) => (
139	              <List.Item
140	                key={result.id}
141	                title={`Review text: ${result.get('text')}`}
142	                description={`Rate: ${result.get('rate')}\nFrom app: ${
143	                  result.get('fromApplication') !== undefined ? 'Yes' : 'No'
144	                }`}
145	                titleStyle={Styles.list_text}
146	                style={Styles.list_item}
147	              />
148	            ))}
149	          {queryResults === null ||
150	          queryResults === undefined ||
151	          (queryResults !== null &&
152	            queryResults !== undefined &&
153	            queryResults.length <= 0) ? (
154	            <PaperText>{'No results here!'}</PaperText>
155	          ) : null}
156	        </View>
157	        <View>
158	          <Title>Action Buttons</Title>
159	          <PaperButton
160	            onPress={() => runGetMovieAverageRating()}
161	            mode="contained"
162	            icon="search-web"
163	            color={'#208AEC'}
164	            style={Styles.list_button}>
165	            {'Calculate Review Average'}
166	          </PaperButton>
167	          <PaperButton
168	            onPress={() => runGetMeMovieAverageRating()}
169	            mode="contained"
170	            icon="search-web"
171	            color={'#208AEC'}
172	            style={Styles.list_button}>
173	            {'Calculate Me Movie Review Average'}
174	          </PaperButton>
175	          <PaperButton
176	            onPress={() => doReviewQuery()}
177	            mode="contained"
178	            icon="search-web"
179	            color={'#208AEC'}
180	            style={Styles.list_button}>
181	            {'Query Reviews'}
182	          </PaperButton>
183	        </View>
184	        <View>
185	          <Title>Add new review</Title>
186	          <PaperTextInput
187	            value={reviewText}
188	            onChangeText={text => setReviewText(text)}
189	            label="Text"
190	            mode="outlined"
191	            style={Styles.form_input}
192	          />
193	          <PaperTextInput
194	            value={reviewRate}
195	            onChangeText={text => setReviewRate(text)}
196	            keyboardType={'number-pad'}
197	            label="Rate (1-5)"
198	            mode="outlined"
199	            style={Styles.form_input}
200	          />
201	          <PaperButton
202	            onPress={() => createReview()}
203	            mode="contained"
204	            icon="plus"
205	            style={Styles.submit_button}>
206	            {'Add'}
207	          </PaperButton>
208	        </View>
209	      </ScrollView>
210	    </>
211	  );
212	};
213	
214	// These define the screen component styles
215	const Styles = StyleSheet.create({
216	  header: {
217	    alignItems: 'center',
218	    paddingTop: 30,
219	    paddingBottom: 50,
220	    backgroundColor: '#208AEC',
221	  },
222	  header_logo: {
223	    height: 50,
224	    width: 220,
225	    resizeMode: 'contain',
226	  },
227	  header_text: {
228	    marginTop: 15,
229	    color: '#f0f0f0',
230	    fontSize: 16,
231	  },
232	  header_text_bold: {
233	    color: '#fff',
234	    fontWeight: 'bold',
235	  },
236	  wrapper: {
237	    width: '90%',
238	    alignSelf: 'center',
239	  },
240	  list_button: {
241	    marginTop: 6,
242	    marginLeft: 15,
243	    height: 40,
244	  },
245	  list_item: {
246	    borderBottomWidth: 1,
247	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
248	  },
249	  list_text: {
250	    fontSize: 15,
251	  },
252	  form_input: {
253	    height: 44,
254	    marginBottom: 16,
255	    backgroundColor: '#FFF',
256	    fontSize: 14,
257	  },
258	  submit_button: {
259	    width: '100%',
260	    maxHeight: 50,
261	    alignSelf: 'center',
262	    backgroundColor: '#208AEC',
263	  },
264	});
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  TextInput as PaperTextInput,
8	  Button as PaperButton,
9	  Text as PaperText,
10	} from 'react-native-paper';
11	
12	export const MovieRatings: FC<{}> = ({}): ReactElement => {
13	  // State variable
14	  const [queryResults, setQueryResults] = useState(null);
15	  const [ratingsAverage, setRatingsAverage] = useState('');
16	  const [reviewText, setReviewText] = useState('');
17	  const [reviewRate, setReviewRate] = useState('');
18	
19	  const runGetMovieAverageRating = async function (): Promise<boolean> {
20	    try {
21	      const params: {movie: string} = {
22	        movie: 'Mission Very Possible',
23	      };
24	      let resultObject: {result: number} = await Parse.Cloud.run(
25	        'getMovieAverageRating',
26	        params,
27	      );
28	      // Set query results to state variable using state hook
29	      setRatingsAverage(resultObject.result.toFixed(1));
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      // or by not having an valid Review object yet
34	      Alert.alert(
35	        'Error!',
36	        'Make sure that the cloud function is deployed and that the Review class table is created',
37	      );
38	      return false;
39	    }
40	  };
41	
42	  const runGetMeMovieAverageRating = async function (): Promise<boolean> {
43	    try {
44	      const params: {movie: string} = {
45	        movie: 'Me',
46	      };
47	      let resultObject: object = await Parse.Cloud.run(
48	        'getMovieAverageRating',
49	        params,
50	      );
51	      return true;
52	    } catch (error) {
53	      // Error can be caused by lack of Internet connection
54	      // or by not having an valid Review object yet
55	      Alert.alert('Error!', JSON.stringify(error.message));
56	      return false;
57	    }
58	  };
59	
60	  const doReviewQuery = async function (): Promise<boolean> {
61	    // Create our query
62	    let parseQuery: Parse.Query = new Parse.Query('Review');
63	    try {
64	      let results: [Parse.Object] = await parseQuery.find();
65	      // Set query results to state variable
66	      setQueryResults(results);
67	      return true;
68	    } catch (error) {
69	      // Error can be caused by lack of Internet connection
70	      Alert.alert('Error!', error.message);
71	      return false;
72	    }
73	  };
74	
75	  const createReview = async function (): Promise<boolean> {
76	    try {
77	      // This values come from state variables linked to
78	      // the screen form fields
79	      const reviewTextValue: string = reviewText;
80	      const reviewRateValue: number = Number(reviewRate);
81	
82	      // Creates a new parse object instance
83	      let Review: Parse.Object = new Parse.Object('Review');
84	
85	      // Set data to parse object
86	      // Simple title field
87	      Review.set('text', reviewTextValue);
88	
89	      // Simple number field
90	      Review.set('rate', reviewRateValue);
91	
92	      // Set default movie
93	      Review.set('movie', 'Mission Very Possible');
94	
95	      // After setting the values, save it on the server
96	      try {
97	        const context = {fromApplication: true};
98	        await Review.save(null, {context: context});
99	        // Success
100	        Alert.alert('Success!');
101	        // Updates query result list
102	        doReviewQuery();
103	        runGetMovieAverageRating();
104	        return true;
105	      } catch (error) {
106	        // Error can be caused by lack of Internet connection
107	        Alert.alert('Error!', error.message);
108	        return false;
109	      }
110	    } catch (error) {
111	      // Error can be caused by lack of field values
112	      Alert.alert('Error!', error.message);
113	      return false;
114	    }
115	  };
116	
117	  return (
118	    <>
119	      <View style={Styles.header}>
120	        <Image
121	          style={Styles.header_logo}
122	          source={ {
123	            uri:
124	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
125	          } }
126	        />
127	        <PaperText style={Styles.header_text}>
128	          <PaperText style={Styles.header_text_bold}>
129	            {'React Native on Back4App - '}
130	          </PaperText>
131	          {' Cloud Code Movie Ratings'}
132	        </PaperText>
133	      </View>
134	      <ScrollView style={Styles.wrapper}>
135	        <View>
136	          <Title>{'Mission Very Possible Reviews'}</Title>
137	          <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
138	          {/* Query list */}
139	          {queryResults !== null &&
140	            queryResults !== undefined &&
141	            queryResults.map((result: Parse.Object) => (
142	              <List.Item
143	                key={result.id}
144	                title={`Review text: ${result.get('text')}`}
145	                description={`Rate: ${result.get('rate')}\nFrom app: ${
146	                  result.get('fromApplication') !== undefined ? 'Yes' : 'No'
147	                }`}
148	                titleStyle={Styles.list_text}
149	                style={Styles.list_item}
150	              />
151	            ))}
152	          {queryResults === null ||
153	          queryResults === undefined ||
154	          (queryResults !== null &&
155	            queryResults !== undefined &&
156	            queryResults.length <= 0) ? (
157	            <PaperText>{'No results here!'}</PaperText>
158	          ) : null}
159	        </View>
160	        <View>
161	          <Title>Action Buttons</Title>
162	          <PaperButton
163	            onPress={() => runGetMovieAverageRating()}
164	            mode="contained"
165	            icon="search-web"
166	            color={'#208AEC'}
167	            style={Styles.list_button}>
168	            {'Calculate Review Average'}
169	          </PaperButton>
170	          <PaperButton
171	            onPress={() => runGetMeMovieAverageRating()}
172	            mode="contained"
173	            icon="search-web"
174	            color={'#208AEC'}
175	            style={Styles.list_button}>
176	            {'Calculate Me Movie Review Average'}
177	          </PaperButton>
178	          <PaperButton
179	            onPress={() => doReviewQuery()}
180	            mode="contained"
181	            icon="search-web"
182	            color={'#208AEC'}
183	            style={Styles.list_button}>
184	            {'Query Reviews'}
185	          </PaperButton>
186	        </View>
187	        <View>
188	          <Title>Add new review</Title>
189	          <PaperTextInput
190	            value={reviewText}
191	            onChangeText={text => setReviewText(text)}
192	            label="Text"
193	            mode="outlined"
194	            style={Styles.form_input}
195	          />
196	          <PaperTextInput
197	            value={reviewRate}
198	            onChangeText={text => setReviewRate(text)}
199	            keyboardType={'number-pad'}
200	            label="Rate (1-5)"
201	            mode="outlined"
202	            style={Styles.form_input}
203	          />
204	          <PaperButton
205	            onPress={() => createReview()}
206	            mode="contained"
207	            icon="plus"
208	            style={Styles.submit_button}>
209	            {'Add'}
210	          </PaperButton>
211	        </View>
212	      </ScrollView>
213	    </>
214	  );
215	};
216	
217	// These define the screen component styles
218	const Styles = StyleSheet.create({
219	  header: {
220	    alignItems: 'center',
221	    paddingTop: 30,
222	    paddingBottom: 50,
223	    backgroundColor: '#208AEC',
224	  },
225	  header_logo: {
226	    height: 50,
227	    width: 220,
228	    resizeMode: 'contain',
229	  },
230	  header_text: {
231	    marginTop: 15,
232	    color: '#f0f0f0',
233	    fontSize: 16,
234	  },
235	  header_text_bold: {
236	    color: '#fff',
237	    fontWeight: 'bold',
238	  },
239	  wrapper: {
240	    width: '90%',
241	    alignSelf: 'center',
242	  },
243	  list_button: {
244	    marginTop: 6,
245	    marginLeft: 15,
246	    height: 40,
247	  },
248	  list_item: {
249	    borderBottomWidth: 1,
250	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
251	  },
252	  list_text: {
253	    fontSize: 15,
254	  },
255	  form_input: {
256	    height: 44,
257	    marginBottom: 16,
258	    backgroundColor: '#FFF',
259	    fontSize: 14,
260	  },
261	  submit_button: {
262	    width: '100%',
263	    maxHeight: 50,
264	    alignSelf: 'center',
265	    backgroundColor: '#208AEC',
266	  },
267	});
```
:::

This is how the component should look like after rendering and querying by one of the query functions:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qdnyTBPvSmISzETSkq2xO_image.png" signedSrc size="50" width="363" height="749" position="center" caption}

## Conclusion

At the end of this guide, you learned how to use Parse Cloud Code function validators and context. In the next guide, you will learn how to work with Users in Parse.

[title] Email Verification
[path] React Native/Parse SDK (REST)/Users/

# User email verification for React Native

## Introduction

Having a mobile app with unrestricted user registration can cause security issues and spam in your application server. Email verification can help you prevent this situation, requiring that any registered user on your app will have a valid email address.

In this guide, you will learn how to set up email verification in your Back4App server, which will automatically handle this verification. You will also learn how to make sure in your application that the user is indeed verified.

::embed[]{url="https://www.youtube.com/embed/5uR3DNymCOQ"}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

To build a User LogIn feature using Apple Sign-in on Parse for a React Native App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## 1 - Configure Email Verification

You will now configure your Parse Server on Back4App to require user email verification. Open your [Back4App dashboard](https://dashboard.back4app.com/apps) and navigate to your server settings control panel. Find the Verification emails feature and click on Settings:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/oTwCSbxk3Su9_KA4QnS2v_image.png)

Go ahead and check the Verify User Emails and Prevent login if email is not verified checkboxes. Feel free to update and customize any settings in this screen, like the verification email message body and reply-to address.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/b03A-sFGUltWeWWX2YxNJ_image.png)

After setting this up, your Parse server instance will now handle user email verification automatically.

:::hint{type="info"}
**Note:** Activating Prevent login if email is not verified is not required, but it is good practice to require your new users to verify before performing any action in your app.
:::

## 2 - Update your UserRegistration component

You need to make some changes in your UserRegistration component to correctly sign up users with email verification. First, add a new input field for your user’s email value. Update the user registration function in the UserRegistration.js (UserRegistration.tsx if you are using TypeScript) file so now you are setting the email attribute on user data:

:::CodeblockTabs
UserRegistration.js

```javascript
1	const doUserSignUp = async function () {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue = username;
4	  const passwordValue = password;
5	  const emailValue = email;
6	  // Since the signUp method returns a Promise, we need to call it using await
7	  // Note that now you are setting the user email value as well
8	  return await Parse.User.signUp(usernameValue, passwordValue, {
9	    email: emailValue,
10	  })
11	    .then(async (createdUser) => {
12	      // Parse.User.signUp returns the already created ParseUser object if successful
13	      Alert.alert(
14	        'Success!',
15	        `User ${createdUser.get(
16	          'username',
17	        )} was successfully created! Verify your email to login`,
18	      );
19	      // Since email verification is now required, make sure to log out
20	      // the new user, so any Session created is cleared and the user can
21	      // safely log in again after verifying
22	      await Parse.User.logOut();
23	      // Go back to the login page
24	      navigation.dispatch(StackActions.popToTop());
25	      return true;
26	    })
27	    .catch((error) => {
28	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
29	      Alert.alert('Error!', error.message);
30	      return false;
31	    });
32	};
```

UserRegistration.tsx

```typescript
1	const doUserSignUp = async function (): Promise<boolean> {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue: string = username;
4	  const passwordValue: string = password;
5	  const emailValue: string = email;
6	  // Since the signUp method returns a Promise, we need to call it using await
7	  // Note that now you are setting the user email value as well
8	  return await Parse.User.signUp(usernameValue, passwordValue, {
9	    email: emailValue,
10	  })
11	    .then(async (createdUser: Parse.User) => {
12	      // Parse.User.signUp returns the already created ParseUser object if successful
13	      Alert.alert(
14	        'Success!',
15	        `User ${createdUser.get(
16	          'username',
17	        )} was successfully created! Verify your email to login`,
18	      );
19	      // Since email verification is now required, make sure to log out
20	      // the new user, so any Session created is cleared and the user can
21	      // safely log in again after verifying
22	      await Parse.User.logOut();
23	      // Go back to the login page
24	      navigation.dispatch(StackActions.popToTop());
25	      return true;
26	    })
27	    .catch((error: object) => {
28	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
29	      Alert.alert('Error!', error.message);
30	      return false;
31	    });
32	};
```
:::

Note that since your user is not supposed to login without verifying his email, you need to log him out after registration to avoid any errors in the current application Session. Test your application and now you should see a message like this after registering a new user:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/29Q1PvXvl7wb0f4So0JXY_image.png" signedSrc size="50" width="355" height="727" position="center" caption}

After successfully registering your new user, Parse will send an email containing a verification link, looking like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AeGAA9vYGT9JAl4IQOZX6_image.png)

## 3 - Set up your UserLogIn component

Your Parse server is now blocking automatically login attempts that are not from verified users. However, it’s also a good practice to make sure that there is no way for your unverified user to access your application, so let’s add a new condition inside your UserLogIn component in the UserLogIn.js (UserLogIn.tsx if you are using TypeScript) file:

:::CodeblockTabs
UserLogIn.js

```javascript
1	const doUserLogIn = async function () {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue = username;
4	  const passwordValue = password;
5	  return await Parse.User.logIn(usernameValue, passwordValue)
6	    .then(async (loggedInUser) => {
7	      // logIn will throw an error if the user is not verified yet,
8	      // but it's safer to check again after login
9	      if (loggedInUser.get('emailVerified') === true) {
10	        Alert.alert(
11	          'Success!',
12	          `User ${loggedInUser.get('username')} has successfully signed in!`,
13	        );
14	        // Verify this is in fact the current user
15	        const currentUser = await Parse.User.currentAsync();
16	        console.log(loggedInUser === currentUser);
17	        // Navigation.navigate takes the user to the home screen
18	        navigation.navigate('Home');
19	        return true;
20	      } else {
21	        await Parse.User.logOut();
22	        return false;
23	      }
24	    })
25	    .catch((error) => {
26	      // Error can be caused by wrong parameters or lack of Internet connection.
27	      // A non-verified user will also cause an error
28	      Alert.alert('Error!', error.message);
29	      return false;
30	    });
31	};
```

UserRegistration.tsx

```typescript
1	const doUserLogIn = async function (): Promise<boolean> {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue: string = username;
4	  const passwordValue: string = password;
5	  return await Parse.User.logIn(usernameValue, passwordValue)
6	    .then(async (loggedInUser: Parse.User) => {
7	      // logIn will throw an error if the user is not verified yet,
8	      // but it's safer to check again after login
9	      if (loggedInUser.get('emailVerified') === true) {
10	        Alert.alert(
11	          'Success!',
12	          `User ${loggedInUser.get('username')} has successfully signed in!`,
13	        );
14	        // Verify this is in fact the current user
15	        const currentUser: Parse.User = await Parse.User.currentAsync();
16	        console.log(loggedInUser === currentUser);
17	        // Navigation.navigate takes the user to the home screen
18	        navigation.navigate('Home');
19	        return true;
20	      } else {
21	        await Parse.User.logOut();
22	        return false;
23	      }
24	    })
25	    .catch((error: object) => {
26	      // Error can be caused by wrong parameters or lack of Internet connection.
27	      // A non-verified user will also cause an error
28	      Alert.alert('Error!', error.message);
29	      return false;
30	    });
31	};
```
:::

## 4 - Test the email verification

Go ahead and test your application, trying to log in using the unauthorized user created before. If you didn’t click the verification link on the email, you should get an error message like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/1KPZT5QNCd02KeL14G29P_image.png" signedSrc size="50" width="359" height="731" position="center" caption}

After clicking on the verification link, you will be able to log in and be redirected to your home screen. You can also verify your user by opening your Users table inside your Back4App dashboard and editing the emailVerified column manually:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GQAvbFIhdqQ6r5QLR8czE_image.png)

## Conclusion

At the end of this guide, you learned how to set up your Parse server to require user email verification and also to enforce this restriction inside your React Native application. In the next guide, we will show you how to perform useful user queries.

[title] SignIn with Apple
[path] React Native/Parse SDK (REST)/Users/

# SignIn with Apple for React Native

## Introduction

In the last tutorial, you built a User login/logout feature to your App using the Parse.User class. Now you will learn how to use Apple Sign-in to retrieve user data from Apple and log in, sign up or link existent users with it. You will also install and configure react-native-apple-authentication lib to achieve that.

The Parse.User.linkWith method is responsible for signing up and logging in users using any third-party authentication method, as long as you pass the right parameters requested by each different provider. After linking the user data to a new or existent Parse.User, Parse will store a valid user session in your device. Future calls to methods like currentAsync will successfully retrieve your User data, just like with regular logins.

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React Native App created and <a href="https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk" target="_blank">connected to Back4App</a>.
- Complete the previous guides so you can have a better understanding of <a href="https://www.back4app.com/docs/react-native/parse-sdk/working-with-users/react-native-login" target="_blank">the Parse User class</a>.
:::

## Goal

To build a User LogIn feature using Apple Sign-in on Parse for a React Native App.

## 1 - Installing dependencies

The most popular way to enable Apple Sign-in on React Native is using react-native-apple-authentication to handle it. Since this library configuration depends on your development environment, target platform, and preferences, set it up following the [official docs](https://github.com/invertase/react-native-apple-authentication).

If you are developing for Android, you also need to install the [jwt-decode](https://github.com/auth0/jwt-decode) library for decoding Apple JWT tokens.

:::hint{type="info"}
**Note:** Make sure to thoroughly follow the instructions for initial setup of Xcode environment, creating your app ID, keys and service ID on Apple Developer portal.
:::

## 2 - Using Apple Sign-in with Parse

Let’s now create a new method inside the UserLogIn component calling Apple Sign-in authentication modal. The react-native-apple-authentication lib has two separate modules to handle this call based on your user platform, so you need to use appleAuth.performRequest on iOS and appleAuthAndroid.signIn on Android. If the user signs in with Apple, this call will retrieve the user data from Apple and you need to store the id, token, and Apple email for later.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogInApple = async function (): Promise<boolean> {
2	  try {
3	    let response = {};
4	    let appleId = '';
5	    let appleToken = '';
6	    let appleEmail = '';
7	    if (Platform.OS === 'ios') {
8	      // Performs login request requesting user email
9	      response = await appleAuth.performRequest({
10	        requestedOperation: appleAuth.Operation.LOGIN,
11	        requestedScopes: [appleAuth.Scope.EMAIL],
12	      });
13	      // On iOS, user ID and email are easily retrieved from request
14	      appleId = response.user;
15	      appleToken = response.identityToken;
16	      appleEmail = response.email;
17	    } else if (Platform.OS === 'android') {
18	      // Configure the request
19	      appleAuthAndroid.configure({
20	        // The Service ID you registered with Apple
21	        clientId: 'YOUR_SERVICE_ID',
22	        // Return URL added to your Apple dev console
23	        redirectUri: 'YOUR_REDIRECT_URL',
24	        responseType: appleAuthAndroid.ResponseType.ALL,
25	        scope: appleAuthAndroid.Scope.ALL,
26	      });
27	      response = await appleAuthAndroid.signIn();
28	      // Decode user ID and email from token returned from Apple,
29	      // this is a common workaround for Apple sign-in via web API
30	      const decodedIdToken = jwt_decode(response.id_token);
31	      appleId = decodedIdToken.sub;
32	      appleToken = response.id_token;
33	      appleEmail = decodedIdToken.email;
34	    }
35	    // Format authData to provide correctly for Apple linkWith on Parse
36	    const authData = {
37	      id: appleId,
38	      token: appleToken,
39	    };
40	  } catch (error) {
41	    // Error can be caused by wrong parameters or lack of Internet connection
42	    Alert.alert('Error!', error);
43	    return false;
44	  }
45	};
```

```typescript
1	const doUserLogInApple = async function (): Promise<boolean> {
2	  try {
3	    let response: object = {};
4	    let appleId: string = '';
5	    let appleToken: string = '';
6	    let appleEmail: string = '';
7	    if (Platform.OS === 'ios') {
8	      // Performs login request requesting user email
9	      response = await appleAuth.performRequest({
10	        requestedOperation: appleAuth.Operation.LOGIN,
11	        requestedScopes: [appleAuth.Scope.EMAIL],
12	      });
13	      // On iOS, user ID and email are easily retrieved from request
14	      appleId = response.user;
15	      appleToken = response.identityToken;
16	      appleEmail = response.email;
17	    } else if (Platform.OS === 'android') {
18	      // Configure the request
19	      appleAuthAndroid.configure({
20	        // The Service ID you registered with Apple
21	        clientId: 'YOUR_SERVICE_ID',
22	        // Return URL added to your Apple dev console
23	        redirectUri: 'YOUR_SERVICE_URL',
24	        responseType: appleAuthAndroid.ResponseType.ALL,
25	        scope: appleAuthAndroid.Scope.ALL,
26	      });
27	      response = await appleAuthAndroid.signIn();
28	      // Decode user ID and email from token returned from Apple,
29	      // this is a common workaround for Apple sign-in via web API
30	      const decodedIdToken: object = jwt_decode(response.id_token);
31	      appleId = decodedIdToken.sub;
32	      appleToken = response.id_token;
33	      appleEmail = decodedIdToken.email;
34	    }
35	    // Format authData to provide correctly for Apple linkWith on Parse
36	    const authData: object = {
37	      id: appleId,
38	      token: appleToken,
39	    };
40	  } catch (error: any) {
41	    // Error can be caused by wrong parameters or lack of Internet connection
42	    Alert.alert('Error!', error);
43	    return false;
44	  }
45	};
```
:::

Note that for Android you need to decode the returning token from Apple because the lib react-native-apple-authentication uses Apple Sign-in web API for authentication. There are restrictions for data access when using this method, so a common workaround for retrieving your user ID and email is through this decoding process, as stated [here](https://docs.parseplatform.org/parse-server/guide/#apple-authdata) in the official Parse guides.

After that, you can use Parse.User.linkWith on a new Parse.User object to register a new user and log in. Note that if your user had already signed up using this Apple authentication, linkWith will log him in using the existent account.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogInApple = async function (): Promise<boolean> {
2	  try {
3	    let response = {};
4	    let appleId = '';
5	    let appleToken = '';
6	    let appleEmail = '';
7	    if (Platform.OS === 'ios') {
8	      // Performs login request requesting user email
9	      response = await appleAuth.performRequest({
10	        requestedOperation: appleAuth.Operation.LOGIN,
11	        requestedScopes: [appleAuth.Scope.EMAIL],
12	      });
13	      // On iOS, user ID and email are easily retrieved from request
14	      appleId = response.user;
15	      appleToken = response.identityToken;
16	      appleEmail = response.email;
17	    } else if (Platform.OS === 'android') {
18	      // Configure the request
19	      appleAuthAndroid.configure({
20	        // The Service ID you registered with Apple
21	        clientId: 'YOUR_SERVICE_IO',
22	        // Return URL added to your Apple dev console
23	        redirectUri: 'YOUR_SERVICE_URL',
24	        responseType: appleAuthAndroid.ResponseType.ALL,
25	        scope: appleAuthAndroid.Scope.ALL,
26	      });
27	      response = await appleAuthAndroid.signIn();
28	      // Decode user ID and email from token returned from Apple,
29	      // this is a common workaround for Apple sign-in via web API
30	      const decodedIdToken = jwt_decode(response.id_token);
31	      appleId = decodedIdToken.sub;
32	      appleToken = response.id_token;
33	      appleEmail = decodedIdToken.email;
34	    }
35	    // Format authData to provide correctly for Apple linkWith on Parse
36	    const authData = {
37	      id: appleId,
38	      token: appleToken,
39	    };
40	    // Log in or sign up on Parse using this Apple credentials
41	    let userToLogin = new Parse.User();
42	    // Set username and email to match provider email
43	    userToLogin.set('username', appleEmail);
44	    userToLogin.set('email', appleEmail);
45	    return await userToLogin
46	      .linkWith('apple', {
47	        authData: authData,
48	      })
49	      .then(async (loggedInUser) => {
50	        // logIn returns the corresponding ParseUser object
51	        Alert.alert(
52	          'Success!',
53	          `User ${loggedInUser.get('username')} has successfully signed in!`,
54	        );
55	        // To verify that this is in fact the current user, currentAsync can be used
56	        const currentUser = await Parse.User.currentAsync();
57	        console.log(loggedInUser === currentUser);
58	        // Navigation.navigate takes the user to the screen named after the one
59	        // passed as parameter
60	        navigation.navigate('Home');
61	        return true;
62	      })
63	      .catch(async (error) => {
64	        // Error can be caused by wrong parameters or lack of Internet connection
65	        Alert.alert('Error!', error.message);
66	        return false;
67	      });
68	  } catch (error) {
69	    // Error can be caused by wrong parameters or lack of Internet connection
70	    Alert.alert('Error!', error);
71	    return false;
72	  }
73	};
```

```typescript
1	const doUserLogInApple = async function (): Promise<boolean> {
2	  try {
3	    let response: object = {};
4	    let appleId: string = '';
5	    let appleToken: string = '';
6	    let appleEmail: string = '';
7	    if (Platform.OS === 'ios') {
8	      // Performs login request requesting user email
9	      response = await appleAuth.performRequest({
10	        requestedOperation: appleAuth.Operation.LOGIN,
11	        requestedScopes: [appleAuth.Scope.EMAIL],
12	      });
13	      // On iOS, user ID and email are easily retrieved from request
14	      appleId = response.user;
15	      appleToken = response.identityToken;
16	      appleEmail = response.email;
17	    } else if (Platform.OS === 'android') {
18	      // Configure the request
19	      appleAuthAndroid.configure({
20	        // The Service ID you registered with Apple
21	        clientId: 'com.back4app.userguide',
22	        // Return URL added to your Apple dev console
23	        redirectUri: 'https://tuhl.software/back4appuserguide/',
24	        responseType: appleAuthAndroid.ResponseType.ALL,
25	        scope: appleAuthAndroid.Scope.ALL,
26	      });
27	      response = await appleAuthAndroid.signIn();
28	      // Decode user ID and email from token returned from Apple,
29	      // this is a common workaround for Apple sign-in via web API
30	      const decodedIdToken: object = jwt_decode(response.id_token);
31	      appleId = decodedIdToken.sub;
32	      appleToken = response.id_token;
33	      appleEmail = decodedIdToken.email;
34	    }
35	    // Format authData to provide correctly for Apple linkWith on Parse
36	    const authData: object = {
37	      id: appleId,
38	      token: appleToken,
39	    };
40	    // Log in or sign up on Parse using this Apple credentials
41	    let userToLogin: Parse.User = new Parse.User();
42	    // Set username and email to match provider email
43	    userToLogin.set('username', appleEmail);
44	    userToLogin.set('email', appleEmail);
45	    return await userToLogin
46	      .linkWith('apple', {
47	        authData: authData,
48	      })
49	      .then(async (loggedInUser: Parse.User) => {
50	        // logIn returns the corresponding ParseUser object
51	        Alert.alert(
52	          'Success!',
53	          `User ${loggedInUser.get('username')} has successfully signed in!`,
54	        );
55	        // To verify that this is in fact the current user, currentAsync can be used
56	        const currentUser: Parse.User = await Parse.User.currentAsync();
57	        console.log(loggedInUser === currentUser);
58	        // Navigation.navigate takes the user to the screen named after the one
59	        // passed as parameter
60	        navigation.navigate('Home');
61	        return true;
62	      })
63	      .catch(async (error: object) => {
64	        // Error can be caused by wrong parameters or lack of Internet connection
65	        Alert.alert('Error!', error.message);
66	        return false;
67	      });
68	  } catch (error: any) {
69	    // Error can be caused by wrong parameters or lack of Internet connection
70	    Alert.alert('Error!', error);
71	    return false;
72	  }
73	};
```
:::

Add this function to your UserSignIn component and assign it to your Apple button onPress parameter. Go ahead and test your new function. Note that the user will be redirected to your home screen after successfully registering and/or signing in.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wRB6RPTTfGtLJPm1Kfyj4_image.png" signedSrc size="50" width="368" height="738" position="center" caption}

## 3 - Verifying user sign in and session creation

To make sure that the Apple sign-in worked, you can look at your Parse dashboard and see your new User (if your Apple authentication data didn’t belong to another user), containing the Apple authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2kFrsjiGKtI8OR236o9Fh_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to that User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7cDKv-fc1S1IWbhlH3s0u_image.png)

## 4 - Linking an existing User to Apple Sign-in

Another linkWith possible use is to link an existing user with another auth provider, in this case, Apple. Add this function that calls linkWith the same way you did in UserLogIn to your HelloUser component or directly to your home screen. The only difference here is that instead of calling the method from an empty Parse.User, you will use it from the logged-in user object.

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLinkApple = async function (){
2	  try {
3	    let response = {};
4	    let appleId = '';
5	    let appleToken = '';
6	    if (Platform.OS === 'ios') {
7	      // Performs login request requesting user email
8	      response = await appleAuth.performRequest({
9	        requestedOperation: appleAuth.Operation.LOGIN,
10	        requestedScopes: [appleAuth.Scope.EMAIL],
11	      });
12	      // On iOS, user ID and email are easily retrieved from request
13	      appleId = response.user;
14	      appleToken = response.identityToken;
15	    } else if (Platform.OS === 'android') {
16	      // Configure the request
17	      appleAuthAndroid.configure({
18	        // The Service ID you registered with Apple
19	        clientId: 'YOUR_SERVICE_ID',
20	        // Return URL added to your Apple dev console
21	        redirectUri: 'YOUR_REDIRECT_URL',
22	        responseType: appleAuthAndroid.ResponseType.ALL,
23	        scope: appleAuthAndroid.Scope.ALL,
24	      });
25	      response = await appleAuthAndroid.signIn();
26	      // Decode user ID and email from token returned from Apple,
27	      // this is a common workaround for Apple sign-in via web API
28	      const decodedIdToken = jwt_decode(response.id_token);
29	      appleId = decodedIdToken.sub;
30	      appleToken = response.id_token;
31	    }
32	    // Format authData to provide correctly for Apple linkWith on Parse
33	    const authData = {
34	      id: appleId,
35	      token: appleToken,
36	    };
37	    let currentUser = await Parse.User.currentAsync();
38	    // Link user with his Apple Credentials
39	    return await currentUser
40	      .linkWith('apple', {
41	        authData: authData,
42	      })
43	      .then(async (loggedInUser) => {
44	        // logIn returns the corresponding ParseUser object
45	        Alert.alert(
46	          'Success!',
47	          `User ${loggedInUser.get(
48	            'username',
49	          )} has successfully linked his Apple account!`,
50	        );
51	        // To verify that this is in fact the current user, currentAsync can be used
52	        currentUser = await Parse.User.currentAsync();
53	        console.log(loggedInUser === currentUser);
54	        return true;
55	      })
56	      .catch(async (error) => {
57	        // Error can be caused by wrong parameters or lack of Internet connection
58	        Alert.alert('Error!', error.message);
59	        return false;
60	      });
61	  } catch (error) {
62	    // Error can be caused by wrong parameters or lack of Internet connection
63	    Alert.alert('Error!', error);
64	    return false;
65	  }
66	};
```

```typescript
1	const doUserLinkApple = async function (): Promise<boolean> {
2	  try {
3	    let response: object = {};
4	    let appleId: string = '';
5	    let appleToken: string = '';
6	    if (Platform.OS === 'ios') {
7	      // Performs login request requesting user email
8	      response = await appleAuth.performRequest({
9	        requestedOperation: appleAuth.Operation.LOGIN,
10	        requestedScopes: [appleAuth.Scope.EMAIL],
11	      });
12	      // On iOS, user ID and email are easily retrieved from request
13	      appleId = response.user;
14	      appleToken = response.identityToken;
15	    } else if (Platform.OS === 'android') {
16	      // Configure the request
17	      appleAuthAndroid.configure({
18	        // The Service ID you registered with Apple
19	        clientId: 'YOUR_SERVICE_ID',
20	        // Return URL added to your Apple dev console
21	        redirectUri: 'YOUR_SERVICE_URL',
22	        responseType: appleAuthAndroid.ResponseType.ALL,
23	        scope: appleAuthAndroid.Scope.ALL,
24	      });
25	      response = await appleAuthAndroid.signIn();
26	      // Decode user ID and email from token returned from Apple,
27	      // this is a common workaround for Apple sign-in via web API
28	      const decodedIdToken: object = jwt_decode(response.id_token);
29	      appleId = decodedIdToken.sub;
30	      appleToken = response.id_token;
31	    }
32	    // Format authData to provide correctly for Apple linkWith on Parse
33	    const authData: object = {
34	      id: appleId,
35	      token: appleToken,
36	    };
37	    let currentUser: Parse.User = await Parse.User.currentAsync();
38	    // Link user with his Apple Credentials
39	    return await currentUser
40	      .linkWith('apple', {
41	        authData: authData,
42	      })
43	      .then(async (loggedInUser: Parse.User) => {
44	        // logIn returns the corresponding ParseUser object
45	        Alert.alert(
46	          'Success!',
47	          `User ${loggedInUser.get(
48	            'username',
49	          )} has successfully linked his Apple account!`,
50	        );
51	        // To verify that this is in fact the current user, currentAsync can be used
52	        currentUser = await Parse.User.currentAsync();
53	        console.log(loggedInUser === currentUser);
54	        return true;
55	      })
56	      .catch(async (error: object) => {
57	        // Error can be caused by wrong parameters or lack of Internet connection
58	        Alert.alert('Error!', error.message);
59	        return false;
60	      });
61	  } catch (error: any) {
62	    // Error can be caused by wrong parameters or lack of Internet connection
63	    Alert.alert('Error!', error);
64	    return false;
65	  }
66	};
```
:::

Assign this function to a Apple button onPress parameter on your home screen. Test your new function, noting that the Parse.User object authData value will be updated with the new auth provider data. Verify if the user has indeed updated in your Parse server dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MNr0-o_nrQgaLIDQfa8sw_image.png)

## Conclusion

At the end of this guide, you learned how to log in, sign up or link existing Parse users on React Native using Apple Sign-in with react-native-apple-authentication. In the next guide, we will show you how to perform useful user queries.

[title] Basic Operations
[path] ReactJS/Data objects/

# React CRUD tutorial

## Introduction

Storing data on Parse is built around the Parse.Object class. Each Parse.Object contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each Parse.Object. You can simply set whatever key-value pairs you want, and our backend will store it.

You can also specify the datatypes according to your application needs and persist types such as number, boolean, string, DateTime, list, GeoPointers, and Object, encoding them to JSON before saving. Parse also supports store and query relational data by using the types Pointers and Relations.

In this guide, you will learn how to perform basic data operations through a CRUD example app, which will show you how to create, read, update and delete data from your Parse server database in React. You will first create your component functions for each CRUD operation, using them later in a complete screen layout, resulting in a to-do list app.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>
- If you want to test/use the screen layout provided by this guide, you should set up the Ant Design <a href="https://ant.design/docs/react/introduce" target="_blank">library</a>.
:::

## Goal

To build a basic CRUD application in React using Parse.

## 1 - Creating data objects

The first step to manage your data in your Parse database is to have some on it. Let’s now make a createTodo function that will create a new instance of Parse.Object with the “Todo” subclass. The to-do will have a title (string) describing the task and a done(boolean) field indicating whether the task is completed.

:::CodeblockTabs
JavaScript

```javascript
1   const createTodo = async function () {
2     // This value comes from a state variable
3     const newTodoTitleValue = newTodoTitle;
4     // Creates a new Todo parse object instance
5     let Todo = new Parse.Object('Todo');
6     Todo.set('title', newTodoTitleValue);
7     Todo.set('done', false);
8     // After setting the to-do values, save it on the server
9     try {
10      await Todo.save();
11      // Success
12      alert('Success! To-do created!');
13      // Refresh to-dos list to show the new one (you will create this function later)
14      readTodos();
15      return true;
16    } catch (error) {
17      // Error can be caused by lack of Internet connection
18      alert(`Error! ${error.message}`);
19      return false;
20    };
21  };
```

```typescript
1   const createTodo = async function (): Promise<boolean> {
2     // This value comes from a state variable
3     const newTodoTitleValue: string = newTodoTitle;
4     // Creates a new Todo parse object instance
5     let Todo: Parse.Object = new Parse.Object('Todo');
6     Todo.set('title', newTodoTitleValue);
7     Todo.set('done', false);
8     // After setting the to-do values, save it on the server
9     try {
10      await Todo.save();
11      // Success
12      alert('Success! To-do created!');
13      // Refresh to-dos list to show the new one (you will create this function later)
14      readTodos();
15      return true;
16    } catch (error: any) {
17      // Error can be caused by lack of Internet connection
18      alert(`Error! ${error.message}`);
19      return false;
20    };
21  };
```
:::

Notice that if your database does not have a Todo table (or subclass) in it yet, Parse will create it automatically and also add any columns set inside the Parse.Object instance using the Parse.Object.set() method, which takes two arguments: the field name and the value to be set.

## 2 - Reading data objects

After creating some data in your database, your application can now be able to read it from the server and show it to your user. Go ahead and create a readTodos function, which will perform a Parse.Query, storing the result inside a state variable.

:::CodeblockTabs
JavaScript

```javascript
1   const readTodos = async function () {
2     // Reading parse objects is done by using Parse.Query
3     const parseQuery = new Parse.Query('Todo');
4     try {
5       let todos = await parseQuery.find();
6       // Be aware that empty or invalid queries return as an empty array
7       // Set results to state variable
8       setReadResults(todos);
9       return true;
10    } catch (error) {
11      // Error can be caused by lack of Internet connection
12      alert(`Error! ${error.message}`);
13      return false;
14    };
15  };
```

```typescript
1   const readTodos = async function (): Promise<boolean> {
2     // Reading parse objects is done by using Parse.Query
3     const parseQuery: Parse.Query = new Parse.Query('Todo');
4     try {
5       let todos: Parse.Object[] = await parseQuery.find();
6       // Be aware that empty or invalid queries return as an empty array
7       // Set results to state variable
8       setReadResults(todos);
9       return true;
10    } catch (error: any) {
11      // Error can be caused by lack of Internet connection
12      alert(`Error! ${error.message}`);
13      return false;
14    };
15  };
```
:::

Many constraints and orderings can be applied to your queries using the Parse.Query class, but for now, we will stick to this simple query, which will retrieve every saved Todo object.

## 3 - Updating data objects

Updating a Parse.Object instance is very similar to creating a new one, except that in this case, you need to assign the previously created objectId to it and then save, after setting your new values.

:::CodeblockTabs
JavaScript

```javascript
1   const updateTodo = async function (todoId, done) {
2     // Create a new Todo parse object instance and set todo id
3     let Todo = new Parse.Object('Todo');
4     Todo.set('objectId', todoId);
5     // Set new done value and save Parse Object changes
6     Todo.set('done', done);
7     try {
8       await Todo.save();
9       // Success
10      alert('Success! To-do updated!');
11      // Refresh to-dos list
12      readTodos();
13      return true;
14    } catch (error) {
15      // Error can be caused by lack of Internet connection
16      alert(`Error! ${error.message}`);
17      return false;
18    };
19  };
```

```typescript
1   const updateTodo = async function (
2     todoId: string,
3     done: boolean,
4   ): Promise<boolean> {
5     // Create a new Todo parse object instance and set todo id
6     let Todo: Parse.Object = new Parse.Object('Todo');
7     Todo.set('objectId', todoId);
8     // Set new done value and save Parse Object changes
9     Todo.set('done', done);
10    try {
11      await Todo.save();
12      // Success
13      alert('Success! To-do updated!');
14      // Refresh to-dos list
15      readTodos();
16      return true;
17    } catch (error: any) {
18      // Error can be caused by lack of Internet connection
19      alert(`Error! ${error.message}`);
20      return false;
21    };
22  };
```
:::

Since this example app represents a to-do list, your update function takes an additional argument, the done value, which will represent if the specific task is completed or not.

## 4 - Deleting data objects

To delete a data object, you need to call the .destroy() method in the Parse.Object instance representing it. Please be careful because this operation is not reversible.

:::CodeblockTabs
JavaScript

```javascript
1	const deleteTodo = async function (todoId) {
2	  // Create a new Todo parse object instance and set todo id
3	  const Todo = new Parse.Object('Todo');
4	  Todo.set('objectId', todoId);
5	  // .destroy should be called to delete a parse object
6	  try {
7	    await Todo.destroy();
8	    alert('Success! To-do deleted!');
9	    // Refresh to-dos list to remove this one
10	    readTodos();
11	    return true;
12	  } catch (error) {
13	    // Error can be caused by lack of Internet connection
14	    alert(`Error ${error.message}`);
15	    return false;
16	  };
17	};
```

```typescript
1	const deleteTodo = async function (todoId: string): Promise<boolean> {
2	  // Create a new Todo parse object instance and set todo id
3	  let Todo: Parse.Object = new Parse.Object('Todo');
4	  Todo.set('objectId', todoId);
5	  // .destroy should be called to delete a parse object
6	  try {
7	    await Todo.destroy();
8	    alert('Success! To-do deleted!');
9	    // Refresh to-dos list to remove this one
10	    readTodos();
11	    return true;
12	  } catch (error: any) {
13	    // Error can be caused by lack of Internet connection
14	    alert(`Error! ${error.message}`);
15	    return false;
16	  };
17	};
```
:::

Let’s now use these four functions in a complete component, so you can test it and make sure that every CRUD operation is working properly.

## 5 - Using CRUD in a React component

Here is the complete component code, including styled user interface elements (using Ant Design), state variables, and calls to your CRUD functions. You should create a separate component in a file called TodoList.js/TodoList.tsx in your src directory containing the following code or add it directly to your main application file (App.js/App.tsx).

:::CodeblockTabs
TodoList.js

```javascript
1   import React, { useState } from 'react';
2   import Parse from 'parse/dist/parse.min.js';
3   import './App.css';
4   import { Button, Input, List } from 'antd';
5   import {
6     CheckOutlined,
7     CloseOutlined,
8     PlusOutlined,
9     RedoOutlined,
10  } from '@ant-design/icons';
11
12  export const TodoList = () => {
13    // State variables
14    const [readResults, setReadResults] = useState([]);
15    const [newTodoTitle, setNewTodoTitle] = useState('');
16
17    // Functions used by the screen components
18    const createTodo = async function () {
19      // This value comes from a state variable
20      const newTodoTitleValue = newTodoTitle;
21      // Creates a new Todo parse object instance
22      let Todo = new Parse.Object('Todo');
23      Todo.set('title', newTodoTitleValue);
24      Todo.set('done', false);
25      // After setting the to-do values, save it on the server
26      try {
27        await Todo.save();
28        // Success
29        alert('Success! To-do created!');
30        // Refresh to-dos list to show the new one (you will create this function later)
31        readTodos();
32        return true;
33      } catch (error) {
34        // Error can be caused by lack of Internet connection
35        alert(`Error! ${error.message}`);
36        return false;
37      }
38    };
39
40    const readTodos = async function () {
41      // Reading parse objects is done by using Parse.Query
42      const parseQuery = new Parse.Query('Todo');
43      try {
44        let todos = await parseQuery.find();
45        // Be aware that empty or invalid queries return as an empty array
46        // Set results to state variable
47        setReadResults(todos);
48        return true;
49      } catch (error) {
50        // Error can be caused by lack of Internet connection
51        alert(`Error! ${error.message}`);
52        return false;
53      }
54    };
55
56    const updateTodo = async function (todoId, done) {
57      // Create a new to-do parse object instance and set todo id
58      let Todo = new Parse.Object('Todo');
59      Todo.set('objectId', todoId);
60      // Set new done value and save Parse Object changes
61      Todo.set('done', done);
62      try {
63        await Todo.save();
64        // Success
65        alert('Success! To-do updated!');
66        // Refresh todos list
67        readTodos();
68        return true;
69      } catch (error) {
70        // Error can be caused by lack of Internet connection
71        alert(`Error! ${error.message}`);
72        return false;
73      }
74    };
75
76    const deleteTodo = async function (todoId) {
77      // Create a new Todo parse object instance and set todo id
78      let Todo = new Parse.Object('Todo');
79      Todo.set('objectId', todoId);
80      // .destroy should be called to delete a parse object
81      try {
82        await Todo.destroy();
83        alert('Success! To-do deleted!');
84        // Refresh to-dos list to remove this one
85        readTodos();
86        return true;
87      } catch (error) {
88        // Error can be caused by lack of Internet connection
89        alert(`Error! ${error.message}`);
90        return false;
91      }
92    };
93
94    return (
95      <div>
96        <div className="header">
97          <img
98            className="header_logo"
99            alt="Back4App Logo"
100           src={
101             'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
102           }
103         />
104         <p className="header_text_bold">{'React on Back4App'}</p>
105         <p className="header_text">{'To-do List'}</p>
106       </div>
107       <div className="container">
108         <div className="flex_between">
109           <h2 className="list_heading">Todo List</h2>
110           {/* To-do read (refresh) button */}
111           <Button
112             type="primary"
113             shape="circle"
114             color={'#208AEC'}
115             size={'default'}
116             onClick={readTodos}
117             icon={<RedoOutlined />}
118           ></Button>
119         </div>
120         <div className="new_todo_wrapper flex_between">
121           {/* Todo create text input */}
122           <Input
123             value={newTodoTitle}
124             onChange={(event) => setNewTodoTitle(event.target.value)}
125             placeholder="New Todo"
126             size="large"
127           />
128           {/* Todo create button */}
129           <Button
130             type="primary"
131             className="create_todo_button"
132             color={'#208AEC'}
133             size={'large'}
134             onClick={createTodo}
135             icon={<PlusOutlined />}
136           >
137             Add
138           </Button>
139         </div>
140         <div>
141           {/* Todo read results list */}
142           {readResults !== null &&
143             readResults !== undefined &&
144             readResults.length > 0 && (
145               <List
146                 dataSource={readResults}
147                 renderItem={(item) => (
148                   <List.Item className="todo_item">
149                     <p
150                       className={
151                         item.get('done') === true
152                           ? 'todo_text_done'
153                           : 'todo_text'
154                       }
155                     >
156                       {item.get('title')}
157                     </p>
158                     <div className="flex_row">
159                       {/* Todo update button */}
160                       {item.get('done') !== true && (
161                         <Button
162                           type="primary"
163                           shape="circle"
164                           className="todo_button"
165                           onClick={() => updateTodo(item.id, true)}
166                           icon={
167                             <CheckOutlined className="todo_button_icon_done" />
168                           }
169                         ></Button>
170                       )}
171                       {/* Todo delete button */}
172                       <Button
173                         type="primary"
174                         shape="circle"
175                         className="todo_button"
176                         onClick={() => deleteTodo(item.id)}
177                         icon={
178                           <CloseOutlined className="todo_button_icon_remove" />
179                         }
180                       ></Button>
181                     </div>
182                   </List.Item>
183                 )}
184               />
185             )}
186         </div>
187       </div>
188     </div>
189   );
190 };
```

TodoList.tsx

```typescript
1   import React, { useState, FC, ReactElement } from 'react';
2   import './App.css';
3   import { Button, Input, List } from 'antd';
4   import {
5     CheckOutlined,
6     CloseOutlined,
7     PlusOutlined,
8     RedoOutlined,
9   } from '@ant-design/icons';
10  const Parse = require('parse/dist/parse.min.js');
11
12  export const TodoList: FC<{}> = (): ReactElement => {
13    // State variables
14    const initialReadResults: Parse.Object[] = [];
15    const [readResults, setReadResults] = useState(initialReadResults);
16    const [newTodoTitle, setNewTodoTitle] = useState('');
17
18    // Functions used by the screen components
19    const createTodo = async function (): Promise<boolean> {
20      // This value comes from a state variable
21      const newTodoTitleValue: string = newTodoTitle;
22      // Creates a new Todo parse object instance
23      let Todo: Parse.Object = new Parse.Object('Todo');
24      Todo.set('title', newTodoTitleValue);
25      Todo.set('done', false);
26      // After setting the to-do values, save it on the server
27      try {
28        await Todo.save();
29        // Success
30        alert('Success! To-do created!');
31        // Refresh to-dos list to show the new one (you will create this function later)
32        readTodos();
33        return true;
34      } catch (error: any) {
35        // Error can be caused by lack of Internet connection
36        alert('Error!' + error.message);
37        return false;
38      }
39    };
40
41    const readTodos = async function (): Promise<boolean> {
42      // Reading parse objects is done by using Parse.Query
43      const parseQuery: Parse.Query = new Parse.Query('Todo');
44      try {
45        let todos: Parse.Object[] = await parseQuery.find();
46        // Be aware that empty or invalid queries return as an empty array
47        // Set results to state variable
48        setReadResults(todos);
49        return true;
50      } catch (error: any) {
51        // Error can be caused by lack of Internet connection
52        alert('Error!' + error.message);
53        return false;
54      }
55    };
56
57    const updateTodo = async function (todoId: string, done: boolean): Promise<boolean> {
58      // Create a new to-do parse object instance and set todo id
59      let Todo: Parse.Object = new Parse.Object('Todo');
60      Todo.set('objectId', todoId);
61      // Set new done value and save Parse Object changes
62      Todo.set('done', done);
63      try {
64        await Todo.save();
65        // Success
66        alert('Success! To-do updated!');
67        // Refresh todos list
68        readTodos();
69        return true;
70      } catch (error: any) {
71        // Error can be caused by lack of Internet connection
72        alert('Error!' + error.message);
73        return false;
74      }
75    };
76
77    const deleteTodo = async function (todoId: string): Promise<boolean> {
78      // Create a new Todo parse object instance and set todo id
79      let Todo: Parse.Object = new Parse.Object('Todo');
80      Todo.set('objectId', todoId);
81      // .destroy should be called to delete a parse object
82      try {
83        await Todo.destroy();
84        alert('Success! To-do deleted!');
85        // Refresh to-dos list to remove this one
86        readTodos();
87        return true;
88      } catch (error: any) {
89        // Error can be caused by lack of Internet connection
90        alert('Error!' + error.message);
91        return false;
92      }
93    };
94
95    return (
96      <div>
97        <div className="header">
98          <img
99            className="header_logo"
100           alt="Back4App Logo"
101           src={
102             'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
103           }
104         />
105         <p className="header_text_bold">{'React on Back4App'}</p>
106         <p className="header_text">{'To-do List'}</p>
107       </div>
108       <div className="container">
109         <div className="flex_between">
110           <h2 className="list_heading">Todo List</h2>
111           {/* To-do read (refresh) button */}
112           <Button
113             type="primary"
114             shape="circle"
115             color={'#208AEC'}
116             onClick={readTodos}
117             icon={<RedoOutlined />}
118           ></Button>
119         </div>
120         <div className="new_todo_wrapper flex_between">
121           {/* Todo create text input */}
122           <Input
123             value={newTodoTitle}
124             onChange={(event: {target: {value: string}}) => setNewTodoTitle(event.target.value)}
125             placeholder="New Todo"
126             size="large"
127           />
128           {/* Todo create button */}
129           <Button
130             type="primary"
131             className="create_todo_button"
132             color={'#208AEC'}
133             size={'large'}
134             onClick={createTodo}
135             icon={<PlusOutlined />}
136           >
137             Add
138           </Button>
139         </div>
140         <div>
141           {/* Todo read results list */}
142           {readResults !== null &&
143             readResults !== undefined &&
144             readResults.length > 0 && (
145               <List
146                 dataSource={readResults}
147                 renderItem={(item: Parse.Object) => (
148                   <List.Item className="todo_item">
149                     <p
150                       className={
151                         item.get('done') === true
152                           ? 'todo_text_done'
153                           : 'todo_text'
154                       }
155                     >
156                       {item.get('title')}
157                     </p>
158                     <div className="flex_row">
159                       {/* Todo update button */}
160                       {item.get('done') !== true && (
161                         <Button
162                           type="primary"
163                           shape="circle"
164                           className="todo_button"
165                           onClick={() => updateTodo(item.id, true)}
166                           icon={
167                             <CheckOutlined className="todo_button_icon_done" />
168                           }
169                         ></Button>
170                       )}
171                       {/* Todo delete button */}
172                       <Button
173                         type="primary"
174                         shape="circle"
175                         className="todo_button"
176                         onClick={() => deleteTodo(item.id)}
177                         icon={
178                           <CloseOutlined className="todo_button_icon_remove" />
179                         }
180                       ></Button>
181                     </div>
182                   </List.Item>
183                 )}
184               />
185             )}
186         </div>
187       </div>
188     </div>
189   );
190 };
```
:::

Also, add these CSS styles at the end of your App.css file:

```css
1   /* ... */
2   /* Your other styles */
3
4   /* Back4App Guide Styles */
5
6   html {
7	   box-sizing: border-box;
8	   outline: none;
9	   overflow: auto;
10  }
11
12  *,
13  *:before,
14  *:after {
15	  margin: 0;
16	  padding: 0;
17	  box-sizing: inherit;
18  }
19
20  h1,
21  h2,
22  h3,
23  h4,
24  h5,
25  h6 {
26    margin: 0;
27  }
28
29  p {
30    margin: 0;
31  }
32
33  body {
34	  margin: 0;
35	  background-color: #fff;
36  }
37
38  .container {
39    width: 100%;
40    max-width: 600px;
41    margin: auto;
42    padding: 20px 0;
43  }
44
45  .wrapper {
46    width: '90%';
47    align-self: 'center';
48  }
49
50  .header {
51    align-items: center;
52    padding: 25px 0;
53    background-color: #208AEC;
54  }
55
56  .header_logo {
57    height: 55px;
58    margin-bottom: 20px;
59    object-fit: contain;
60  }
61
62  .header_text_bold {
63    margin-bottom: 3px;
64    color: rgba(255, 255, 255, 0.9);
65    font-size: 16px;
66    font-weight: bold;
67  }
68
69  .header_text {
70    color: rgba(255, 255, 255, 0.9);
71    font-size: 15px;
72  }
73
74  .flex_row {
75    display: flex;
76  }
77
78  .flex_between {
79    display: flex;
80    align-items: center;
81    justify-content: space-between;
82  }
83
84  .list_heading {
85    font-weight: bold;
86  }
87
88  .new_todo_wrapper {
89    margin-top: 20px;
90    margin-bottom: 10px;
91  }
92
93  .new_todo_wrapper > input {
94    margin-right: 20px;
95  }
96
97  .todo_item {
98    border-bottom-width: 1;
99    border-bottom-color: 'rgba(0, 0, 0, 0.12)';
100  }
101
102  .todo_text {
103    font-size: 15px;
104  }
105
106  .todo_text_done {
107    color: rgba(0, 0, 0, 0.3);
108    font-size: 15px;
109    text-decoration-line: line-through;
110  }
111
112  .todo_button {
113    width: 32px;
114    height: 32px;
115    margin-left: 5px;
116    background-color: transparent;
117    border-radius: 50px;
118    border: none;
119    cursor: pointer;
120  }
121
122  .todo_button:hover,
123  .todo_button:focus {
124    background-color: rgba(0, 0, 0, 0.1);
125  }
126
127  .todo_button_icon_done {
128    color: #52c41a;
129    font-size: 16px;
130  }
131
132  .todo_button_icon_remove {
133    color: #f5222d;
134    font-size: 16px;
135  }
```

If your component is properly set up, you should see something like this after running the app:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/D0I4xfjjvDNq4inZAbUcd_image.png)

Go ahead and add some to-dos by typing its titles in the input box one at a time and pressing on the Add button. Note that after every successful creation, the createTodo function triggers the readTodos one, refreshing your task list automatically. You should now have a sizeable to-do list like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vEEcPYfh_NX3Y6eEhsNzP_image.png)

You can now mark your tasks as done by clicking in the checkmark beside it, causing their done value to be updated to true and changing its icon status on the left.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/mrjtEdu2yMPdDh7Xh8c8t_image.png)

The only remaining data operation is now the delete one, which can be done by pressing on the trash can icon at the far right of your to-do list object. After successfully deleting an object, you should get an alert message like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qs_4jFok4k9SHdbIkNehG_image.png)

## Conclusion

At the end of this guide, you learned how to perform basic data operations (CRUD) in Parse on React. In the next guide, we will show you which data types are supported in Parse and how to use them.

[title] Query Users
[path] Flutter/Parse SDK (REST)/User Authentication/Authentication/

# Querying users in Parse on Flutter

## Introduction

Some applications need to directly manage users or be able to view a list of them. Parse has query tools and they can be used to list the users of your application.

In this guide, you will learn how to use ParseQuery to perform user queries in your Flutter application using the **Flutter plugin for Parse Server**.

## Goal

To build a user querying feature using Parse for a Flutter App.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## Understanding the Query Users App

To better understand the Query Users process, we will create an app to query. We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse.

Following the next steps, you will build a Todo App that will store the tasks at Back4App Database.

## Let’s get started!

Following the next steps you will be able to build a Sign App that will create user Account in Back4App Database.

## 1 - Create Query Users App Template

Open your Flutter project from the previous guide **Flutter plugin for Parse Server**. Go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'dart:async';
2
3   import 'package:flutter/material.dart';
4   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
5
6   void main() async {
7     WidgetsFlutterBinding.ensureInitialized();
8  
9     final keyApplicationId = 'YOUR_APP_ID_HERE';
10    final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
11    final keyParseServerUrl = 'https://parseapi.back4app.com';
12
13    await Parse().initialize(keyApplicationId, keyParseServerUrl,
14        clientKey: keyClientKey, debug: true);
15  
16    runApp(MaterialApp(
17      home: Home(),
18    ));
19  }
20
21  class Home extends StatefulWidget {
22    @override
23    _HomeState createState() => _HomeState();
24  }
25 
26  class _HomeState extends State<Home> {
27    final _scaffoldKey = GlobalKey<ScaffoldState>();
28  
29    @override
30    Widget build(BuildContext context) {
31      return Scaffold(
32          appBar: AppBar(
33            title: Text("Parse Query Users"),
34            backgroundColor: Colors.blueAccent,
35            centerTitle: true,
36          ),
37          key: _scaffoldKey,
38          body: FutureBuilder<List<ParseObject>>(
39              future: doUserQuery(),
40              builder: (context, snapshot) {
41                switch (snapshot.connectionState) {
42                  case ConnectionState.none:
43                  case ConnectionState.waiting:
44                    return Center(
45                      child: Container(
46                          width: 100,
47                          height: 100,
48                          child: CircularProgressIndicator()),
49                    );
50                  default:
51                    if (snapshot.hasError) {
52                      return Center(
53                        child: Text("Error...: ${snapshot.error.toString()}"),
54                      );
55                    } else {
56                      if (snapshot.data!.isEmpty) {
57                        return Center(
58                          child: Text('None user found'),
59                        );
60                      }
61  
62                      return ListView.builder(
63                          padding: EdgeInsets.only(top: 10.0),
64                          itemCount: snapshot.data!.length,
65                          itemBuilder: (context, index) {
66                            final user = snapshot.data![index] as ParseUser;
67                            final userVerified = user.emailVerified ?? false;
68                            return ListTile(
69                              title: Text(
70                                  'Username: ${user.username} - Verified: ${userVerified.toString()}'),
71                              subtitle: Text(user.createdAt.toString()),
72                            );
73                          });
74                    }
75                }
76              }));
77    }
78
79    Future<List<ParseObject>> doUserQuery() async {
80      return [];
81    }
82  }
83
```

:::hint{type="info"}
When debug parameter in function Parse().initialize is true, allows displaying Parse API calls on the console. This configuration can assist in debugging the code. It is advisable to disable debug in the release version.
:::

## 2 - Connect Template to Back4app Project

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = App Id**
- **keyClientKey = Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-9q1hAy87h1LFCBqdVRdx_image.png" signedSrc size="50" width="322" height="638" position="center" caption}

## 3 - Code for Query Users

Any Parse query operation uses the ParseQuery object type, which will help you retrieve specific data from your database throughout your app.

A ParseQuery will only resolve after calling a retrieve method, so you can set up a query and chain its several modifiers before submitting the retrieve method.

To create a new ParseQuery, you need to pass as a parameter the desired ParseObject subclass, which is the one that will contain your query results.

You can see a user query example below. Using the code provided, find the doUserQuery function in the file main.dart. Replace the code inside doUserQuery with:

```dart
1       QueryBuilder<ParseUser> queryUsers =
2           QueryBuilder<ParseUser>(ParseUser.forQuery());
3       final ParseResponse apiResponse = await queryUsers.query();
4   
5       if (apiResponse.success && apiResponse.results != null) {
6         return apiResponse.results as List<ParseObject>;
7       } else {
8         return [];
9       }
```

To build this function, follow these steps:

1. Create an instance ofParseQuery class e pass as a parameter to the ParseUser.forQuery
2. Call thequery function that will execute the query against the database.
3. If the operations succeed, will return a list ofParseUser objects. If the operation does not find any objects, the success property will be false, and the results are null.

The complete code should look like this:

```dart
1     Future<List<ParseObject>> doUserQuery() async {
2       QueryBuilder<ParseUser> queryUsers =
3           QueryBuilder<ParseUser>(ParseUser.forQuery());
4       final ParseResponse apiResponse = await queryUsers.query();
5
6       if (apiResponse.success && apiResponse.results != null) {
7         return apiResponse.results as List<ParseObject>;
8       } else {
9         return [];
10      }
11    }
```

You can also try to retrieve a single user using the following structure:

>   user?.get("username");

:::hint{type="info"}
To test it, click on the Run button in Android Studio/VSCode.
:::

After performing this query, your user list on your app should be showing something like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CJFg4YCROokh3Wye9z4LN_image.png" signedSrc size="50" width="322" height="638" position="center" caption}

## It’s done!

At the end of this guide, you learned how to perform queries on Parse users on Flutter.

[title] Users
[path] ReactJS/


[title] Chat App Example
[path] ReactJS/Real Time/

# React Chat App - Real Time

## Introduction

In the last guide, you got to know more about the @parse/react helper library that quickly enables Parse Live Query support on your React application. The lib is written entirely in Typescript, on top of <a href="https://www.npmjs.com/package/parse" target="_blank">Parse Javascript SDK</a>, and is currently on the Alpha version.

Now, in this guide, you will use the Parse React hook in a realistic situation creating a simplistic live chat application. This app is composed of two React components that highlight the usefulness of Parse’s Live Query and also show everything you need to know to create your complete live app.

:::hint{type="danger"}
Parse React Native is currently on the Alpha version. The lib is under testing, so we recommend proceeding with caution. Your feedback is very appreciated, so feel free to use the lib and send us your questions and first impressions by dropping an email to community\@back4app.com.
:::

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:


- A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- Complete the previous guide so you can have a better understanding of <a href="https://www.back4app.com/docs/react/ral-time/react-real-time-hoook" target="_blank">the Parse React hook and Live Query</a>.
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library</a>.
:::

## Goal

To build a live chat application on React using @parse/react hook as an enabler for Live Query on Parse.

## 1 - Understanding and creating the database classes

The chat application will be composed of two small database classes: Nickname and Message. Nickname only has a text field called name and will represent the users in the application. Message will hold any text message sent between two users, so it needs to have a text field called text and two object pointer fields called sender and receiver, both related to the Nickname class.

Run the following snippet on your Back4App dashboard’s Javascript console to create these classes and populate them with some samples.

```javascript
1	// Create Nicknames
2	let NicknameA = new Parse.Object('Nickname');
3	NicknameA.set('name', 'SmartBoy22');
4	NicknameA = await NicknameA.save();
5	let NicknameB = new Parse.Object('Nickname');
6	NicknameB.set('name', 'CleverGirl23');
7	NicknameB = await NicknameB.save();
8	
9	// Create Message linked to Nicknames
10	let Message = new Parse.Object('Message');
11	Message.set('text', 'Hi! How are you?');
12	Message.set('sender', NicknameA);
13	Message.set('receiver', NicknameA);
14	Message = await Message.save();
15	console.log('success');
```

## 2 - Enabling Live Query

Now that you have created the Nickname and Message classes, we need to enable them with live query capabilities. Go to your Back4App dashboard and navigate to App Settings > Server Settings > Server URL and Live Query. After activating your Back4App subdomain, you can then activate Live Query and select which DB classes will be enabled to it. Make sure to select the new classes and save the changes.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/t7sc31kFzneS0eVyTZLU6_image.png)

The next thing to do is to create our chat app, which consists of two components, ChatSetup and LiveChat.

## 3 - Creating the Chat Setup component

This component is responsible for creating and setting up the sender and receiver Nickname objects while serving also as a container for the LiveChat component structure. The layout for the setup part has only two input fields for the nicknames and a button that triggers the setup function. Create a new file in your src directory called ChatSetup.js (or ChatSetup.tsx) and use the following code:

:::CodeblockTabs
ChatSetup.js

```javascript
1	import React, { useState } from "react";
2	import "./App.css";
3	import { Button, Input } from "antd";
4	import Parse from "parse";
5	import { LiveChat } from "./LiveChat";
6	
7	export const ChatSetup = () => {
8	  // State variables holding input values and results
9	  const [senderNicknameInput, setSenderNicknameInput] = useState("");
10	  const [senderNicknameId, setSenderNicknameId] = useState(null);
11	  const [receiverNicknameInput, setReceiverNicknameInput] = useState("");
12	  const [receiverNicknameId, setReceiverNicknameId] = useState(null);
13	
14	  // Create or retrieve Nickname objects and start LiveChat component
15	  const startLiveChat = async () => {
16	    const senderNicknameName = senderNicknameInput;
17	    const receiverNicknameName = receiverNicknameInput;
18	
19	    // Check if user informed both nicknames
20	    if (senderNicknameName === null || receiverNicknameName === null) {
21	      alert("Please inform both sender and receiver nicknames!");
22	      return false;
23	    }
24	
25	    // Check if sender nickname already exists, if not create new parse object
26	    let senderNicknameObject = null;
27	    try {
28	      const senderParseQuery = new Parse.Query("Nickname");
29	      senderParseQuery.equalTo("name", senderNicknameName);
30	      const senderParseQueryResult = await senderParseQuery.first();
31	      if (
32	        senderParseQueryResult !== undefined &&
33	        senderParseQueryResult !== null
34	      ) {
35	        senderNicknameObject = senderParseQueryResult;
36	      } else {
37	        senderNicknameObject = new Parse.Object("Nickname");
38	        senderNicknameObject.set("name", senderNicknameName);
39	        senderNicknameObject = await senderNicknameObject.save();
40	      }
41	    } catch (error) {
42	      alert(error);
43	      return false;
44	    }
45	
46	    // Check if receiver nickname already exists, if not create new parse object
47	    let receiverNicknameObject = null;
48	    try {
49	      const receiverParseQuery = new Parse.Query("Nickname");
50	      receiverParseQuery.equalTo("name", receiverNicknameName);
51	      const receiverParseQueryResult = await receiverParseQuery.first();
52	      if (
53	        receiverParseQueryResult !== undefined &&
54	        receiverParseQueryResult !== null
55	      ) {
56	        receiverNicknameObject = receiverParseQueryResult;
57	      } else {
58	        receiverNicknameObject = new Parse.Object("Nickname");
59	        receiverNicknameObject.set("name", receiverNicknameName);
60	        receiverNicknameObject = await receiverNicknameObject.save();
61	      }
62	    } catch (error) {
63	      alert(error);
64	      return false;
65	    }
66	
67	    // Set nickname objects ids, so live chat component is instantiated
68	    setSenderNicknameId(senderNicknameObject.id);
69	    setReceiverNicknameId(receiverNicknameObject.id);
70	    return true;
71	  };
72	
73	  return (
74	    <div>
75	      <div className="header">
76	        <img
77	          className="header_logo"
78	          alt="Back4App Logo"
79	          src={
80	            "https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png"
81	          }
82	        />
83	        <p className="header_text_bold">{"React on Back4App"}</p>
84	        <p className="header_text">{"Live query chat app"}</p>
85	      </div>
86	      <div className="container">
87	        {senderNicknameId === null && receiverNicknameId === null && (
88	          <div>
89	            <Input
90	              className="form_input"
91	              value={senderNicknameInput}
92	              onChange={(event) => setSenderNicknameInput(event.target.value)}
93	              placeholder={"Sender (Your) Nickname"}
94	              size="large"
95	            />
96	            <Input
97	              className="form_input"
98	              value={receiverNicknameInput}
99	              onChange={(event) => setReceiverNicknameInput(event.target.value)}
100	              placeholder={"Receiver (Their) Nickname"}
101	              size="large"
102	            />
103	            <Button
104	              type="primary"
105	              className="form_button"
106	              color={"#208AEC"}
107	              size={"large"}
108	              onClick={startLiveChat}
109	            >
110	              Start live chat
111	            </Button>
112	          </div>
113	        )}
114	        {senderNicknameId !== null && receiverNicknameId !== null && (
115	          <LiveChat
116	            senderNicknameName={senderNicknameInput}
117	            senderNicknameId={senderNicknameId}
118	            receiverNicknameName={receiverNicknameInput}
119	            receiverNicknameId={receiverNicknameId}
120	          />
121	        )}
122	      </div>
123	    </div>
124	  );
125	};
```

ChatSetup.tsx

```typescript
1	import React, { useState, FC, ReactElement } from "react";
2	import "./App.css";
3	import { Button, Input } from "antd";
4	import { LiveChat } from "./LiveChat";
5	import Parse from "parse";
6	
7	export const ChatSetup: FC<{}> = (): ReactElement => {
8	  // State variables holding input values and results
9	  const [senderNicknameInput, setSenderNicknameInput] = useState("");
10	  const [senderNicknameId, setSenderNicknameId] = useState<string | null>(null);
11	  const [receiverNicknameInput, setReceiverNicknameInput] = useState("");
12	  const [receiverNicknameId, setReceiverNicknameId] = useState<string | null>(null);
13	
14	  // Create or retrieve Nickname objects and start LiveChat component
15	  const startLiveChat = async (): Promise<Boolean> => {
16	    const senderNicknameName: string = senderNicknameInput;
17	    const receiverNicknameName: string = receiverNicknameInput;
18	
19	    // Check if user informed both nicknames
20	    if (senderNicknameName === "" || receiverNicknameName === "") {
21	      alert("Please inform both sender and receiver nicknames!");
22	      return false;
23	    }
24	
25	    // Check if sender nickname already exists, if not create new parse object
26	    let senderNicknameObject: Parse.Object | null = null;
27	    try {
28	      const senderParseQuery: Parse.Query = new Parse.Query("Nickname");
29	      senderParseQuery.equalTo("name", senderNicknameName);
30	      const senderParseQueryResult: Parse.Object | undefined = await senderParseQuery.first();
31	      if (
32	        senderParseQueryResult !== undefined
33	      ) {
34	        senderNicknameObject = senderParseQueryResult;
35	      } else {
36	        senderNicknameObject = new Parse.Object("Nickname");
37	        if (senderNicknameObject !== null) {
38	          senderNicknameObject.set("name", senderNicknameName);
39	          senderNicknameObject = await senderNicknameObject.save();
40	        }
41	      }
42	    } catch (error) {
43	      alert(error);
44	      return false;
45	    }
46	
47	    // Check if receiver nickname already exists, if not create new parse object
48	    let receiverNicknameObject: Parse.Object | null = null;
49	    try {
50	      const receiverParseQuery: Parse.Query = new Parse.Query("Nickname");
51	      receiverParseQuery.equalTo("name", receiverNicknameName);
52	      const receiverParseQueryResult: Parse.Object | undefined = await receiverParseQuery.first();
53	      if (
54	        receiverParseQueryResult !== undefined
55	      ) {
56	        receiverNicknameObject = receiverParseQueryResult;
57	      } else {
58	        receiverNicknameObject = new Parse.Object("Nickname");
59	        if (receiverNicknameObject !== null) {
60	          receiverNicknameObject.set("name", receiverNicknameName);
61	          receiverNicknameObject = await receiverNicknameObject.save();
62	        }
63	      }
64	    } catch (error: any) {
65	      alert(error);
66	      return false;
67	    }
68	
69	    // Set nickname objects ids, so live chat component is instantiated
70	    if (senderNicknameObject !== null && receiverNicknameObject !== null) {
71	      setSenderNicknameId(senderNicknameObject.id);
72	      setReceiverNicknameId(receiverNicknameObject.id);
73	    }
74	    return true;
75	  };
76	
77	  return (
78	    <div>
79	      <div className="header">
80	        <img
81	          className="header_logo"
82	          alt="Back4App Logo"
83	          src={
84	            "https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png"
85	          }
86	        />
87	        <p className="header_text_bold">{"React on Back4App"}</p>
88	        <p className="header_text">{"Live query chat app"}</p>
89	      </div>
90	      <div className="container">
91	        {senderNicknameId === null && receiverNicknameId === null && (
92	          <div>
93	            <Input
94	              className="form_input"
95	              value={senderNicknameInput}
96	              onChange={(event) => setSenderNicknameInput(event.target.value)}
97	              placeholder={"Sender (Your) Nickname"}
98	              size="large"
99	            />
100	            <Input
101	              className="form_input"
102	              value={receiverNicknameInput}
103	              onChange={(event) => setReceiverNicknameInput(event.target.value)}
104	              placeholder={"Receiver (Their) Nickname"}
105	              size="large"
106	            />
107	            <Button
108	              type="primary"
109	              className="form_button"
110	              color={"#208AEC"}
111	              size={"large"}
112	              onClick={startLiveChat}
113	            >
114	              Start live chat
115	            </Button>
116	          </div>
117	        )}
118	        {senderNicknameId !== null && receiverNicknameId !== null && (
119	          <LiveChat
120	            senderNicknameName={senderNicknameInput}
121	            senderNicknameId={senderNicknameId}
122	            receiverNicknameName={receiverNicknameInput}
123	            receiverNicknameId={receiverNicknameId}
124	          />
125	        )}
126	      </div>
127	    </div>
128	  );
129	};
```
:::

Note that the LiveChat component is only initialized and rendered when the setup process is successful and all the state variables are properly set. Likewise, the setup inputs are hidden after the process and the child component layout is rendered.

## 4 - Creating the Live Chat component

The LiveChat component handles the exhibition and sending of the Messages between the two Nicknames passed as parameters on its initialization. It’s in this component that you will finally use the useParseQuery hook from @parse/react to set up the Live Query that will retrieve any Message object related to this chat instance. Create a new file in your src directory called LiveChat.js (or LiveChat.tsx) and insert the following code.

:::CodeblockTabs
LiveChat.js

```javascript
1	import React, { useState } from "react";
2	import "./App.css";
3	import { Button, Input, Tooltip } from "antd";
4	import { SyncOutlined } from "@ant-design/icons";
5	import Parse from "parse";
6	import { useParseQuery } from "@parse/react";
7	
8	export const LiveChat = (props) => {
9	  // State variable to hold message text input
10	  const [messageInput, setMessageInput] = useState("");
11	
12	  // Create parse query for live querying using useParseQuery hook
13	  const parseQuery = new Parse.Query("Message");
14	  // Get messages that involve both nicknames
15	  parseQuery.containedIn("sender", [
16	    props.senderNicknameId,
17	    props.receiverNicknameId,
18	  ]);
19	  parseQuery.containedIn("receiver", [
20	    props.senderNicknameId,
21	    props.receiverNicknameId,
22	  ]);
23	  // Set results ordering
24	  parseQuery.ascending("createdAt");
25	
26	  // Include nickname fields, to enable name getting on list
27	  parseQuery.includeAll();
28	
29	  // Declare hook and variables to hold hook responses
30	  const { isLive, isLoading, isSyncing, results, count, error, reload } =
31	    useParseQuery(parseQuery, {
32	      enableLocalDatastore: true, // Enables cache in local datastore (default: true)
33	      enableLiveQuery: true, // Enables live query for real-time update (default: true)
34	    });
35	
36	  // Message sender handler
37	  const sendMessage = async () => {
38	    try {
39	      const messageText = messageInput;
40	
41	      // Get sender and receiver nickname Parse objects
42	      const senderNicknameObjectQuery = new Parse.Query("Nickname");
43	      senderNicknameObjectQuery.equalTo("objectId", props.senderNicknameId);
44	      let senderNicknameObject = await senderNicknameObjectQuery.first();
45	      const receiverNicknameObjectQuery = new Parse.Query("Nickname");
46	      receiverNicknameObjectQuery.equalTo("objectId", props.receiverNicknameId);
47	      let receiverNicknameObject = await receiverNicknameObjectQuery.first();
48	
49	      // Create new Message object and save it
50	      let Message = new Parse.Object("Message");
51	      Message.set("text", messageText);
52	      Message.set("sender", senderNicknameObject);
53	      Message.set("receiver", receiverNicknameObject);
54	      Message.save();
55	
56	      // Clear input
57	      setMessageInput("");
58	    } catch (error) {
59	      alert(error);
60	    }
61	  };
62	
63	  // Helper to format createdAt value on Message
64	  const formatDateToTime = (date) => {
65	    return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
66	  };
67	
68	  return (
69	    <div>
70	      <div className="flex_between">
71	        <h2 class="list_heading">{`${props.senderNicknameName} sending, ${props.receiverNicknameName} receiving!`}</h2>
72	        <Tooltip title="Reload">
73	          <Button
74	            onClick={reload}
75	            type="primary"
76	            shape="circle"
77	            icon={<SyncOutlined />}
78	          />
79	        </Tooltip>
80	      </div>
81	      {results && (
82	        <div className="messages">
83	          {results
84	            .sort((a, b) => a.get("createdAt") > b.get("createdAt"))
85	            .map((result) => (
86	              <div
87	                key={result.id}
88	                className={
89	                  result.get("sender").id === props.senderNicknameId
90	                    ? "message_sent"
91	                    : "message_received"
92	                }
93	              >
94	                <p className="message_bubble">{result.get("text")}</p>
95	                <p className="message_time">
96	                  {formatDateToTime(result.get("createdAt"))}
97	                </p>
98	                <p className="message_name">
99	                  {result.get("sender").get("name")}
100	                </p>
101	              </div>
102	            ))}
103	        </div>
104	      )}
105	      <div className="new_message">
106	        <h2 className="new_message_title">New message</h2>
107	        <Input
108	          className="form_input"
109	          value={messageInput}
110	          onChange={(event) => setMessageInput(event.target.value)}
111	          placeholder={"Your message..."}
112	          size="large"
113	        />
114	        <Button
115	          type="primary"
116	          className="form_button"
117	          color={"#208AEC"}
118	          size={"large"}
119	          onClick={sendMessage}
120	        >
121	          Send message
122	        </Button>
123	      </div>
124	      <div>
125	        {isLoading && <p>{"Loading…"}</p>}
126	        {isSyncing && <p>{"Syncing…"}</p>}
127	        {isLive ? <p>{"Status: Live"}</p> : <p>{"Status: Offline"}</p>}
128	        {error && <p>{error.message}</p>}
129	        {count && <p>{`Count: ${count}`}</p>}
130	      </div>
131	    </div>
132	  );
133	};
```

LiveChat.tsx

```typescript
1	import React, { useState, FC, ReactElement } from "react";
2	import "./App.css";
3	import { Button, Input, Tooltip } from "antd";
4	import { SyncOutlined } from "@ant-design/icons";
5	import Parse from "parse";
6	import { useParseQuery } from  "@parse/react";
7	
8	type LiveChatProps = {
9	  senderNicknameId: string,
10	  senderNicknameName: string,
11	  receiverNicknameId: string,
12	  receiverNicknameName: string,
13	}
14	
15	export const LiveChat: FC<LiveChatProps> = (props: LiveChatProps): ReactElement => {
16	  const Parse = require("parse/dist/parse.min.js");
17	    // State variable to hold message text input
18	  const [messageInput, setMessageInput] = useState("");
19	
20	  // Create parse query for live querying using useParseQuery hook
21	  const parseQuery: Parse.Query = new Parse.Query("Message");
22	  // Get messages that involve both nicknames
23	  parseQuery.containedIn("sender", [
24	    props.senderNicknameId,
25	    props.receiverNicknameId,
26	  ]);
27	  parseQuery.containedIn("receiver", [
28	    props.senderNicknameId,
29	    props.receiverNicknameId,
30	  ]);
31	  // Set results ordering
32	  parseQuery.ascending("createdAt");
33	
34	  // Include nickname fields, to enable name getting on list
35	  parseQuery.includeAll();
36	
37	  // Declare hook and variables to hold hook responses
38	  const { isLive, isLoading, isSyncing, results, count, error, reload } =
39	    useParseQuery(parseQuery, {
40	      enableLocalDatastore: true, // Enables cache in local datastore (default: true)
41	      enableLiveQuery: true, // Enables live query for real-time update (default: true)
42	    });
43	
44	  // Message sender handler
45	  const sendMessage = async () => {
46	    try {
47	      const messageText: string = messageInput;
48	
49	      // Get sender and receiver nickname Parse objects
50	      const senderNicknameObjectQuery: Parse.Query = new Parse.Query("Nickname");
51	      senderNicknameObjectQuery.equalTo("objectId", props.senderNicknameId);
52	      let senderNicknameObject: Parse.Object | undefined = await senderNicknameObjectQuery.first();
53	      const receiverNicknameObjectQuery: Parse.Query = new Parse.Query("Nickname");
54	      receiverNicknameObjectQuery.equalTo("objectId", props.receiverNicknameId);
55	      let receiverNicknameObject: Parse.Object | undefined = await receiverNicknameObjectQuery.first();
56	
57	      // Create new Message object and save it
58	      let Message: Parse.Object = new Parse.Object("Message");
59	      Message.set("text", messageText);
60	      Message.set("sender", senderNicknameObject);
61	      Message.set("receiver", receiverNicknameObject);
62	      Message.save();
63	
64	      // Clear input
65	      setMessageInput("");
66	    } catch (error: any) {
67	      alert(error);
68	    }
69	  };
70	
71	  // Helper to format createdAt value on Message
72	  const formatDateToTime = (date: Date): string => {
73	    return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
74	  };
75	
76	  return (
77	    <div>
78	      <div className="flex_between">
79	        <h2 class="list_heading">{`${props.senderNicknameName} sending, ${props.receiverNicknameName} receiving!`}</h2>
80	        <Tooltip title="Reload">
81	          <Button
82	            onClick={reload}
83	            type="primary"
84	            shape="circle"
85	            icon={<SyncOutlined />}
86	          />
87	        </Tooltip>
88	      </div>
89	      {results && (
90	        <div className="messages">
91	          {results
92	            .sort((a, b) => a.get("createdAt") > b.get("createdAt"))
93	            .map((result) => (
94	              <div
95	                key={result.id}
96	                className={
97	                  result.get("sender").id === props.senderNicknameId
98	                    ? "message_sent"
99	                    : "message_received"
100	                }
101	              >
102	                <p className="message_bubble">{result.get("text")}</p>
103	                <p className="message_time">
104	                  {formatDateToTime(result.get("createdAt"))}
105	                </p>
106	                <p className="message_name">
107	                  {result.get("sender").get("name")}
108	                </p>
109	              </div>
110	            ))}
111	        </div>
112	      )}
113	      <div className="new_message">
114	        <h2 className="new_message_title">New message</h2>
115	        <Input
116	          className="form_input"
117	          value={messageInput}
118	          onChange={(event) => setMessageInput(event.target.value)}
119	          placeholder={"Your message..."}
120	          size="large"
121	        />
122	        <Button
123	          type="primary"
124	          className="form_button"
125	          color={"#208AEC"}
126	          size={"large"}
127	          onClick={sendMessage}
128	        >
129	          Send message
130	        </Button>
131	      </div>
132	      <div>
133	        {isLoading && <p>{"Loading…"}</p>}
134	        {isSyncing && <p>{"Syncing…"}</p>}
135	        {isLive ? <p>{"Status: Live"}</p> : <p>{"Status: Offline"}</p>}
136	        {error && <p>{error.message}</p>}
137	        {count && <p>{`Count: ${count}`}</p>}
138	      </div>
139	    </div>
140	  );
141	};
```
:::

Let’s break down this component structure into four parts, so you can better understand its layout:


- At the top we have the message’s Parse.Query and Parse React hook setup. Here you can see how the props parameters are used to enable the query to retrieve the messages that we want;
- After that, you have the sendMessage function, which will create a new Message object relating it to the Nickname used in this chat instance. There is also a helper function for formatting the messages date value;
- Now, inside the JSX code, we have the status flags that are related to the Parse React hook variables and also the connection reload button;
- Lastly, you can see the Message list in which the rendered list items style is dictated by its sender value. At the bottom, we have the message sending part, with a simple text input and a button.

Finally, add these classes to your App.css file if you want to fully render the components layout, and let’s proceed to test our app.

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	html {
4	  box-sizing: border-box;
5	  outline: none;
6	  overflow: auto;
7	}
8	
9	body {
10	  margin: 0;
11	  background-color: #fff;
12	}
13	
14	*,
15	*:before,
16	*:after {
17	  margin: 0;
18	  padding: 0;
19	  box-sizing: inherit;
20	}
21	
22	h1,
23	h2,
24	h3,
25	h4,
26	h5,
27	h6 {
28	  margin: 0;
29	  font-weight: bold;
30	}
31	
32	li {
33	  list-style: none;
34	}
35	
36	p {
37	  margin: 0;
38	}
39	
40	.flex_between {
41	  display: flex;
42	  align-items: center;
43	  justify-content: space-between;
44	}
45	
46	.list_heading {
47	  font-weight: bold;
48	}
49	
50	.App {
51	  text-align: center;
52	}
53	
54	.container {
55	  width: 100%;
56	  max-width: 500px;
57	  margin: auto;
58	  padding: 20px 0;
59	  text-align: left;
60	}
61	
62	.header {
63	  align-items: center;
64	  padding: 25px 0;
65	  background-color: #208AEC;
66	}
67	
68	.header_logo {
69	  height: 55px;
70	  margin-bottom: 20px;
71	  object-fit: contain;
72	}
73	
74	.header_text_bold {
75	  margin-bottom: 3px;
76	  color: rgba(255, 255, 255, 0.9);
77	  font-size: 16px;
78	  font-weight: bold;
79	}
80	
81	.header_text {
82	  color: rgba(255, 255, 255, 0.9);
83	  font-size: 15px;
84	}
85	
86	.heading {
87	  font-size: 22px;
88	}
89	
90	.form_wrapper {
91	  margin-top: 20px;
92	  margin-bottom: 10px;
93	}
94	
95	.form_input {
96	  margin-bottom: 20px;
97	}
98	
99	.form_button {
100	  width: 100%;
101	}
102	
103	.messages {
104	  margin-top: 25px;
105	}
106	
107	.message_sent {
108	  position: relative;
109	  width: 50%;
110	  margin-left: auto;
111	}
112	
113	.message_received {
114	  position: relative;
115	  width: 50%;
116	}
117	
118	.message_bubble {
119	  padding: 12px;
120	  border-radius: 25px;
121	  background-color: rgba(0, 0, 0, 0.2);
122	}
123	
124	.message_sent .message_bubble {
125	  background-color: #1E88E5;
126	  color: #fff;
127	}
128	
129	.message_time {
130	  position: absolute;
131	  top: 35%;
132	  left: -62px;
133	  font-size: 13px;
134	  color: rgba(0, 0, 0, 0.35);
135	  transform: translateY(-50%);
136	}
137	
138	.message_sent .message_time {
139	  left: initial;
140	  right: -62px;
141	}
142	
143	.message_name {
144	  margin-top: 5px;
145	  color: rgba(0, 0, 0, 0.55);
146	  font-size: 13px;
147	  font-weight: 600;
148	}
149	
150	.message_sent .message_name {
151	  text-align: right;
152	}
153	
154	.new_message {
155	  padding-top: 15px;
156	  margin-top: 20px;
157	  margin-bottom: 10px;
158	  border-top: 1px solid rgba(0, 0, 0, 0.12);
159	}
160	
161	.new_message_title {
162	  margin-bottom: 15px;
163	}
```
:::

## 5 - Testing the chat application

Go ahead and test the live chat app by declaring and calling the ChatSetup component on your App.js (or App.tsx) JSX code. Here is an example of how you could do that:

:::CodeblockTabs
App.js or App.tsx

```javascript
1	import React from "react";
2	import "./App.css";
3	import { initializeParse } from "@parse/react";
4	import { ChatSetup } from "./ChatSetup";
5	
6	// Your Parse initialization configuration goes here
7	// Note the live query URL instead of the regular server url
8	const PARSE_APPLICATION_ID = "YOUR_PARSE_APPLICATION_ID";
9	// const PARSE_SERVER_URL = "https://parseapi.back4app.com/";
10	const PARSE_LIVE_QUERY_URL = "https://YOUR_APP_NAME.b4a.io/";
11	const PARSE_JAVASCRIPT_KEY = "YOUR_PARSE_JAVASCRIPT_KEY";
12	
13	// Initialize parse using @parse/react instead of regular parse JS SDK
14	initializeParse(
15	  PARSE_LIVE_QUERY_URL,
16	  PARSE_APPLICATION_ID,
17	  PARSE_JAVASCRIPT_KEY
18	);
19	
20	function App() {
21	  return (
22	    <div className="App">
23	      <ChatSetup />
24	    </div>
25	  );
26	}
27	
28	export default App;
```
:::

Start your app by running yarn start on your console. You should now be presented with the following screen, in which you need to inform the sending and receiving nicknames to begin chatting.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/hNWQU-JPUktv9kZSmyXBX_image.png" signedSrc size="80" width="658" height="500" position="center" caption}

To better see how the app and live query are working, open the same app on two different browser windows and set them side by side. Immediately after sending a message in a window, you should see it pop on the other if the nicknames match and the connection is live.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Mm1yxJC_FolmPtYx-kGkB_image.png)

## Conclusion

At the end of this guide, you learned how to use the Parse React hook for live queries in Parse in a realistic application example.

[title] via Dashboard
[path] Android/Push Notifications/

# Parse Server push notifications setup

## Introduction

This section explains how you can send push notifications using Firebase Cloud Messaging and Parse Dashboard through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qgiN9Mi67yrzjVKNjEgE5_push-dashboard.gif" signedSrc size="50" width="296" height="580" position="center" caption}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.0 (Ice Cream Sandwich) or newer.
:::

## **1 - Link your Firebase Project with your Android Studio Project**

To send push notifications through your Dashboard, you will have to create a Project at <a href="https://firebase.google.com/" target="_blank">Firebase Website</a> and link it to your Android Studio Project. To do so, follow the steps described below:

:::hint{type="danger"}
Pay attention to the steps below because you are not going to follow exactly the same steps that Firebase suggests.
:::

1. Go to <a href="https://firebase.google.com/" target="_blank">Firebase Website </a>and log in with a Google Account.
2. At Firebase Website, in the right corner click on GO TO CONSOLE and click on Add Project, then give your Project a name follow the steps to create a new project.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/I3KkZ46BVYCT6f5UOYDnt_screenshot-2024-05-28-at-145828.png)



&#x20;    3\. Then, connect your Android Studio Project to the Firebase Project you created. To do so, click on the Android icon, as shown in the following image.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/odAIcQRq04N36D_9J7mJr_screenshot-2024-05-28-at-150040.png)

&#x20;    4\. You will be asked to inform the package name of your Android Studio Project, as shown in the following image.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZKZpUf_WbXD_-YHI7CF4X_screenshot-2024-05-28-at-150111.png)

&#x20;    5\. To discover the package name of your Android Studio Project, leave the Firebase page opened and go to your Project in Android Studio and go to app > manifest > AndroidManifest.xml. In your manifest file you will be able to find the package name of your project, as you can see in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/m-lB5DO7DC2sTHUwgYaQz_image.png)

&#x20;    6\. Copy the package name in the required box at the Firebase page. You can also fill the other fields, but they are optional. After that, click on the Register app button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/1A7UC2lK1q11Na87mvzyp_screenshot-2024-05-28-at-150207.png)

7\. Now, you have to download google-services.json file and move it to your Android Studio project module root directory.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wqNQs8uAMWr_HWf3U3RDi_screenshot-2024-05-28-at-150237.png)

> 1    classpath 'com.google.gms:google-services:latest.version.here'

&#x20;    9\. After that, go to the build.gradle (Module\:app) file and, on the top of the file, add the code below.

> 1    apply plugin: 'com.google.gms.google-services'

&#x20;   10\. Continue on the build.gradle (Module\:app)\` file and add these lines of code

> *1   // Don't forget to change the line below with the latest versions of Firebase SDKs*
>
>
> 2     implementation 'com.google.firebase:firebase-core:latest.version.here'
> 3     implementation 'com.google.firebase:firebase-messaging:latest.version.here'

:::hint{type="danger"}
Don’t forget to change these lines with the latest versions of Firebase SDKs.
:::

## 2 - Link your Firebase Project with Back4App

To link your Firebase Project with Back4App and easily send push notification through your Dashboard, simply follow these steps:

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on Server Settings.
2. Find the “Android Push notification” block and click on SETTINGS > EDIT. The “Android Push notification” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WtdLo5lTTQjr61nyGZnps_image.png)

&#x20;    3\. Leave the *Back4App Android Push Notification *page you visited opened and go to your project on the <a href="https://firebase.google.com/" target="_blank">Firebase Website.</a>

&#x20;    4\. Click on the settings icon and then the Project Settings button, as shown below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rt8h-mSbpZzytrp05JixG_screenshot-2024-05-28-at-150316.png)

5\. Click on CLOUD MESSAGING and then on `Manage Service Accounts`.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vEgYzTj2KGfZ5YGi1-D_Z_screenshot-2024-05-28-at-150402.png)

&#x20;    6\. Click on `Manage details` (under Actions).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/u-2oRp7O1LzRYd5O9TUrE_screenshot-2024-05-28-at-150721.png)

&#x20;    7\. Go to `Keys` > `ADD KEY` > `Create new key`.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/mCdi_aNLu47qzDfncy_J6_screenshot-2024-05-28-at-150842.png)

&#x20;    8\. Choose the JSON Format and create.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0nTbeaA_qfR2sYpNobZp8_screenshot-2024-05-28-at-151008.png)

&#x20;    9\. To set up your Service Account Configuration, click on the `Set Up Push Settings` button

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uMbwX3vw5KoDzzfqtcsOV_screenshot-2024-05-28-at-151227.png)

&#x20;    10\. To finish the configuration, click on the Choose File button and select the JSON file you got from Firebase and NEXT.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/19Dpf38IqJtWvGvUHsAEv_screenshot-2024-05-28-at-151547.png)



## 3 - Set up the Manifest File

1. Open your Project at Android Studio and go to app > manifest > AndroidManifest.xml. In this file, use the code below right after the meta-data tags that are inside the application tag:

```xml
1   <meta-data android:name="com.parse.push.gcm_sender_id"
2              android:value="INSERT_YOUR_SENDER_ID" />
```

:::hint{type="danger"}
Don’t forget to insert theGCM Sender IDyou obtained at Firebase in this line of code.
:::

&#x20;    2\. Use the following code right before the application tag ends:

:::CodeblockTabs
AndroidX

```xml
1   <service android:name="com.parse.fcm.ParseFirebaseMessagingService" android:exported="false">
2           <intent-filter>
3               <action android:name="com.google.firebase.MESSAGING_EVENT"/>
4           </intent-filter>
5       </service>
6
7       <receiver android:name="com.parse.ParsePushBroadcastReceiver" android:exported="false">
8          <intent-filter>
9              <action android:name="com.parse.push.intent.RECEIVE" />
10             <action android:name="com.parse.push.intent.OPEN" />
11             <action android:name="com.parse.push.intent.DELETE" />
12         </intent-filter>
13      </receiver>
```

Android

```xml
1   <service android:name="com.parse.fcm.ParseFirebaseInstanceIdService" android:exported="false">
2         <intent-filter>
3         <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
4         </intent-filter>
5      </service>
6
7      <service
8          android:name="com.parse.fcm.ParseFirebaseMessagingService" android:exported="false">
9          <intent-filter>
10              <action android:name="com.google.firebase.MESSAGING_EVENT"/>
11          </intent-filter>
12      </service>
13
14      <receiver android:name="com.parse.ParsePushBroadcastReceiver" android:exported="false">
15         <intent-filter>
16             <action android:name="com.parse.push.intent.RECEIVE" />
17             <action android:name="com.parse.push.intent.OPEN" />
18             <action android:name="com.parse.push.intent.DELETE" />
19         </intent-filter>
20      </receiver>    
```
:::

&#x20;Use the following permissions right after theuses-permission tags that you placed to allow your app to have access to internet.

```xml
1   <uses-permission android:name="android.permission.WAKE_LOCK" />
2   <uses-permission android:name="android.permission.VIBRATE" />
3   <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
4   <uses-permission android:name="android.permission.GET_ACCOUNTS" />
5   <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
```

:::hint{type="info"}
You added permissions to allow internet access in the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK Tutorial</a> instructions. If you didn’t, access Install Parse SDK Tutorial and follow its steps.
:::

## 4 - Set up build.gradle (Module: app)

Install the Parse FCM SDK and the Parse Bolts SDK for Android. To do so, open build.gradle (Module: app) and add the code below in the dependecies\{} tag.

> *1    // Don't forget to change the lines belows with the latest versions these SDKs*
>
>
> 2    implementation "com.github.parse-community.Parse-SDK-Android:fcm:latest.version.here"
> 3    implementation 'com.parse.bolts:bolts-android:latest.version.here'

:::hint{type="danger"}
Don’t forget to change these lines with the latest versions of these SDKs.
:::

:::hint{type="danger"}
If you are not using AndroidX, you cannot use the latest version. <a href="https://github.com/parse-community/Parse-SDK-Android/blob/master/CHANGELOG.md" target="_blank">Check the changelog</a>
:::

## 5 - Create an installation

Every Parse application installed on a device registered for push notifications has an associated Installation object that stores all the data needed to target push notifications.

In Android, Installation objects are available through the ParseInstallation class. This class uses the same API for storing and retrieving data. To access the current Installation object from your Android app, use the ParseInstallation.getCurrentInstallation() method.

In the first time you save a ParseInstallation, Parse will add it to your Installation class and it will be available for targeting push notifications.

To create a ParseInstallation in your app, go to your Android Studio Project and in the Java file called App that extends Application that you created to initialize the Parse SDK, on its onCreate method, right after Parse.initialize() call, use the following code to create a ParseInstallation.

> **1    ParseInstallation**
>
>  installation 
>
> **=**
>
>  
>
> **ParseInstallation.**
>
> getCurrentInstallation
>
> **();**
>
>
> 2    installation
>
> **.**
>
> put
>
> **(**
>
> "GCMSenderId"
>
> **,**
>
>  INSERT_YOUR_SENDER_ID
>
> **);**
>
>
> 3    installation
>
> **.**
>
> saveInBackground
>
> **();**

:::hint{type="danger"}
Don’t forget to insert theGCM Sender IDyou obtained at Firebase in the code above.
:::

:::hint{type="danger"}
If you don’t have an App.java file as described in this step, access the [Install Parse SDK for Android](https://www.back4app.com/docs/android/parse-android-sdk) documentation and make sure that you have followed all the steps required to install Parse SDK correctly. If you do not install Parse SDK properly your facebook login with Parse will not work.
:::

## 6 - Test your app

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on Dashboard.

2. Click on  > Push > Send New Push and create an audience for your push notification.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9T7-Hr7t1iKy0CEKSUBVC_screenshot-2024-05-28-at-152236.png)

&#x20;    3\. Write your message and look at the preview by clicking at the Android option.

&#x20;    4\. If you already reviewed the push notification and you want to send it, click onSend push.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nYqE52F-L0uOrmNsZnuzc_screenshot-2024-05-28-at-152459.png)

:::hint{type="info"}
You may explore the other options for Push Notification at Parse Dashboard.
There, it’s also possible to look at Past Pushes you sent and the Audiences you created for them.
:::

## It’s done!

At this stage, you can send push notifications using the Parse Dashboard through Back4App!

[title] Mutations
[path] Flutter/GraphQL/

# Flutter CRUD app example using GraphQL



## Introduction

On our first Flutter-GraphQL guide, we’ve learned how to set up a Flutter project using GraphQL API to connect and query simple data on Back4App. Also, how we can use the Back4App GraphQL Playground to run queries/mutations to create/populate the App Database.

In this tutorial, we will add a feature with which we will directly create, update, and delete data from our Back4App backend directly from our App using GraphQL mutations.

## Goals

At the end of this article we expect that you will be able to:

- Create data in our Backend using GraphQL API.
- Update data from our Backend using GraphQL API.
- Delete existing data from Backend using GraphQL API.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kPiedmPCadGXn4Vz8Kz3Q_flutter-createdata.gif" signedSrc size="50" width="270" height="480" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- You’ll need to read our previous guide <a href="https://www.back4app.com/docs/flutter/graphql/flutter-graphql-project-with-source-code-download" target="_blank">“Flutter GraphQL Project”</a> in order to understand our starting point at this guide.
- We’re going to use the previous project if you don’t have it you can <a href="https://github.com/templates-back4app/Flutter-GraphQL" target="_blank">download here</a>.
- An IDE for writing Flutter code like Android Studio or VS code.
- A Back4App account that can be <a href="https://www.back4app.com/" target="_blank">created here</a>.
- [Back4App GraphQL\_flutter dependency](https://pub.dev/packages/graphql_flutter)
:::

:::hint{type="success"}
NOTE: If you want a better understanding of the Back4App GraphQL API take a look at our [GraphQL Cook Book](https://www.back4app.com/docs/parse-graphql/graphql-getting-started) and also check our GraphQL Schema on our API Playground.
:::

## 1 - Setting up GUI

Create a new file mutation\_page.dart. Here we will create our GUI for taking input from the user and perform Mutation tasks. Now paste the following code in mutation\_page.dart.

```dart
1   import 'package:back4appgraphqldemo/app_ui.dart';
2   import 'package:back4appgraphqldemo/database_utils.dart';
3   import 'package:flutter/material.dart';
4
5   class MutationPage extends StatelessWidget {
6
7     String langName,saveFormat,objectId;
8     DatabaseUtils utils;
9
10    @override
11    Widget build(BuildContext context) {
12      return AppUI(
13        onChangedName: (text){
14        langName=text;
15        },
16        onChangedSaveFormat: (text){
17                saveFormat=text;
18          },
19        onChangedObjectId: (text){
20        objectId=text;
21        },
22        sendDataButtonPressed: (){
23        },
24        deleteDataButtonPressed: (){
25        },
26        updateButtonPressed: (){
27        },
28      );
29    }
30  }
```

AppUI() widget has already been made for you. So we have created a class named MutationPage and returnded the AppUI widget as its widget. We have also initialsed callback functions for our Text Fields such that Text in first textField will be stored in langName, second one in saveFormat and objectId from the last. Now proceed to main.dart and add a floatingActionButton parameter in Scaffold() widget of MyHomePage class and pass the following code into it:

```dart
1   floatingActionButton: Row(
2             mainAxisAlignment: MainAxisAlignment.end,
3             children: [
4               FloatingActionButton(
5                 heroTag: 'mutation_page',
6                 child: Text('M',
7                 style: TextStyle(
8                   color: Colors.white,
9                 ),
10                ),
11                onPressed: (){
12                  Navigator.pushReplacement(context, MaterialPageRoute(
13                    builder: ((context){
14                      return MutationPage();
15                    })
16                  ));
17                },
18              ),
19            ],
20          ),
```

This will create a Floating Button that will Navigate us to the MutationPage() from our Home page. So now our GUI is set. You can now Hot Restart your App to see the GUI. This is how your main.dart should look like:

```dart
1   import 'package:back4appgraphqldemo/mutation_page.dart';
2   import 'package:flutter/material.dart';
3   import 'package:graphql_flutter/graphql_flutter.dart';
4   import 'consonents.dart';
5   import 'dart:ui';
6
7   void main() {
8     runApp(MyApp());
9   }
10
11  class MyApp extends StatelessWidget {
12    @override
13    Widget build(BuildContext context) {
14      final HttpLink httpLink = HttpLink(
15        uri: 'https://parseapi.back4app.com/graphql',
16        headers: {
17          'X-Parse-Application-Id': kParseApplicationId,
18          'X-Parse-Client-Key': kParseClientKey,
19          'X-Parse-Master-Key': kParseMasterKey,
20          //'X-Parse-REST-API-Key' : kParseRestApiKey,
21        }, //getheaders()
22      );
23
24      ValueNotifier<GraphQLClient> client = ValueNotifier(
25        GraphQLClient(
26          cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
27          link: httpLink,
28        ),
29      );
30  
31      return MaterialApp(
32        home: GraphQLProvider(
33          child: MyHomePage(),
34          client: client,
35        ),
36      );
37    }
38  }
39
40  class MyHomePage extends StatefulWidget {
41    @override
42    _MyHomePageState createState() => _MyHomePageState();
43  }
44
45  class _MyHomePageState extends State<MyHomePage> {
46    String name;
47    String saveFormat;
48    String objectId;
49  
50    String query = '''
51    query FindLanguages{
52    languages{
53      count,
54      edges{
55        node{
56          name,
57          saveFormat
58        }
59      }
60    }
61  }
62    ''';
63  
64    @override
65    Widget build(BuildContext context) {
66      return SafeArea(
67        child: Scaffold(
68          appBar: AppBar(
69            title: Text(
70              'Parsing data using GraphQL',
71            ),
72          ),
73          floatingActionButton: Row(
74            mainAxisAlignment: MainAxisAlignment.end,
75            children: [
76              FloatingActionButton(
77                heroTag: 'mutation_page',
78                child: Text('M',
79                style: TextStyle(
80                  color: Colors.white,
81                ),
82                ),
83                onPressed: (){
84                  Navigator.pushReplacement(context, MaterialPageRoute(
85                    builder: ((context){
86                      return MutationPage();
87                    })
88                  ));
89                },
90              ),
91            ],
92          ),
93          body: Query(
94            options: QueryOptions(
95              documentNode: gql(query),
96            ),
97            builder: (
98              QueryResult result, {
99              Refetch refetch,
100             FetchMore fetchMore,
101           }) {
102             if (result.data == null) {
103               return Center(
104                   child: Text(
105                 "Loading...",
106                 style: TextStyle(fontSize: 20.0),
107               ));
108             } else {
109               return ListView.builder(
110                 itemBuilder: (BuildContext context, int index) {
111                   return ListTile(
112                     title: Text(result.data["languages"]["edges"][index]["node"]
113                         ['name']),
114                     trailing: Text(result.data["languages"]["edges"][index]
115                         ["node"]['saveFormat']),
116 
117                   );
118                 },
119                 itemCount: result.data["languages"]["edges"].length,
120               );
121             }
122           },
123         ),
124       ),
125     );
126   }
127 }
```

## 2 -  Creating/adding data to the database

If you proceed to graphql\_configration.dart you could see that we have already set our GraphQLclient and now we can use it anywhere. Let’s proceed to and create a file database\_utils.dart and perform an operation for **Creating** data. Create a class DatabaseUtils\{} and create a constructor that would receive the data parameters we would work on, here we will require langName,saveFormat, and objectId:

```dart
1   import 'package:back4appgraphqldemo/graphql_configration.dart';
2   import 'package:graphql_flutter/graphql_flutter.dart';
3
4   class DatabaseUtils{
5     final String langName,saveFormat,objectId;
6     DatabaseUtils({this.langName="",this.saveFormat="",this.objectId=""});
7   }
```

Create a functiion addData() which will be an asynchronous function to Create data and initialse our GraphQLClient by initialsing GraphQLConfigration class. Paste the following code into sendData() function:

```dart
1    Future<QueryResult> sendData() async{
2        String addData='''
3         mutation CreateObject(\$input: CreateLanguageFieldsInput){
4           createLanguage(input: {fields: \$input}){
5             language{
6               name,
7               saveFormat
8             }
9           }
10        }
11         ''';
12        final variable ={
13       "input":{
14         "name" : langName,
15         "saveFormat" : saveFormat,
16       }
17     };
18        
19      GraphQlConfiguration configuration = GraphQlConfiguration();
20     GraphQLClient client = configuration.clientToQuery();
21   
22     }
```

Here we have initialized a variable addData and have passed the query to create data and initialized variable that will pass the query Variables. We also initialized the GraphQLClient that will help us to pass queries. We can pass the queries in the manner below:

```dart
1   QueryResult queryResult = await client.query(
2        QueryOptions(documentNode: gql(addData), variables: variable),
3      );
4      return queryResult;
```

We used our GraphQLClient instance to write a query that accepts QueryOptions() that helps us to send a query as you have seen from the last tutorial. The result is stored in queryResult.
This is how your database\_utils.dart should look like:

```dart
1   import 'package:back4appgraphqldemo/graphql_configration.dart';
2   import 'package:graphql_flutter/graphql_flutter.dart';
3
4   class DatabaseUtils{
5     final String langName,saveFormat,objectId;
6     DatabaseUtils({this.langName="",this.saveFormat="",this.objectId=""});
7     Future<QueryResult> sendData() async{
8       String addData='''
9         mutation CreateObject(\$input: CreateLanguageFieldsInput){
10          createLanguage(input: {fields: \$input}){
11            language{
12              name,
13              saveFormat
14            }
15          }
16        }
17        ''';
18      final variable ={
19        "input":{
20          "name" : langName,
21          "saveFormat" : saveFormat,
22         }
23      };
24 
25      GraphQlConfiguration configuration = GraphQlConfiguration();
26      GraphQLClient client = configuration.clientToQuery();
27
28      QueryResult queryResult = await client.query(
29        QueryOptions(documentNode: gql(addData), variables: variable),
30      );
31      return queryResult;
32    }
33  }
```

Now proceed to your UI class mutation\_page.dart. Lets code for send data button which can be coded inside the sendDataButtonPressed: parameter. Since we need the langName and saveFormat first check if it is not empty and then Create an instance of DatabaseUtils and the pass the langName and saveFormat parameter.

```dart
1   if(langName.isNotEmpty && saveFormat.isNotEmpty){
2             utils = DatabaseUtils(
3               langName: langName,
4               saveFormat: saveFormat ,
5             );
6           }
```

After this call the sendData() function from the DatabaseUtils instance.

> utils
>
> **.**
>
> sendData();

Now you can **Hot Restart** app, fill the two text fields with their respective data and press the send data button. Now go back to your Query page by pressing the floating action button and you could see one more data is added to our table. This is how your MutationPage class would look like:

```dart
1   import 'package:back4appgraphqldemo/app_ui.dart';
2   import 'package:back4appgraphqldemo/database_utils.dart';
3   import 'package:flutter/material.dart';
4
5   class MutationPage extends StatelessWidget {
6 
7     String langName,saveFormat,objectId;
8     DatabaseUtils utils;
9
10    @override
11    Widget build(BuildContext context) {
12      return AppUI(
13        onChangedName: (text){
14        langName=text;
15        },
16        onChangedSaveFormat: (text){
17                saveFormat=text;
18          },
19        onChangedObjectId: (text){
20        objectId=text;
21        },
22        sendDataButtonPressed: (){
23        if(langName.isNotEmpty && saveFormat.isNotEmpty){
24            utils = DatabaseUtils(
25              langName: langName,
26              saveFormat: saveFormat ,
27            );
28            utils.sendData();
29          }
30        },
31        deleteDataButtonPressed: (){
32        },
33        updateButtonPressed: (){
34        },
35      );
36    }
37  }
```

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MR8HfB7BOEW99Nko0Jpwz_flutter-createdata-1.gif" signedSrc size="50" width="270" height="480" position="center" caption}

## 3 - Updating Data

In DatabaseUtils create a Future function updateData(). Initialise a String update and pass in the update query and *query variables* in final variables:

```dart
1    Future<QueryResult> updateData() async{
2     String update='''
3     mutation UpdateObject(\$id: ID!,\$input: UpdateLanguageFieldsInput){
4       updateLanguage(input: {id:\$id, fields:\$input}){
5         language{
6           name,
7           id
8         }
9       }
10    }
11    ''';
12     final variable={
13       "id":objectId,
14       "input":{
15         "name" : langName
16       }
17     };
18   }
```

Now initialse our GraphQLClient and send the query through QueryOptions(). This is how your code would look like:

```dart
1   Future<QueryResult> updateData() async{
2 
3      String update='''
4       mutation UpdateObject(\$id: ID!,\$input: UpdateLanguageFieldsInput){
5         updateLanguage(input: {id:\$id, fields:\$input}){
6          language{
7             name,
8             id
9           }
10        }
11      }
12      ''';
13   
14     final variable={
15       "id":objectId,
16       "input":{
17         "name" : langName
18       }
19     };
20  
21     GraphQlConfiguration configuration = GraphQlConfiguration();
22     GraphQLClient client = configuration.clientToQuery();
23  
24     QueryResult queryResult = await client.query(
25       QueryOptions(documentNode: gql(update), variables: variable),
26    );
27     return queryResult;
28   }
```

Now come back to mutaion\_page.dart and code in the updateButtonPressed: parameter. Check if langName, objectId and saveFormat are not empty and then call the updateData() funtion from DatabaseUtils class.

```dart
1    updateButtonPressed: (){
2           if(langName.isNotEmpty && saveFormat.isNotEmpty && objectId.isNotEmpty){
3             utils = DatabaseUtils(
4               langName: langName,
5               saveFormat: saveFormat ,
6               objectId: objectId
7             );
8             utils.updateData();
9           }
10        },
```

Go to Back4App Dashboard and choose a language to update, then copy its objectID. **Hot restart** your app and fill all the 3 text fields: the new **name** of the language you want to insert in first text field and the new **save Format** in second and **objectId** in third . Now press the Update data button and check the updated information on the App by clicking over the Q floating action button on bottom right.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Dhk13aMGmSxLvzbop-BuN_flutter-updatedata.gif" signedSrc size="50" width="237" height="432" position="center" caption}

## 4 - Deleting data

Create a deleteData() **async** function in DatabaseUtils and initialize String delete and pass the GraphQL query to delete data. Take final variables and pass the query variables into it.

```dart
1   Future<QueryResult> deleteData() async{
2       String delete='''
3     mutation DeleteObject(\$id: ID!){
4       deleteLanguage(input: {id:\$id}){
5         language{
6           name,
7           id
8         }
9       }
10    }
11    ''';
12     final variable={
13       "id":objectId,
14     };
15   }
```

In this, we only require the objectId of the row that needs to be deleted. Initialize the GraphQLClient and send the query through QueryOptions().

```dart
1   Future<QueryResult> deleteData() async{
2        String delete='''
3     mutation DeleteObject(\$id: ID!){
4       deleteLanguage(input: {id:\$id}){
5         language{
6           name,
7           id
8         }
9       }
10    }
11    ''';
12     final variable={
13       "id":objectId,
14     };
15
16     GraphQlConfiguration configuration = GraphQlConfiguration();
17     GraphQLClient client = configuration.clientToQuery();
18  
19     QueryResult queryResult = await client.query(
20       QueryOptions(documentNode: gql(delete), variables: variable),
21     );
22
23     return queryResult;
24   }
```

In MutationPage in the deleteDataButtonPressed: parameter check if objectId is not Empty or null and call the deleteData() function. **Hot Restart** application, enter the **objectId** of the row to be deleted and press the **Delete Data** button. It should delete a specific row from your Language class.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cFrVVPxzehrwhzb9yosaD_flutter-deletedata.gif" signedSrc size="50" width="270" height="480" position="center" caption}

***SUCCESS*** your app has finally done your mutation operations !!!

main.dart should look like this:

```dart
1   import 'package:back4appgraphqldemo/mutation_page.dart';
2   import 'package:flutter/material.dart';
3   import 'package:graphql_flutter/graphql_flutter.dart';
4   import 'consonents.dart';
5   import 'dart:ui';
6
7   void main() {
8     runApp(MyApp());
9   }
10
11  class MyApp extends StatelessWidget {
12    @override
13    Widget build(BuildContext context) {
14      final HttpLink httpLink = HttpLink(
15        uri: 'https://parseapi.back4app.com/graphql',
16        headers: {
17          'X-Parse-Application-Id': kParseApplicationId,
18          'X-Parse-Client-Key': kParseClientKey,
19          'X-Parse-Master-Key': kParseMasterKey,
20          //'X-Parse-REST-API-Key' : kParseRestApiKey,
21        }, //getheaders()
22      );
23  
24      ValueNotifier<GraphQLClient> client = ValueNotifier(
25        GraphQLClient(
26          cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
27          link: httpLink,
28        ),
29      );
30  
31      return MaterialApp(
32        home: GraphQLProvider(
33          child: MyHomePage(),
34          client: client,
35        ),
36      );
37   }
38  }
39  
40  class MyHomePage extends StatefulWidget {
41    @override
42    _MyHomePageState createState() => _MyHomePageState();
43  }
44 
45  class _MyHomePageState extends State<MyHomePage> {
46    String name;
47    String saveFormat;
48    String objectId;
49
50    String query = '''
51    query FindLanguages{
52    languages{
53      count,
54      edges{
55        node{
56          name,
57          saveFormat
58        }
59      }
60    }
61  }
62    ''';
63  
64    @override
65    Widget build(BuildContext context) {
66      return SafeArea(
67        child: Scaffold(
68          appBar: AppBar(
69            title: Text(
70              'Parsing data using GraphQL',
71            ),
72          ),
73          floatingActionButton: Row(
74            mainAxisAlignment: MainAxisAlignment.end,
75            children: [
76              FloatingActionButton(
77                heroTag: 'mutation_page',
78                child: Text('M',
79                style: TextStyle(
80                  color: Colors.white,
81                ),
82                ),
83                onPressed: (){
84                  Navigator.pushReplacement(context, MaterialPageRoute(
85                    builder: ((context){
86                      return MutationPage();
87                    })
88                  ));
89                },
90              ),
91            ],
92          ),
93          body: Query(
94            options: QueryOptions(
95              documentNode: gql(query),
96            ),
97            builder: (
98              QueryResult result, {
99              Refetch refetch,
100             FetchMore fetchMore,
101           }) {
102             if (result.data == null) {
103               return Center(
104                   child: Text(
105                 "Loading...",
106                 style: TextStyle(fontSize: 20.0),
107               ));
108             } else {
109               return ListView.builder(
110                 itemBuilder: (BuildContext context, int index) {
111                   return ListTile(
112                     title: Text(result.data["languages"]["edges"][index]["node"]
113                         ['name']),
114                     trailing: Text(result.data["languages"]["edges"][index]
115                         ["node"]['saveFormat']),
116 
117                   );
118                 },
119                 itemCount: result.data["languages"]["edges"].length,
120              );
121             }
122           },
123         ),
124       ),
125     );
126   }
127 }
```

database\_utils.dart should like this:

```dart
1   import 'package:back4appgraphqldemo/graphql_configration.dart';
2   import 'package:graphql_flutter/graphql_flutter.dart';
3
4   class DatabaseUtils{
5
6     final String langName,saveFormat,objectId;
7
8     DatabaseUtils({this.langName="",this.saveFormat="",this.objectId=""});
9
10    String delete='''
11    mutation DELETE_LANGUAGES(\$id: ID!){
12      deleteLanguage(input: {id:\$id}){
13        language{
14          name,
15          id
16        }
17      }
18    }
19    ''';
20  
21    String addData='''
22    mutation CREATE_LANGUAGES(\$input: CreateLanguageFieldsInput){
23      createLanguage(input: {fields: \$input}){
24        language{
25          name,
26          saveFormat
27        }
28      }
29    }
30    ''';
31    String update='''
32    mutation UPDATE_LANGUAGES(\$id: ID!,\$input: UpdateLanguageFieldsInput){
33      updateLanguage(input: {id:\$id, fields:\$input}){
34        language{
35          name,
36          id
37        }
38      }
39    }
40    ''';
41
42   Future<QueryResult> sendData() async{
43
44     final variable ={
45       "input":{
46         "name" : langName,
47         "saveFormat" : saveFormat,
48       }
49     };
50     print('sendingData');
51  
52      GraphQlConfiguration configuration = GraphQlConfiguration();
53     GraphQLClient client = configuration.clientToQuery();
54  
55     QueryResult queryResult = await client.query(
56       QueryOptions(documentNode: gql(addData), variables: variable),
57     );
58     return queryResult;
59  
60   }
61   Future<QueryResult> updateData() async{
62     final variable={
63       "id":objectId,
64       "input":{
65         "name" : langName
66       }
67     };
68  
69     GraphQlConfiguration configuration = GraphQlConfiguration();
70     GraphQLClient client = configuration.clientToQuery();
71  
72     QueryResult queryResult = await client.query(
73       QueryOptions(documentNode: gql(update), variables: variable),
74     );
75     return queryResult;
76   }
77
78
79   Future<QueryResult> deleteData() async{
80     final variable={
81       "id":objectId,
82     };
83  
84     GraphQlConfiguration configuration = GraphQlConfiguration();
85     GraphQLClient client = configuration.clientToQuery();
86  
87     QueryResult queryResult = await client.query(
88     QueryOptions(documentNode: gql(delete), variables: variable),
89     );
90  
91     return queryResult;
92   }
93  }
```

mutaion\_page.dart should look like this :

```dart
1   import 'package:back4appgraphqldemo/app_ui.dart';
2   import 'package:back4appgraphqldemo/database_utils.dart';
3   import 'package:flutter/material.dart';
4
5   class MutationPage extends StatelessWidget {
6
7     String langName,saveFormat,objectId;
8     DatabaseUtils utils;
9
10    @override
11    Widget build(BuildContext context) {
12      return AppUI(
13        onChangedName: (text){
14          langName=text;
15        },
16        onChangedSaveFormat: (text){
17          saveFormat=text;
18          },
19        onChangedObjectId: (text){
20          objectId=text;
21        },
22
23        sendDataButtonPressed: (){
24          if(langName.isNotEmpty && saveFormat.isNotEmpty){
25            utils = DatabaseUtils(
26              langName: langName,
27              saveFormat: saveFormat ,
28            );
29            utils.sendData();
30          }
31        },
32        deleteDataButtonPressed: (){
33          if(objectId.isNotEmpty){
34            utils = DatabaseUtils(
35              objectId: objectId,
36            );
37            utils.deleteData();
38          }
39        },
40        updateButtonPressed: (){
41          if(langName.isNotEmpty && saveFormat.isNotEmpty && objectId.isNotEmpty){
42            utils = DatabaseUtils(
43              langName: langName,
44              saveFormat: saveFormat ,
45              objectId: objectId
46            );
47            utils.updateData();
48           }
49        },
50      );
51    }
52  }
```

## Conclusion

In this guide we used GraphQL mutations in the Flutter app to:

- **create new objects on Back4App;**
- **update objects on Back4App;**
- **delete objects on Back4App.**

At this point you have a fully functional Flutter GraphQL CRUD project where you can use at starting point to develop your next App. In the next guide we are going to dive deep into queries showing how to make them to retrieve data from Back4App and show on our Flutter App.

**Have a great day!**

[title] Untitled
[path] React Native/Relay (GraphQL)/


[title] Install Parse SDK
[path] Android/

# Install Parse SDK on your Android Studio Project

## Introduction

In this section you learn how to install Parse Android SDK into your Android Studio project.

This tutorial uses a basic app created in Android Studio Arctic Fox 2020.3.1 Patch 1 with compileSdk 30 , minSdk 21 and targetSdk 30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

Learn how to install Parse-SDK on your Android project.

## Prerequisites

:::hint{type="info"}
**To complete this section, you will need:**

- An app created at Back4App.
  - **Note: **If you don’t have an app now please follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial </a>to learn how to create a Parse app at Back4App.
- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- Basic android app.
  - **Note: **If you don’t have a basic app created you can follow the <a href="https://developer.android.com/studio/projects/create-project.html" target="_blank">Create a Project tutorial</a> from Android Studio official website.
:::

:::hint{type="danger"}
Note: Parse Android SDK works with compileSdk 27 and targetSdk 27 or higher.
:::

## 1 - Install SDK

We need to implement Parse SDK to our Android project for this we will use <a href="https://www.gradle.org/" target="_blank">Gradle</a>, an open-source build automation tool that is designed to be flexible enough to build almost any type of software. Android Studio uses Gradle for build process and import external libraries like Parse SDK.

1 - In your Android Studio project, open your settings.gradle file.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5d_tVQHxlUjkVgdXG420s_image.png)

Now we will add <a href="https://jitpack.io/" target="_blank">Jitpack</a> to our Project. Jitpack is a package repository for JVM and Android projects.

2 - Now we need to add `maven {url 'https://jitpack.io'}` line to `repositories{}` tag in the settings.gradle file:

```java
1   repositories {
2      ...
3      ...
4     maven { url 'https://jitpack.io' }
5   }
```

3 - It’s also necessary to look in the `android{}` tag if your compileSdk is **27** or higher and also if your targetSdk is **27** or higher. If they aren’t, you **must change** these versions to **27** or higher, otherwise your Parse SDK for Android may not work properly. After checking this, your build.gradle (Module\:app) should look like the one in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XvKscevOVe8mNMeFFKS93_image.png)

4 - If all previous steps are set now we are able to add Parse Android SDK to our project.

> implementation "com.github.parse-community.Parse-SDK-Android\:parse"

In `dependencies{}` tag change the latest-version-here value with version of your choice. It will look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PEnrdlXhjwCx2EMPe8_-s_image.png)

:::hint{type="info"}
You can see current version of SDK in here <a href="https://jitpack.io/#parse-community/Parse-SDK-Android" target="_blank">SDK Versions.</a>
:::

5 - Now we need to sync our build.gradle to last changes to effect our project.

:::hint{type="success"}
To learn more about adding support libraries to your Android Studio Project, see the <a href="https://developer.android.com/topic/libraries/support-library/setup.html" target="_blank">Android Studio’s Support Library Setup page.</a>
:::

## 2 - Connect to Back4App

Now it is time to use Android Parse SDK in action. We need internet access and server credentials to connect to Back4App.

To use Parse SDK, our application need to have access to the internet network. To allow our app to have it, we need to grant permissions in the AndroidManifest.xml file. Also, we have to set up the app’s credentials to connect our app to Back4App. For achiving this we need to follow the steps below.

1 - Go to app > manifests > AndroidManifest.xml in your Android Studio Project.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/af4mtPasGyAzepVI6ETba_image.png)

2 - Now, before the application tag in the AndroidManifest.xml file, copy the following code snippet:

```java
1   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
2   <uses-permission android:name="android.permission.INTERNET"/>
```

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jub_rXBXhlLxrcPUn3VJ-_image.png)

3 - Inside the application section of the AndroidManifest.xml file, add the following code:

```java
1      <meta-data
2          android:name="com.parse.SERVER_URL"
3          android:value="@string/back4app_server_url" />
4     <meta-data
5          android:name="com.parse.APPLICATION_ID"
6          android:value="@string/back4app_app_id" />
7     <meta-data
8          android:name="com.parse.CLIENT_KEY"
9          android:value="@string/back4app_client_key" />
```

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7OZujVFUP87UrL-MzQRP6_image.png)

4 - Go to app > res > values > strings.xml file.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/RHmeXgu8hTiyJ00z8JYYz_image.png)

5 - In the strings.xml file, add the following code:

```xml
1       <string name="back4app_server_url">https://parseapi.back4app.com/</string>
2
3       <!-- Paste BOTH strings as required -->
4       <string name="back4app_app_id">PASTE_YOUR_APPLICATION_ID_HERE</string>
5       <string name="back4app_client_key">PASTE_YOUR_CLIENT_KEY_HERE</string>
```

6 - Leave the string.xml opened and go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.
Now you will find your keys to replace in the code. Go your Dashboard and then click on App Settings > Security & Keys.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ctca44UgoK4mWJxsTFtKs_image.png)

## 3 - Initialize Parse SDK in Our App

In this step we will complete the Parse Initialization using App Id and Client key which we have obtained at the end of Step 2.

We recommend you to write the installation codes in the App.java or App.kt files which you will create. The reason for this, to ensure that our Parse SDK initialize codes will work before any other Activity or Context, Application Context is the first that creates and last destroy.
So, create the App.javain the same folder as your MainAcitivty, and then follow the steps below:

1 - Import parse library to your App file:

```java
1  import com.parse.Parse;
```

2 - Inside App file call the following code:

:::CodeblockTabs
App.java

```java
1    public class App extends Application {
2       @Override
3       public void onCreate() {
4          super.onCreate();
5          Parse.initialize(new Parse.Configuration.Builder(this)
6                   .applicationId(getString(R.string.back4app_app_id))
7                   .clientKey(getString(R.string.back4app_client_key))
8                   .server(getString(R.string.back4app_server_url))
9                   .build());
10         }
11      }
```

App.kt

```kotlin
1    class App : Application() {
2       override fun onCreate() {
3          super.onCreate()
4          Parse.initialize(
5                Parse.Configuration.Builder(this)
6                      .applicationId(getString(R.string.back4app_app_id))
7                      .clientKey(getString(R.string.back4app_client_key))
8                      .server(getString(R.string.back4app_server_url))
9                      .build());
10      }
11   }
```
:::

Please check the image below as an example using Java:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/H1xURGFgeMWMMiNgaleqd_image.png)

We put our code to onCreate() method because we want to connect to our server first before taking any other action.

Don’t forget to define this file in the AndroidManifest.xml. For doing this, go to the AndroidManifest.xml file and add the following line of code inside the applicationtag:

> android=".App"

At the end, your AndroidManifest.xml should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/y6HicUiYpm96rJzns3Mvg_image.png)

:::hint{type="info"}
If the name of the java file that extends Application that you created on the previous step isn’t “App”, don’t forget that the code above should have the correct name of the file (android\:name=".name\_of\_the\_file").
:::

## 4 - Test your connection

To test your connection with Parse SDK, let’s save an object in the MainActivity of your Android Studio Project.

1 - Go to your Android Studio Project and add the following code to your onCreate() method in order to save your first Parse Object of the application into your Dashboard.

:::CodeblockTabs
App.java

```java
1    public class MainActivity extends AppCompatActivity {
2       @Override
3       protected void onCreate(Bundle savedInstanceState) {
4          super.onCreate(savedInstanceState);
5          setContentView(R.layout.activity_main);
6          ParseObject firstObject = new  ParseObject("FirstClass");
7          firstObject.put("message","Hey ! First message from android. Parse is now connected");
8          firstObject.saveInBackground(e -> {
9             if (e != null){
10               Log.e("MainActivity", e.getLocalizedMessage());
11               }else{
12                  Log.d("MainActivity","Object saved.");
13               }
14            });
15         }
16      }
```

App.kt

```kotlin
1    class MainActivity : AppCompatActivity() {
2     override fun onCreate(savedInstanceState: Bundle?) {
3         super.onCreate(savedInstanceState)
4         setContentView(R.layout.activity_main)
5         val firstObject = ParseObject("FirstClass")
6         firstObject.put("message","Hey ! First message from android. Parse is now connected")
7         firstObject.saveInBackground {
8             if (it != null){
9                 it.localizedMessage?.let { message -> Log.e("MainActivity", message) }
10            }else{
11                Log.d("MainActivity","Object saved.")
12            }
13        }
14    }
15   }
```
:::

2 - Launch your app and go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>. Find your app and go to its Dashboard.

3 - Now click on Database > Browser > First Class. You should see the First Class with an object, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HKOfg3OLg365GYhm6phIW_image.png)

## It’s done!

We completed the section! Now you have learned how to install the Parse SDK in your application.

:::hint{type="success"}
Learn more by walking around our <a href="https://www.back4app.com/docs/android/android-project-with-source-code-download" target="_blank">Android Tutorials.</a>
:::


[title] Geoqueries
[path] iOS/Parse Swift SDK/Data Objects/

# Geoqueries

## Introduction

We use the term **Geoqueries** to refer to the type of queries where their conditions involve ParseGeoPoint type fields. It is recommended to use the ParseGeoPoint struct to store geographic location data in a Back4App Database. The ParseSwift SDK provides a set of methods that allows us to query data according to conditions applied on ParseGeoPointdata type.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
- A basic iOS App to test queries
:::

## Goal

To understand how to query data using conditions on geographic location data.

## 1 - Quick review about the **Query\<U>** class

Any query performed on a Back4App Database is done via the generic class Query\<U>. The generic parameter U (conforming to the ParseObject protocol) is the data type of the objects we are trying to retrieve from the database.

Given a data type like MyObject, we retrieve these objects from a Back4App Database in the following way

```swift
1   import ParseSwift
2
3   struct MyObject: ParseObject {
4     ...
5   }
6
7   let query = MyObject.query()
8
9   // Executes the query asynchronously
10  query.find { result in
11    // Handle the result (of type Result<[MyObject], ParseError>)
12  }
```

You can read more about the Query\<U> class [here at the official documentation](https://github.com/parse-community/Parse-Swift).

## 2 - Save some data on Back4App

Before we begin to execute queries, we should store some sample data on a Back4App Database. By following the [Quickstart](https://www.back4app.com/docs/ios/parse-swift-sdk/install-sdk) guide, you can configure and link your sample iOS App to your Back4App database. For this guide, we will store information about cities. We use the following struct to organize a city’s information:

```swift
1   import ParseSwift
2
3   struct City: ParseObject {
4       ...
5    
6       var name: String? // City's name
7       var location: ParseGeoPoint? // It will store the city's coordinate on Earth
8   }
```

Now, we proceed to store the sample data. This step can be implemented using **Swift** or directly from your app’s [console](https://www.youtube.com/watch?v=nVWRYRZbCmA) on the Back4App platform.&#x20;

:::CodeblockTabs
```swift
//After setting up your XCode project, calling the following method should save some sample data on your Back4App Database
1   import ParseSwift
2
3   func setupSampleData() {
4     do {
5       // Montevideo - Uruguay
6       _ = try City(
7         name: "Montevideo",
8         location: ParseGeoPoint(coordinate: CLLocationCoordinate2D(latitude: -34.85553195363169, longitude: -56.207280375137955))
9       ).save()
10            
11      // Brasília - Brazil
12      _ = try City(
13        name: "Brasília",
14        location: ParseGeoPoint(coordinate: CLLocationCoordinate2D(latitude: -15.79485821477289, longitude: -47.88391074690196))
15      ).save()
16              
17      // Bogotá - Colombia
18      _ = try City(
19        name: "Bogotá",
20        location: ParseGeoPoint(coordinate: CLLocationCoordinate2D(latitude: 4.69139880891712, longitude: -74.06936691331047))
21      ).save()
22            
23      // Mexico City - Mexico
24      _ = try City(
25        name: "Mexico City",
26        location: ParseGeoPoint(coordinate: CLLocationCoordinate2D(latitude: 19.400977162618933, longitude: -99.13311378164776))
27      ).save()
28            
29      // Washington, D.C. - United States
30        _ = try City(
31          name: "Washington, D.C.",
32          location: ParseGeoPoint(coordinate: CLLocationCoordinate2D(latitude: 38.930727220189944, longitude: -77.04626261880388))
33        ).save()
34            
35      // Ottawa - Canada
36      _ = try City(
37        name: "Ottawa",
38        location: ParseGeoPoint(latitude: 45.41102167733425, longitude: -75.695414598736)
39      ).save()
40    } catch let error as ParseError {
41      print("[ParseSwift ERROR]", error.message)
42    } catch {
43      print("[ERROR]", error.localizedDescription)
44    }
45  }
```

Back4App's console

```swift
//A quick way to insert elements on your Back4App Database is via the console located in your App’s API section. Once you are there, you can start running Javascript code to save the sample data
1   // Add City objects and create table
2   // Note how GeoPoints are created, passing latitude and longitude as arguments
3   // Montevideo
4   City = new Parse.Object('City');
5   City.set('name', 'Montevideo - Uruguay');
6   City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
7   await City.save();
8
9   // Brasília
10  City = new Parse.Object('City');
11  City.set('name', 'Brasília - Brazil');
12  City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
13  await City.save();
14
15  // Bogotá
16  City = new Parse.Object('City');
17  City.set('name', 'Bogotá - Colombia');
18  City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
19  await City.save();
20
21  // Mexico City
22  City = new Parse.Object('City');
23  City.set('name', 'Mexico City - Mexico');
24  City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
25  await City.save();
26
27  // Washington, D.C.
28  City = new Parse.Object('City');
29  City.set('name', 'Washington, D.C. - USA');
30  City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
31  await City.save();
32
33  // Ottawa
34  City = new Parse.Object('City');
35  City.set('name', 'Ottawa - Canada');
36  City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
37  await City.save();
```
:::

## 3 - Query the data

**Sorting the results**

With the sample data saved, we can start performing different query types.

For our first example, we will select all the cities and sort them depending on how far they are from a reference geopoint. We implement this query by passing a constraint to the Query\<City> object. The method near(key\:geoPoint:) available via the ParseSwift SDK allows us to construct such constraint. As arguments, we pass the field’s name (usually referred to as key) containing the reference geoPoint.

```swift
1   // The reference geopoint will be Kingston city - Jamaica
2   guard let kingstonGeoPoint = try? ParseGeoPoint(latitude: 18.018086950599134, longitude: -76.79894232253473) else { return }
3
4   let query = City.query(near(key: "location", geoPoint: kingstonGeoPoint)) // The method near(key:geoPoint:) returns the constraint needed to sort the query
5
6   let sortedCities: [City]? = try? query.find() // Executes the query synchronosuly and returns an array containing the cities properly sorted
7
8   query.find { result in // Executes the query asynchronosuly and returns a Result<[City], ParseError> type object to handle the results
9     switch result {
10    case .success(let cities):
11      // cities = [Bogotá, Washington DC, Mexico City, Ottawa, Brasília, Montevideo]
12    case .failure(let error):
13      // Handle the error if something happened
14    }
15  }
```

**Selecting results within a given region**

Suppose we want to select cities within a certain region. We can achieve this with a constraint created by the method withinKilometers(key\:geoPoint\:distance:). As arguments, we pass the field’s name containing the city’s location, the region’s center (a ParseGeoPoint data type) and the maximum distance (in **km**) a city can be from this region’s center. To select all cities that are at most **3000km** away from Kingston - Jamaica, we can do it in the following way

```swift
1   let distance: Double = 3000 // km
2   guard let kingstonGeoPoint = try? ParseGeoPoint(latitude: 18.018086950599134, longitude: -76.79894232253473) else { return }
3
4   let query = City.query(withinKilometers(key: "location", geoPoint: kingstonGeoPoint, distance: distance))
5   // The method withinKilometers(key:geoPoint:distance:) returns the constraint we need for this case
6
7   let sortedCities: [City]? = try? query.find() // Executes the query synchronosuly and returns an array containing the cities we are looking for (Bogotá, Washington DC and Mexico city in this case)
8
9   query.find { result in // Executes the query asynchronosuly and returns a Result<[City], ParseError> type object to handle the results
10    switch result {
11    case .success(let cities):
12      // cities = [Bogotá, Washington DC, Mexico City]
13    case .failure(let error):
14      // Handle the error if something happened
15    }
16  }
```

Additionally, when the distance is given in miles instead of kilomenters, we can use the withinMiles(key\:geoPoint\:distance\:sorted:) method.

A less common method, withinRadians(key\:geoPoint\:distance\:sorted:), is also available if the distance is given in radians. Its use is very similar to the previous methods.

**Selecting results within a given polygon**

In the previous example, we selected cities within a region represented by a circular region. In case we require to have a non-circular shape for the region, the ParseSwift SDK does allow us to construct such regions from their vertices.

Now, the goal for this example is to select cities within a five vertex polygon. These vertices are expressed using the ParseGeoPoint struct. Once we have created the vertices, we instantiate a ParsePolygon. This polygon is then passed to the withinPolygon(key\:polygon:) method (provided by the ParseSwift SDK) to construct the constraint that will allow us to select cities within this polygon.

```swift
1   // The polygon where the selected cities are
2   let polygon: ParsePolygon? = {
3     do {
4       // We first instantiate the polygon vertices
5       let geoPoint1 = try ParseGeoPoint(latitude: 15.822238344514378, longitude: -72.42845934415942)
6       let geoPoint2 = try ParseGeoPoint(latitude: -0.7433770196268968, longitude: -97.44765968406668)
7       let geoPoint3 = try ParseGeoPoint(latitude: -59.997149373299166, longitude: -76.52969196322749)
8       let geoPoint4 = try ParseGeoPoint(latitude: -9.488786415007201, longitude: -18.346101586021952)
9       let geoPoint5 = try ParseGeoPoint(latitude: 15.414859532811047, longitude: -60.00625459569375)
10                
11      // Next we compose the polygon
12      return try ParsePolygon([geoPoint1, geoPoint2, geoPoint3, geoPoint4, geoPoint5])
13    } catch let error as ParseError {
14      print("Failed to instantiate vertices: \(error.message)")
15      return nil
16    } catch {
17      print("Failed to instantiate vertices: \(error.localizedDescription)")
18      return nil
19    }
20  }()
21        
22  guard let safePolygon = polygon else { return }
23        
24  let query = City.query(withinPolygon(key: "location", polygon: safePolygon))
25  // withinPolygon(key:polygon:) returns the required constraint to apply on the query
26        
27  let cities = try? query.find() // Executes the query synchronously
28        
29  query.find { result in // Executes the query asynchronously and returns a result of type Result<[], ParseError>
30    // Handle the result
31  }
```

## Conclusion

Nowadays doing operations on location data to offer custom services is very important. Back4App together with the ParseSwift SDK makes it easy to implement those kinds of operations.

[title] Basic Operations
[path] iOS/Parse Swift SDK/Data Objects/

# CRUD Parse objects in iOS

## Introduction

Storing data on Parse is built around the Parse.Object class. Each Parse.Object contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each Parse.Object. You can simply set whatever key-value pairs you want, and our backend will store it.

You can also specify the datatypes according to your application needs and persist types such as number, boolean,string, DateTime, list, GeoPointers, and Object, encoding them to JSON before saving. Parse also supports store and query relational data by using the types Pointers and Relations.

In this guide, you will learn how to perform basic data operations through a CRUD example app (ToDo list App), which will show you how to create, read, update and delete data from your Parse server database using the ParseSwift SDK.

This tutorial uses a basic app created in Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

## Goal

To learn how to perform basic database operations on back4app using a ToDo list app as an example

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
:::

## Understanding our To-do List App

To better understand the ParseSwift SDK you will perform CRUD operations on a To-do List App. The application database will have a simple task class with a title and a description (both strings). You can update each task’s title and/or description.

## Quick reference of commands we are going to use

Once an object conforms the ParseSwift protocol, it automatically implements a set of methods that will allow you to manage the object and update any changes on your Back4App Database. Given the object ToDoListItem

```swift
1   struct ToDoListItem: ParseObject {
2       ...
3    
4       /// Title for the todo item
5       var title: String?
6    
7       /// Description for the todo item
8       var description: String?
9   }
```

these methods are listed below.

:::CodeblockTabs
Create

```swift
  //Once created an instance of ToDoListItem object and set its custom properties, you can save it on your Back4App Database by calling any of the following methods
1   var newItem: ToDoIListtem
2   // newItem's properties
3
4   // Saves newItem on your Back4App Database synchronously and returns the new saved Item. It throws and error if something went wrong.
5   let savedItem = try? newItem.save()
6
7   // Saves newItem on your Back4App Database asynchronously, and passes a Result<ToDoListItem, ParseError> object to the completion block to handle the save proccess.
8   newItem.save { result in
9       // Handle the result to check the save was successfull or not
10  } 
11 
```

Read

```swift
//For reading objects stored on your Back4App Database, ToDoListItem now provides the query() static method which returns a Query<ToDoListItem>. This query object can be constructed using on or more QueryConstraint objects int he following way
1   let query = ToDoListItem.query() // A query to fetch all ToDoListItem items on your Back4App Database.
2   let query = ToDoListItem.query("title" == "Some title") // A query to fetch all ToDoListItem items with title "Some title" on your Back4App Database.
3   let query = ToDoListItem.query(["title" == "Some title", "description" = "Ok"]) // A query to fetch all ToDoListItem items with title = "Some title" and description = "Ok".
4
5   // Fetchs the items synchronously or throws an error if found.
6   let fetchedItems = try? query.find()
7
8   // Fetchs the items asynchronously and calls a completion block passing a result object containing the result of the operation.
9   query.find { result in
10      // Handle the result
11  }
```

Update

```swift
//Given the objectId of an object stored on you Back4App Database, you can update it in the following way
1   let itemToUpdate = ToDoListItem(objectId: "OOBJECT_ID")
2   // Update the properites of itemToUpdate
3
4   // Save changes synchronousty
5   itemToUpdate.save()
6
7   // Or save changes asynchronously
8   itemToUpdate.save { result in
9       // handle the result
10  }
```

Delete

```swift
//The deletion process is performed by calling the method delete() on the object to be deleted
1   var itemToDelete: ToDoListItem
2
3   // Delete itemToDelete synchronously
4   try? itemToDelete.delete()
5
6   // Delte itemToDelete asynchronously
7   itemToDelete.delete { result in
8       // Handleresult
9   }
```
:::

## 1 - Create To-do List App Template

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vHgxQhVfQwjNx1FCYIC30_image.png)

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

Go to Xcode, and find the SceneDelegate.swift file. In order to add a navigation bar on top of the app, we setup a UINavigationController as the root view controller in the following way

```swift
1   class SceneDelegate: UIResponder, UIWindowSceneDelegate {
2
3       var window: UIWindow?
4
5       func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
6           guard let scene = (scene as? UIWindowScene) else { return }
7        
8           window = .init(windowScene: scene)
9           window?.rootViewController = UINavigationController(rootViewController: ToDoListController())
10          window?.makeKeyAndVisible()
11
12          // Additional logic
13      }
14
15      ...
16  }
```

The root view controller class (ToDoListController) for the navigation controller is a subclass of UITableViewController, this makes easy to layout a list of items.

## 2 - Setup the CRUD object

Objects you want to save on your Back4App Database have to conform the ParseObject protocol. On our To-do Liat app this object is ToDoListItem. Therefore, you first need to create this object:

```swift
1   import Foundation
2   import ParseSwift
3
4   struct ToDoListItem: ParseObject {
5       // Required properties from ParseObject protocol
6       var objectId: String?
7       var createdAt: Date?
8       var updatedAt: Date?
9       var ACL: ParseACL?
10    
11      /// Title for the todo item
12      var title: String?
13    
14      /// Description for the todo item
15      var description: String?
16  }
```

This object defines a class in your Back4App Database. Any new instance of this object is then stored in your database under the ToDoListItem class.

## 3 - Setup ToDoListController

In ToDoListController we should implement all the necessary configuration for the navigationBar, and tableView properties

```swift
1   class ToDoListController: UITableViewController {    
2       var items: [ToDoListItem] = []
3    
4       override func viewDidLoad() {
5           super.viewDidLoad()
6        
7           setupTableView()
8           setupNavigationBar()
9       }
10    
11      private func setupNavigationBar() {
12          navigationItem.title = "To-do list".uppercased()
13          navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(handleNewItem))
14      }
15    
16      private func setupTableView() {
17          tableView.register(ToDoListItemCell.self, forCellReuseIdentifier: ToDoListItemCell.identifier)
18      }
19
20      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
21          items.count
22      }
23    
24      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
25          let cell = tableView.dequeueReusableCell(withIdentifier: ToDoListItemCell.identifier, for: indexPath) as! ToDoListItemCell
26          cell.item = items[indexPath.row]
27          return cell
28      }
29
30      /// This method is called when the user wants to add a new item to the to-do list
31      @objc private func handleNewItem() {
32          ...
33      }
34
35      ...
36  }
```

To conclude this step, we implement the custom table view cell ToDoListItemCell

```swift
1   // Content of ToDoListItemCell.swift file
2   class ToDoListItemCell: UITableViewCell {
3       class var identifier: String { "\(NSStringFromClass(Self.self)).identifier" } // Cell's identifier
4    
5       /// When set, it updates the title and detail texts of the cell
6       var item: ToDoListItem? {
7           didSet {
8               textLabel?.text = item?.title
9               detailTextLabel?.text = item?.description
10          }
11      }
12    
13      override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
14          super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
15          
16          accessoryType = .detailButton // This accessory button will be used to present edit options for the item
17      }
18    
19      required init?(coder: NSCoder) {
20          super.init(coder: coder)
21        
22          accessoryType = .detailButton // This accessory button will be used to present edit options for the item
23      }
24  }
```

## 4 - CRUD flow

We implement all CRUD logic in the ToDoListController class. Go to ToDoListController.swift and add the following methods to the ToDoListController class

```swift
1   // MARK: - CRUD Flow
2   extension ToDoListController {
3       /// Creates a ToDoListItem and stores it on your Back4App Database
4       /// - Parameters:
5       ///   - title: The title for the to-do task
6       ///   - description: An optional description for the to-to task
7       func createObject(title: String, description: String?) {
8
9       }
10    
11      /// Retrieves all the ToDoListItem objects from your Back4App Database
12      func readObjects() {
13
14      }
15    
16      /// Updates a ToDoListItem object on your Back4App Database
17      /// - Parameters:
18      ///   - objectId: The object id of the ToDoListItem to update
19      ///   - newTitle: New title for the to-to task
20      ///   - newDescription: New description for the to-do task
21      func updateObject(objectId: String, newTitle: String, newDescription: String?) {
22
23      }
24    
25      /// Deletes a ToDoListItem on your Back4App Database
26      /// - Parameter item: The item to be deleted on your Back4App Database
27      func deleteObject(item: ToDoListItem) {
28
29      }
30  }
```

**- Create Object**

Now we start implementing the createObject(title\:description:) method. Create an instance of ToDoListItem using the init(title\:description:) initializer. In order to save this new item on your Back4App Database, the ParseSwift protocol provides a save() method. This method can be called synchronously or asynchronously, choose one of them according to your use case. An asynchrononous implementation should look like this

```swift
1   func createObject(title: String, description: String?) {
2       let item = ToDoListItem(title: title, description: description)
3        
4       item.save { [weak self] result in
5           guard let self = self else { return }
6           switch result {
7           case .success(let savedItem):
8               self.items.append(savedItem)
9               DispatchQueue.main.async {
10                  self.tableView.insertRows(at: [IndexPath(row: self.items.count - 1, section: 0)], with: .right)
11              }
12          case .failure(let error):
13              DispatchQueue.main.async {
14                  self.showAlert(title: "Error", message: "Failed to save item: \(error.message)")
15              }
16          }
17      }
18  }
```

Now we can complete the action for the add button located at the right side of the navigation bar. Go toToDoListController** **and add the following

```swift
1   class ToDoListController: UITableViewController {
2       enum ItemDescription: Int { case title = 0, description = 1 }
3
4       ...
5
6       /// This method is called when the user wants to add a new item to the to-do list
7       @objc private func handleNewItem() {
8           showEditController(item: nil)
9       }
10    
11      /// Presents an alert where the user enters a to-do task for either create a new one (item parameter is nil) or edit an existing one
12      private func showEditController(item: ToDoListItem?) {
13          let controllerTitle: String = item == nil ? "New item" : "Update item"
14        
15          let editItemAlertController = UIAlertController(title: controllerTitle, message: nil, preferredStyle: .alert)
16        
17          editItemAlertController.addTextField { textField in
18              textField.tag = ItemDescription.title.rawValue
19              textField.placeholder = "Title"
20              textField.text = item?.title
21          }
22
23          editItemAlertController.addTextField { textField in
24              textField.tag = ItemDescription.description.rawValue
25              textField.placeholder = "Description"
26              textField.text = item?.description
27          }
28        
29          let mainActionTitle: String = item == nil ? "Add" : "Update"
30        
31          let mainAction: UIAlertAction = UIAlertAction(title: mainActionTitle, style: .default) { [weak self] _ in
32              guard let title = editItemAlertController.textFields?.first(where: { $0.tag == ItemDescription.title.rawValue })?.text else {
33                  return editItemAlertController.dismiss(animated: true, completion: nil)
34              }
35            
36              let description = editItemAlertController.textFields?.first(where: { $0.tag == ItemDescription.description.rawValue })?.text
37            
38              editItemAlertController.dismiss(animated: true) {
39                  if let objectId = item?.objectId { // if the item passed as parameter is not nil, the alert will update it
40                      self?.updateObject(objectId: objectId, newTitle: title, newDescription: description)
41                  } else {
42                      self?.createObject(title: title, description: description)
43                  }
44              }
45          }
46                
47          let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
48        
49          editItemAlertController.addAction(mainAction)
50          editItemAlertController.addAction(cancelAction)
51
52          present(editItemAlertController, animated: true, completion: nil)
53      }
54  }
```

**- Read Object**

We move to the readObjects() method. Retreiveing ToDoListItem items from your Back4App Database is performed via a Query\<ToDoListItem> object. This query is instanciated in the following way

```swift
1   func readObjects() {
2       let query = ToDoListItem.query()
3       ...
4   }
```

In this tutorial we use a query which will retreive all the items of type ToDoListItem from your Back4App Database. In case you want to retreive a set of specific items, you can provide QueryConstraint elements to ToDoListItem.query(QueryConstraint...). For instance, to fetch all items where title == "Some title", the query takes the form

```swift
1   let query = ToDoListItem.query("title" == "Some title")
```

Once you have the query ready, we proceed to retreive the items by callingquery.find(). Again, this can be done synchronously or asynchronously. In our To-do List app we implement it asynchronously

```swift
1   func readObjects() {
2       let query = ToDoListItem.query()
3        
4       query.find { [weak self] result in
5           guard let self = self else { return }
6           switch result {
7           case .success(let items):
8               self.items = items
9               DispatchQueue.main.async {
10                  self.tableView.reloadSections([0], with: .top)
11              }
12          case .failure(let error):
13              DispatchQueue.main.async {
14                  self.showAlert(title: "Error", message: "Failed to save item: \(error.message)")
15              }
16          }
17      }
18  }
```

With readObjects() completed, we can now fetch all the tasks stored in your Back4App Database and show them right after the app enters to foreground. Go back to ToDoListController and override the viewDidAppear() method

```swift
1   class ToDoListController: UITableViewController {
2       ...
3    
4       override func viewDidAppear(_ animated: Bool) {
5           super.viewDidAppear(animated)
6        
7           readObjects()
8       }
9
10      ...
11  }
```

**- Update object**

Given the objectId of a ToDoListItem object, it is straightforward to perform an update. We simply instanciate a ToDoListItem object using the init(objectId:) initializer. Next, we update the properties we need and call the save() method (of ToDoListItem) to save changes

```swift
1   func updateObject(objectId: String, newTitle: String, newDescription: String?) {
2       var item = ToDoListItem(objectId: objectId)
3       item.title = newTitle
4       item.description = newDescription
5        
6       item.save { [weak self] result in
7           switch result {
8           case .success:
9               if let row = self?.items.firstIndex(where: { $0.objectId == item.objectId }) {
10                  self?.items[row] = item
11                  DispatchQueue.main.async {
12                      self?.tableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .fade)
13                  }
14              }
15          case .failure(let error):
16              DispatchQueue.main.async {
17                  self?.showAlert(title: "Error", message: "Failed to save item: \(error.message)")
18              }
19          }
20      }
21  }
```

**- Delete object**

Deleting objects on your Back4App Database is very similar to creating objects. We begin by creating an instance of ToDoListItem with the objectId of the item we want to delete. Next, we simply call (synchronously or ascynchronously) the delete() method of the object. If the deletion was successfull we update the UI, otherwise we report the error

```swift
1   func deleteObject(item: ToDoListItem) {
2       item.delete { [weak self] result in
3           switch result {
4           case .success:
5               if let row = self?.items.firstIndex(where: { $0.objectId == item.objectId }) {
6                   self?.items.remove(at: row)
7                   DispatchQueue.main.async {
8                       self?.tableView.deleteRows(at: [IndexPath(row: row, section: 0)], with: .left)
9                   }
10              }
11          case .failure(let error):
12              DispatchQueue.main.async {
13                  self?.showAlert(title: "Error", message: "Failed to save item: \(error.message)")
14              }
15          }
16      }
17  }
```

With deleteObject(item:) and updateObject(objectId\:newTitle\:newDescription) completed, we proceed to add the corresponding actions to call these operations. Go back to ToDoListController and add

```swift
1   // MARK: UITableViewDataSource delegate
2   extension ToDoListController {
3       // When the user taps on the accessory button of a cell, we present the edit options for the to-do list task
4       override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
5           guard !items.isEmpty else { return }
6        
7           showEditOptions(item: items[indexPath.row])
8       }
9    
10      /// Presents a sheet where the user can select an action for the to-do list item
11      private func showEditOptions(item: ToDoListItem) {
12          let alertController = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
13        
14          let editAction = UIAlertAction(title: "Edit", style: .default) { [weak self] _ in
15              self?.showEditController(item: item)
16          }
17        
18          let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in
19              alertController.dismiss(animated: true) {
20                  self?.deleteObject(item: item)
21              }
22          }
23        
24          let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
25              alertController.dismiss(animated: true, completion: nil)
26          }
27        
28          alertController.addAction(editAction)
29          alertController.addAction(deleteAction)
30          alertController.addAction(cancelAction)
31
32          present(alertController, animated: true, completion: nil)
33      }
34  }
```

As we pointed out earlier, the accessory button in each ToDoListItemCell triggers an edit sheet via the tableView(\_:accessoryButtonTappedForRowWith:) delegate method.

### It’s done!

At this point, you have learned how to do the basic CRUD operations with Parse on iOS.

[title] Untitled
[path] iOS/


[title] Email Verification
[path] Flutter/Parse SDK (REST)/User Authentication/Authentication/

# User email verification for Flutter

## Introduction

Enabling email verification in an application’s settings allows the application to reserve part of its experience for users with confirmed email addresses.

Email verification adds the emailVerified key to the ParseUser object. When a ParseUseremail is set or modified, emailVerified is set to false.

Parse then emails the user a link which will set emailVerified to true.

There are three emailVerified states to consider:

1. true - the user confirmed his or her email address by clicking on the link Parse emailed them.
2. false - at the time the ParseUser object was last fetched, the user had not confirmed his or her email address. If emailVerified is false. If emailVerified is false, consider calling getUpdatedUser() on the ParseUser.
3. missing - the&#x20;

In this guide, you will learn how to set up a user email verification process to a user registration feature (Sign Up).

You will create an app that includes user registration with email verification using [Parse Server core features](https://www.back4app.com/product/parse-server) through Back4App.

You will use the same method you used to implement the user registration, but instead of redirecting the user to a logged screen, you will ask the user to verify their email to log in.

## Goal

Build a User verification email process feature using Parse for a Flutter App.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- A Flutter app created in previous guide
  - **Note: **Follow the <a href="https://app.archbee.com/docs/_roxIyUMXoBue9I7uv49e/3uUNUqP0K0awBtYuIl4ES" target="_blank">How to implement user password reset</a>
- Complete the previous guide so ou can have a better understanding of the ParseUser class.
- A device (not Simulator) running Android or iOS.
:::

## Understanding Email verification function

To better understand Email verification function, we will continue the development of the application started in the previous guide and implement the function.

We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse. Following the next steps, you will build a Login e Logout App at Back4App Database.

## Let’s get started!

In the following steps, you will be able to build a Email verification function in App.

## 1 - Enable Email Verification

Let’s now enable the email verification on Back4App Dashboard. The email verification page has two properties: Verify User Emails and Prevent login if the email is not verified.
If you enable only the Verify User Emails option, the user will receive the verification email but will be able to log in and use the application normally. If you also enable the Prevent login if email is not verified option, the user will only log in after concluding the email verification process.

1. Go to your App at [Back4App Website ](https://www.back4app.com/)and click on Server Settings.
2. Find the Verification emails card and click on Settings\`.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wr9xlDI1N7Nc4Vy5ofv4k_image.png)

&#x20;    3\. Click on Verify User Email and Prevent login if the email is not verified.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_9JbI7rA6mvuVSNrdpjKt_image.png)

&#x20;    4\. Optional: Fill the empty fields and modify the ones that have already been filled based on your preferences.

&#x20;    5\. Click on the SAVE button.

## 2 - Update the Login/Logout/Reset Password App

Open Flutter project from the previous guide [How to add user reset password to a Flutter App](https://www.back4app.com/docs/flutter/parse-sdk/users/flutter-reset-password).

Search for the function doUserRegistration in the file main.dart.

After call function user.signUp();, call the user.logout() function, to ensure that the user does not log in until the email is confirmed.
Update the message informing the user to check the mailbox e redirect the user to Home Screen.

Replace the code inside doUserRegistration with:

```dart
1     void doUserRegistration() async {
2       final username = controllerUsername.text.trim();
3       final email = controllerEmail.text.trim();
4       final password = controllerPassword.text.trim();
5   
6       final user = ParseUser.createUser(username, password, email);
7  
8       var response = await user.signUp();
9
10      if (response.success) {
11        Message.showSuccess(
12            context: context,
13            message: 'User was successfully created! Please verify your email before Login',
14            onPressed: () async {
15              Navigator.pop(context);          
16            });
17      } else {
18        Message.showError(context: context, message: response.error!.message);
19      }
20    }
```

Note: The code for SignUp function has been explained previously.

## 3 - Test Sign Up

To test it, click on the Run button in Android Studio/VSCode.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/iNh3vr_XzZCIlbsEmBIUC_image.png" signedSrc size="50" width="325" height="636" position="center" caption}

Perform the registration process, clicking in button Sign Up.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pDtStKebbW_WqtIxYUndH_image.png" signedSrc size="50" width="328" height="637" position="center" caption}

After SignUp we will receieve an email like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kYovPCgzCVE_Ctjv40sTC_image.png)

After click in link to verify the email, the property will be setted to true in Parse Dashboard:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/e749HwlRRhsSWP_tU0tgv_image.png)

## 4 - Log in

To implement the Log In with Email Verification, you have just to implement a Parse User Logins just as described on [User LogIn guide](https://www.back4app.com/docs/flutter/parse-sdk/users/flutter-login).

If you have enabled the ‘Prevent login if email is not verified’ option in Step 2, you will get the following error if you try to login without verifying your email.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/1KdIfOJ1iG_hASdIqt-Rr_image.png" signedSrc size="50" width="328" height="637" position="center" caption}

## It’s done!

At this stage, you can Log in, Sign Up or Log out of your app using email verification with Parse Server core features through Back4App!

[title] Basic Queries
[path] iOS/Parse Swift SDK/Data Objects/

## Basic Queries

### Introduction

In most use cases, we require to fetch data from a database with certain conditions. These conditions may include complex comparisons and ordering requirements. Thus, in any application, it is fundamental to construct efficient queries and, at the same time, the database has to be able to execute them as fast as possible.

The ParseSwift SDK does provide the necessary tools for you to construct any query according to the application requirements. In this tutorial, we explore these tools and use them in a real-world application.

This tutorial uses a basic app created in Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

## Goal

- To understand how to create basic queries to retrieve data from a Back4App Database.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
:::

## Understanding our Constacts App

The project template is a Contacts App where the user adds a contact’s information to save it on a Back4App Database

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/B0R7v-NV5x-uLPAJ96d8r_image.png" signedSrc size="50" width="1242" height="2208" position="center" caption}

On the app’s homescreen you will find a set of buttons for different types of queries. Using the + button located on the top-right side of the navigation bar, we can add as many Contacts as needed.

## Quick reference of commands we are going to use

For this example, we use the object Contact

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Contact: ParseObject {
5       // Required properties from ParseObject protocol
6       var originalData: Data?
7       var objectId: String?
8       var createdAt: Date?
9       var updatedAt: Date?
10      var ACL: ParseACL?
11    
12      // Custom fields for the contact's information
13      var name: String?
14      var birthday: Date?
15      var numberOfFriends: Int?
16      var favoriteFoods: [String]?
17    
18      ...
19  }
```

The following methods will allows us to save and queryContactobjects:

:::CodeblockTabs
Create contact

```swift
//When creating and saving a new instance of Contact we can use
1   var newContact: Contact = Contact(name: "John Doe", birthday: Date(), numberOfFriends: 5, favoriteFoods: ["Bread", "Pizza"])
2
3   // Saves newContact on your Back4App Database synchronously and returns the new saved Item. It throws and error if something went wrong.
4   let savedContact = try? newContact.save()
5
6   // Saves newContact on your Back4App Database asynchronously, and passes a Result<Contact, ParseError> object to the completion block to handle the save process.
7   newContact.save { result in
8       // Handle the result to check wether the save process was successfull or not
9   }
```

Query all

```swift
//For retrieving all the Contact items saved on a Back4App Database, we construct a Query<Contact> object and call the find() method on it
1   let contactsQuery = Contact.query() // A query to fetch all Contact items on your Back4App Database.
2
3   // Fetches the items synchronously or throws an error if found.
4   let fetchedContacts = try? query.find()
5
6   // Fetches the items asynchronously and calls a completion block passing a result object containing the result of the operation.
7   query.find { result in
8       // Handle the result
9   }
```

Query by name

```swift
//In order to create a query with a specific condition, we use the static method query(_:) provided by the ParseObject protocol. We pass a QueryConstraint object to the method as a parameter. This QueryConstraint object represents the type of constraint we are imposing on the query. For queries involving comparison constraints, the ParseSwift SDK provides the following methods to create them
1   import ParseSwift
2
3   // A constraint to retreive all Contact items that have exactly the string 'Jhon Doe' in their 'name' field
4   let constraint1 = try? equalTo(key: "name", value: "John Doe")
5
6   // An operator-like implementation for the equalTo(key:value:) method
7   let constraint2: QueryConstraint = "name" == "Jhon Doe"
8
9   // A constraint to retrieve all Contact items that have the string 'John' in their 'name' field (only workd with String-type fields)
10  let constraint3: QueryConstraint = containsString(key: "name", substring: "Jhon")
11
12  let query = Contact.Query(constrint1) // Depending on your use case, you can send any of the above constraints as parameter
13
14  // Executes the query synchronously. It throws an error if something happened
15  let fetchedContacts = try? query.find()
16
17  // Executes que query asynchronously and returns a Result<[Contact], ParseError> object with the result
18  query.find() { result in
19      // Handle the result
20  }
```

Query by friend count

```swift
//When we want to query contacts which have a certain amount of friends or more, we do it in the following way
1   import ParseSwift
2
3   // A constraint to retrieve all Contact items that have 30 or more number of friends
4   let constraint1: QueryConstraint = "numberOfFriends" >= 30
5
6   // A constraint to retrieve all Contact items that have more than 30 number of friends
7   let constraint2: QueryConstraint = "numberOfFriends" > 30
8
9   let query = Contact.query(constraint1) // Depending on your use case, you can send any of the above constraints as parameter
10
11  // Executes the query synchronously. It throws an error if something happened
12  let fetchedContacts = try? query.find()
13
14  // Executes que query asynchronously and returns a Result<[Contact], ParseError> object with the result
15  query.find() { result in
16       // Handle the result
17  }
```

Query with ordering

```swift
//Adding an ordering option to queries is straightforward. Any Query<Contact> object has the order(_:) method to do so. A simple query using the birthday as descending order can be implemented in the folowing way
1   import ParseSwift
2
3   // A query without order to retrieve all the Contact items
4   let unorderedQuery = Contact.query()
5
6   // Sorts the result by the brithday field. The parameter in the enumeration is the key of the field used to order the results
7   let descendingOrder = Query<Contact>.Order.descending("birthday")
8
9   let orderedQuery = unorderedQuery.order([descendingOrder]) // Returns a new query with the requested (descending) ordering option
10
11  // Executes the query synchronously. It throws an error if something happened
12  let orderedContacts = try? orderedQuery.find()
13
14  // Executes que query asynchronously and returns a Result<[Contact], ParseError> object with the result
15  orderedContacts.find() { result in
16      // Handle the result
17  }
```
:::

## 1 - Download the Contacts App Template

The XCode project has the following structure

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/63yY4ygI7vQ4MRHG4U8h4_image.png)

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

To focus on the main objective of this guide, we will only detail the sections strictly related to queries and the **ParseSwift SDK**.

## 2 - Additional CRUD flow

Before getting started with queries, it is necessary to have some contacts already saved on your Back4App Database. In the NewContactController class, we implement a basic form to add a Contact. To save an instance of a Contact object, we use the handleAddContact() method implemented in the NewContactController class

```swift
1   // NewContactController.swift file
2   ...
3
4   extension NewContactController {
5       /// Retrieves the info the user entered for a new contact and stores it on your Back4App Database
6       @objc fileprivate func handleAddContact() {
7           view.endEditing(true)
8        
9           // Collect the contact's information from the form
10          guard let name = nameTextField.text,
11                let numberOfFriendsString = numberOfFriendsTextField.text,
12                let numberOfFriends = Int(numberOfFriendsString),
13                let favoriteFoods = favoriteFoodsTextField.text?.split(separator: ",") else {
14              return showAlert(title: "Error", message: "The data you entered is con valid.")
15          }
16        
17          // Once the contact's information is collected, instantiate a Contact object to save it on your Back4App Database
18          let contact = Contact(
19              name: name,
20              birthday: birthdayDatePicker.date,
21              numberOfFriends: numberOfFriends,
22              favoriteFoods: favoriteFoods.compactMap { String($0).trimmingCharacters(in: .whitespaces) }
23          )
24        
25          // Save the new Contact
26          contact.save { [weak self] result in
27              switch result {
28              case .success(_):
29                  self?.showAlert(title: "Success", message: "Contact saved.") {
30                      self?.dismiss(animated: true, completion: nil)
31                  }
32              case .failure(let error):
33                  self?.showAlert(title: "Error", message: "Failed to save contact: \(error.message)")
34              }
35          }
36      }
37  }
```

:::hint{type="success"}
For more details about this step, you can go to the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/swift-crud-database-operations" target="_blank">basic operations guide</a>.
:::

## 3 - Performing basic queries

**- By name**

The first example we look at is a query that allows us to retrieve contacts which have a specific substring in their name field. In order to do this, we first create a QueryConstraint object. This object will contain the constraint we want. The ParseSwift SDK provides the following methods to (indirectly) create a QueryConstraint

```swift
1   // QueryConstraint.swift file
2
3   /**
4     Add a constraint for finding string values that contain a provided substring.
5     - warning: This will be slow for large datasets.
6     - parameter key: The key that the string to match is stored in.
7     - parameter substring: The substring that the value must contain.
8     - parameter modifiers: Any of the following supported PCRE modifiers (defaults to nil):
9       - `i` - Case insensitive search
10      - `m` - Search across multiple lines of input
11    - returns: The resulting `QueryConstraint`.
12   */
13  public func containsString(key: String, substring: String, modifiers: String? = nil) -> QueryConstraint
14
15  /**
16   Add a constraint that requires that a key is equal to a value.
17   - parameter key: The key that the value is stored in.
18   - parameter value: The value to compare.
19   - returns: The same instance of `QueryConstraint` as the receiver.
20   - warning: See `equalTo` for more information.
21   Behavior changes based on `ParseSwift.configuration.isUsingEqualQueryConstraint`
22   where isUsingEqualQueryConstraint == true is known not to work for LiveQuery on
23   Parse Servers  <= 5.0.0.
24   */
25  public func == <T>(key: String, value: T) -> QueryConstraint where T: Encodable
```

For instance, a query that allows us to retrieve all the Contact’s with **John** in their name field can be created with

```swift
1   // Create the query sending the constraint as parameter
2   let constraint: QueryConstraint = containsString(key: "name", substring: "John") // The first parameter (key) referres to the name of the field
3   let query = Contact.query(constrain)
4
5   // Retrieve the contacts asynchronously (or sinchronously if needed)
6   query.find() { result in
7       // Handle the result and do the corresponding UI update
8   }
```

In case the constraint requires the name** **field to match exactly a given string, we can use

```swift
1   // Create the query sending the constraint as parameter
2   let value = "John"
3   let constraint: QueryConstraint = "name" == value
4   let query = Contact.query(constrain)
```

**- By number of friends**

A query with a constraint involving a numerical comparison can be constructed by creating aQueryConstraint** **with

```swift
1   /**
2    Add a constraint that requires that a key is greater than a value.
3    - parameter key: The key that the value is stored in.
4    - parameter value: The value to compare.
5    - returns: The same instance of `QueryConstraint` as the receiver.
6    */
7   public func > <T>(key: String, value: T) -> QueryConstraint where T: Encodable
8
9   /**
10   Add a constraint that requires that a key is greater than or equal to a value.
11   - parameter key: The key that the value is stored in.
12   - parameter value: The value to compare.
13   - returns: The same instance of `QueryConstraint` as the receiver.
14   */
15  public func >= <T>(key: String, value: T) -> QueryConstraint where T: Encodable
16
17  /**
18   Add a constraint that requires that a key is less than a value.
19   - parameter key: The key that the value is stored in.
20   - parameter value: The value to compare.
21   - returns: The same instance of `QueryConstraint` as the receiver.
22   */
23  public func < <T>(key: String, value: T) -> QueryConstraint where T: Encodable
24
25  /**
26   Add a constraint that requires that a key is less than or equal to a value.
27   - parameter key: The key that the value is stored in.
28   - parameter value: The value to compare.
29   - returns: The same instance of `QueryConstraint` as the receiver.
30   */
31  public func <= <T>(key: String, value: T) -> QueryConstraint where T: Encodable

```

To query all contacts with 30 or more friends, we use

```swift
1   let query = Contacts.query("numberOfFriends" >= 30)
2
3   // Retrieve the contacts asynchronously (or sinchronously if needed)
4   query.find() { result in
5       // Handle the result and do the corresponding UI update
6   }
```

**- Ordering query results**

For ordering the results from a query, the Query\<contacts> object provides the method order(\_:) which returns a new Query\<contact> object considering the requested ordering option. As a parameter, we pass an enumeration (Query\<contact>.Order) to indicate the ordering we want. The following snippet applies a descending order based on the birthday field

```swift
1   // A query without order to retrieve all the Contact items
2   let unorderedQuery = Contact.query()
3
4   // Sorts the contacts based on their brithday. The parameter in the enumeration is the key of the field used to order the results
5   let descendingOrder = Query<Contact>.Order.descending("birthday")
6
7   let orderedQuery = unorderedQuery.order([descendingOrder]) // Returns a new query with the requested (descending) ordering option
8
9   // Executes que query asynchronously and returns a Result<[Contact], ParseError> object with the result
10  orderedContacts.find() { result in
11      // Handle the result
12  }
```

In the [project example](https://github.com/templates-back4app/ios-basic-queries-example), we implemented the queries mentioned above. The ContactsController class has the method fetchContacts() where you will find the following snippet&#x20;

```swift
1   ...
2
3   class ContactsController {
4       let queryType: QueryType
5
6       ...
7
8       private func fetchContacts() {
9           // We create a Query<Contact> according to the queryType enumeration
10          let query: Query<Contact> = {
11              switch queryType {
12              case .byName(let value):
13                  return Contact.query(containsString(key: "name", substring: value))
14              case .byNumberOfFriends(let quantity):
15                  return Contact.query("numberOfFriends" >= quantity)
16              case .byOrdering(let order):
17                  let query = Contact.query()
18                  switch order {
19                  case .ascending: return query.order([.ascending("birthday")])
20                  case .descending: return query.order([.descending("birthday")])
21                  }
22              case .all:
23                  return Contact.query()
24              }
25          }()
26        
27          // Execute the query
28          query.find { [weak self] result in
29              switch result {
30              case .success(let contacts):
31                  self?.contacts = contacts
32                
33                  // Update the UI
34                  DispatchQueue.main.async { self?.tableView.reloadData() }
35              case .failure(let error):
36                  // Notify the user about the error that happened during the fetching process
37                  self?.showAlert(title: "Error", message: "Failed to retrieve contacts: \(error.message)")
38                  return
39              }
40          }
41      }
42  }
```

## 4 - Run the app!

:::hint{type="info"}
Before pressing the run button on XCode, do not forget to configure your Back4App application in the AppDelegate** **class!
:::

Using the+button in the navigation bar, add a counple of contacts and test the different queries.

[title] User Authentication
[path] React Native/Relay (GraphQL)/Users/

# React Native authentication using Relay

## Introduction

Using the GraphQL API, after signing up or logging a user in, you will receive a session token that you can use to retrieve the logged user at any time. The session token comes from a Relay Mutation. You will find those Relay Mutation examples on the previous guides of <a href="https://www.back4app.com/docs/parse-graphql/graphql-sign-up" target="_blank">Sign Up</a> or <a href="https://www.back4app.com/docs/parse-graphql/graphql-login" target="_blank">Log In</a>.

The session token value represents the current session and controls if the user is authenticated or not. At the moment of authentication, this value needs to start to be on header params. On Relay, we use the Environment to handle the header params, so you should insert the session token inside this file.

After adding the session to the headers, each request will be authenticated by Back4App and, the user will access the private resources.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## Goal

Authenticate the user requests on Back4App using the session token on header params.

## Prerequisites

- **An app created at Back4App using the Parse Server Version 3.10 or above.**
- **You have to conclude the **<a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">**Relay Environment setup tutorial**</a>**:**
- **You have to conclude the **<a href="https://www.back4app.com/docs/react-native/graphql/users/react-native-login-sample" target="_blank">**React Native Login sample using Relay**</a>**:**
- **For this tutorial we are going to use the Expo as a React Native framework;**
- **For this tutorial we are going to use Javascript as our default implementation language;**
- **For this tutorial we are going to use Async Storage;**

## 1 - Install Async Storage

After conclude the tutorials <a href="https://www.back4app.com/docs/parse-graphql/graphql-sign-up" target="_blank">Sign Up</a> or <a href="https://www.back4app.com/docs/parse-graphql/graphql-login" target="_blank">Log In</a>, your app will receive a session token. Let’s store the token using the Async Storage. Follow the official docs to install the Async Storage Lib on your App.

:::hint{type="success"}
You can use async storage, redux, or your preferred local storage solution. You only make sure that this value will be available in the Environment.
:::

## 2 - Retrieve the token

Let’s go ahead using the <a href="https://www.back4app.com/docs/parse-graphql/graphql-login" target="_blank">last guide code</a>. You’ll need to get the session token and persist this value in your application using Async Storage.

Start by changing the session token state management from useState hook to Async Storage. The first step is to create a function inside of the environment file to retrieve the session token from Async Storage.

Import the Async Storage:

```javascript
1    import AsyncStorage from '@react-native-async-storage/async-storage';
```

Now, create the function:

```javascript
1    export const getSessionToken = async () => {
2      const sessionToken = await AsyncStorage.getItem('sessionToken');
3      return sessionToken;
4    };
```

## 3 - Save the token on the Client-side

Let’s now improve the Sign In component to persist the session token instead of managing it using the useState hook. The component will now keep the logged-in state even when reloading the App because it has the session token persisted.

Open the Sign In component. Inside of the onCompleted from onSubmit, saving the session token on Async Storage getting the following result:

Then improve the onCompleted:

```javascript
1	onCompleted: async (response) => {
2	    if(!response?.logIn || response?.logIn === null) {
3	      alert('Error while logging');
4	      return;
5	    }
6	
7	    const { viewer } = response?.logIn;
8	    const { sessionToken, user } = viewer;
9	
10	    if (sessionToken !== null) {
11	      setUserLogged(user);
12	      alert(`user ${user.username} successfully logged`);
13	      await AsyncStorage.setItem('sessionToken', sessionToken);
14	      return;
15	    }
16	},
```

After, inside of the SignIn component declaration, create a new useState for the session token:

```javascript
1	const [sessionToken, setSessionToken] = useState(null);
```

Add a useEffect to be called every time that the component is mounted and check if has a session token (import thegetSessionTokenfrom environment file:

```javascript
1	useEffect(() => {
2	  (async () => {
3	    const sT = await getSessionToken();
4	    setSessionToken(sT);
5	  })();
6	}, []);
```

&#x20;By last, let’s change again the onCompleted for now handle the new useState, getting the new lines of code:

```javascript
1	onCompleted: async (response) => {
2	    if (!response?.logIn || response?.logIn === null) {
3	    alert('Error while logging');
4	    return;
5	    }
6	    
7	    const { viewer } = response?.logIn;
8	    const { sessionToken, user } = viewer;
9	    
10	    if (sessionToken !== null) {
11	       setSessionToken(sessionToken);
12	       await AsyncStorage.setItem('sessionToken', sessionToken);
13	       return;
14	  
15	    }
16	},
```

Remove the useState for user logged, the both lines below from respective places:

```javascript
1	const [userLogged, setUserLogged] = useState(null);
```

and

```javascript
1	setUserLogged(user);
```

We avoid the alert and start to set the info of the user and the token in a useState followed by the Async Storage saving the token.

Changes the if to handle now the session token.

```javascript
1	if (sessionToken) {
2	  return (
3	    <View>
4	      <Text>User logged</Text>
5	    </View>
6	  );
7	}
```

## 4 - Final result of SignIn component

After all changes, the SignIn component will be similar to the below.

```javascript
1	import React, {useEffect, useState} from 'react';
2	import LogInMutation from './mutations/LogInMutation';
3	import environment, { getSessionToken } from '../../relay/environment';
4	import {FormikProvider, useFormik} from 'formik';
5	import { Button, Text, TextInput, View, TouchableOpacity } from 'react-native';
6	import AsyncStorage from '@react-native-async-storage/async-storage';
7	
8	const SignIn = () => {
9	  const [sessionToken, setSessionToken] = useState(null);
10	
11	  useEffect(() => {
12	    (async () => {
13	      const sT = await getSessionToken();
14	      setSessionToken(sT);
15	    })();
16	  }, []);
17	
18	  const onSubmit = async (values) = {
19	    const { username, password } = values;
20	    const input = {
21	      username,
22	      password,
23	    };
24	
25	    LogInMutation.commit({
26	      environment,
27	      input,
28	      onCompleted: async (response) => {
29	        if (!response?.logIn || response?.logIn === null) {
30	          alert('Error while logging');
31	          return;
32	        }
33	
34	        const { viewer } = response?.logIn;
35	        const { sessionToken, user } = viewer;
36	
37	        if (sessionToken !== null) {
38	          setSessionToken(sessionToken);
39	          setUserLogged(user);
40	
41	          await AsyncStorage.setItem('sessionToken', sessionToken);
42	          return;
43	        }
44	      },
45	      onError: (errors) => {
46	        alert(errors[0].message);
47	      },
48	    });
49	  };
50	
51	  const formikbag = useFormik({
52	    initialValues: {
53	      username: '',
54	      password: '',
55	    },
56	    onSubmit,
57	  });
58	
59	 const { handleSubmit, setFieldValue } = formikbag;
60	
61	  if (sessionToken) {
62	    return (
63	      <View style={ {marginTop: 15, alignItems: 'center'} }>
64	        <Text>User logged</Text>
65	      </View>
66	    );
67	  }
68	
69	  return (
70	      <FormikProvider value={formikbag}>
71	          <View style={Styles.login_wrapper}>
72	              <View style={Styles.form}>
73	                  <Text>Username</Text>
74	                  <TextInput
75	                      name={"username"}
76	                      style={Styles.form_input}
77	                      autoCapitalize="none"
78	                      onChangeText={(text) => setFieldValue("username", text)}
79	                  />
80	                  <Text>Password</Text>
81	                  <TextInput
82	                      style={Styles.form_input}
83	                      name={"password"}
84	                      autoCapitalize="none"
85	                      secureTextEntry
86	                      onChangeText={(text) => setFieldValue("password", text)}
87	                  />
88	                  <TouchableOpacity onPress={() => handleSubmit()}>
89	                      <View style={Styles.button}>
90	                          <Text style={Styles.button_label}>{"Sign in"}</Text>
91	                      </View>
92	                  </TouchableOpacity>
93	              </View>
94	          </View>
95	      </FormikProvider>
96	  );
97	};
98	
99	export default SignIn;
```

## 5 - Testing

It’s time to test the new changes of Sign In component. To make sure there is no one user logged in, kill your application and open again.

:::hint{type="success"}
Remember also to clear the Async Storage. You can do it calling the AsyncStorage.clear() method and clear the actual state.
:::

Log in again and you will see the following message.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FVeoyIrC2dFPWlcJEFgU0_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

## 6 - Set the Session token on Relay Environment

Now, let’s insert the session token on the application’s requests to Back4App GraphQL API. Inside of the Environment file, retrieve the sessionToken and add it to the headers object. You should pass the sessionToken on the variable X-Parse-Session-Token on the headers.
Here, we will reuse the getSessionToken function we already created.

Create a function before the fetchQuery function and paste the following code:

```javascript
1	export const getToken = async () => {
2	  const sessionToken = await getSessionToken();
3	
4	  if (sessionToken) {
5	    return {
6	      'X-Parse-Session-Token': sessionToken,
7	    };
8	  }
9	
10	  return {};
11	};
```

The function above retrieve the session token only if it exists. Now, add it to the headers object deconstructing it.

```javascript
1	const headers = {
2	  Accept: 'application/json',
3	  'Content-type': 'application/json',
4	  'X-Parse-Application-Id': 'YOUR_APP_ID_HERE',
5	  'X-Parse-Client-Key': 'YOUR_CLIENT_KEY_HERE',
6	  ...await getToken(),
7	};
```

Right below of headers, there is the try catch for make the request. Let’s set an if after the request that will handle when the http status of the request will be 401. This will mean that the actually token is not valid anymore. So, we will clear the storage and kill the current user:

```javascript
1	try {
2	    const response = await fetch(`https://parseapi.back4app.com/graphql`, {
3	        method: "POST",
4	        headers,
5	        body,
6	    });
7	
8	    const data = await response.json();
9	
10	    
11	    // this if will retrive the response when status code 401 and clear the session token
12	    if (response.status === 401) {
13	        await AsyncStorage.getItem("sessionToken");
14	        return;
15	    }
16	
17	    if (data.errors) {
18	        throw data.errors;
19	    }
20	
21	    return data;
22	} catch (err) {
23	    console.log("err on fetch graphql", err);
24	
25	    throw err;
26	}
```

Now, your application can start to retrieve private resources from the Back4App backend. And, if the session token does not exist, won’t break because we won’t pass it.

:::hint{type="success"}
Do not forget to configure the security mechanisms to guarantee the desired level of access for users. To better understand access the link <a href="https://docs.parseplatform.org/js/guide/#security" target="_blank">security docs</a> from Parse.
:::

## Conclusion

In this guide, you have saved the session token using async storage and, now can start to retrieve resources that need a user logged.

In the next doc, let’s prepare a component that will retrieve the info about the user logged and stop using a useState to show it.

:::hint{type="success"}
For user SignUp you can follow the same approach of this tutorial to handle the session token
:::


[title] User Login
[path] Flutter/Parse SDK (REST)/User Authentication/Authentication/

# User Login and Logout for Flutter using Parse Server

## Introduction

After implementing the User Registration for Flutter on Parse in the last guide, you will learn how to login and logout users using the same ParseUser class. After a Signup, the login operation is performed automatically, and a new user session is created. The Logout operation deletes the active Session object for the logged user.

In this guide, you will learn how to use the **Flutter plugin for Parse Server** to perform login/logout using ParseUser class for your Flutter App.

## Goal

To build a User Login/Logout feature using Parse for a Flutter App.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- Complete the previous guide so you can have a better understanding of the ParseUser class.
- A device (or virtual device) running Android or iOS.
:::

## Understanding the Login/Logout App

To better understand the Login/SingOut process, we will create an app to login e logout user on your account.

We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse. Following the next steps, you will build a Login e Logout App at Back4App Database.

## Let’s get started!

In the following steps, you will be able to build a Login/Logout App.

## 1 - Create the Login/Logout App Template

Open your Flutter project from the previous guide **Flutter plugin for Parse Server**. Go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/material.dart';
2   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
3
4   void main() async {
5     WidgetsFlutterBinding.ensureInitialized();
6 
7     final keyApplicationId = 'YOUR_APP_ID_HERE';
8     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
9     final keyParseServerUrl = 'https://parseapi.back4app.com';
10
11    await Parse().initialize(keyApplicationId, keyParseServerUrl,
12        clientKey: keyClientKey, debug: true);
13
14    runApp(MyApp());
15  }
16
17  class MyApp extends StatelessWidget {
18    @override
19    Widget build(BuildContext context) {
20      return MaterialApp(
21        title: 'Flutter Login/Logout',
22        theme: ThemeData(
23          primarySwatch: Colors.blue,
24          visualDensity: VisualDensity.adaptivePlatformDensity,
25        ),
26        home: HomePage(),
27      );
28    }
29  }
30  
31  class HomePage extends StatefulWidget {
32    @override
33    _HomePageState createState() => _HomePageState();
34  }
35  
36  class _HomePageState extends State<HomePage> {
37    final controllerUsername = TextEditingController();
38    final controllerPassword = TextEditingController();
39    bool isLoggedIn = false;
40  
41    @override
42    Widget build(BuildContext context) {
43      return Scaffold(
44          appBar: AppBar(
45            title: const Text('Flutter Login/Logout'),
46          ),
47          body: Center(
48            child: SingleChildScrollView(
49              padding: const EdgeInsets.all(8),
50              child: Column(
51                crossAxisAlignment: CrossAxisAlignment.stretch,
52                children: [
53                  Container(
54                    height: 200,
55                    child: Image.network(
56                        'http://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
57                  ),
58                  Center(
59                    child: const Text('Flutter on Back4App',
60                        style:
61                            TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
62                  ),
63                  SizedBox(
64                    height: 16,
65                  ),
66                  Center(
67                    child: const Text('User Login/Logout',
68                        style: TextStyle(fontSize: 16)),
69                  ),
70                  SizedBox(
71                    height: 16,
72                  ),
73                  TextField(
74                    controller: controllerUsername,
75                    enabled: !isLoggedIn,
76                    keyboardType: TextInputType.text,
77                    textCapitalization: TextCapitalization.none,
78                    autocorrect: false,
79                    decoration: InputDecoration(
80                        border: OutlineInputBorder(
81                            borderSide: BorderSide(color: Colors.black)),
82                        labelText: 'Username'),
83                  ),
84                  SizedBox(
85                    height: 8,
86                  ),
87                  TextField(
88                    controller: controllerPassword,
89                    enabled: !isLoggedIn,
90                    obscureText: true,
91                    keyboardType: TextInputType.text,
92                    textCapitalization: TextCapitalization.none,
93                    autocorrect: false,
94                    decoration: InputDecoration(
95                        border: OutlineInputBorder(
96                            borderSide: BorderSide(color: Colors.black)),
97                        labelText: 'Password'),
98                  ),
99                  SizedBox(
100                   height: 16,
101                 ),
102                 Container(
103                   height: 50,
104                   child: TextButton(
105                     child: const Text('Login'),
106                     onPressed: isLoggedIn ? null : () => doUserLogin(),
107                   ),
108                 ),
109                 SizedBox(
110                   height: 16,
111                 ),
112                 Container(
113                  height: 50,
114                   child: TextButton(
115                     child: const Text('Logout'),
116                     onPressed: !isLoggedIn ? null : () => doUserLogout(),
117                   ),
118                 )
119               ],
120             ),
121           ),
122         ));
123   }
124 
125   void showSuccess(String message) {
126     showDialog(
127       context: context,
128       builder: (BuildContext context) {
129         return AlertDialog(
130           title: const Text("Success!"),
131           content: Text(message),
132           actions: <Widget>[
133             new TextButton(
134               child: const Text("OK"),
135               onPressed: () {
136                 Navigator.of(context).pop();
137               },
138             ),
139           ],
140         );
141       },
142     );
143   }
144 
145   void showError(String errorMessage) {
146     showDialog(
147       context: context,
148       builder: (BuildContext context) {
149         return AlertDialog(
150           title: const Text("Error!"),
151           content: Text(errorMessage),
152           actions: <Widget>[
153             new TextButton(
154               child: const Text("OK"),
155               onPressed: () {
156                 Navigator.of(context).pop();
157               },
158             ),
159           ],
160         );
161       },
162     );
163   }
164 
165   void doUserLogin() async {
166    
167   }
168 
169   void doUserLogout() async {
170    
171   }
172 }
173
```

:::hint{type="info"}
When debug parameter in function Parse().initialize is true, allows displaying Parse API calls on the console. This configuration can assist in debugging the code. It is advisable to disable debug in the release version.
:::

## 2 - Connect Template to Back4app Project

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = App Id**
- **keyClientKey = Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VE1p8XtbVl9YvxJ4oH470_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

## 3 - Code for Login User

The User Login function creates a Session object, which points to the User logged in and stores in your local storage a valid user session.

Future calls to methods like currentUser will successfully retrieve your User data and sessionToken for Session object which created in the Dashboard.

Search for the function doUserLogin in the file main.dart. Replace the code inside doUserLogin with:

```dart
1   final username = controllerUsername.text.trim();
2       final password = controllerPassword.text.trim();
3
4       final user = ParseUser(username, password, null);
5 
6       var response = await user.login();
7
8       if (response.success) {
9         showSuccess("User was successfully login!");
10        setState(() {
11          isLoggedIn = true;
12        });
13      } else {
14        showError(response.error!.message);
15      }
```

To build this function, follow these steps:

1. Create a newParseUser class instance with the command ParseUser(username, password, null); using the data entered in the app. The e-mail field is not necessary and must be informed with null.
2. Call thelogin function, which will create a Session in your database in the Parse Dashboard and save the token to local storage
3. Check if the user login was successful. If it wasn’t successful, show the error description message.

The complete function should look like this:

```dart
1     void doUserLogin() async {
2       final username = controllerUsername.text.trim();
3       final password = controllerPassword.text.trim();
4
5       final user = ParseUser(username, password, null);
6
7       var response = await user.login();
8
9       if (response.success) {
10        showSuccess("User was successfully login!");
11        setState(() {
12          isLoggedIn = true;
13        });
14      } else {
15        showError(response.error!.message);
16      }
17    }
```

:::hint{type="info"}
To test it, click on the Run button in Android Studio/VSCode.
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Z8Z2CgsB7YUKk4tJ91_4F_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

After providing the desired user credentials, you will see this message after pressing on Login if everything was successful:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Bw7YyqHurBgLJCvBg9EYq_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

Error handling can be tested if you try to login a user with invalid credentials:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SaBJxLYqWUo9ua2rIN1O6_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

You will get another error if you try to login with no password:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/odoAAZAaQk60dIyGy_g6m_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

## 4 - Code for Logout User

The User Logout function deletes the Session object, which was created in the login function. It will clear this session on the device and log out of any linked services in your Parse server.

Search for the function doUserLogout in the file main.dart. Replace the code inside doUserLogout with:

```dart
1       final user = await ParseUser.currentUser() as ParseUser;
2       var response = await user.logout();
3
4       if (response.success) {
5         showSuccess("User was successfully logout!");
6         setState(() {
7           isLoggedIn = false;
8         });
9       } else {
10        showError(response.error!.message);
11      }
```

To build this function, follow these steps:

1. Get the current logged user using functionParseUser.currentUser().
2. Call thelogout function for ParseUser object, which will delete Session in your database and clean the token in the local storage.
3. Check if the user logout was successfull. If it wasn’t successful, show the error description message.

The complete code should look like this:

```dart
1     void doUserLogout() async {
2       final user = await ParseUser.currentUser() as ParseUser;
3       var response = await user.logout();
4   
5       if (response.success) {
6         showSuccess("User was successfully logout!");
7         setState(() {
8           isLoggedIn = false;
9         });
10      } else {
11        showError(response.error!.message);
12      }
13    }
```

:::hint{type="info"}
To test it, click on the Run button in Android Studio/VSCode.
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6T1LA_tKJ9CZ15grhE7Vj_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

After providing the desired user credentials, you will see this message after pressing on Login if everything was successful:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GBTX41Q1jYmHMHrKfTGU2_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

Click the “Logout” button:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Yq2P3Vt15tCf75963iJZv_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

## It’s done!

At the end of this guide, you can login and logout Parse Users of your app using Parse Server core features through Back4App!

[title] Query cookbook
[path] ReactJS/Data objects/

# React Query Cookbook for Parse

## Introduction

We’ve already seen how a Parse.Query with get can retrieve a single Parse.Object from Back4App. There are many other ways to retrieve data with Parse.Query - you can retrieve many objects at once, use conditions on the objects you wish to retrieve, and more.

In this guide, you will ding deep into the Parse.Query class and see all the methods you can use to build your Queries. You will use a simple database class with some mocked data to perform the Queries using the Javascript Console on Back4App.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
:::

### Goal

Explore the Parse.Query class different methods.

### The Parse.Query class

Any query operation on Parse uses the Parse.Query object type, which will help you retrieve specific data from your Back4App throughout your app. 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.

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.

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## Using the JS Console on Back4App

Inside your Back4App application’s dashboard, you will find a very useful API console in which you can run JavaScript code directly. In this guide you will use to store and query data objects from Back4App. On your App main dashboard go to Core->API Console->JS Console.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2YJSIqySaVbv0UDA4WtHp_image.png)

## Save your Data Objects

To run the queries on this guide you’ll need first to populate your App with some data. Let’s create a sample class called Profile, which mocks a social media profile class using famous people names and the following fields:

- string type name;
- Date type birthDay;
- Number (integer) type friendCount;
- Array (string array) type favoriteFoods;
- Array (Number array) type luckyNumbers;
- GeoPoint type lastLoginLocation;
- Nullable pointer type premiumMembership, related to a Membership class containing string name and Date expirationDate fields.

Here is the Parse.Object classes creation code, so go ahead and run it in your API console:

```javascript
1	// Add Profile objects and create table
2	// Adam Sandler
3	let Profile = new Parse.Object('Profile');
4	Profile.set('name', 'Adam Sandler');
5	Profile.set('birthDay', new Date('09/09/1966'));
6	Profile.set('friendCount', 2);
7	Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8	Profile.set('luckyNumbers', [2, 7]);
9	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
10	await Profile.save();
11	
12	// Britney Spears
13	Profile = new Parse.Object('Profile');
14	Profile.set('name', 'Britney Spears');
15	Profile.set('birthDay', new Date('12/02/1981'));
16	Profile.set('friendCount', 52);
17	Profile.set('favoriteFoods', ['Cake', 'Bread']);
18	Profile.set('luckyNumbers', [22, 7]);
19	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
20	await Profile.save();
21	
22	// Carson Kressley
23	Profile = new Parse.Object('Profile');
24	Profile.set('name', 'Carson Kressley');
25	Profile.set('birthDay', new Date('11/11/1969'));
26	Profile.set('friendCount', 12);
27	Profile.set('favoriteFoods', ['Fish', 'Cookies']);
28	Profile.set('luckyNumbers', [8, 2]);
29	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
30	await Profile.save();
31	
32	// Dan Aykroyd
33	// Creates related object Membership for this user only
34	let Membership = new Parse.Object('Membership');
35	Membership.set('name', 'Premium');
36	Membership.set('expirationDate', new Date('10/10/2030'))
37	await Membership.save();
38	Profile = new Parse.Object('Profile');
39	Profile.set('name', 'Dan Aykroyd');
40	Profile.set('birthDay', new Date('07/01/1952'));
41	Profile.set('friendCount', 66);
42	Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
43	Profile.set('luckyNumbers', [22, 77]);
44	Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
45	Profile.set('premiumMembership', Membership);
46	await Profile.save();
47	
48	// Eddie Murphy
49	Profile = new Parse.Object('Profile');
50	Profile.set('name', 'Eddie Murphy');
51	Profile.set('birthDay', new Date('04/03/1961'));
52	Profile.set('friendCount', 49);
53	Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
54	Profile.set('luckyNumbers', [6, 5]);
55	Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
56	await Profile.save();
57	
58	// Fergie
59	Profile = new Parse.Object('Profile');
60	Profile.set('name', 'Fergie');
61	Profile.set('birthDay', new Date('03/27/1975'));
62	Profile.set('friendCount', 55);
63	Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
64	Profile.set('luckyNumbers', [13, 7]);
65	Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
66	await Profile.save();
67	
68	console.log('Success!');

```

After running this code, you should now have a Profile class in your database with six objects created. Your new class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/azwzv3mzfo1D70FbLHziz_image.png)

Let’s now take a look at examples from every Parse.Query method, along with brief explanations on what they do. Please note that some methods in this list can take options as an additional argument, but in most cases, it is only related to masterKey usage and not relevant to this guide content, so this possibility will be omitted whenever not relevant.

## Query retrievers

These methods are responsible for running the query and retrieving its results, being always present in your query implementation.

:::CodeblockTabs
cancel

```javascript
// Cancels the current network request.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.find();
3	// This is hard to test in small databases, since by the time
4	// "cancel" is called, the "find" request is already resolved
5	queryResult = query.cancel();
6	console.log(queryResult);
```

count

```javascript
// Retrieves the count of Parse.Object results that meet the query criteria.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.count();
3	console.log(queryResult);
```

distinct

```javascript
// Runs the query and returns a list of unique values from the results and the specified key.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.distinct('favoriteFoods');
3	console.log(queryResult);
```

find

```javascript
// This is the basic method for retrieving your query results, always returning an array of Parse.Object instances, being empty when none are found.
1	let query = new Parse.Query('Profile');
2	// When using .find() in a query without other operations, you will
3	// get every object saved in your class
4	let queryResult = await query.find();
5	console.log(queryResult);
```

findAll

```javascript
// Retrieves a complete list of Parse.Objects that satisfy this query.
1	let query = new Parse.Query('Profile');
2	// When using .findAll() in a query without other operations, you will
3	// get every object saved in your class
4	let queryResult = await query.findAll();
5	console.log(queryResult);
```

&#x20;first

```javascript
// Retrieves the first Parse.Object instance that meets the query criteria.
1	let query = new Parse.Query('Profile');
2	// Pay attention to your query ordering when using .first
3	let queryResult = await query.first();
4	console.log(queryResult);
```

get

```javascript
// This quick method is used to retrieve a single Parse.Object instance when you know its objectId.
1	let query = new Parse.Query('Profile');
2	// Since objectId is randomly set in your database, make sure to inform a
3	// valid one when testing this method
4	let queryResult = await query.get('C6ENdLnFdQ');
5	console.log(queryResult);		
```
:::

## Query conditioners

These methods give you the possibility of applying conditional constraints to your query, which are arguably the most important operations in querying. Remember that these operations can all be chained before the results are retrieved, so many combinations can be achieved to solve your querying needs.

:::CodeblockTabs
\_addCondition

```javascript
// Helper that is called by Parse for filtering objects using conditional constants, such as “$gt”, “$eq” and so on. Only usable by users in very specific cases.
1	let query = new Parse.Query('Profile');
2	query._addCondition('friendCount', '$gt', 25);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

\_regexStartWith

```javascript
// Helper used by Parse that converts string for regular expression at the beginning.1	let query = new Parse.Query('Profile');
1	let query = new Parse.Query('Profile');
2	let result = query._regexStartWith('text');
3	console.log(result);
```

containedBy

```javascript
// Filters objects in which a key value must be contained by the provided array of values. Get objects where all array elements match.
1	let query = new Parse.Query('Profile');
2	query.containedBy('luckyNumbers', [2, 7]);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

containedIn

```javascript
// Filters objects in which a key value is contained in the provided array of values.
1	let query = new Parse.Query('Profile');
2	// containedIn can be used on any data type such as numbers and strings
3	query.containedIn('luckyNumbers', [2, 7]);
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

:::CodeblockTabs
contains

```javascript
// Filters objects in which a string key value contains the provided text value. Be aware that this condition is case-sensitive.
1	let query = new Parse.Query('Profile');
2	// This can be slow in large databases and are case sensitive
3	query.contains('name', 'da');
4	let queryResult = await query.find();
5	console.log(queryResult);
```

&#x20;containsAll

```javascript
// Filters objects in which an array type key value must contain every value provided.
1	let query = new Parse.Query('Profile');
2	query.containsAll('luckyNumbers', [2, 7]);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

containsAllStartingWith

```javascript
// Filters objects in which an array type key value must contain every string value provided.
1	let query = new Parse.Query('Profile');
2	// These should be string values
3	query.containsAllStartingWith('favoriteFoods', ['Shrimp', 'Lobster']);
4	let queryResult = await query.find();
5	console.log(queryResult);
```

doesNotExist

```javascript
// Filters objects in which a key value is not set.
1	let query = new Parse.Query('Profile');
2	query.doesNotExist('premiumMembership');
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

:::CodeblockTabs
doesNotMatchKeyInQuery

```javascript
// Requires that a key’s value does not match a value in an object returned by a different Parse.Query. Useful for multi-object querying.
1	let query = new Parse.Query('Profile');
2	// Multiple queries can be combined using this, useful when there are more objects
3	// related, not our example case
4	query.doesNotMatchKeyInQuery('friendCount', 'friendCount', new Parse.Query('Profile').lessThan('friendCount', 50));
5	query.greaterThan('friendCount', 10);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

&#x20;doesNotMatchQuery

```javascript
// Requires that an object contained in the given key does not match another query. Useful for multi-object querying.
1	let innerQuery = new Parse.Query('Membership');
2	innerQuery.greaterThan('expirationDate', new Date());
3	let query = new Parse.Query('Profile');
4	query.exists('premiumMembership');
5	query.doesNotMatchQuery('premiumMembership', innerQuery);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

&#x20;endsWith

```javascript
// Filters objects in which a string key’s value ends with the provided text value.
1	let query = new Parse.Query('Profile');
2	// This is faster than other string searches by using backend index
3	query.endsWith('name', 'ie');
4	let queryResult = await query.find();
5	console.log(queryResult);
```

equalTo

```javascript
// Filters objects in which a specific key’s value is equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// equalTo can be used in any data type
3	query.equalTo('friendCount', 2);
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

:::CodeblockTabs
exists

```javascript
// Filters objects in which a key value is set.
1	let query = new Parse.Query('Profile');
2	query.exists('premiumMembership');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;fullText

```javascript
// Filters objects in which a string key’s value wrap matches the provided text value in it. It can have many customizable options to improve its results, like language specification and score order. Check the Parse docs to have a complete understanding of that.
1	let query = new Parse.Query('Profile');
2	// fullText can be very powerful in text search queries, but
3	// also slow in large datasets when its options are not optimized
4	query.fullText('name', 'Spears', { diacriticSensitive: false, caseSensitive: false });
5	// In order to sort you must use select and ascending ($score is required)
6	query.ascending('$score');
7	query.select('$score');
8	let queryResult = await query.find();
9	console.log(queryResult);
```

&#x20;greaterThan

```javascript
// Filters objects in which a specific key’s value is greater than the provided value.
1	let query = new Parse.Query('Profile');
2	// greaterThan can be used on numbers and dates
3	query.greaterThan('birthDay', new Date('08/19/1980'));
4	let queryResult = await query.find();
5	console.log(queryResult);
```

greaterThanOrEqualTo

```javascript
// Filters objects in which a specific key’s value is greater than or equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// greaterThanOrEqualTo can be used on numbers and dates
3	query.greaterThanOrEqualTo('friendCount', 49);
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

:::CodeblockTabs
lessThan

```javascript
// Filters objects in which a specific key’s value is lesser than the provided value.
1	let query = new Parse.Query('Profile');
2	// lessThan can be used on numbers and dates
3	query.lessThan('birthDay', new Date('08/19/1980'));
4	let queryResult = await query.find();
5	console.log(queryResult);
```

lessThanOrEqualTo

```javascript
// Filters objects in which a specific key’s value is lesser than or equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// lessThanOrEqualTo can be used on numbers and dates
3	query.lessThanOrEqualTo('friendCount', 49);
4	let queryResult = await query.find();
5	console.log(queryResult);
```

&#x20;matches

```javascript
// Filters objects in which a string type key value must match the provided regular expression and its modifiers.
1	let query = new Parse.Query('Profile');
2	// Using the "i" modifier is a quick way to achieve
3	// case insensitive string querying
4	query.matches('name', 'da', 'i');
5	let queryResult = await query.find();
6	console.log(queryResult);
```

matchesKeyInQuery

```javascript
// Requires that a key’s value matches a value in an object returned by a different Parse.Query. Useful for multi-object querying.
1	let query = new Parse.Query('Profile');
2	// Multiple queries can be combined using this, useful when there are more objects
3	// related, not our example case
4	query.matchesKeyInQuery('friendCount', 'friendCount', new Parse.Query('Profile').lessThan('friendCount', 50));
5	query.greaterThan('friendCount', 10);
6	let queryResult = await query.find();
7	console.log(queryResult);
```
:::

:::CodeblockTabs
matchesQuery

```javascript
// Requires that an object contained in the given key matches another query. Useful for multi-object querying.
1	let innerQuery = new Parse.Query('Membership');
2	innerQuery.greaterThan('expirationDate', new Date());
3	let query = new Parse.Query('Profile');
4	query.exists('premiumMembership');
5	query.matchesQuery('premiumMembership', innerQuery);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

notEqualTo

```javascript
// Filters objects in which a specific key’s value is not equal to the provided value.
1	let query = new Parse.Query('Profile');
2	// notEqualTo can be used in any data type
3	query.notEqualTo("friendCount", 2);
4	let queryResult = await query.find();
5	console.log(queryResult);
```

&#x20;startsWith

```javascript
// Filters objects in which a string key’s value starts with the provided text value.
1	let query = new Parse.Query('Profile');
2	// This is faster than other string searches by using backend index
3	query.startsWith('name', 'Brit');
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

## Query ordering

Essential in most queries, ordering can be easily achieved in Parse and even chained between two or more ordering constraints.

:::CodeblockTabs
addAscending

```javascript
// Sort the results in ascending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.addAscending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

addDescending

```javascript
// Sort the results in descending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.addDescending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

ascending

```javascript
// Sort the results in ascending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.ascending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

descending

```javascript
// Sort the results in descending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1	let query = new Parse.Query('Profile');
2	query.descending('friendCount');
3	let queryResult = await query.find();
4	console.log(queryResult);
```

sortByTextScore

```javascript
// Sorts by text score when using Parse.Query.fullText.
1	let query = new Parse.Query('Profile');
2	query.fullText('name', 'Dan', { diacriticSensitive: false, caseSensitive: false });
3	query.sortByTextScore();
4	let queryResult = await query.find();
5	console.log(queryResult);
```
:::

## Field selecting

These methods affect which field values can be in your query results.

:::CodeblockTabs
exclude

```javascript
// Return all fields in the returned objects except the ones specified.
1	let query = new Parse.Query('Profile');
2	query.exclude('name');
3	let queryResult = await query.find();
4	console.log(queryResult[0].get('name') === undefined);
5	console.log(queryResult[0].get('birthDay'));
```

include

```javascript
// Includes nested Parse.Objects for the provided key.
1	let query = new Parse.Query('Profile');
2	query.exists('premiumMembership');
3	query.include('premiumMembership');
4	let queryResult = await query.find();
5	console.log(queryResult[0].get('premiumMembership'));
```

&#x20;includeAll

```javascript
// Includes all nested Parse.Objects.
1	let query = new Parse.Query('Profile');
2	query.exists('premiumMembership');
3	query.includeAll();
4	let queryResult = await query.find();
5	console.log(queryResult[0].get('premiumMembership'));
```

&#x20;select

```javascript
// Return only the specified fields in the returned objects.
1	let query = new Parse.Query('Profile');
2	query.select('name');
3	let queryResult = await query.find();
4	console.log(queryResult[0].get('birthDay') === undefined);
5	console.log(queryResult[0].get('name'));
```
:::

## Geopoint querying

These are methods specific to GeoPoint querying.

:::CodeblockTabs
near

```javascript
// Order objects by how near the key value is from the given GeoPoint.
1	let query = new Parse.Query('Profile');
2	query.near('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
3	let queryResult = await query.find();
4	console.log(queryResult);
```

polygonContains

```javascript
// Find objects whose key value contains the specified GeoPoint.
1	let query = new Parse.Query('Profile');
2	query.polygonContains('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;withinGeoBox

```javascript
// Find objects whose key value is contained within the specified bounding box, composed by two GeoPoint values that set the lower-left and upper-right corner values.
1	let query = new Parse.Query('Profile');
2	query.withinGeoBox('lastLoginLocation', new Parse.GeoPoint(37.48412167489413, -122.11268034622319), new Parse.GeoPoint(37.28412167489413, -121.91268034622319));
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;withinKilometers

```javascript
// Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1	let query = new Parse.Query('Profile');
2	query.withinKilometers('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319), 100, true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

:::CodeblockTabs
withinMiles

```javascript
// Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1	let query = new Parse.Query('Profile');
2	query.withinMiles('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319), 60, true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

withinPolygon

```javascript
// Find objects whose key value is contained within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.
1	let query = new Parse.Query('Profile');
2	query.withinPolygon('lastLoginLocation', [new Parse.GeoPoint(37.48412167489413, -122.11268034622319), new Parse.GeoPoint(37.48412167489413, -121.91268034622319), new Parse.GeoPoint(37.28412167489413, -121.91268034622319), new Parse.GeoPoint(37.28412167489413, -122.01268034622319)]);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;withinRadians

```javascript
// Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1	let query = new Parse.Query('Profile');
2	query.withinRadians('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319), 1.5, true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## Pagination

These methods are related to pagination utilities, useful for queries that will retrieve a large number of results.

:::CodeblockTabs
limit

```javascript
// Sets the maximum value of returned results, the default value is 100.
1	let query = new Parse.Query('Profile');
2	query.limit(2);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

skip

```javascript
// Skips the first n results in the query, essential for pagination.
1	let query = new Parse.Query('Profile');
2	query.skip(2);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;withCount

```javascript
// Sets a flag that will wrap or not the query response in an object containing results, holding the array of Parse.Object and count integer holding the total number of results.
1	let query = new Parse.Query('Profile');
2	query.withCount(true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## Response handling

These methods are helpers for handling the query responses, making it possible to queue callbacks that will be called after your query is resolved. They act as query resolvers as well, like find and first.

:::CodeblockTabs
each

```javascript
// Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.each((result) => console.log(result));
3	console.log(queryResult);
```

eachBatch

```javascript
// Iterates over each result from the query and calls a callback for each batch of results, in an unspecified order. The batchSize value needs to be passed inside the options object parameter, being the default 100. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.eachBatch((result) => console.log(result), {batchSize: 2});
3	console.log(queryResult);
```

filter

```javascript
// Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises. Differs from each for passing more parameters down on callback execution.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.filter((currentObject, index, query) => console.log(`${index} - ${currentObject} - ${query}`));
3	console.log(queryResult);
```

&#x20;map

```javascript
// Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises. Differs from each for passing more parameters down on callback execution.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.map((currentObject, index, query) => console.log(`${index} - ${currentObject} - ${query}`));
3	console.log(queryResult);
```

reduce

```javascript
// Iterates over each result from the query and calls a callback for each one, in an unspecified order. Note that execution will halt on a rejected promise, so make sure to handle this case if using promises. Differs from each for passing more parameters down on callback execution and by allowing direct accumulator handling.
// The initialValue is the value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first object in the query will be used and skipped.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.reduce((accumulator, currentObject, index) => console.log(`${index} - ${currentObject} - ${accumulator}`));
3	console.log(queryResult);
```
:::

## Compound query

These methods will create compound queries, which can combine more than one Parse.Query instance to achieve more complex results.

:::CodeblockTabs
\_andQuery

```javascript
// Helper that is used by Parse to add a constraint that all of passed in queries must match when using Parse.Query.and.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = new Parse.Query('Profile');
6	query._andQuery([query1, query2]);
7	let queryResult = await query.find();
8	console.log(queryResult);
```

\_norQuery

```javascript
// Helper that is used by Parse when using Parse.Query.nor.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = new Parse.Query('Profile');
6	query._norQuery([query1, query2]);
7	let queryResult = await query.find();
8	console.log(queryResult);
```

\_orQuery

```javascript
// Helper that is used by Parse to add a constraint that any of passed in queries must match when using Parse.Query.or.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = new Parse.Query('Profile');
6	query._orQuery([query1, query2]);
7	let queryResult = await query.find();
8	console.log(queryResult);
```

&#x20;and

```javascript
// Compose a compound query that is the AND of the passed queries.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = Parse.Query.and(query1, query2);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

nor

```javascript
// Compose a compound query that is the NOR of the passed queries.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = Parse.Query.nor(query1, query2);
6	let queryResult = await query.find();
7	console.log(queryResult);
```

or

```javascript
// Compose a compound query that is the OR of the passed queries.
1	let query1 = new Parse.Query('Profile');
2	query1.greaterThan('friendCount', 10);
3	let query2 = new Parse.Query('Profile');
4	query1.lessThan('friendCount', 50);
5	let query = Parse.Query.or(query1, query2);
6	let queryResult = await query.find();
7	console.log(queryResult);
```
:::

## Database related

These methods are related to the database preferences and operations.

:::CodeblockTabs
aggregate

```javascript
// Executes an aggregate query, retrieving objects over a set of input values. Please refer to MongoDB documentation on aggregate(https://docs.mongodb.com/v3.2/reference/operator/aggregation/)for better understanding.
1	let query = new Parse.Query('Profile');
2	let queryResult = await query.aggregate({ limit: 5 });
3	console.log(queryResult);
```

explain

```javascript
// Investigates the query execution plan, related to MongoDB explain operation.
1	let query = new Parse.Query('Profile');
2	query.explain(true);
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;readPreference

```javascript
// When using a MongoDB replica set, use this method to choose from which replica the objects will be retrieved. The possible values are PRIMARY (default), PRIMARY_PREFERRED, SECONDARY, SECONDARY_PREFERRED, or NEAREST.
1	let query = new Parse.Query('Profile');
2	query.readPreference("PRIMARY");
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## Local datastore

These methods enable selecting the source of the queries and using a local datastore.

:::CodeblockTabs
fromLocalDatastore

```javascript
// Changes the source of this query to all pinned objects.
1	// This should be set before using fromLocalDatastore
2	Parse.enableLocalDatastore();
3	let query = new Parse.Query('Profile');
4	query.fromLocalDatastore();
5	let queryResult = await query.find();
6	console.log(queryResult);
```

fromNetwork

```javascript
// Changes the source of this query to your online server.
1	let query = new Parse.Query('Profile');
2	query.fromNetwork();
3	let queryResult = await query.find();
4	console.log(queryResult);

```

fromPin

```javascript
// Changes the source of this query to the default group of pinned objects.
1	let query = new Parse.Query('Profile');
2	query.fromPin();
3	let queryResult = await query.find();
4	console.log(queryResult);
```

&#x20;fromPinWithName

```javascript
// Changes the source of this query to a specific group of pinned objects.
1	let query = new Parse.Query('Profile');
2	query.fromPinWithName('pinnedObjects');
3	let queryResult = await query.find();
4	console.log(queryResult);
```
:::

## JSON specifics

Methods that allow queries to be represented as JSON and retrieved.

:::CodeblockTabs
toJSON

```json
// Returns a JSON representation of this query operations.
1	let query = new Parse.Query('Profile');
2	query.greaterThan('friendCount', 10);
3	let queryJSON = await query.toJSON();
4	console.log(queryJSON);
```

withJSON

```json
// Add a previously generated JSON representation of query operations to this query.
1	let query = new Parse.Query('Profile');
2	query.greaterThan('friendCount', 10);
3	let queryJSON = await query.toJSON();
4	let query2 = new Parse.Query('Profile');
5	query2.withJSON(queryJSON);
6	let queryResult = await query2.find();
7	console.log(queryResult);


// Static method to restore Parse.Query by JSON representation, internally calling Parse.Query.withJSON.
1	let query = new Parse.Query('Profile');
2	query.greaterThan('friendCount', 10);
3	let queryJSON = await query.toJSON();
4	let query2 = Parse.Query.withJSON('Profile', queryJSON);
5	let queryResult = await query2.find();
6	console.log(queryResult);
```
:::

## Conclusion

At the end of this guide, you learned how to perform every data query method in Parse. In the next guide, you will learn about relational Parse querying in React.

[title] Untitled
[path] Flutter/Parse SDK (REST)/


[title] Reset Password
[path] Flutter/Parse SDK (REST)/User Authentication/Authentication/

# How to add user reset password to a Flutter App

## Introduction

It’s a fact that as soon as you introduce passwords into a system, users will forget them. Parse Server provides a way to let them securely reset their password.
The password reset flow starts getting the user’s email address and calling the requestPasswordReset method from Parse.User class.
This will attempt to match the given email with the user’s email or username field and send them a password reset email. By doing this, you can opt to have users use their email as their username, or you can collect it separately and store it in the email field.

The flow for password reset is as follows:

1. **User requests that their password be reset by typing in their email.**
2. **Back4App sends an email to their address with a special password reset link.**
3. **User clicks on the reset link and is directed to a special Back4App page to type in a new password.**
4. **User types in a new password. Their password has now been reset to a value they specify.**

In this guide, you will learn how to use the **Flutter plugin for Parse Server** to implement user reset password feature using the ParseUser class for your Flutter App.

## Goal

Build a reset password feature using Parse for a Flutter App.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- A Flutter app created in previous guide
  - **Note: **Follow the <a href="https://app.archbee.com/docs/_roxIyUMXoBue9I7uv49e/HzbuRMIWCUKmJmsxuDirt" target="_blank">Get current User</a> on session.
- Complete the previous guide so ou can have a better understanding of the ParseUser class.
- A device (not Simulator) running Android or iOS.
:::

## Understanding Reset Password process

To better understand Reset Password process, we will continue the development of the application started in the previous guide and implement the function.

We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse. Following the next steps, you will build a Login e Logout App at Back4App Database.

## Let’s get started!

In the following steps, you will be able to build a reset password function in our application.

## 1 - Open the Login/Logout/Reset Password App Project

Open Flutter project from the previous guide [Get current User on session](https://www.back4app.com/docs/flutter/parse-sdk/flutter-current-user%22).

Go to the main.dart file.

## 2 - Code for Reset Password

To start the password reset flow, we need the user’s email. Search for the function doUserResetPassword in the file main.dart. Replace the code inside doUserResetPassword with:

```dart
1       final ParseUser user = ParseUser(null, null, controllerEmail.text.trim());
2       final ParseResponse parseResponse = await user.requestPasswordReset();
3       if (parseResponse.success) {
4         Message.showSuccess(
5             context: context,
6             message: 'Password reset instructions have been sent to email!',
7             onPressed: () {
8               Navigator.of(context).pop();
9             });
10      } else {
11        Message.showError(context: context, message: parseResponse.error!.message);
12      }
```

To build this function, follow these steps:

1. Create a newParseUser class instance with the command ParseUser(null, null, controllerEmail.text.trim());. The email field is required for the other fields you can use null.
2. Call theuser.requestPasswordReset function to send the recovery email.

The complete function should look like this:

```dart
1     void doUserResetPassword() async {
2       final ParseUser user = ParseUser(null, null, controllerEmail.text.trim());
3       final ParseResponse parseResponse = await user.requestPasswordReset();
4       if (parseResponse.success) {
5         Message.showSuccess(
6             context: context,
7             message: 'Password reset instructions have been sent to email!',
8             onPressed: () {
9               Navigator.of(context).pop();
10            });
11      } else {
12        Message.showError(context: context, message: parseResponse.error!.message);
13      }
14    }
```

:::hint{type="info"}
To test it, click on theRunbutton in Android Studio/VSCode.
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XYYxTqeltDKSytjtIrUL7_image.png" signedSrc size="50" width="321" height="635" position="center" caption}

Click on Reset Password button.

On the next screen enter the user’s email and click Reset Password again.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IH_Bhhsw25tcslVOTp4Me_image.png" signedSrc size="50" width="325" height="636" position="center" caption}

## It’s done!

At the end of this guide, you can implement password reset function of your app using Parse Server core features through Back4App!

[title] Current User
[path] React Native/Relay (GraphQL)/Users/

# Get Current User using Relay for a React Native App

## Introduction

After implementing user registration and login to your React Native App using Relay, you need to retrieve the currently logged user data to perform different actions and requests. In this guide we’re going to follow the [Get User Logged GraphQL Cookbook guide](https://www.back4app.com/docs/parse-graphql/graphql-get-current-user) and the [Query Renderer](https://www.back4app.com/docs/react-native/graphql/relay-query-renderer) to retrieve information about the current user.

The query as GraphQL is represented as:

```graphql
1	query Me {
2	  viewer {
3	    user{
4	      id
5	      createdAt
6	      updatedAt
7	      username
8	    }
9	    sessionToken
10	  }
11	}
```

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## Goal

Create a component to get information about the current user.

## Prerequisites

- An app created at Back4App using the Parse Server Version 3.10 or above.
- You have to conclude the <a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">Relay Environment setup tutorial</a>:
- You have to conclude the <a href="https://www.back4app.com/docs/react-native/graphql/users/react-native-login-sample" target="_blank">React Native Login sample using Relay</a>:
- You have to conclude the <a href="https://app.archbee.com/docs/_roxIyUMXoBue9I7uv49e/6qyoyUmFLdRtjjy7dcgnr" target="_blank">React Native User Logged</a>:
- For this tutorial we are going to use the Expo as a React Native framework;
- For this tutorial we are going to use Javascript as our default implementation language;

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## 1 - Creating the User Logged component

On SignIn component folder create a new file and name it UserLoggedRenderer.js.

Inside of UserLoggedRenderer.js, let’s create a component very similar to the Query Renderer tutorial, but in this case, just the query renderer is needed. With a valid session token into the application, the component will be called and will get the current user info.

The Query Renderer component will look like the following:

```javascript
1	return (
2	  <QueryRenderer
3	    environment={environment}
4	    query={// @todo implement the query necessary}
5	    variables={null}
6	    render={({error, props}) => {
7	      if (error) {
8	        return (
9	          <View>
10	            <Text>{error.message}</Text>
11	          </View>
12	        );
13	      } else if (props) {
14	        // @todo implement a funcion to render the viewer
15	      }
16	      return (
17	        <View>
18	          <Text>loading</Text>
19	        </View>
20	      );
21	    }}
22	  />
23	);
```

The first @todo is should be implemented with the query to retrieve the info from the user logged. The Query used for this operation is decribed in [Get User Logged GraphQL Cookbook guide](https://www.back4app.com/docs/parse-graphql/graphql-get-current-user).&#x20;

```graphql
1	graphql`
2	  query UserLoggedRendererQuery {
3	    viewer {
4	      user {
5	        id
6	        createdAt
7	        updatedAt
8	        username
9	      }
10	      sessionToken
11	    }
12	  }
13	
```

The second @todo should be implemented with a function that will render the info about the user only if exists. If there is no error we are going to return therenderContentfunction described below. The function will render the current user info(email and username) in a secure way.

```javascript
1	const renderContent = (viewer) => {
2	  if (!viewer?.user) {
3	    return null;
4	  }
5	
6	  const {user} = viewer;
7	
8	  return (
9	    <View style={ {marginTop: 15, alignItems: 'center'} }>
10	      <Text>User {user?.username || user?.email} logged</Text>
11	    </View>
12	  );
13	};
```

You should implement the function before the QueryRenderer component. The final result of the component should look like this:

```javascript
1	import React from 'react';
2	import {graphql, QueryRenderer} from 'react-relay';
3	import environment from '../../relay/environment';
4	import {Text, View} from 'react-native';
5	
6	const UserLoggedRenderer = () => {
7	  const renderContent = (viewer) => {
8	    if (!viewer?.user) {
9	      return null;
10	    }
11	
12	    const {user} = viewer;
13	
14	    return (
15	      <View style={ {marginTop: 15, alignItems: 'center'} }>
16	        <Text>User {user?.username || user?.email} logged</Text>
17	      </View>
18	    );
19	  };
20	
21	  return (
22	    <QueryRenderer
23	      environment={environment}
24	      query={graphql`
25	        query UserLoggedRendererQuery {
26	          viewer {
27	            user {
28	              id
29	              createdAt
30	              updatedAt
31	              username
32	            }
33	            sessionToken
34	          }
35	        }
36	      `}
37	      variables={null}
38	      render={({error, props}) => {
39	        if (error) {
40	          return (
41	            <View>
42	              <Text>{error.message}</Text>
43	            </View>
44	          );
45	        } else if (props) {
46	          return renderContent(props.viewer);
47	        }
48	        return (
49	          <View>
50	            <Text>loading</Text>
51	          </View>
52	        );
53	      }}
54	    />
55	  );
56	};
57	
58	export default UserLoggedRenderer;
```

## 2 - Importing the UserLoggedRenderer into SignIn component

Back into the FormSignIn component, replace the code which returns the current user info with the new User Logged component.

From

```javascript
1	  if (sessionToken) {
2	     console.warn(sessionToken);
3	     return (
4	       <View style={ { marginTop: 15, alignItems: 'center' } }>
5	         <Text>User logged</Text>
6	       </View>
7	     );
8	   }
```

To

```javascript
1   if (sessionToken) {
2     return <UserLoggedRenderer />;
3   }
```

Don’t forget to import theUserLoggedRenderer:

```javascript
1    import UserLoggedRenderer from './UserLoggedRenderer';
```

Now run yarn relay command to update with the new query:

yarn relay

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rphO0ccrDfSPmfDQ8kR4A_image.png" signedSrc size="70" width="636" height="268" position="center" caption}

Now, will be displayed the username or email with a valid session token. Otherwise, the component should not render and, the login will run.

Also, the useState userLogged can be removed from the code that not makes sense anymore.

Don’t forget to remove it from the onCompleted function into LogIn mutation.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HagVjHmYJezVZGVbukt_-_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

## Conclusion

The final result of this step should be the same as the last one. The info about it will be displayed but now followed by a username or email.

In the next doc, let’s create a button to do the log out of this user and clean the session, returning the app for login or sign-up flow.

[title] Untitled
[path] Android/


[title] Untitled
[path] Flutter/Parse SDK (REST)/User Authentication/


[title] Relational Queries
[path] iOS/Parse Swift SDK/Data Objects/

# Relational Queries

## Introduction

In the [previous guide](https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/query-cookbook) we detailed how we can perform miscellaneous queries on a Back4App Database. In this guide we focus on a specific type of query that involves objects with relations.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
- A basic iOS App to test queries
:::

## Goal

Query relational data stored on a Back4App Database using the ParseSwift SDK.

## 1 - Quick review about the Query\<U>class

Any query performed on a Back4App Database is done via the generic class Query\<U>. The generic parameter U (conforming to the ParseObject protocol) is the data type of the objects we are trying to retrieve from the database.

Given a data type like MyObject, we retrieve these objects from a Back4App Database in the following way

```swift
1   import ParseSwift
2
3   struct MyObject: ParseObject {
4     ...
5   }
6
7   let query = MyObject.query()
8
9   // Executes the query asynchronously
10  query.find { result in
11    // Handle the result (of type Result<[MyObject], ParseError>)
12  }
```

You can read more about the Query\<U> class [here at the official documentation](https://github.com/parse-community/Parse-Swift).&#x20;

## 2 - Save some data on a Back4App Database

Before we begin to execute queries, it is necessary to set up some data on a Back4App Database. We will store five types of objects:

:::CodeblockTabs
Author

```swift
1   struct Author: ParseObject {
2     ...
3     var name: String?
4   }
```

Book

```swift
1   struct Book: ParseObject {
2     ...
3     var title: String?
4     var publisher: Publisher?
5     var publishingDate: Date?
6   }
```

ISBD

```swift
1   struct ISBD: ParseObject {
2     ...
3     var isbn: String?
4     var book: Pointer<Book>?
5   }
```

Publisher

```swift
1   struct Publisher: ParseObject {
2     ...
3     var name: String?
4   }
```

BookStore

```swift
1   struct BookStore: ParseObject {
2     ...
3     var name: String?
4   }
```
:::

Additionally, in order to construct queries for relational data, we will implement the following relations

- **1:1 **relation between Book and ISBD.
- **1\:N **relation between Book and Publisher.
- **M\:N **relation between Book and Author.
- **M\:N **relation between BookStore and Book.

We now proceed to store some data on the Back4App Database. This step can be implemented using Swift or directly from your app’s console on the Back4App platform.

:::CodeblockTabs
Swift

```swift
//After setting up your XCode project, calling the following method should save some sample data on your Back4App Database
1   import ParseSwift
2
3   func saveSampleData() {
4     do {
5       // Authors
6       let aaronWriter = try Author(name: "Aaron Writer").save()
7       let beatriceNovelist = try Author(name: "Beatrice Novelist").save()
8       let caseyColumnist = try Author(name: "Casey Columnist").save()
9            
10      // Publishers
11      let acaciaPublishings = try Publisher(name: "Acacia Publishings").save()
12      let birchDistributions = try Publisher(name: "Birch Distributions").save()
13            
14      // Books with their corresponding ISBD
15      let aLoveStoryBook = try Book(
16        title: "A Love Story",
17        publisher: acaciaPublishings,
18        publishingDate: Date(string: "05/07/1998")
19      ).save()
20      .relation?.add("authors", objects: [aaronWriter]).save() // Establishes the M:N relatin between Book and Author
21
22      // Fetches the ISBD associated to aLoveStoryBook and establishes the 1:1 relation
23      if let book = aLoveStoryBook, var isbd = try book.fetch(includeKeys: ["isbd"]).isbd  {
24        isbd.book = try Pointer<Book>(book)
25        _ = try isbd.save()
26      } else {
27        fatalError()
28      }
29            
30      let benevolentElvesBook = try Book(
31        title: "Benevolent Elves",
32        publisher: birchDistributions,
33        publishingDate: Date(string: "11/30/2008")
34      ).save()
35      .relation?.add("authors", objects: [beatriceNovelist]).save() // Establishes the M:N relatin between Book and Author
36
37      // Fetches the ISBD associated to benevolentElvesBook and establishes the 1:1 relation
38      if let book = benevolentElvesBook, var isbd = try book.fetch(includeKeys: ["isbd"]).isbd  {
39        isbd.book = try Pointer<Book>(book)
40        _ = try isbd.save()
41      } else {
42        fatalError()
43      }
44             
45      let canYouBelieveItBook = try Book(
46        title: "Can You Believe It",
47        publisher: birchDistributions,
48        publishingDate: Date(string: "08/21/2018")
49      ).save()
50      .relation?.add("authors", objects: [aaronWriter, caseyColumnist]).save() // Establishes the M:N relatin between Book and Author
51
52      // Fetches the ISBD associated to canYouBelieveItBook and establishes the 1:1 relation
53      if let book = canYouBelieveItBook, var isbd = try book.fetch(includeKeys: ["isbd"]).isbd  {
54        isbd.book = try Pointer<Book>(book)
55        _ = try isbd.save()
56      } else {
57        fatalError()
58      }
59
60      // Book store
61      guard let safeALoveStoryBook = aLoveStoryBook,
62            let safeBenevolentElvesBook = benevolentElvesBook,
63            let safeCanYouBelieveItBook = canYouBelieveItBook
64      else {
65        throw NSError(
66          domain: Bundle.main.description,  
67          code: 0,
68          userInfo: [NSLocalizedDescriptionKey: "Failed to unwrapp stored books."]
69        )
70      }
71
72      // Saves the stores together with their 1:N relation with Book's 
73      let booksOfLove = try BookStore(name: "Books of Love").save()
74      _ = try booksOfLove.relation?.add("books", objects: [safeALoveStoryBook]).save()
75              
76      let fantasyBooks = try BookStore(name: "Fantasy Books").save()
77      _ = try fantasyBooks.relation?.add("books", objects: [safeBenevolentElvesBook]).save()
78              
79      let generalBooks = try BookStore(name: "General Books").save()
80      _ = try generalBooks.relation?.add("books", objects: [safeALoveStoryBook, safeCanYouBelieveItBook]).save()
81            
82    } catch let error as ParseError {
83      print("ERROR:\n", error.message)
84    } catch {
85      print("ERROR:\n", error.localizedDescription)
86    }
87  }
```

Back4App's console

```swift
//A quick way to insert elements on your Back4App Database is via the console located in your App’s API section. Once you are there, you can start running Javascript code to save the sample data
1   // Authors
2   const aaronWriter = new Parse.Object('Author');
3   aaronWriter.set('name', 'Aaron Writer');
4   await aaronWriter.save();
5
6   const beatriceNovelist = new Parse.Object('Author');
7   beatriceNovelist.set('name', 'Beatrice Novelist');
8   await beatriceNovelist.save();
9
10  const caseyColumnist = new Parse.Object('Author');
11  caseyColumnist.set('name', 'Casey Columnist');
12  await caseyColumnist.save();
13
14  // Publishers
15  const acaciaPublishings = new Parse.Object('Publisher');
16  acaciaPublishings.set('name', 'Acacia Publishings');
17  await acaciaPublishings.save();
18
19  const birchDistributions = new Parse.Object('Publisher');
20  birchDistributions.set('name', 'Birch Distributions');
21  await birchDistributions.save();
22
23  // Books with their corresponding ISBD
24  const aLoveStoryISBD = new Parse.Object('ISBD');
25  aLoveStoryISBD.set('isbn', '9781401211868');
26  await aLoveStoryISBD.save();
27
28  const aLoveStoryBook = new Parse.Object('Book');
29  aLoveStoryBook.set('title', 'A Love Story');
30  aLoveStoryBook.set('publisher', acaciaPublishings);
31  aLoveStoryBook.set('publishingDate', new Date('05/07/1998'));
32  aLoveStoryBook.set('isbd', aLoveStoryISBD);
33  const bookARelation = aLoveStoryBook.relation("authors");
34  bookARelation.add(aaronWriter);
35  await aLoveStoryBook.save();
36  aLoveStoryISBD.set('book', aLoveStoryBook.toPointer());
37  await aLoveStoryISBD.save();
38
39  const benevolentElvesISBD = new Parse.Object('ISBD');
40  benevolentElvesISBD.set('isbn', '9781401211868');
41  await benevolentElvesISBD.save();
42
43  const benevolentElvesBook = new Parse.Object('Book');
44  benevolentElvesBook.set('title', 'Benevolent Elves');
45  benevolentElvesBook.set('publisher', birchDistributions);
46  benevolentElvesBook.set('publishingDate', new Date('11/31/2008'));
47  benevolentElvesBook.set('isbd', benevolentElvesISBD);
48  const bookBRelation = benevolentElvesBook.relation("authors");
49  bookBRelation.add(beatriceNovelist);
50  await benevolentElvesBook.save();
51  benevolentElvesISBD.set('book', benevolentElvesBook.toPointer());
52  await benevolentElvesISBD.save();
53  
54  const canYouBelieveItISBD = new Parse.Object('ISBD');
55  canYouBelieveItISBD.set('isbn', '9781401211868');
56  await canYouBelieveItISBD.save();
57
58  const canYouBelieveItBook = new Parse.Object('Book');
59  canYouBelieveItBook.set('title', 'Can You Believe It?');
60  canYouBelieveItBook.set('publisher', birchDistributions);
61  canYouBelieveItBook.set('publishingDate', new Date('08/21/2018'));
62  canYouBelieveItBook.set('isbd', canYouBelieveItISBD);
63  const bookCRelation = canYouBelieveItBook.relation("authors");
64  bookCRelation.add(aaronWriter);
65  bookCRelation.add(caseyColumnist);
66  await canYouBelieveItBook.save();
67  canYouBelieveItISBD.set('book', canYouBelieveItBook.toPointer());
68  await canYouBelieveItISBD.save();
69
70  // Book store
71  const booksOfLoveStore = new Parse.Object('BookStore');
72  booksOfLoveStore.set('name', 'Books of Love');
73  const bookStoreARelation = booksOfLoveStore.relation("books");
74  bookStoreARelation.add(aLoveStoryBook);
75  await booksOfLoveStore.save();
76
77  const fantasyBooksStore = new Parse.Object('BookStore');
78  fantasyBooksStore.set('name', 'Fantasy Books');
79  const bookStoreBRelation = fantasyBooksStore.relation("books");
80  bookStoreBRelation.add(benevolentElvesBook);
81  await fantasyBooksStore.save();
82
83  const generalBooksStore = new Parse.Object('BookStore');
84  generalBooksStore.set('name', 'General Books');
85  const bookStoreCRelation = generalBooksStore.relation("books");
86  bookStoreCRelation.add(aLoveStoryBook);
87  bookStoreCRelation.add(canYouBelieveItBook);
88  await generalBooksStore.save();
```
:::

## 3 - Query the data

Once the database has some sample data to work with, we start executing the different kinds of queries associated with the relations detailed earlier.

**Queries involving 1:1 relations**

Given two data types sharing a **1:1** relation (Book and ISBD in this case), we can retrieve one from the other as follows. The way we implemented the relation in Book allows us to retrieve its related ISBD object simply by calling the include(\_:) method on the query. Let us retrieve the ISBD from the book **A Love Story**:

```swift
1   let aLoveStoryBookQuery = Book.query("title" == "A Love Story").include("isbd") // Note how we include the ISBD with the include(_:) method
2
3   let book = try? aLoveStoryBookQuery.first() // Retrieves synchronously the book including its ISBD
4
5   aLoveStoryBookQuery.first { result in // Retrieves asynchronously the book including its ISBD
6     // Handle the result (of type Result<Book, ParseError>)
7   }
```

On the other hand, a query to retrieve a Book object related to a given ISBD is implemented in the following way. By looking at the implementation of ISBD, we note that the relation is represented by the book property (of type Pointer\<book>). This pointer provides a set of methods and properties to retrieve information about the object it points to. In particular, we call the fetch(...) method on the book property to fetch the associated Book

```swift
1   let someISBD: ISBD
2
3   let book: Book? = try? someISBD.book?.fetch() // Retrieves synchronously the book asscociated to someISBD
4
5   someISBD.book?.fetch { result in // Retrieves asynchronously the book asscociated to someISBD
6     // Handle the result (of type Result<Book, ParseError>)
7   }
```

We should remark that this implemetation for a **1:1** relation is not unique. Depending on your use case, you can implement **1:1** relations in different ways.

**Queries involving 1\:N relations**

In a scenario where we need to query all the books published by a given publisher, we first need to retrieve the publisher. For instance, we first retrieve the data object associated with the publisher **Acacia Publishings**. Depending on the situation, this procces may vary

```swift
1   do {
2     // Using the object's objectId
3     let acaciaPublishings = try Publisher(objectId: "SOME_OBJECT_ID").fetch()
4
5     // Or
6     // Using a Query
7     let acaciaPublishings = try Publisher.query("name" == "Acacia Publishings").first() // Returns (synchronously) the first Publisher with name 'Acacia Publishings'. The constraint is constructed using the == operator provided by the ParseSwift SDK
8  
9     ... // To be completed below
10  } catch {
11  // Hanlde the error (of type ParseError)
12  }
```

Now that we have access to acaciaPublishings, we can construct the query to retrieve its related books. We proceed to create the query by instantiating a Query\<Book> class. In this case, this class is instantiated using the static method query(...) provided by the Book object. The (variadic) arguments for this method are the standard QueryConstraint objects. Therefore, the books we are looking for are retrieved with the following snippet

```swift
1   do {
2     let acaciaPublishings = try Publisher.query("name" == "Acacia Publishings").first() // Returns the first Publisher with name 'Acacia Publishings'
3
4     let constraint: QueryConstraint = try "publisher" == publisher
5     let query = Book.query(constraint) // Creates the query to retrieve all Book objects where its publisher field equalt to 'acaciaPublishings'
6
7     let books: [Book] = try query.find() // Executes the query synchronously
8
9     // books should contain only one element: the book 'A Love Story'
10  } catch {
11    // Hanlde the error (of type ParseError)
12  }
```

An asynchronous implentation for the above snippet may be written in the following way

```swift
1   // We retrieve the Publisher with name 'Acacia Publishings'
2   Publisher.query("name" == "Acacia Publishings").first { result in
3     switch result {
4     case .success(let publisher):
5       guard let constraint: QueryConstraint = try? "publisher" == publisher else { fatalError() }
6
7       // Then, we retrieve the books with the corresponding constraint          
8       Book.query(constraint).find { result in
9         switch result {
10        case .success(let books):
11          // books should contain only one element: the book 'A Love Story'
12          break
13        case .failure(let error):
14          // handle the error (of type ParseError)
15          break
16        }
17      }
18
19    case .failure(let error):
20      // handle the error (of type ParseError)
21      break
22    }
23  }
```

**Queries involving M\:N relations (Case 1)**

To illustrate this case, we consider the following scenario; we want to list all the stores containing books published after a given date (e.g., **01/01/2010**). Firstly we require an intermediate query to select the books. Next, we construct the main query to list the stores.

Therefore, we prepare the first query for the books

```swift
1   let booksQuery = Book.query("publishingDate" > Date(string: "01/01/2010")) // We construct the date constraint using the > operator provided by the ParseSwift SDK
2        
3   do {
4     let books = try booksQuery.find()
5     ... // To be completed below
6   } catch let error as ParseError {
7     // Handle any potential error
8   } catch {
9     // Handle any potential error
10  }
```

We then construct the stores’ query using booksQuery’s results. The method containedIn(\_:array:) returns the constraint we need for this case

```swift
1   let booksQuery = Book.query("publishingDate" > Date(string: "01/01/2010")) // We construct the date constraint using the > operator provided by the ParseSwift SDK
2        
3   do {
4     let books = try booksQuery.find()
5  
6     // Here is where we construct the stores' query with the corresponding constraint
7     let storesQuery = BookStore.query(try containedIn(key: "books", array: books))
8    
9     let stores = try storesQuery.find()
10    
11    // stores should containt only one element: the 'General Books' BookStore 
12  } catch let error as ParseError {
13    // Handle any potential error
14  } catch {
15    // Handle any potential error
16  }
```

Similarly, we can implement this process asynchronously

```swift
1   let booksQuery = Book.query("publishingDate" > Date(string: "01/01/2010")) // We construct the date constraint using the > operator provided by the ParseSwift SDK
2
3   booksQuery.find { result in
4     switch result {
5     case .success(let books):
6       guard let constraint = try? containedIn(key: "books", array: books) else { fatalError() }
7       let storesQuery = BookStore.query(constraint)
8                
9       storesQuery.find { result in
10        switch result {
11        case .success(let stores):
12        
13        case .failure(let error):
14          // Handle the error (of type ParseError)
15        }
16      }
17    case .failure(let error):
18      // Handle the error (of type ParseError)
19    }
20  }
```

**Queries involving M\:N relations (Case 2)**

Suppose we need to select all the stores that have books written by a given author, say, **Aaron Writer**. In order to achieve this, we require two additional queries:

- A query (Query\<Author>) to obtain the object associated with the author Aaron Writer.
- A query (Query\<Book>)to select all the books written by **Aaron Writer**.
- The main query (Query\<BookStore>) to select the stores we are looking for.

The procedure to implement these queries are very similar to the previous ones:

```swift
1   let authorQuery = Author.query("name" == "Aaron Writer") // The first query to retrieve the data object associated to 'Aaron Writer'
2
3   do {
4     let aaronWriter = try authorQuery.first()
5            
6     let booksQuery = Book.query(try containedIn(key: "authors", array: [aaronWriter])) // The second query to retrieve the books written by 'Aaron Writer'
7            
8     let books = try booksQuery.find()
9            
10    let storesQuery = BookStore.query(try containedIn(key: "books", array: books)) // The main query to select the stores where the author ('Aaron Writer') has his books available
11  
12    let stores = try storesQuery.find()
13            
14    // stores should contain two items: 'Books of Love' and 'General Books'
15  } catch let error as ParseError {
16    // Handle the error
17  } catch {
18    // Handle the error
19  }
```

## Conclusion

With the ParseSwift SDK, we were able to construct relational queries that allowed us to select items based on the type of relations they have with other data types.

[title] SignIn with Facebook
[path] Flutter/Parse SDK (REST)/User Authentication/Third party Authentication/

# Setting up Facebook login on your Flutter app using Parse

## Introduction

Parse Server supports 3rd party authentication. In this guide you will learn how to setup Facebook authentication/login on your Flutter app using Parse.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- A Flutter app created and connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- \[Facebook for Developers Account] (https\://developers.facebook.com)
- Set up the plugin <a href="https://pub.dev/packages/flutter_facebook_auth" target="_blank">flutter\_facebook\_auth</a> on project.
- A device (not Simulator) running Android or iOS.
:::

## Goal

To add Facebook login on your Flutter app using Parse Server

## 1 - Configure Facebook Login for Android - Quickstart

- Follow instructions the plugin [flutter\_facebook\_auth](https://pub.dev/packages/flutter_facebook_auth)for Android on link [https://facebook.meedu.app/#/android](https://facebook.meedu.app/#/android)

## 2 - Configure Facebook Login for Android - Quickstart

- Follow instructions the plugin [flutter\_facebook\_auth](https://pub.dev/packages/flutter_facebook_auth)for iOS on link [https://facebook.meedu.app/#/ios](https://facebook.meedu.app/#/ios)

## 3 - Set up Parse Auth for Facebook

Go to Back4App website, log in and then find your app. After that, click on Server Settings and search for the Facebook Login block and select Settings.

The Facebook Login section looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IVkJfZPOVXwJ-NTOFUcPf_image.png" signedSrc size="50" width="286" height="343" position="center" caption}

Now, you just need to paste your Facebook appId in the field below and click on the button to save.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_YzitRpHt4srxz4jfpJiH_image.png" signedSrc size="60" width="630" height="500" position="center" caption}

In case you face any trouble while integrating Facebook Login, please contact our team via chat!

## 4 - Add the Sign In with Facebook

Now that you have the project set up, we can get the user data and sign in to Parse.

According to the documentation, we must send a Map with user authentication data.

```dart
1         //Make a Login request
2         final LoginResult result = await FacebookAuth.instance.login();
3
4         if (result.status != LoginStatus.success) {
5           Message.showError(context: context, message: result.message!);
6           return;
7         }
8
9         final AccessToken accessToken = result.accessToken!;
10
11        //https://docs.parseplatform.org/parse-server/guide/#facebook-authdata
12        //According to the documentation, we must send a Map with user authentication data.
13
14        //Make sign in with Facebook
15        final parseResponse = await ParseUser.loginWith('facebook',
16            facebook(accessToken.token, accessToken.userId, accessToken.expires));
```

## 5 - Sign in with Facebook from Flutter

Let’s now use our example for Sign in with Facebook in Flutter App, with a simple interface.

Open your Flutter project, go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/foundation.dart' show kIsWeb;
2   import 'package:flutter/material.dart';
3   import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
4   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
5
6   void main() async {
7     WidgetsFlutterBinding.ensureInitialized();
8
9     final keyApplicationId = 'YOUR_APP_ID_HERE';
10    final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
11    final keyParseServerUrl = 'https://parseapi.back4app.com';
12
13    await Parse().initialize(keyApplicationId, keyParseServerUrl,
14        clientKey: keyClientKey, debug: true);
15  
16    if (kIsWeb) {
17      // initialiaze the facebook javascript SDK
18      FacebookAuth.i.webInitialize(
19        appId: "YOUR_FACEBOOK_APP_ID", //<-- YOUR APP_ID
20        cookie: true,
21        xfbml: true,
22        version: "v9.0",
23      );
24    }
25
26    runApp(MyApp());
27  }
28
29  class MyApp extends StatelessWidget {
30    Future<bool> hasUserLogged() async {
31      ParseUser? currentUser = await ParseUser.currentUser() as ParseUser?;
32      if (currentUser == null) {
33        return false;
34      }
35      //Checks whether the user's session token is valid
36     final ParseResponse? parseResponse =
37          await ParseUser.getCurrentUserFromServer(currentUser.sessionToken!);
38 
39      if (parseResponse?.success == null || !parseResponse!.success) {
40        //Invalid session. Logout
41        await currentUser.logout();
42        return false;
43      } else {
44        return true;
45      }
46    }
47  
48    @override
49    Widget build(BuildContext context) {
50      return MaterialApp(
51        title: 'Flutter - Sign In with Facebook',
52        theme: ThemeData(
53          primarySwatch: Colors.blue,
54          visualDensity: VisualDensity.adaptivePlatformDensity,
55        ),
56        home: FutureBuilder<bool>(
57            future: hasUserLogged(),
58            builder: (context, snapshot) {
59              switch (snapshot.connectionState) {
60                case ConnectionState.none:
61                case ConnectionState.waiting:
62                  return Scaffold(
63                    body: Center(
64                      child: Container(
65                          width: 100,
66                          height: 100,
67                          child: CircularProgressIndicator()),
68                    ),
69                  );
70                default:
71                  if (snapshot.hasData && snapshot.data!) {
72                    return UserPage();
73                  } else {
74                    return HomePage();
75                  }
76              }
77            }),
78      );
79    }
80  }
81 
82  class HomePage extends StatefulWidget {
83    @override
84    _HomePageState createState() => _HomePageState();
85  }
86
87  class _HomePageState extends State<HomePage> {
88    @override
89    Widget build(BuildContext context) {
90      return Scaffold(
91          appBar: AppBar(
92            title: const Text('Flutter - Sign In with Facebook'),
93          ),
94          body: Center(
95            child: SingleChildScrollView(
96              padding: const EdgeInsets.all(8),
97              child: Column(
98                crossAxisAlignment: CrossAxisAlignment.stretch,
99                children: [
100                 Container(
101                   height: 200,
102                   child: Image.network(
103                       'http://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
104                 ),
105                 Center(
106                   child: const Text('Flutter on Back4App',
107                       style:
108                           TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
109                 ),
110                 SizedBox(
111                   height: 100,
112                 ),
113                 Container(
114                   height: 50,
115                   child: ElevatedButton(
116                     child: const Text('Sign In with Facebook'),
117                     onPressed: () => doSignInFacebook(),
118                   ),
119                 ),
120                 SizedBox(
121                   height: 16,
122                 ),
123               ],
124             ),
125           ),
126         ));
127   }
128 
129   void doSignInFacebook() async {
130     late ParseResponse parseResponse;
131     try {
132       //Check if the user is logged.
133       final AccessToken? currentAccessToken =
134           await FacebookAuth.instance.accessToken;
135       if (currentAccessToken != null) {
136         //Logout
137         await FacebookAuth.instance.logOut();
138       }
139 
140       //Make a Login request
141       final LoginResult result = await FacebookAuth.instance.login();
142 
143       if (result.status != LoginStatus.success) {
144         Message.showError(context: context, message: result.message!);
145         return;
146       }
147
148       final AccessToken accessToken = result.accessToken!;
149
150       //https://docs.parseplatform.org/parse-server/guide/#facebook-authdata
151       //According to the documentation, we must send a Map with user authentication data.
152
153       //Make sign in with Facebook
154       parseResponse = await ParseUser.loginWith('facebook',
155           facebook(accessToken.token, accessToken.userId, accessToken.expires));
156
157       if (parseResponse.success) {
158         final ParseUser parseUser = await ParseUser.currentUser() as ParseUser;
159
160         //Get user data from Facebook
161         final userData = await FacebookAuth.instance.getUserData();
162 
163         //Additional Information in User
164         if (userData.containsKey('email')) {
165           parseUser.emailAddress = userData['email'];
166         }
167 
168         if (userData.containsKey('name')) {
169           parseUser.set<String>('name', userData['name']);
170         }
171         if (userData["picture"]["data"]["url"] != null) {
172           parseUser.set<String>('photoURL', userData["picture"]["data"]["url"]);
173         }
174 
175         await FacebookAuth.instance.logOut();
176         parseResponse = await parseUser.save();
177 
178         if (parseResponse.success) {
179           Message.showSuccess(
180               context: context,
181               message: 'User was successfully with Sign In Facebook!',
182               onPressed: () async {
183                 Navigator.pushAndRemoveUntil(
184                   context,
185                   MaterialPageRoute(builder: (context) => UserPage()),
186                   (Route<dynamic> route) => false,
187                 );
188               });
189         } else {
190           Message.showError(
191               context: context, message: parseResponse.error!.message);
192         }
193       } else {
194         Message.showError(
195             context: context, message: parseResponse.error!.message);
196       }
197     } on Exception catch (e) {
198       print(e.toString());
199       Message.showError(context: context, message: e.toString());
200     }
201   }
202 }
203
204 class UserPage extends StatelessWidget {
205   Future<ParseUser?> getUser() async {
206     return await ParseUser.currentUser() as ParseUser?;
207   }
208
209   @override
210   Widget build(BuildContext context) {
211     void doUserLogout() async {
212       final currentUser = await ParseUser.currentUser() as ParseUser;
213       var response = await currentUser.logout();
214       if (response.success) {
215         Message.showSuccess(
216             context: context,
217             message: 'User was successfully logout!',
218             onPressed: () {
219               Navigator.pushAndRemoveUntil(
220                 context,
221                 MaterialPageRoute(builder: (context) => HomePage()),
222                 (Route<dynamic> route) => false,
223               );
224             });
225       } else {
226         Message.showError(context: context, message: response.error!.message);
227       }
228     }
229 
230     return Scaffold(
231         appBar: AppBar(
232           title: Text('Flutter - Sign In with Facebook'),
233         ),
234         body: FutureBuilder<ParseUser?>(
235             future: getUser(),
236             builder: (context, snapshot) {
237               switch (snapshot.connectionState) {
238                 case ConnectionState.none:
239                 case ConnectionState.waiting:
240                   return Center(
241                     child: Container(
242                         width: 100,
243                         height: 100,
244                         child: CircularProgressIndicator()),
245                   );
246                 default:
247                   return Padding(
248                     padding: const EdgeInsets.all(8.0),
249                     child: Column(
250                       crossAxisAlignment: CrossAxisAlignment.stretch,
251                       mainAxisAlignment: MainAxisAlignment.center,
252                       children: [
253                         Center(
254                             child: Text(
255                                 'Hello, ${snapshot.data!.get<String>('name')}')),
256                         SizedBox(
257                           height: 16,
258                         ),
259                         Container(
260                           height: 50,
261                           child: ElevatedButton(
262                             child: const Text('Logout'),
263                             onPressed: () => doUserLogout(),
264                           ),
265                         ),
266                       ],
267                     ),
268                   );
269               }
270             }));
271   }
272 }
273 
274 class Message {
275   static void showSuccess(
276       {required BuildContext context,
277       required String message,
278       VoidCallback? onPressed}) {
279     showDialog(
280       context: context,
281       builder: (BuildContext context) {
282         return AlertDialog(
283           title: const Text("Success!"),
284           content: Text(message),
285           actions: <Widget>[
286             new ElevatedButton(
287               child: const Text("OK"),
288               onPressed: () {
289                 Navigator.of(context).pop();
290                 if (onPressed != null) {
291                   onPressed();
292                 }
293               },
294             ),
295           ],
296         );
297       },
298     );
299   }
300
301   static void showError(
302       {required BuildContext context,
303       required String message,
304       VoidCallback? onPressed}) {
305     showDialog(
306       context: context,
307       builder: (BuildContext context) {
308         return AlertDialog(
309           title: const Text("Error!"),
310           content: Text(message),
311           actions: <Widget>[
312             new ElevatedButton(
313               child: const Text("OK"),
314               onPressed: () {
315                 Navigator.of(context).pop();
316                 if (onPressed != null) {
317                   onPressed();
318                 }
319               },
320             ),
321           ],
322         );
323       },
324     );
325   }
326 }
```

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId =** App Id
- **keyClientKey =** Client Key

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ru2vj4PCs76KF-pcyEAwv_image.png" signedSrc size="50" width="313" height="629" position="center" caption}

## Conclusion

At this stage, you are able to use Sign in with Facebook in Flutter on Back4app.

[title] Untitled
[path] Flutter/Parse SDK (REST)/User Authentication/


[title] from client side
[path] Android/Push Notifications/

# Send Parse push notifications from client side

## Introduction

Client Push is a feature that is available on Back4App’s Parse API, however it is not
enabled by default due to security issues.

Enabling Client Push and allowing your App to
use its features is quite simple, but not encouraged. The main function of Client Push is
for debugging and test purposes.

In this tutorial an example app will be built and this is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MiXeAo_L2BZYYjyi7Re5o_client-push.gif" signedSrc size="50" width="298" height="578" position="center" caption}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our <a href="https://github.com/back4app/android-cloud-code-push" target="_blank">Github repository.</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.0 (Ice Cream Sandwich) or newer.
:::

## 1 - Enable Client Push

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on Server Settings.
2. Find the “Core Settings” block and click on SETTINGS. The “Core Settings” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nF_OiqB6hiRbYzmlexEDn_image.png)

&#x20;    3\. Scroll to the end of the page and click on the EDIT DETAILS button, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YsCu9POQ8kNSYOLmVbtDI_image.png)

&#x20;   4\. You will see a checkbox called Allow Push Notification from Client in the end of the edit page, tick that box and click on the SAVE button, as shown below:



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2fBoraTPhR0mXsezJeGDz_image.png)

## 2 - Push from your app

1. At the beginning of your activity, import the following dependencies:

> *// Imports to the JSONObject object, necessary for the push message*
>
>
>
>
> **import**
>
>  org.json.JSONException;
>
>
> **import**
>
>  org.json.JSONObject;
>
>
> *// Parse Dependencies*
>
>
>
>
> **import**
>
>  com.parse.ParsePush;

2\. Use ParsePush to send the push message, as shown in the following code:

:::hint{type="info"}
Remember to set up the channels and the message.
:::

```json
1   JSONObject data = new JSONObject();
2   // Put data in the JSON object
3   try {
4    data.put("alert", "Back4App Rocks!");
5    data.put("title", "Hello from Device");
6   } catch ( JSONException e) {
7    // should not happen
8    throw new IllegalArgumentException("unexpected parsing error", e);
9   }
10  // Configure the push
11  ParsePush push = new ParsePush();
12  push.setChannel("News");
13  push.setData(data);
14  push.sendInBackground();
```

&#x20;    3\. To test the push notifications, just call that function while the device is opened.

## It’s done!

At this stage, you can send push notifications using your own device with Client Push through Back4App!

:::hint{type="info"}
**To learn more about Android push notification, see **<a href="http://docs.parseplatform.org/android/guide/#push-notifications" target="_blank">**Parse Android Push Notification Official Documentation**</a>**.**
:::


[title] Relational Queries
[path] Android/Data objects/

# Relational Queries on Android

## Introduction

In this guide, you will perform relational queries in Parse and implement an Android app using these queries. You will learn how to set up and query realistic data using Back4App and Android.

This tutorial uses an app created in Android Studio Arctic Fox -2020.3.1 with compileSdk = 30 , minSdk = 23 and targetSdk = 30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

Our goal is query data stored on Back4App from an Android app.

Here is a preview of what we are gonna achieve:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/r_ztIV4fRNQoI9ol77tTJ_image.png" signedSrc size="50" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Let’s get started!

Before next steps, we need to connect Back4App to our application. You should save the appId and clientKey from the Back4App to string.xml file and then init Parse in our App.java or App.kt file.
Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">New Parse App tutorial</a> if you don’t know how to init Parse to your app.

Or you can download the projects we shared the github links above and edit only the appId and clientKey parts according to you.

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## 1 - Save some data on Back4App

In this step, we will create a Class with the JS Console and Javascript codes provided by Parse and we will create queries for this Class.

Let’s create an assortment of classes, which will be the target of our queries in this guide. The classes are: Author, Book, Publisher and BookStore, in which Book has a 1\:N relation with Publisher and N\:N with Author, and BookStore has an N\:N relation with Book.

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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5bdz5cp1kW2Hknxagtm5w_image.png)

Go ahead and create the classes with the following example content:

```java
1   // Add objects and create tables
2   // Authors
3   const AuthorA = new Parse.Object('Author');
4   AuthorA.set('name', 'Aaron Writer');
5   await AuthorA.save();
6 
7   const AuthorB = new Parse.Object('Author');
8   AuthorB.set('name', 'Beatrice Novelist');
9   await AuthorB.save();
10
11  const AuthorC = new Parse.Object('Author');
12  AuthorC.set('name', 'Casey Columnist');
13  await AuthorC.save();
14
15  // Publishers
16  const PublisherA = new Parse.Object('Publisher');
17  PublisherA.set('name', 'Acacia Publishings');
18  await PublisherA.save();
19
20  const PublisherB = new Parse.Object('Publisher');
21  PublisherB.set('name', 'Birch Distributions');
22  await PublisherB.save();
23
24  // Books
25  const BookA = new Parse.Object('Book');
26  BookA.set('title', 'A Love Story');
27  BookA.set('publisher', PublisherA);
28  BookA.set('publishingDate', new Date('05/07/1998'));
29  const BookARelation = BookA.relation("authors");
30  BookARelation.add(AuthorA);
31  await BookA.save();
32  
33  const BookB = new Parse.Object('Book');
34  BookB.set('title', 'Benevolent Elves');
35  BookB.set('publisher', PublisherB);
36  BookB.set('publishingDate', new Date('11/31/2008'));
37  const BookBRelation = BookB.relation("authors");
38  BookBRelation.add(AuthorB);
39  await BookB.save();
40
41  const BookC = new Parse.Object('Book');
42  BookC.set('title', 'Can You Believe It?');
43  BookC.set('publisher', PublisherB);
44  BookC.set('publishingDate', new Date('08/21/2018'));
45  const BookCRelation = BookC.relation("authors");
46  BookCRelation.add(AuthorA);
47  BookCRelation.add(AuthorC);
48  await BookC.save();
49
50  // BookStore
51  const BookStoreA = new Parse.Object('BookStore');
52  BookStoreA.set('name', 'Books of Love');
53  const BookStoreARelation = BookStoreA.relation("books");
54  BookStoreARelation.add(BookA);
55  await BookStoreA.save();
56
57  const BookStoreB = new Parse.Object('BookStore');
58  BookStoreB.set('name', 'Fantasy Books');
59  const BookStoreBRelation = BookStoreB.relation("books");
60  BookStoreBRelation.add(BookB);
61  await BookStoreB.save();
62
63  const BookStoreC = new Parse.Object('BookStore');
64  BookStoreC.set('name', 'General Books');
65  const BookStoreCRelation = BookStoreC.relation("books");
66  BookStoreCRelation.add(BookA);
67  BookStoreCRelation.add(BookC);
68  await BookStoreC.save();
69
70  console.log('Success');
```

## 2 - Query the data from Android app

Now that you have populated all the classes, we can now perform some relational queries in it. Let’s begin by filtering Book results by the publisher, searching for the ones that belong to the Publisher “Acacia Publishings” (or “Publisher A”) using the basic ParseQuery.equalTo method:

:::CodeblockTabs
```java
1       progressDialog.show();
2       ParseQuery<ParseObject> publisherQuery = new ParseQuery<>("Publisher");
3       publisherQuery.whereEqualTo("name", "Acacia Publishings");
4       try {
5           ParseObject publisher = publisherQuery.getFirst();
6           ParseQuery<ParseObject> bookQuery = new ParseQuery<ParseObject>("Book");
7           bookQuery.whereEqualTo("publisher", publisher);
8           bookQuery.findInBackground((objects, e) -> {
9               progressDialog.hide();
10              if (e == null) {
11                  initData(objects);
12              } else {
13                  Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14              }
15          });
16      } catch (ParseException e) {
17          progressDialog.hide();
18          e.printStackTrace();
19      }
```

```kotlin
1      progressDialog?.show()
2      val publisherQuery = ParseQuery<ParseObject>("Publisher")
3      publisherQuery.whereEqualTo("name", "Acacia Publishings")
4      try {
5          val publisher = publisherQuery.first
6          val bookQuery = ParseQuery<ParseObject>("Book")
7          bookQuery.whereEqualTo("publisher", publisher)
8          bookQuery.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
9              progressDialog?.hide()
10             if (e == null) {
11                initData(objects!!)
12             } else {
13                 Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
14             }
15         }
16     } catch (e: ParseException) {
17         progressDialog?.hide()
18         e.printStackTrace()
19     }
```
:::

Let’s now query which BookStore objects contain Book objects with publishing date greater than 01/01/2010, using an inner query with the ParseQuery.whereGreaterThan method and then the ParseQuery.whereMatchesQuery method:

:::CodeblockTabs
```java
1       progressDialog.show();
2       ParseQuery<ParseObject> bookQuery = new ParseQuery<>("Book");
3
4       Calendar calendar = Calendar.getInstance();
5       calendar.set(2010,1,1,59,59,59);
6       Date date = calendar.getTime();
7
8       bookQuery.whereGreaterThan("publishingDate", date);
9
10      ParseQuery<ParseObject> bookStoreQuery = new ParseQuery<>("BookStore");
11      bookStoreQuery.whereMatchesQuery("books",bookQuery);
12      bookStoreQuery.findInBackground((objects, e) -> {
13          progressDialog.hide();
14          if (e==null){
15              initData(objects);
16          } else {
17              Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
18          }
19      });
```

```kotlin
1      progressDialog?.show()
2      val bookQuery = ParseQuery<ParseObject>("Book")
3      val calendar = Calendar.getInstance()
4      calendar[2010, 1, 1, 59, 59] = 59
5      val date = calendar.time
6      bookQuery.whereGreaterThan("publishingDate", date)
7      val bookStoreQuery = ParseQuery<ParseObject>("BookStore")
8      bookStoreQuery.whereMatchesQuery("books", bookQuery)
9      bookStoreQuery.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
10         progressDialog?.hide()
11         if (e == null) {
12             initData(objects!!)
13         } else {
14             Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
15         }
16     }
```
:::

Now lets create a more complex relational query, looking for BookStore objects that have at least one Book written by Author “Aaron Writer” (or “AuthorA”), using whereEqualTo and whereMatchesQuery:

:::CodeblockTabs
```java
1       progressDialog.show();
2       ParseQuery<ParseObject> authorQuery = new ParseQuery<>("Author");
3       authorQuery.whereEqualTo("name","Aaron Writer");
4       try {
5           ParseObject authorA = authorQuery.getFirst();
6           ParseQuery<ParseObject> bookQuery = new ParseQuery<>("Book");
7           bookQuery.whereEqualTo("authors",authorA);
8
9           ParseQuery<ParseObject> bookStoreQuery = new ParseQuery<>("BookStore");
10          bookStoreQuery.whereMatchesQuery("books",bookQuery);
11
12          bookStoreQuery.findInBackground((objects, e) -> {
13             progressDialog.hide();
14             if (e==null){
15                  initData(objects);
16             } else {
17                 Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
18             }
19         });
20
21
22     } catch (ParseException e) {
23         progressDialog.hide();
24         e.printStackTrace();
25     }
```

```kotlin
1      progressDialog?.show()
2     val authorQuery = ParseQuery<ParseObject>("Author")
3     authorQuery.whereEqualTo("name", "Aaron Writer")
4     try {
5         val authorA = authorQuery.first
6         val bookQuery = ParseQuery<ParseObject>("Book")
7         bookQuery.whereEqualTo("authors", authorA)
8         val bookStoreQuery = ParseQuery<ParseObject>("BookStore")
9         bookStoreQuery.whereMatchesQuery("books", bookQuery)
10        bookStoreQuery.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
11            progressDialog?.hide()
12            if (e == null) {
13                initData(objects!!)
14            } else {
15                Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
16            }
17        }
18    } catch (e: ParseException) {
19        progressDialog?.hide()
20        e.printStackTrace()
21    }
```
:::

## It’s done!

At the end of this guide, you learned how relational queries work on Parse and how to perform them on Back4App from an Android App.

[title] SignIn with Facebook
[path] ReactJS/Users/

# React Facebook Login

## Introduction

In the last tutorials, you built a User login/logout feature to your App using the Parse.User class. Now you will learn how to use Facebook Login to retrieve user data from Facebook and log in, sign up or link existent users with it. You will also install and configure react-facebook-login lib to achieve that.

The Parse.User.linkWith method is responsible for signing up and logging in users using any third-party authentication method, as long as you pass the right parameters requested by each different provider. After linking the user data to a new or existent Parse.User, Parse will store a valid user session on your device. Future calls to methods like current will successfully retrieve your User data, just like with regular logins.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- Complete the previous guide so you can have a better understanding of [the Parse.User class and the Parse.User.logIn method](https://www.back4app.com/docs/react/working-with-users/react-user-login)
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library.</a>
:::

## Goal

To build a User LogIn feature using Facebook Login on Parse for a React App.

## 1 - Installing dependencies

The most popular way to enable Facebook Login on React is using react-facebook-login to handle it. Set it up following the <a href="https://github.com/keppelen/react-facebook-login/" target="_blank">official docs</a>.

To use the lib component, you need to inform a valid Facebook app appID, so create one on on your Facebook for Developers dashboard, following one of their <a href="https://developers.facebook.com/docs/development/create-an-app/" target="_blank">official guides</a>. After that, add Facebook Login capability to your app while choosing the WWW option, informing https\://localhost:3000 as your website URL.

Facebook Login requires that your connection is secure, even when logging in on a development environment. So, you need to setup HTTPS access for your React app by creating a local certificate, giving it full permissions in your OS and adding the files to your project. Since each OS can have its particularities, this won’t be covered in the guide.

After creating the certificate, make sure to change in the scripts session of your package.json file the start command to:

```json
1   "start": "export HTTPS=true&&SSL_CRT_FILE=cert.pem&&SSL_KEY_FILE=key.pem react-scripts start",
```

If you are using Typescript, you need to create a new file in your src folder called react-facebook-login.d.ts containing the react-facebook-login module declaration to use the styleless version of the component:

:::CodeblockTabs
react-facebook-login.d.ts

```typescript
1   declare module 'react-facebook-login/dist/facebook-login-render-props';
```
:::

## 2 - Using Facebook Login with Parse

Let’s now create a new method inside the UserLogIn component that will handle the response when calling a Facebook Login authentication modal. If the user signs in with Facebook, this call will retrieve the user data from Facebook and you need to store the id, accessToken, and the profile email. Then, the function will try to log in on Parse using the Parse.User.linkWith method and these credentials. Note that if your user had already signed up using this Facebook authentication, linkWith will log him in using the existent account.

:::CodeblockTabs
JavaScript

```javascript
1	const handleFacebookLoginLoginResponse = async function(response) {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Facebook user info
9	      const userId = response.id;
10	      const userEmail = response.email;
11	      const userAccessToken = response.accessToken;
12	      // Try to login on Parse using linkWith and these credentials
13	      // Create a new Parse.User object
14	      const userToLogin = new Parse.User();
15	      // Set username and email to match facebook profile email
16	      userToLogin.set('username', userEmail);
17	      userToLogin.set('email', userEmail);
18	      try {
19	        let loggedInUser = await userToLogin
20	        .linkWith('facebook', {
21	          authData: {id: userId, access_token: userAccessToken},
22	        });
23	        // logIn returns the corresponding ParseUser object
24	        alert(
25	          `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
26	        );
27	        // Update state variable holding current user
28	        getCurrentUser();
29	        return true;
30	      } catch (error) {
31	        // Error can be caused by wrong parameters or lack of Internet connection
32	        alert(`Error! ${error.message}`);
33	        return false;
34	      }
35	    } catch (error) {
36	      console.log("Error gathering Facebook user info, please try again!")
37	      return false;
38	    }
39	  }
40	}
```

```typescript
1	const handleFacebookLoginLoginResponse = async function(response: any): Promise<boolean> {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Facebook user info
9	      const userId: string = response.id;
10	      const userEmail: string = response.email;
11	      const userAccessToken: string = response.accessToken;
12	      // Try to login on Parse using linkWith and these credentials
13	      // Create a new Parse.User object
14	      const userToLogin: Parse.User = new Parse.User();
15	      // Set username and email to match facebook profile email
16	      userToLogin.set('username', userEmail);
17	      userToLogin.set('email', userEmail);
18	      try {
19	        let loggedInUser: Parse.User = await userToLogin
20	        .linkWith('facebook', {
21	          authData: {id: userId, access_token: userAccessToken},
22	        });
23	        // logIn returns the corresponding ParseUser object
24	        alert(
25	          `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
26	        );
27	        // Update state variable holding current user
28	        getCurrentUser();
29	        return true;
30	      } catch (error: any) {
31	        // Error can be caused by wrong parameters or lack of Internet connection
32	        alert(`Error! ${error.message}`);
33	        return false;
34	      }
35	    } catch (error: any) {
36	      console.log("Error gathering Facebook user info, please try again!")
37	      return false;
38	    }
39	  }
40	}
```
:::

After that, you need to use the react-facebook-login FacebookLogin component to call the Facebook Login modal, adding it to your JSX code. You can use Facebook’s default styling or create a custom one, which is the way followed by this guide. Here is the full UserLogin component code, note the react-facebook-login button and how it is tied to the modal response method created before.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';
4	import './App.css';
5	import { Button, Divider, Input } from 'antd';
6	
7	export const UserLogin = () => {
8	  // State variables
9	  const [username, setUsername] = useState('');
10	  const [password, setPassword] = useState('');
11	  const [currentUser, setCurrentUser] = useState(null);
12	
13	  const doUserLogIn = async function () {
14	    // Note that these values come from state variables that we've declared before
15	    const usernameValue = username;
16	    const passwordValue = password;
17	    try {
18	      const loggedInUser = await Parse.User.logIn(usernameValue, passwordValue);
19	      // logIn returns the corresponding ParseUser object
20	      alert(
21	        `Success! User ${loggedInUser.get(
22	          'username'
23	        )} has successfully signed in!`
24	      );
25	      // To verify that this is in fact the current user, `current` can be used
26	      const currentUser = await Parse.User.current();
27	      console.log(loggedInUser === currentUser);
28	      // Clear input fields
29	      setUsername('');
30	      setPassword('');
31	      // Update state variable holding current user
32	      getCurrentUser();
33	      return true;
34	    } catch (error) {
35	      // Error can be caused by wrong parameters or lack of Internet connection
36	      alert(`Error! ${error.message}`);
37	      return false;
38	    }
39	  };
40	
41	  const doUserLogOut = async function () {
42	    try {
43	      await Parse.User.logOut();
44	      // To verify that current user is now empty, currentAsync can be used
45	      const currentUser = await Parse.User.current();
46	      if (currentUser === null) {
47	        alert('Success! No user is logged in anymore!');
48	      }
49	      // Update state variable holding current user
50	      getCurrentUser();
51	      return true;
52	    } catch (error) {
53	      alert(`Error! ${error.message}`);
54	      return false;
55	    }
56	  };
57	
58	  // Function that will return current user and also update current username
59	  const getCurrentUser = async function () {
60	    const currentUser = await Parse.User.current();
61	    // Update state variable holding current user
62	    setCurrentUser(currentUser);
63	    return currentUser;
64	  };
65	
66	  const handleFacebookLoginLoginResponse = async function (response) {
67	    // Check if response has an error
68	    if (response.error !== undefined) {
69	      console.log(`Error: ${response.error}`);
70	      return false;
71	    } else {
72	      try {
73	        // Gather Facebook user info
74	        const userId = response.id;
75	        const userEmail = response.email;
76	        const userAccessToken = response.accessToken;
77	        // Try to login on Parse using linkWith and these credentials
78	        // Create a new Parse.User object
79	        const userToLogin = new Parse.User();
80	        // Set username and email to match facebook profile email
81	        userToLogin.set('username', userEmail);
82	        userToLogin.set('email', userEmail);
83	        try {
84	          let loggedInUser = await userToLogin.linkWith('facebook', {
85	            authData: { id: userId, access_token: userAccessToken },
86	          });
87	          // logIn returns the corresponding ParseUser object
88	          alert(
89	            `Success! User ${loggedInUser.get(
90	              'username'
91	            )} has successfully signed in!`
92	          );
93	          // Update state variable holding current user
94	          getCurrentUser();
95	          return true;
96	        } catch (error) {
97	          // Error can be caused by wrong parameters or lack of Internet connection
98	          alert(`Error! ${error.message}`);
99	          return false;
100	        }
101	      } catch (error) {
102	        console.log('Error gathering Facebook user info, please try again!');
103	        return false;
104	      }
105	    }
106	  };
107	
108	  return (
109	    <div>
110	      <div className="header">
111	        <img
112	          className="header_logo"
113	          alt="Back4App Logo"
114	          src={
115	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
116	          }
117	        />
118	        <p className="header_text_bold">{'React on Back4App'}</p>
119	        <p className="header_text">{'User Login'}</p>
120	      </div>
121	      {currentUser === null && (
122	        <div className="container">
123	          <h2 className="heading">{'User Login'}</h2>
124	          <Divider />
125	          <div className="form_wrapper">
126	            <Input
127	              value={username}
128	              onChange={(event) => setUsername(event.target.value)}
129	              placeholder="Username"
130	              size="large"
131	              className="form_input"
132	            />
133	            <Input
134	              value={password}
135	              onChange={(event) => setPassword(event.target.value)}
136	              placeholder="Password"
137	              size="large"
138	              type="password"
139	              className="form_input"
140	            />
141	          </div>
142	          <div className="form_buttons">
143	            <Button
144	              onClick={() => doUserLogIn()}
145	              type="primary"
146	              className="form_button"
147	              color={'#208AEC'}
148	              size="large"
149	              block
150	            >
151	              Log In
152	            </Button>
153	          </div>
154	          <Divider />
155	          <div className="login-social">
156	            <FacebookLogin
157	              appId="4573670722644997"
158	              fields="email"
159	              callback={handleFacebookLoginLoginResponse}
160	              render={(renderProps) => (
161	                <div className="login-social-item login-social-item--facebook">
162	                  <img onClick={renderProps.onClick}  className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
163	                </div>
164	              )}
165	            />
166	            <div className="login-social-item">
167	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
168	            </div>
169	            <div className="login-social-item">
170	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
171	            </div>
172	          </div>
173	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
174	        </div>
175	      )}
176	      {currentUser !== null && (
177	        <div className="container">
178	          <h2 className="heading">{'User Screen'}</h2>
179	          <Divider />
180	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
181	          <div className="form_buttons">
182	            <Button
183	              onClick={() => doUserLogOut()}
184	              type="primary"
185	              className="form_button"
186	              color={'#208AEC'}
187	              size="large"
188	              block
189	            >
190	              Log Out
191	            </Button>
192	          </div>
193	        </div>
194	      )}
195	    </div>
196	  );
197	};
```

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';
5	const Parse = require('parse/dist/parse.min.js');
6	
7	export const UserLogin: FC<{}> = (): ReactElement => {
8	  // State variables
9	  const [username, setUsername] = useState('');
10	  const [password, setPassword] = useState('');
11	  const [currentUser, setCurrentUser] = useState<Parse.Object | null>(null);
12	
13	  const doUserLogIn = async function (): Promise<boolean> {
14	    // Note that these values come from state variables that we've declared before
15	    const usernameValue: string = username;
16	    const passwordValue: string = password;
17	    try {
18	      const loggedInUser: Parse.User = await Parse.User.logIn(usernameValue, passwordValue);
19	      // logIn returns the corresponding ParseUser object
20	      alert(
21	        `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
22	      );
23	      // To verify that this is in fact the current user, `current` can be used
24	      const currentUser: Parse.User = await Parse.User.current();
25	      console.log(loggedInUser === currentUser);
26	      // Clear input fields
27	      setUsername('');
28	      setPassword('');
29	      // Update state variable holding current user
30	      getCurrentUser();
31	      return true;
32	    } catch (error: any) {
33	      // Error can be caused by wrong parameters or lack of Internet connection
34	      alert(`Error! ${error.message}`);
35	      return false;
36	    }
37	  };
38	
39	  const doUserLogOut = async function (): Promise<boolean> {
40	    try {
41	      await Parse.User.logOut();
42	      // To verify that current user is now empty, currentAsync can be used
43	      const currentUser: Parse.User = await Parse.User.current();
44	      if (currentUser === null) {
45	        alert('Success! No user is logged in anymore!');
46	      }
47	      // Update state variable holding current user
48	      getCurrentUser();
49	      return true;
50	    } catch (error: any) {
51	      alert(`Error! ${error.message}`);
52	      return false;
53	    }
54	  };
55	
56	  // Function that will return current user and also update current username
57	  const getCurrentUser = async function (): Promise<Parse.User | null> {
58	    const currentUser: (Parse.User | null) = await Parse.User.current();
59	    // Update state variable holding current user
60	    setCurrentUser(currentUser);
61	    return currentUser;
62	  }
63	
64	  const handleFacebookLoginLoginResponse = async function(response: any): Promise<boolean> {
65	    // Check if response has an error
66	    if (response.error !== undefined) {
67	      console.log(`Error: ${response.error}`);
68	      return false;
69	    } else {
70	      try {
71	        // Gather Facebook user info
72	        const userId: string = response.id;
73	        const userEmail: string = response.email;
74	        const userAccessToken: string = response.accessToken;
75	        // Try to login on Parse using linkWith and these credentials
76	        // Create a new Parse.User object
77	        const userToLogin: Parse.User = new Parse.User();
78	        // Set username and email to match facebook profile email
79	        userToLogin.set('username', userEmail);
80	        userToLogin.set('email', userEmail);
81	        try {
82	          let loggedInUser: Parse.User = await userToLogin
83	          .linkWith('facebook', {
84	            authData: {id: userId, access_token: userAccessToken},
85	          });
86	          // logIn returns the corresponding ParseUser object
87	          alert(
88	            `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
89	          );
90	          // Update state variable holding current user
91	          getCurrentUser();
92	          return true;
93	        } catch (error: any) {
94	          // Error can be caused by wrong parameters or lack of Internet connection
95	          alert(`Error! ${error.message}`);
96	          return false;
97	        }
98	      } catch (error: any) {
99	        console.log("Error gathering Facebook user info, please try again!")
100	        return false;
101	      }
102	    }
103	  }
104	
105	  return (
106	    <div>
107	      <div className="header">
108	        <img
109	          className="header_logo"
110	          alt="Back4App Logo"
111	          src={
112	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
113	          }
114	        />
115	        <p className="header_text_bold">{'React on Back4App'}</p>
116	        <p className="header_text">{'User Login'}</p>
117	      </div>
118	      {currentUser === null && (
119	        <div className="container">
120	          <h2 className="heading">{'User Login'}</h2>
121	          <Divider />
122	          <div className="form_wrapper">
123	            <Input
124	              value={username}
125	              onChange={(event) => setUsername(event.target.value)}
126	              placeholder="Username"
127	              size="large"
128	              className="form_input"
129	            />
130	            <Input
131	              value={password}
132	              onChange={(event) => setPassword(event.target.value)}
133	              placeholder="Password"
134	              size="large"
135	              type="password"
136	              className="form_input"
137	            />
138	          </div>
139	          <div className="form_buttons">
140	            <Button
141	              onClick={() => doUserLogIn()}
142	              type="primary"
143	              className="form_button"
144	              color={'#208AEC'}
145	              size="large"
146	              block
147	            >
148	              Log In
149	            </Button>
150	          </div>
151	          <Divider />
152	          <div className="login-social">
153	            <FacebookLogin
154	              appId="4573670722644997"
155	              fields="email"
156	              callback={handleFacebookLoginLoginResponse}
157	              render={(renderProps: {onClick: () => void}) => (
158	                <div className="login-social-item login-social-item--facebook">
159	                  <img onClick={renderProps.onClick}  className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
160	                </div>
161	              )}
162	            />
163	            <div className="login-social-item">
164	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
165	            </div>
166	            <div className="login-social-item">
167	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
168	            </div>
169	          </div>
170	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
171	        </div>
172	      )}
173	      {currentUser !== null && (
174	        <div className="container">
175	          <h2 className="heading">{'User Screen'}</h2>
176	          <Divider />
177	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
178	          <div className="form_buttons">
179	            <Button
180	              onClick={() => doUserLogOut()}
181	              type="primary"
182	              className="form_button"
183	              color={'#208AEC'}
184	              size="large"
185	              block
186	            >
187	              Log Out
188	            </Button>
189	          </div>
190	        </div>
191	      )}
192	    </div>
193	  );
194	};
```
:::

Add these classes to your App.css file if you want to fully render this component’s layout.

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 900px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.flex {
77	  display: flex;
78	}
79	
80	.flex_between {
81	  display: flex;
82	  justify-content: space-between;
83	}
84	
85	.flex_child {
86	  flex: 0 0 45%;
87	}
88	
89	.heading_button {
90	  margin-left: 12px;
91	}
92	
93	.list_item {
94	  padding-bottom: 15px;
95	  margin-bottom: 15px;
96	  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
97	  text-align: left;
98	}
99	
100	.list_item_title {
101	  color: rgba(0, 0, 0, 0.87);
102	  font-size: 17px;
103	}
104	
105	.list_item_description {
106	  color: rgba(0, 0, 0, 0.5);
107	  font-size: 15px;
108	}
109	
110	.form_input {
111	  margin-bottom: 20px;
112	}
113	
114	.login-social {
115	  display: flex;
116	  justify-content: center;
117	  margin-bottom: 30px;
118	}
119	
120	.login-social-item {
121	  width: 54px;
122	  height: 54px;
123	  border-radius: 54px;
124	  padding: 12px;
125	  margin: 0 12px;
126	  border: 1px solid #e6e6e6;
127	  box-shadow: 0 2px 4px #d6d6d6;
128	}
129	
130	.login-social-item--facebook {
131	  padding: 4px;
132	  background-color: #3C5B9B;
133	}
134	
135	.login-social-item__image {
136	  width: 100%;
137	}
138	
139	.form__hint {
140	  color: rgba(0, 0, 0, 0.5);
141	  font-size: 16px;
142	  text-align: center;
143	}
```
:::

Go ahead and test your new function. If you were able to sign in to Facebook and the Parse linkWith call was successful, you should see a success message like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ju89eb7Laa8HBiuWLN4ml_image.png)

## 3 - Verifying user sign in and session creation

To make sure that the Facebook Login worked, you can look at your Parse dashboard and see your new User (if your Facebook authentication data didn’t belong to another user), containing the Facebook authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Fz64NIvC6JXLdq5BEZeZe_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to that User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OpSekb5DCXz2CI3Enp-qR_image.png)

## 4 - Linking an existing User to Facebook Sign-in

Another linkWith possible use is to link an existing user with another auth provider, in this case, Facebook. Add this function that calls linkWith the same way as logging in to your UserLogIn component. The only difference here is that instead of calling the method from an empty Parse.User, you will use it from the currently logged-in user object.

:::CodeblockTabs
JavaScript

```javascript
1	const handleFacebookLoginLinkResponse = async function(response) {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Facebook user info
9	      const userId = response.id;
10	      const userAccessToken = response.accessToken;
11	      // Try to link current Parse user using linkWith and these credentials
12	      // Get current user
13	      const userToLink = await Parse.User.current();
14	      try {
15	        let loggedInUser = await userToLink
16	        .linkWith('facebook', {
17	          authData: {id: userId, access_token: userAccessToken},
18	        });
19	        // logIn returns the corresponding ParseUser object
20	        alert(
21	          `Success! User ${loggedInUser.get(
22	            'username',
23	          )} has successfully linked his Facebook account!`,
24	        );
25	        // Update state variable holding current user
26	        getCurrentUser();
27	        return true;
28	      } catch (error) {
29	        // Error can be caused by wrong parameters or lack of Internet connection
30	        alert(`Error! ${error.message}`);
31	        return false;
32	      }
33	    } catch (error) {
34	      console.log("Error gathering Facebook user info, please try again!")
35	      return false;
36	    }
37	  }
38	}
```

```typescript
1	const handleFacebookLoginLinkResponse = async function(response: any): Promise<boolean> {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Facebook user info
9	      const userId: string = response.id;
10	      const userAccessToken: string = response.accessToken;
11	      // Try to link current Parse user using linkWith and these credentials
12	      // Get current user
13	      const userToLink: Parse.User = await Parse.User.current();
14	      try {
15	        let loggedInUser: Parse.User = await userToLink
16	        .linkWith('facebook', {
17	          authData: {id: userId, access_token: userAccessToken},
18	        });
19	        // logIn returns the corresponding ParseUser object
20	        alert(
21	          `Success! User ${loggedInUser.get(
22	            'username',
23	          )} has successfully linked his Facebook account!`,
24	        );
25	        // Update state variable holding current user
26	        getCurrentUser();
27	        return true;
28	      } catch (error: any) {
29	        // Error can be caused by wrong parameters or lack of Internet connection
30	        alert(`Error! ${error.message}`);
31	        return false;
32	      }
33	    } catch (error: any) {
34	      console.log("Error gathering Facebook user info, please try again!")
35	      return false;
36	    }
37	  }
38	}
```
:::

Assign this function to another react-facebook-login component in your home screen, which is shown only when there is a current user logged in in your app. Test your new function, noting that the Parse.User object authData value will be updated with the new auth provider data. Verify if the user has indeed updated in your Parse server dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6r0_4qP2ScDfW6IJyWL8E_image.png)

## Conclusion

At the end of this guide, you learned how to log in, sign up or link existing Parse users on React using Facebook Login with react-google-login. In the next guide, we will show you how to use Apple sign-in.

[title] Query Cookbook
[path] Android/Data objects/

# Parse Query Cookbook in Android

## Introduction

We’ve already seen how a ParseQuery with get can retrieve a single ParseQuery from Back4App. There are many other ways to retrieve data withParseQueryyou can retrieve many objects at once, use conditions on the objects you wish to retrieve, and more.

In this guide, you will ding deep into the ParseQuery class and see all the methods you can use to build your Queries. You will use a simple database class with some mocked data to perform the Queries using the Javascript Console on Back4App.

:::hint{type="info"}
You can use Javascript Console on Back4App to create mocked data easily, or you can create your data with your own android app by following <a href="https://www.back4app.com/docs/android/data-objects/android-crud-tutorial" target="_blank">this guide</a>.
:::

This tutorial uses an app created in Android Studio 4.1.1 with buildToolsVersion=30.0.2 , Compile SDK Version = 30.0.2 and targetSdkVersion=30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Goal

Explore the ParseQuery class different methods and learn query types you can create on Android.

## The ParseQuery class

Any query operation on Parse uses the ParseQuery object type, which will help you retrieve specific data from your Back4App throughout your app. To create a new ParseQuery, you need to pass as a parameter the desired ParseQuery subclass, which is the one that will contain your query results.

It is crucial to know that a ParseQuery will only resolve after calling a retrieve method (like ParseQuery.find or ParseQuery.get), so a query can be set up and several modifiers can be chained before actually being called.

You can read more about the ParseQuery class [here at the official documentation](https://parseplatform.org/Parse-SDK-Android/api/).

## Using the JS Console on Back4App

Inside your Back4App application’s dashboard, you will find a very useful API console in which you can run JavaScript code directly. In this guide you will use to store and query data objects from Back4App. On your App main dashboard go to Core->API Console->JS Console.



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VmgZDeOUXd-46heXu9bWb_image.png)

## Save your Data Objects

To run the queries on this guide you’ll need first to populate your App with some data. Let’s create a sample class called Profile, which mocks a social media profile class using famous people names and the following fields:

- string type
- Date type
- Number (integer) type
- Array (string array) type
- Array (Number array) type
- GeoPoint type
- Nullable pointer type

Here is the Parse.Object classes creation code, so go ahead and run it in your API console:

```java
1   // Add Profile objects and create table
2   // Adam Sandler
3   let Profile = new Parse.Object('Profile');
4   Profile.set('name', 'Adam Sandler');
5   Profile.set('birthDay', new Date('09/09/1966'));
6   Profile.set('friendCount', 2);
7   Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8   Profile.set('luckyNumbers', [2, 7]);
9   Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
10  await Profile.save();
11
12  // Britney Spears
13  Profile = new Parse.Object('Profile');
14  Profile.set('name', 'Britney Spears');
15  Profile.set('birthDay', new Date('12/02/1981'));
16  Profile.set('friendCount', 52);
17  Profile.set('favoriteFoods', ['Cake', 'Bread']);
18  Profile.set('luckyNumbers', [22, 7]);
19  Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
20  await Profile.save();
21
22  // Carson Kressley
23  Profile = new Parse.Object('Profile');
24  Profile.set('name', 'Carson Kressley');
25  Profile.set('birthDay', new Date('11/11/1969'));
26  Profile.set('friendCount', 12);
27  Profile.set('favoriteFoods', ['Fish', 'Cookies']);
28  Profile.set('luckyNumbers', [8, 2]);
29  Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
30  await Profile.save();
31
32  // Dan Aykroyd
33  // Creates related object Membership for this user only
34  let Membership = new Parse.Object('Membership');
35  Membership.set('name', 'Premium');
36  Membership.set('expirationDate', new Date('10/10/2030'))
37  await Membership.save();
38  Profile = new Parse.Object('Profile');
39  Profile.set('name', 'Dan Aykroyd');
40  Profile.set('birthDay', new Date('07/01/1952'));
41  Profile.set('friendCount', 66);
42  Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
43  Profile.set('luckyNumbers', [22, 77]);
44  Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
45  Profile.set('premiumMembership', Membership);
46  await Profile.save();
47
48  // Eddie Murphy
49  Profile = new Parse.Object('Profile');
50  Profile.set('name', 'Eddie Murphy');
51  Profile.set('birthDay', new Date('04/03/1961'));
52  Profile.set('friendCount', 49);
53  Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
54  Profile.set('luckyNumbers', [6, 5]);
55  Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
56  await Profile.save();
57  
58  // Fergie
59  Profile = new Parse.Object('Profile');
60  Profile.set('name', 'Fergie');
61  Profile.set('birthDay', new Date('03/27/1975'));
62  Profile.set('friendCount', 55);
63  Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
64  Profile.set('luckyNumbers', [13, 7]);
65  Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
66  await Profile.save();
67
68  console.log('Success!');
```

After running this code, you should now have a Profile class in your database with six objects created. Your new class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AS_KAw59PrxFqVPELsUp2_image.png)

Let’s now take a look at examples from every ParseQuery method, along with brief explanations on what they do. Please note that some methods in this list can take options as an additional argument, but in most cases, it is only related to masterKey usage and not relevant to this guide content, so this possibility will be omitted whenever not relevant.

## Query retrievers

These methods are responsible for running the query and retrieving its results, being always present in your query implementation.

This is the **java** methods:

:::CodeblockTabs
cancel

```java
//Cancels the current network request.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.findInBackground();
3   query.cancel();
```

count

```java
//Retrieves the count of ParseObject results that meet the query criteria.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     try {
3         int queryCount = query.count();
4         Log.d(TAG, "Count: " + queryCount);
5     } catch (com.parse.ParseException parseException) {
6         parseException.printStackTrace();
7     }
```

find

```java
//This is the basic method for retrieving your query results, always returning an array of ParseObject instances, being empty when none are found.
1   //This find function works synchronously.
2     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
3     try {
4         List<ParseObject> list = query.find();
5         Log.d(TAG, "List: " + list);
6     } catch (com.parse.ParseException e) {
7         e.printStackTrace();
8     }
```

findInBackground

```java
//This is the basic method for retrieving your query results, always returning an array of ParseObject instances, being empty when none are found.
1   //This find function works asynchronously.
2     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
3     query.findInBackground((objects, e) -> {
4         if (e == null) {
5             Log.d(TAG, "Objects: " + objects);
6         } else {
7             Log.e(TAG, "ParseError: ", e);
8         }
9     });
```

first

```java
//Retrieves the first ParseObject instance that meets the query criteria.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     try {
3         ParseObject firstItem = query.getFirst();
4         Log.d(TAG, "First Item: " + firstItem);
5     } catch (ParseException e) {
6         e.printStackTrace();
7     }
```

get

```java
//This quick method is used to retrieve a single ParseObject instance when you know its objectId.
1   //We can call a parse object with an object id with the get() function.
2     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
3     try {
4         ParseObject object = query.get("C6ENdLnFdQ");
5         Log.d(TAG, "Object: " + object);
6     } catch (ParseException e) {
7         e.printStackTrace();
8     }
```
:::

This is the **kotlin** methods:

:::CodeblockTabs
cancel

```kotlin
//Cancels the current network request.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.findInBackground()
3   query.cancel()
```

count

```kotlin
//Retrieves the count of ParseObject results that meet the query criteria.
1     val query = ParseQuery<ParseObject>("Profile")
2     try {
3         val queryCount = query.count()
4         Log.d(Companion.TAG, "Count: $queryCount")
5     } catch (parseException: ParseException) {
6         parseException.printStackTrace()
7     }
```

find

```kotlin
//This is the basic method for retrieving your query results, always returning an array of ParseObject instances, being empty when none are found.
1   //This find function works synchronously.
2     val query = ParseQuery<ParseObject>("Profile")
3     try {
4         val list = query.find()
5         Log.d(Companion.TAG, "List: $list")
6     } catch (e: ParseException) {
7         e.printStackTrace()
8     }
```

findInBackground

```kotlin
//This is the basic method for retrieving your query results, always returning an array of ParseObject instances, being empty when none are found.
1   //This find function works asynchronously.
2     val query = ParseQuery<ParseObject>("Profile")
3     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4         if (e == null) {
5             Log.d(Companion.TAG, "Objects: $objects")
6         } else {
7             Log.e(Companion.TAG, "ParseError: ", e)
8         }
9     }
```

first

```kotlin
//Retrieves the first ParseObject instance that meets the query criteria.
1     val query = ParseQuery<ParseObject>("Profile")
2     try {
3         val firstItem = query.first
4         Log.d(Companion.TAG, "First Item: $firstItem")
5     } catch (e: ParseException) {
6         e.printStackTrace()
7     }
```

get

```kotlin
//This quick method is used to retrieve a single ParseObject instance when you know its objectId.
1   //We can call a parse object with an object id with the get() function.
2     val query = ParseQuery<ParseObject>("Profile")
3     try {
4         val `object` = query["C6ENdLnFdQ"]
5         Log.d(Companion.TAG, "Object: $`object`")
6     } catch (e: ParseException) {
7         e.printStackTrace()
8     }
```
:::

## Query conditioners

These methods give you the possibility of applying conditional constraints to your query, which are arguably the most important operations in querying. Remember that these operations can all be chained before the results are retrieved, so many combinations can be achieved to solve your querying needs.

These are **Java** methods

:::CodeblockTabs
containedIn

```java
//Filters objects in which a key value is contained in the provided array of values.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     query.whereContainedIn("luckyNumbers", List.of(2, 7));
3     query.findInBackground((objects, e) -> {
4         if (e == null) {
5             Log.d(TAG, "Objects: " + objects);
6         } else {
7             Log.e(TAG, "Parse Error: ", e);
8         }
9
10    });
```

contains

```java
//Filters objects in which a string key value contains the provided text value. Be aware that this condition is case-sensitive.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     query.whereContains("name", "da");
3     query.findInBackground((objects, e) -> {
4         if (e == null) {
5             Log.d(TAG, "Objects: " + objects);
6         } else {
7             Log.e(TAG, "Parse Error: ", e);
8         }
9
10    });
```

cointansAll

```java
//Filters objects in which an array type key value must contain every value provided.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     query.whereContainsAll("luckyNumbers", List.of(2, 7));
3     query.findInBackground((objects, e) -> {
4         if (e == null) {
5             Log.d(TAG, "Objects: " + objects);
6         } else {
7             Log.e(TAG, "Parse Error: ", e);
8         }
9
10    });
```

containsAllStartingWith

```java
//Filters objects in which an array type key value must contain every string value provided.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     query.whereContainsAllStartsWith("favoriteFoods", List.of("Shrimp", "Lobster"));
3     query.findInBackground((objects, e) -> {
4         if (e == null) {
5             Log.d(TAG, "Objects: " + objects);
6         } else {
7             Log.e(TAG, "Parse Error: ", e);
8         }
9
10    });
```

doesNotExist

```java
//Filters objects in which a key value is not set.
1     ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2     query.whereDoesNotExist("premiumMembership");
3     query.findInBackground((objects, e) -> {
4         if (e == null) {
5             Log.d(TAG, "Objects: " + objects);
6        } else {
7            Log.e(TAG, "Parse Error: ", e);
8        }
9    });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
containedIn

```kotlin
//Filters objects in which a key value is contained in the provided array of values.
1     val query = ParseQuery<ParseObject>("Profile")
2     query.whereContainedIn("luckyNumbers", java.util.List.of(2, 7))
3     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4         if (e == null) {
5             Log.d(Companion.TAG, "Objects: $objects")
6         } else {
7             Log.e(Companion.TAG, "Parse Error: ", e)
8         }
9     }
```

contains

```kotlin
//Filters objects in which a string key value contains the provided text value. Be aware that this condition is case-sensitive.
1     val query = ParseQuery<ParseObject>("Profile")
2     query.whereContains("name", "da")
3     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4         if (e == null) {
5             Log.d(Companion.TAG, "Objects: $objects")
6         } else {
7             Log.e(Companion.TAG, "Parse Error: ", e)
8         }
9     }
```

cointansAll

```kotlin
//Filters objects in which an array type key value must contain every value provided.
1     val query = ParseQuery<ParseObject>("Profile")
2     query.whereContainsAll("luckyNumbers", java.util.List.of(2, 7))
3     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4         if (e == null) {
5             Log.d(Companion.TAG, "Objects: $objects")
6         } else {
7             Log.e(Companion.TAG, "Parse Error: ", e)
8         }
9     }
```

containsAllStartingWith

```kotlin
//Filters objects in which an array type key value must contain every string value provided.
1     val query = ParseQuery<ParseObject>("Profile")
2     query.whereContainsAllStartsWith("favoriteFoods", java.util.List.of("Shrimp", "Lobster"))
3     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4         if (e == null) {
5             Log.d(Companion.TAG, "Objects: $objects")
6         } else {
7             Log.e(Companion.TAG, "Parse Error: ", e)
8         }
9     }
```

doesNotExist

```kotlin
//Filters objects in which a key value is not set.
1     val query = ParseQuery<ParseObject>("Profile")
2     query.whereDoesNotExist("premiumMembership")
3     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4         if (e == null) {
5             Log.d(Companion.TAG, "Objects: $objects")
6         } else {
7             Log.e(Companion.TAG, "Parse Error: ", e)
8         }
9     }
```
:::

These are **Java** methods

:::CodeblockTabs
canceldoesNotMatchKeyInQuery

```java
//Requires that a key’s value does not match a value in an object returned by a different ParseQuery. Useful for multi-object querying.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   ParseQuery<ParseObject> innerQuery = new ParseQuery<>("Profile");
3   innerQuery.whereLessThan("friendCount", 50);
4   query.whereDoesNotMatchKeyInQuery("friendCount", "friendCount", innerQuery);
5   query.whereGreaterThan("friendCount", 10);
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10          Log.e(TAG, "Parse Error: ", e);
11      }
12  });
```

doesNotMatchQuery

```java
//Requires that an object contained in the given key does not match another query. Useful for multi-object querying.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   ParseQuery<ParseObject> innerQuery = new ParseQuery<>("Membership");
3   innerQuery.whereGreaterThan("expirationDate", new Date());
4   query.whereExists("premiumMembership");
5   query.whereDoesNotMatchQuery("premiumMembership", innerQuery);
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10          Log.e(TAG, "Parse Error: ", e);
11      }
12  });
```

endsWith

```java
//Filters objects in which a string key’s value ends with the provided text value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereEndsWith("name", "ie");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```

equalTo

```java
//Filters objects in which a specific key’s value is equal to the provided value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereEqualTo("friendCount", 2);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```
:::

:::CodeblockTabs
exists

```java
//Filters objects in which a key value is set.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereExists("premiumMembership");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```

fullText

```java
//Filters objects in which a string key’s value wrap matches the provided text value in it. It can have many customizable options to improve its results, like language specification and score order.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereFullText("name", "Spears");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```

greaterThan

```java
//Filters objects in which a specific key’s value is greater than the provided value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   Calendar calendar = Calendar.getInstance();
3   calendar.set(1980, 8, 19, 59, 59, 59);
4   Date date = calendar.getTime();
5   query.whereGreaterThan("birthDay", date);
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10          Log.e(TAG, "Parse Error: ", e);
11      }
12  });
```

greaterThanOrEqualTo

```java
//Filters objects in which a specific key’s value is greater than or equal to the provided value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereGreaterThanOrEqualTo("friendCount", 49);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9  });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
canceldoesNotMatchKeyInQuery

```java
//Requires that a key’s value does not match a value in an object returned by a different ParseQuery. Useful for multi-object querying.
1   val query = ParseQuery<ParseObject>("Profile")
2   val innerQuery = ParseQuery<ParseObject>("Profile")
3   innerQuery.whereLessThan("friendCount", 50)
4   query.whereDoesNotMatchKeyInQuery("friendCount", "friendCount", innerQuery)
5   query.whereGreaterThan("friendCount", 10)
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

doesNotMatchQuery

```java
//Requires that an object contained in the given key does not match another query. Useful for multi-object querying.
1   val query = ParseQuery<ParseObject>("Profile")
2   val innerQuery = ParseQuery<ParseObject>("Membership")
3   innerQuery.whereGreaterThan("expirationDate", Date())
4   query.whereExists("premiumMembership")
5   query.whereDoesNotMatchQuery("premiumMembership", innerQuery)
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

endsWith

```java
//Filters objects in which a string key’s value ends with the provided text value.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereEndsWith("name", "ie")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

equalTo

```java
//Filters objects in which a specific key’s value is equal to the provided value.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereEqualTo("friendCount", 2)
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```
:::

:::CodeblockTabs
exists

```java
//Filters objects in which a key value is set.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereExists("premiumMembership")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

fullText

```java
//Filters objects in which a string key’s value wrap matches the provided text value in it. It can have many customizable options to improve its results, like language specification and score order.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereFullText("name", "Spears")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

greaterThan

```java
//Filters objects in which a specific key’s value is greater than the provided value.
1   val query = ParseQuery<ParseObject>("Profile")
2   val calendar = Calendar.getInstance()
3   calendar[1980, 8, 19, 59, 59] = 59
4   val date = calendar.time
5   query.whereGreaterThan("birthDay", date)
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

greaterThanOrEqualTo

```java
//Filters objects in which a specific key’s value is greater than or equal to the provided value.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereGreaterThanOrEqualTo("friendCount", 49)
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```
:::

These are **Java** methods

:::CodeblockTabs
lessThan

```java
//Filters objects in which a specific key’s value is lesser than the provided value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   Calendar calendar = Calendar.getInstance();
3   calendar.set(1980, 8, 19, 59, 59, 59);
4   Date date = calendar.getTime();
5   query.whereLessThan("birthDay", date);
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10          Log.e(TAG, "Parse Error: ", e);
11      }
12  });
```

lessThanOrEqualTo

```java
//Filters objects in which a specific key’s value is lesser than or equal to the provided value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereLessThanOrEqualTo("friendCount", 49);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```

matches

```java
//Filters objects in which a string type key value must match the provided regular expression and its modifiers.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereMatches("name", "da", "i");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```

matchesKeyInQuery

```java
//Requires that a key’s value matches a value in an object returned by a different ParseQuery. Useful for multi-object querying.1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   ParseQuery<ParseObject> innerQuery = new ParseQuery<>("Profile");
3   innerQuery.whereLessThan("friendCount", 50);
4   query.whereMatchesKeyInQuery("friendCount", "friendCount", innerQuery);
5   query.whereGreaterThan("friendCount", 10);
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10           Log.e(TAG, "Parse Error: ", e);
11      }
12  });
```

matchesQuery

```java
//Requires that an object contained in the given key matches another query. Useful for multi-object querying.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   ParseQuery<ParseObject> innerQuery = new ParseQuery<>("Membership");
3   innerQuery.whereGreaterThan("expirationDate", new Date());
4   query.whereExists("premiumMembership");
5   query.whereMatchesQuery("premiumMembership", innerQuery);
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10          Log.e(TAG, "Parse Error: ", e);
11      }
12  });
```

notEqualTo

```java
//Filters objects in which a specific key’s value is not equal to the provided value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereNotEqualTo("friendCount", 2);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```

startsWith

```java
//Filters objects in which a string key’s value starts with the provided text value.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereStartsWith("name", "Brit");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
lessThan

```kotlin
//Filters objects in which a specific key’s value is lesser than the provided value.
1   val query = ParseQuery<ParseObject>("Profile")
2   val calendar = Calendar.getInstance()
3   calendar[1980, 8, 19, 59, 59] = 59
4   val date = calendar.time
5   query.whereLessThan("birthDay", date)
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

lessThanOrEqualTo

```kotlin
//Filters objects in which a specific key’s value is lesser than or equal to the provided value.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereLessThanOrEqualTo("friendCount", 49)
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

matches

```kotlin
//Filters objects in which a string type key value must match the provided regular expression and its modifiers.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereMatches("name", "da", "i")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

matchesKeyInQuery

```kotlin
//Requires that a key’s value matches a value in an object returned by a different ParseQuery. Useful for multi-object querying.1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
1   val query = ParseQuery<ParseObject>("Profile")
2   val innerQuery = ParseQuery<ParseObject>("Profile")
3   innerQuery.whereLessThan("friendCount", 50)
4   query.whereMatchesKeyInQuery("friendCount", "friendCount", innerQuery)
5   query.whereGreaterThan("friendCount", 10)
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

matchesQuery

```kotlin
//Requires that an object contained in the given key matches another query. Useful for multi-object querying.
1   val query = ParseQuery<ParseObject>("Profile")
2   val innerQuery = ParseQuery<ParseObject>("Membership")
3   innerQuery.whereGreaterThan("expirationDate", Date())
4   query.whereExists("premiumMembership")
5   query.whereMatchesQuery("premiumMembership", innerQuery)
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

notEqualTo

```kotlin
//Filters objects in which a specific key’s value is not equal to the provided value.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereNotEqualTo("friendCount", 2)
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

startsWith

```kotlin
//Filters objects in which a string key’s value starts with the provided text value.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereStartsWith("name", "Brit")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```
:::

## Query ordering

Essential in most queries, ordering can be easily achieved in Parse and even chained between two or more ordering constraints.

These are **Java** methods

:::CodeblockTabs
addAscending

```java
//Sort the results in ascending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.addAscendingOrder("friendCount");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

addDescending

```java
//Sort the results in descending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.addDescendingOrder("friendCount");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

ascending

```java
//Sort the results in ascending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.orderByAscending("friendCount");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

descending

```java
//Sort the results in descending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.orderByDescending("friendCount");
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
addAscending

```kotlin
//Sort the results in ascending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.addAscendingOrder("friendCount")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

addDescending

```kotlin
//Sort the results in descending order, overwrites previous orderings. Multiple keys can be used to solve ordering ties.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.addDescendingOrder("friendCount")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

ascending

```kotlin
//Sort the results in ascending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.orderByAscending("friendCount")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

descending

```kotlin
//Sort the results in descending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.orderByDescending("friendCount")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```
:::

## Field selecting

These methods affect which field values can be in your query results.

These are **Java** methods

:::CodeblockTabs
include

```java
//Return all fields in the returned objects except the ones specified.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereExists("premiumMembership");
3   query.include("premiumMembership");
4   query.findInBackground((objects, e) -> {
5       if (e == null) {
6           Log.d(TAG, "Objects: " + objects);
7           Log.d(TAG, "Object Premium Membership: " + objects.get(0).get("premiumMembership"));
8       } else {
9           Log.e(TAG, "Parse Error: ", e);
10      }
11
12  });
```

select

```java
//Return only the specified fields in the returned objects.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.selectKeys(List.of("name"));
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6           Log.d(TAG, "Object name: " + objects.get(0).get("name"));
7       } else {
8           Log.e(TAG, "Parse Error: ", e);
9       }
10
11 });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
include

```kotlin
//Return all fields in the returned objects except the ones specified.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereExists("premiumMembership")
3   query.include("premiumMembership")
4   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
5       if (e == null) {
6           Log.d(Companion.TAG, "Objects: $objects")
7           Log.d(
8               Companion.TAG,
9               "Object Premium Membership: " + objects[0]["premiumMembership"]
10          )
11      } else {
12          Log.e(Companion.TAG, "Parse Error: ", e)
13      }
14  }
```

select

```kotlin
//Return only the specified fields in the returned objects.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.selectKeys(java.util.List.of("name"))
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6           Log.d(Companion.TAG, "Object name: " + objects[0]["name"])
7       } else {
8           Log.e(Companion.TAG, "Parse Error: ", e)
9       }
10  }
```
:::

## Geopoint querying

These are methods specific to GeoPoint querying.

These are **Java** Methods

:::CodeblockTabs
near

```java
//Order objects by how near the key value is from the given GeoPoint.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereNear("lastLoginLocation", new ParseGeoPoint(37.38412167489413, -122.01268034622319));
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9
10  });
```

polygonContains

```java
//Find objects whose key value contains the specified GeoPoint.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.wherePolygonContains("lastLoginLocation", new ParseGeoPoint(37.38412167489413, -122.01268034622319));
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

withinGeoBox

```java
//Find objects whose key value is contained within the specified bounding box, composed by two GeoPoint values that set the lower-left and upper-right corner values.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereWithinGeoBox("lastLoginLocation", new ParseGeoPoint(37.48412167489413, -122.11268034622319), new ParseGeoPoint(37.28412167489413, -121.91268034622319));
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

withinKilometers

```java
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereWithinKilometers("lastLoginLocation", new ParseGeoPoint(37.38412167489413, -122.01268034622319), 100);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```
:::

:::CodeblockTabs
withinMiles

```java
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereWithinMiles("lastLoginLocation", new ParseGeoPoint(37.38412167489413, -122.01268034622319), 100);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

withinPolygon

```java
//Find objects whose key value is contained within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereWithinPolygon("lastLoginLocation", List.of(new ParseGeoPoint(37.48412167489413, -122.11268034622319),
3           new ParseGeoPoint(37.48412167489413, -121.91268034622319),
4           new ParseGeoPoint(37.28412167489413, -121.91268034622319),
5           new ParseGeoPoint(37.28412167489413, -122.01268034622319)));
6   query.findInBackground((objects, e) -> {
7       if (e == null) {
8           Log.d(TAG, "Objects: " + objects);
9       } else {
10          Log.e(TAG, "Parse Error: ", e);
11      }
12
13  });
```

withinRadians

```java
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.whereWithinRadians("lastLoginLocation", new ParseGeoPoint(37.38412167489413, -122.01268034622319), 100);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
near

```kotlin
//Order objects by how near the key value is from the given GeoPoint.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereNear("lastLoginLocation", ParseGeoPoint(37.38412167489413, -122.01268034622319))
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

polygonContains

```kotlin
//Find objects whose key value contains the specified GeoPoint.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.wherePolygonContains(
3       "lastLoginLocation",
4       ParseGeoPoint(37.38412167489413, -122.01268034622319)
5   )
6   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
7       if (e == null) {
8           Log.d(Companion.TAG, "Objects: $objects")
9       } else {
10          Log.e(Companion.TAG, "Parse Error: ", e)
11      }
12  }
```

withinGeoBox

```kotlin
//Find objects whose key value is contained within the specified bounding box, composed by two GeoPoint values that set the lower-left and upper-right corner values.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereWithinGeoBox(
3       "lastLoginLocation",
4       ParseGeoPoint(37.48412167489413, -122.11268034622319),
5       ParseGeoPoint(37.28412167489413, -121.91268034622319)
6   )
7   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
8       if (e == null) {
9           Log.d(Companion.TAG, "Objects: $objects")
10      } else {
11          Log.e(Companion.TAG, "Parse Error: ", e)
12      }
13  }
```

withinKilometers

```kotlin
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereWithinKilometers(
3       "lastLoginLocation",
4       ParseGeoPoint(37.38412167489413, -122.01268034622319),
5       100.0
6   )
7   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
8       if (e == null) {
9           Log.d(Companion.TAG, "Objects: $objects")
10      } else {
11          Log.e(Companion.TAG, "Parse Error: ", e)
12      }
13  }
```
:::

:::CodeblockTabs
withinMiles

```kotlin
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereWithinMiles(
3       "lastLoginLocation",
4       ParseGeoPoint(37.38412167489413, -122.01268034622319),
5       100.0
6   )
7   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
8       if (e == null) {
9           Log.d(Companion.TAG, "Objects: $objects")
10      } else {
11          Log.e(Companion.TAG, "Parse Error: ", e)
12      }
13  }
```

withinPolygon

```kotlin
//Find objects whose key value is contained within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereWithinPolygon(
3       "lastLoginLocation", java.util.List.of(
4           ParseGeoPoint(37.48412167489413, -122.11268034622319),
5           ParseGeoPoint(37.48412167489413, -121.91268034622319),
6           ParseGeoPoint(37.28412167489413, -121.91268034622319),
7           ParseGeoPoint(37.28412167489413, -122.01268034622319)
8       )
9   )
10  query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
11      if (e == null) {
12          Log.d(Companion.TAG, "Objects: $objects")
13      } else {
14          Log.e(Companion.TAG, "Parse Error: ", e)
15      }
16  }
```

withinRadians

```kotlin
//Find objects whose key value is near the given GeoPoint and within the maxDistance value. The sorted boolean value determines if the results should be sorted by distance ascending.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.whereWithinRadians(
3       "lastLoginLocation",
4       ParseGeoPoint(37.38412167489413, -122.01268034622319),
5       100.0
6   )
7   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
8       if (e == null) {
9           Log.d(Companion.TAG, "Objects: $objects")
10      } else {
11          Log.e(Companion.TAG, "Parse Error: ", e)
12      }
13  }
```
:::

## Pagination

These methods are related to pagination utilities, useful for queries that will retrieve a large number of results.

These are **Java** methods

:::CodeblockTabs
limit

```java
//Sets the maximum value of returned results, the default value is 100.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.setLimit(2);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

skip

```java
//Skips the first n results in the query, essential for pagination.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.setSkip(2);
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
limit

```kotlin
//Sets the maximum value of returned results, the default value is 100.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.limit = 2
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

skip

```kotlin
//Skips the first n results in the query, essential for pagination.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.skip = 2
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```
:::

## Local datastore

These methods enable selecting the source of the queries and using a local datastore.

These are **Java** methods

:::CodeblockTabs
fromLocalDatastore

```java
//Changes the source of this query to all pinned objects.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   //If you want use LocalDataStore you should enable local data store in the App.java or App.kt file.
3   query.fromLocalDatastore();
4   query.findInBackground((objects, e) -> {
5       if (e == null) {
6           Log.d(TAG, "Objects: " + objects);
7       } else {
8           Log.e(TAG, "Parse Error: ", e);
9       }
10
11  });
```

fromNetwork

```java
//Changes the source of this query to your online server.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.fromNetwork();
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

fromPin

```java
//Changes the source of this query to the default group of pinned objects.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.fromPin();
3   query.findInBackground((objects, e) -> {
4       if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9 
10  });
```

fromPinWithName

```java
//Changes the source of this query to a specific group of pinned objects.
1   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
2   query.fromPin("pinnedObjects");
3   query.findInBackground((objects, e) -> {
4      if (e == null) {
5           Log.d(TAG, "Objects: " + objects);
6       } else {
7           Log.e(TAG, "Parse Error: ", e);
8       }
9   });
```
:::

These are **Kotlin** methods

:::CodeblockTabs
fromLocalDatastore

```kotlin
//Changes the source of this query to all pinned objects.
1   val query = ParseQuery<ParseObject>("Profile")
2   //If you want use LocalDataStore you should enable local data store in the App.java or App.kt file.
3   query.fromLocalDatastore()
4   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
5       if (e == null) {
6           Log.d(Companion.TAG, "Objects: $objects")
7       } else {
8           Log.e(Companion.TAG, "Parse Error: ", e)
9       }
10  }
```

fromNetwork

```kotlin
//Changes the source of this query to your online server.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.fromNetwork()
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9  }
```

fromPin

```kotlin
//Changes the source of this query to the default group of pinned objects.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.fromPin()
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```

fromPinWithName

```kotlin
//Changes the source of this query to a specific group of pinned objects.
1   val query = ParseQuery<ParseObject>("Profile")
2   query.fromPin("pinnedObjects")
3   query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
4       if (e == null) {
5           Log.d(Companion.TAG, "Objects: $objects")
6       } else {
7           Log.e(Companion.TAG, "Parse Error: ", e)
8       }
9   }
```
:::

## Conclusion

At the end of this guide, you learned how to use different query types in Android.

[title] Untitled
[path] /


[title] GraphQL Client Setup
[path] Flutter/GraphQL/

# Flutter GraphQL Setup



## Introduction

In the last tutorial we understood the benefits of using Back4app GraphQL with Flutter. In this article we are going to setup the basic scaffold for the project and connect to the Back4app Server

## Goals

- **Setup the Flutter Environment**
- **Flutter GraphQL setup anatomy**
- **Flutter GraphQL connection**
- **Flutter GraphQL connection reuse and patterns**

## Prerequisites

:::hint{type="info"}
- We require that the user has some basic understanding of Dart and Flutter;
- Though not necessary, GraphQL cookbook will be useful in understanding the <a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">GraphQL concepts</a>.
:::

## 0 - Setup the App from Back4app Hub

We would need to create an app, you can follow documentation on: [https://www.back4app.com/docs/get-started/new-parse-app](https://www.back4app.com/docs/get-started/new-parse-app)

We would be using Back4app Hub to set up necessary classes for this tutorial.
Please go to: [https://www.back4app.com/database/chinmay/flutter-graphql](https://www.back4app.com/database/chinmay/flutter-graphql). Click on connect to API.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BPEg6W5LOVp_grHWrF541_image.png)

Select the newly created app and then you are done!

## 1 - Setting up Flutter

Setting up flutter is relatively painless. We will follow the setup instructions at the [official flutter website](https://flutter.dev/docs/get-started/install).
After this we will create a simple flutter application using the command:

> flutter create flutter_graphql_setup

Check if everything is OK using the command flutter doctor, by Running the application:

> cd flutter_graphql_setup 
> flutter run

## 2 - Installing the Flutter GraphQL library

For implementing this client we are going to use the flutter\_graphql library as mentioned in the first article.
We will now add adding this to your packages pubspec.yaml.

> dependencies:
> graphql_flutter: ^3.0.0

## 3 - Create a Flutter GraphQL Provider

In GraphQL we do not have to work with multiple endpoints, we only have a single endpoint that is used to query the request data. And we send GraphQL queries to that endpoint. So generally what we do is that we create an instance of the client that is responsible for sending the appropriate headers and format the queries as per our need.
We will be creating a client, for this we need a link (instance of the HttpLink class) and a cache store. We will be using HttpLink as our link and OptimisticCache for our caching. Code would be written in the following manner:

In the [main.dart](https://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-setup/lib/main.dart) we will write the following:

```dart
1   final HttpLink httpLink = HttpLink(
2         uri: 'https://parseapi.back4app.com/graphql',
3         headers: {
4           'X-Parse-Application-Id': kParseApplicationId,
5           'X-Parse-Client-Key': kParseClientKey,
6         }, //getheaders()
7       );
8
9       ValueNotifier<GraphQLClient> client = ValueNotifier(
10        GraphQLClient(
11          cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
12          link: httpLink,
13        ),
14      );
15
```

## 4 - Connect to Back4app using the GraphQL

Go to the Back4app Dashboard in the option “API Console” > “GraphQl Console”:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-xlGmAHlGt5Y-dUY_Zj3J_image.png)

Note down:

1. **API url**
2. **Parse App ID**
3. **Parse Client ID**

We will create a new file [constants.dart](https://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-setup/lib/constants.dart) in lib folder of our project.

> String kParseApplicationId= "";
> String kParseClientKey = "";

## 5 - Querying Data

Our component will be wrapped by the GraphQLProvider widget, which would provide necessary details for the widget.
We will need to provide an instance of client that we created in step 2.
We will use GraphQL console, to write our query. You can learn more about GraphQL queries in our [GraphQL cookbook section.](https://www.back4app.com/docs/parse-graphql/graphql-getting-started)

```graphql
1   import 'package:graphql_flutter/graphql_flutter.dart';
2   import 'constants.dart';
3
4   class _MyHomePageState extends State<MyHomePage> {
5     String name;
6     String saveFormat;
7     String objectId;
8
9     String query = '''
10    query FindLanguages{
11    languages{
12      count,
13      edges{
14        node{
15          name,
16          saveFormat
17        }
18      }
19    }
20  }
21    ''';
22
23    @override
24    Widget build(BuildContext context) {
25      return SafeArea(
26        child: Scaffold(
27          appBar: AppBar(
28            title: Text(
29              'Parsing data using GraphQL',
30            ),
31          ),
32          body: Query(
33            options: QueryOptions(
34              documentNode: gql(query),
35            ),
36            builder: (
37             QueryResult result, {
38              Refetch refetch,
39              FetchMore fetchMore,
40            }) {
41              if (result.data == null) { //check if data is loading
42                return Center(
43                    child: Text(
44                  "Loading...",
45                  style: TextStyle(fontSize: 20.0),
46                ));
47              }  
48               //to implement rendering logic
49            },
50          ),
51        ),
52      );
53    }
54  }
```

## 6 -  Render the list

We will use the ListView widget to render the list, in the [main.dart](https://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-setup/lib/main.dart)

```dart
1   else {
2                 return ListView.builder(
3                   itemBuilder: (BuildContext context, int index) {
4                     return ListTile(
5                       title: Text(result.data["programmingLanguages"]["edges"][index]["node"]['name']),
6                       trailing: Text(
7                         result.data["programmingLanguages"]["edges"][index]
8                           ["node"]['stronglyTyped'] ? "Strongly Typed":"Weekly Typed"
9                       ),
10                    );
11                  },
12                  itemCount: result.data["programmingLanguages"]["edges"].length,
13                );
14              }
15            
```

We should get the following on our screen:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6T_PJltnmJwKxobbbkw2Q_image.png" signedSrc size="50" width="396" height="673" position="center" caption}

## Conclusion

We have configured the Flutter GraphQL client and connect to the Back4app GraphQL api.
You can find the code for the same here: [https://github.com/templates-back4app/Flutter-GraphQL/tree/flutter-graphql-setup.](https://github.com/templates-back4app/Flutter-GraphQL/tree/flutter-graphql-setup)

[title] Query Cookbook
[path] Flutter/Parse SDK (REST)/Data Queries/

# Parse Query Cookbook in Flutter

## Introduction

We’ve already seen how a QueryBuilder with get can retrieve a single ParseObject from Back4App. There are many other ways to retrieve data with QueryBuilder - you can retrieve many objects at once, use conditions on the objects you wish to retrieve, and more.

In this guide, you will ding deep into the QueryBuilder class and see all the methods you can use to build your Queries. You will use a simple database class with some mocked data to perform the Queries using Flutter on Back4App.

## Prerequisites

:::hint{type="info"}
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## Goal

Explore the QueryBuilder class different methods.

## The QueryBuilder class

Any Parse query operation uses the QueryBuilder object type, which will help you retrieve specific data from your database throughout your app.

To create a new QueryBuilder, you need to pass as a parameter the desired ParseObject subclass, which is the one that will contain your query results.

It is crucial to know that a QueryBuilder will only resolve after calling a retrieve method query, so a query can be set up and several modifiers can be chained before actually being called.

You can read more about the QueryBuilder class <a href="https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#complex-queries" target="_blank">here at the official documentation</a>.

## Using the JavaScript Console on Back4App

Inside your Back4App application’s dashboard, you will find a very useful API console in which you can run JavaScript code directly. In this guide you will use to store data objects in Back4App. On your App main dashboard go to Core->API Console->Javascript.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3TcRJJck5LuleYZ4hQI4Y_image.png)

## Save your Data Objects

To run the queries on this guide you’ll need first to populate your App with some data. Let’s create a sample class called Profile, which mocks a social media profile class using famous people names and the following fields:

- string type name:
- Date type birthDay:
- Number (integer) type friendCount:
- Array (string list) type favoriteFoods:
- Array (Number list) type luckyNumbers:
- GeoPoint type lastLoginLocation:

Here is the Parse.Object classes creation code, so go ahead and run it in your API console:

```dart
1   // Add Profile objects and create table
2   // Adam Sandler
3   let Profile = new Parse.Object('Profile');
4   Profile.set('name', 'Adam Sandler');
5   Profile.set('birthDay', new Date('09/09/1966'));
6   Profile.set('friendCount', 2);
7   Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8   Profile.set('luckyNumbers', [2, 7]);
9   Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
10  await Profile.save();
11
12  // Britney Spears
13  Profile = new Parse.Object('Profile');
14  Profile.set('name', 'Britney Spears');
15  Profile.set('birthDay', new Date('12/02/1981'));
16  Profile.set('friendCount', 52);
17  Profile.set('favoriteFoods', ['Cake', 'Bread']);
18  Profile.set('luckyNumbers', [22, 7]);
19  Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
20  await Profile.save();
21
22  // Carson Kressley
23  Profile = new Parse.Object('Profile');
24  Profile.set('name', 'Carson Kressley');
25  Profile.set('birthDay', new Date('11/11/1969'));
26  Profile.set('friendCount', 12);
27  Profile.set('favoriteFoods', ['Fish', 'Cookies']);
28  Profile.set('luckyNumbers', [8, 2]);
29  Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
30  await Profile.save();
31
32  // Dan Aykroyd
33  // Creates related object Membership for this user only
34  let Membership = new Parse.Object('Membership');
35  Membership.set('name', 'Premium');
36  Membership.set('expirationDate', new Date('10/10/2030'))
37  await Membership.save();
38
39  Profile = new Parse.Object('Profile');
40   Profile.set('name', 'Dan Aykroyd');
41  Profile.set('birthDay', new Date('07/01/1952'));
42  Profile.set('friendCount', 66);
43  Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
44  Profile.set('luckyNumbers', [22, 77]);
45  Profile.set('lastLoginLocation', new Parse.GeoPoint(37.38412167489413, -122.01268034622319));
46  Profile.set('premiumMembership', Membership);
47  await Profile.save();
48
49  // Eddie Murphy
50  Profile = new Parse.Object('Profile');
51  Profile.set('name', 'Eddie Murphy');
52  Profile.set('birthDay', new Date('04/03/1961'));
53  Profile.set('friendCount', 49);
54  Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
55  Profile.set('luckyNumbers', [6, 5]);
56  Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
57  await Profile.save();
58
59  // Fergie
60  Profile = new Parse.Object('Profile');
61  Profile.set('name', 'Fergie');
62  Profile.set('birthDay', new Date('03/27/1975'));
63  Profile.set('friendCount', 55);
64  Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
65  Profile.set('luckyNumbers', [13, 7]);
66  Profile.set('lastLoginLocation', new Parse.GeoPoint(-27.104919974838154, -52.61428045237739));
67  await Profile.save();
68
69  console.log('Success!');
```

After running this code, you should now have a Profile class in your database with six objects created. Your new class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/K59IBJVFpp-kIDPI2S5ho_image.png)

Let’s now take a look at examples from every QueryBuilder method, along with brief explanations on what they do. Please note that some methods in this list can take options as an additional argument, but in most cases, it is only related to masterKey usage and not relevant to this guide content, so this possibility will be omitted whenever not relevant.

## Query retrievers

These methods are responsible for running the query and retrieving its results, being always present in your query implementation.

:::CodeblockTabs
count

```javascript
//Retrieves the count of ParseObject results that meet the query criteria.
1   QueryBuilder<ParseObject> queryBuilder = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   var apiResponse = await queryPlayers.count();
3   if (apiResponse.success && apiResponse.result != null) {
4     int countGames = apiResponse.count;
5   }
```

getObject

```javascript
//This quick method is used to retrieve a single ParseObject instance when you know its objectId.
1   ///To retrieve one object of a Class it is not necessary to create a ParseQuery.
2   ///We can query using ParseObject
3   final apiResponse = await ParseObject('Profile').getObject('C6ENdLnFdQ');
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var o in apiResponse.results) {
7       final object = o as ParseObject;
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

getAll

```javascript
//Retrieves a complete list of ParseObjects.
1   ///To retrieve all objects of a Class it is not necessary to create a ParseQuery.
2   ///We can query using ParseObject
3   final apiResponse = await ParseObject('Profile').getAll();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var o in apiResponse.results) {
7       final object = o as ParseObject;
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

query

```javascript
//This is the basic method for retrieving your query results, always returning an list of ParseObject instances, being empty when none are found.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   // When using .query() in a query without other operations, you will
3   // get every object saved in your class
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var o in apiResponse.results) {
8       final object = o as ParseObject;
9       print('${object.objectId} - ${object.get<String>('name')}');
10    }
11  }
```
:::

## Query conditioners

These methods give you the possibility of applying conditional constraints to your query, which are arguably the most important operations in querying.

Remember that these operations can all be chained before the results are retrieved, so many combinations can be achieved to solve your querying needs.

:::CodeblockTabs
whereDoesNotMatchKeyInQuery

```dart
//Requires that a key’s value does not match a value in an object returned by a different Parse.Query. Useful for multi-object querying.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereDoesNotMatchKeyInQuery(
3     'friendCount',
4     'friendCount',
5     QueryBuilder<ParseObject>(ParseObject('Profile'))
6     ..whereLessThan('friendCount', 50));
7   parseQuery.whereGreaterThan('friendCount', 10);
8   final apiResponse = await parseQuery.query();
9
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')} - friendCount: ${object.get<int>('friendCount')}');
13    }
14  }
```

whereArrayContainsAll

```dart
//Filters objects in which an array type key value must contain every value provided.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereArrayContainsAll('luckyNumbers', [2, 7]);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - ${object.get<String>('name')}');
8     }
9   }
```

whereCoitainedIn

```dart
//Filters objects in which a key value is contained in the provided array of values.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereContainedIn('luckyNumbers', [2, 7]);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - ${object.get<String>('name')}');
8     }
9   }
```
:::

:::CodeblockTabs
whereContains

```dart
//Filters objects in which a string key value contains the provided text value. Be aware that this condition is case-sensitive.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   // This can be slow in large databases
3   parseQuery.whereContains('name', 'an');
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var object in apiResponse.results as List<ParseObject>) {
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

whereDoesNotMatchQuery

```dart
//Requires that an object contained in the given key does not match another query. Useful for multi-object querying.
1   final QueryBuilder<ParseObject> innerQuery = QueryBuilder<ParseObject>(ParseObject('Membership'));
2   innerQuery.whereGreaterThan('expirationDate', DateTime.now());
3
4   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
5
6   parseQuery.whereDoesNotMatchQuery('premiumMembership', innerQuery);
7
8   final apiResponse = await parseQuery.query();
9
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')}');
13    }
14  }
```

whereEndsWith

```dart
//Filters objects where specific key’s value ends with value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereEndsWith('name', 'ie');
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var o in apiResponse.results) {
8       final object = o as ParseObject;
9       print('${object.objectId} - ${object.get<String>('name')}');
10    }
11  }
```
:::

:::CodeblockTabs
whereEqualTo

```dart
//Filters objects in which a specific key’s value is equal to the provided value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereEqualTo('friendCount', 2);
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var o in apiResponse.results) {
8       final object = o as ParseObject;
9       print('${object.objectId} - ${object.get<String>('name')}');
10    }
11  }
```

whereGreaterThan

```dart
//Filters objects in which a specific key’s value is greater than the provided value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereGreaterThan('birthDay', DateTime(1980, 08, 19));
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var object in apiResponse.results as List<ParseObject>) {
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

whereGreaterThanOrEqualsTo

```dart
//Filters objects in which a specific key’s value is greater than or equal to the provided value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereGreaterThanOrEqualsTo('friendCount', 49);
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var object in apiResponse.results as List<ParseObject>) {
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```
:::

:::CodeblockTabs
whereLessThan

```dart
//Filters objects in which a specific key’s value is lesser than the provided value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereLessThan('birthDay', DateTime(1980, 08, 19));
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var object in apiResponse.results as List<ParseObject>) {
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

whereLessThanOrEqualTo

```dart
//Filters objects in which a specific key’s value is lesser than or equal to the provided value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereLessThanOrEqualTo('friendCount', 49);
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var object in apiResponse.results as List<ParseObject>) {
8       print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

whereMatchesKeyInQuery

```dart
//Requires that a key’s value matches a value in an object returned by a different Parse.Query. Useful for multi-object querying.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereMatchesKeyInQuery(
3     'friendCount',
4     'friendCount',
5     QueryBuilder<ParseObject>(ParseObject('Profile'))
6     ..whereLessThan('friendCount', 50));
7  
8   final apiResponse = await parseQuery.query();
9  
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')} - friendCount: ${object.get<int>('friendCount')}');
13    }
14  }
```
:::

:::CodeblockTabs
whereMatchesQuery

```dart
//Requires that an object contained in the given key matches another query. Useful for multi-object querying.
1   final QueryBuilder<ParseObject> innerQuery = QueryBuilder<ParseObject>(ParseObject('Membership'));
2   innerQuery.whereGreaterThan('expirationDate', DateTime.now());
3
4   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
5
6   parseQuery.whereValueExists('premiumMembership', true);
7   parseQuery.whereMatchesQuery('premiumMembership', innerQuery);
8
9   final apiResponse = await parseQuery.query();
10
11  if (apiResponse.success && apiResponse.results != null) {
12    for (var object in apiResponse.results as List<ParseObject>) {
13      print('${object.objectId} - Name: ${object.get<String>('name')}');
14    }
15  }
```

whereNotContainedIn

```dart
//Filters objects in which a key value is not contained in the provided array of values.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereNotContainedIn('luckyNumbers', [2, 7]);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - ${object.get<String>('name')}');
8     }
9  }
```

whereNotEqualTo

```dart
//Filters objects in which a specific key’s value is not equal to the provided value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereNotEqualTo('friendCount', 2);
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var object in apiResponse.results as List<ParseObject>) {
8      print('${object.objectId} - ${object.get<String>('name')}');
9     }
10  }
```

whereStartsWith

```dart
//Filters objects where specific key’s value starts with value.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2
3   parseQuery.whereStartsWith('name', 'Adam');
4   final apiResponse = await parseQuery.query();
5
6   if (apiResponse.success && apiResponse.results != null) {
7     for (var o in apiResponse.results) {
8       final object = o as ParseObject;
9       print('${object.objectId} - ${object.get<String>('name')}');
10    }
11  }
```

whereValueExists

```dart
//Filters objects in which a key value is set or no.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   //true - retrieve documents with field set
3   //false - retrieve documents without field set
4   parseQuery.whereValueExists('premiumMembership', true);
5   final apiResponse = await parseQuery.query();
6
7   if (apiResponse.success && apiResponse.results != null) {
8     for (var object in apiResponse.results as List<ParseObject>) {
9       print('${object.objectId} - ${object.get<String>('name')}');
10    }
11  }
```
:::

## Query ordering

Essential in most queries, ordering can be easily achieved in Parse and even chained between two or more ordering constraints.

:::CodeblockTabs
orderByAscending

```dart
//Sort the results in ascending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.orderByAscending('name');
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - ${object.get<String>('name')}');
8     }
9   }
```

orderByDescending

```dart
//Sort the results in descending order, this can be chained without overwriting previous orderings. Multiple keys can be used to solve ordering ties.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.orderByDescending('name');
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - ${object.get<String>('name')}');
8     }
9   }
```
:::

## Field selecting

These methods affect which field values can be in your query results.

:::CodeblockTabs
excludeKeys

```dart
//Return all fields in the returned objects except the ones specified.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.excludeKeys(['name']);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       //Name = null
8       print('${object.objectId} - Name: ${object.get<String>('name')} - Birthday: ${object.get<DateTime>('birthDay')}');
9     }
10  }
```

includeObject

```dart
//Includes nested ParseObjects for the provided key.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.includeObject(['premiumMembership']);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       //Name = null
8       print('${object.objectId} - Name: ${object.get<String>('name')} - premiumMembership: ${object.get<ParseObject>('premiumMembership').toString()}');
9     }
10  }
```

keysToReturn

```dart
   //Return only the specified fields in the returned objects.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.keysToReturn(['name']);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       //Birthday = null
8       print('${object.objectId} - Name: ${object.get<String>('name')} - Birthday: ${object.get<DateTime>('birthDay')}');
9     }
10  }
```
:::

## Geopoint querying

These are methods specific to GeoPoint querying.

:::CodeblockTabs
whereNear

```dart
//Return all fields in the returned objects except the ones specified.
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereNear(
3     'lastLoginLocation',
4     ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319)
5   );
6
7   final apiResponse = await parseQuery.query();
8 
9   if (apiResponse.success && apiResponse.results != null) {
10    for (var object in apiResponse.results as List<ParseObject>) {
11      print('${object.objectId} - Name: ${object.get<String>('name')} - GeoPoint: ${object.get<ParseGeoPoint>('lastLoginLocation').toJson()}');
12    }
13  }
```

whereWithinGeoBox

```dart
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereWithinGeoBox(
3     'lastLoginLocation',
4     ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
5     ParseGeoPoint(latitude: 37.28412167489413, longitude: -121.91268034622319)
6   );
7     
8   final apiResponse = await parseQuery.query();
9
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')} - GeoPoint: ${object.get<ParseGeoPoint>('lastLoginLocation').toJson()}');
13    }
14  }
```
:::

:::CodeblockTabs
whereWithinKilometers

```dart
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereWithinKilometers(
3     'lastLoginLocation',
4     ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
5     100
6   );
7
8   final apiResponse = await parseQuery.query();
9
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')} - GeoPoint: ${object.get<ParseGeoPoint>('lastLoginLocation').toJson()}');
13    }
14  }
```

whereWithinMiles

```dart
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereWithinMiles(
3     'lastLoginLocation',
4     ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
5     100
6   );
7
8   final apiResponse = await parseQuery.query();
9
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')} - GeoPoint: ${object.get<ParseGeoPoint>('lastLoginLocation').toJson()}');
13    }
14  }
```

whereWithinRadians

```dart
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.whereWithinRadians(
3     'lastLoginLocation',
4     ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
5     100
6   );
7
8   final apiResponse = await parseQuery.query();
9
10  if (apiResponse.success && apiResponse.results != null) {
11    for (var object in apiResponse.results as List<ParseObject>) {
12      print('${object.objectId} - Name: ${object.get<String>('name')} - GeoPoint: ${object.get<ParseGeoPoint>('lastLoginLocation').toJson()}');
13    }
14  }
```
:::

## Pagination

These methods are related to pagination utilities, useful for queries that will retrieve a large number of results.

:::CodeblockTabs
setAmountToSkip

```dart
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.setAmountToSkip(2);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - Name: ${object.get<String>('name')}');
8     }
9   }
```

setLimit

```dart
1   final QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   parseQuery.setLimit(2);
3   final apiResponse = await parseQuery.query();
4
5   if (apiResponse.success && apiResponse.results != null) {
6     for (var object in apiResponse.results as List<ParseObject>) {
7       print('${object.objectId} - Name: ${object.get<String>('name')}');
8     }
9   }
```
:::

## Compound query

These method will create compound queries, which can combine more than one ParseQuery instance to achieve more complex results.

:::CodeblockTabs
or

```dart
//Compose a compound query that is the OR of the passed queries.
1   final QueryBuilder<ParseObject> query1 = QueryBuilder<ParseObject>(ParseObject('Profile'));
2   query1.whereGreaterThan('friendCount', 10);
3
4   final QueryBuilder<ParseObject> query2 = QueryBuilder<ParseObject>(ParseObject('Profile'));
5   query2.whereLessThan('friendCount', 50);
6   
7   QueryBuilder<ParseObject> mainQuery = QueryBuilder.or(
8     ParseObject("Profile"),
9     [query1, query2],
10  );
11
12  final apiResponse = await mainQuery.query();
13  
14  if (apiResponse.success && apiResponse.results != null) {
15    for (var object in apiResponse.results as List<ParseObject>) {
16      print('${object.objectId} - Name: ${object.get<String>('name')}');
17    }
18  }
```
:::

## Conclusion

At the end of this guide, you learned how to perform every data query method in Parse. In the next guide, you will learn about complex Parse querying in Flutter.

[title] User Registration
[path] ReactJS/Users/

# Sign up page in React using Parse

## Introduction

At the core of many apps, user accounts have a notion that lets users securely access their information. Parse provides a specialized user class called Parse.User that automatically handles much of the functionality required for user account management.

In this guide, you will learn how the Parse.User class works by creating a user registration feature for your React App using Parse JS SDK.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library</a>.
:::

## Goal

To build a User Registration feature using Parse for a React App.

## 1 - Understanding the SignUp method

Parse User management uses the Parse.User object type, which extends the default ParseObject type while containing unique helper methods, such as current and getUsername, that will help you retrieve user data throughout your app. You can read more about the Parse.User object [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html).

In this guide, you will learn how to use the signUp method that creates a new valid and unique Parse.User object both locally and on the server, taking as arguments valid username and password values.

## 2 - Create the user registration component

Let’s now build the functional component, which will call the signUp method in our App. First, create a new file in your src directory called UserRegistration.js (UserRegistration.tsx if you are using TypeScript) and also add the needed input elements (username and password inputs), using state hooks via useState to manage their data:

:::CodeblockTabs
UserRegistration.js

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider, Input } from 'antd';
5	
6	export const UserRegistration = () => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	
11	  return (
12	    <div>
13	      <div className="header">
14	        <img
15	          className="header_logo"
16	          alt="Back4App Logo"
17	          src={
18	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
19	          }
20	        />
21	        <p className="header_text_bold">{'React on Back4App'}</p>
22	        <p className="header_text">{'User Registration'}</p>
23	      </div>
24	      <div className="container">
25	        <h2 className="heading">{'User Registration'}</h2>
26	        <Divider />
27	        <div className="form_wrapper">
28	          <Input
29	            value={username}
30	            onChange={(event) => setUsername(event.target.value)}
31	            placeholder="Username"
32	            size="large"
33	            className="form_input"
34	          />
35	          <Input
36	            value={password}
37	            onChange={(event) => setPassword(event.target.value)}
38	            placeholder="Password"
39	            size="large"
40	            type="password"
41	            className="form_input"
42	          />
43	        </div>
44	        <div className="form_buttons">
45	          <Button
46	            onClick={() => doUserRegistration()}
47	            type="primary"
48	            className="form_button"
49	            color={'#208AEC'}
50	            size="large"
51	          >
52	            Sign Up
53	          </Button>
54	        </div>
55	      </div>
56	    </div>
57	  );
58	};
```

UserRegistration.tsx

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	const Parse = require('parse/dist/parse.min.js');
5	
6	export const UserRegistration: FC<{}> = (): ReactElement => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	
11	  return (
12	    <div>
13	      <div className="header">
14	        <img
15	          className="header_logo"
16	          alt="Back4App Logo"
17	          src={
18	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
19	          }
20	        />
21	        <p className="header_text_bold">{'React on Back4App'}</p>
22	        <p className="header_text">{'User Registration'}</p>
23	      </div>
24	      <div className='container'>
25	        <h2 className="heading">{'User Registration'}</h2>
26	        <Divider />
27	        <div className="form_wrapper">
28	          <Input
29	            value={username}
30	            onChange={(event) => setUsername(event.target.value)}
31	            placeholder="Username"
32	            size="large"
33	            className="form_input"
34	          />
35	          <Input
36	            value={password}
37	            onChange={(event) => setPassword(event.target.value)}
38	            placeholder="Password"
39	            size="large"
40	            type="password"
41	            className="form_input"
42	          />
43	        </div>
44	        <div className="form_buttons">
45	          <Button
46	            onClick={() => doUserRegistration()}
47	            type="primary"
48	            className="form_button"
49	            color={'#208AEC'}
50	            size="large"
51	          >
52	            Sign Up
53	          </Button>
54	        </div>
55	      </div>
56	    </div>
57	  );
58	};
```
:::

## 3 - Create a Sign Up function

You can now create the sign-up function that will call the signUp method:

:::CodeblockTabs
JavaScript

```javascript
1	const doUserRegistration = async function () {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue = username;
4	  const passwordValue = password;
5	  try {
6	    // Since the signUp method returns a Promise, we need to call it using await
7	    const createdUser = await Parse.User.signUp(usernameValue, passwordValue);
8	    alert(
9	      `Success! User ${createdUser.getUsername()} was successfully created!`
10	    );
11	    return true;
12	  } catch (error) {
13	    // signUp can fail if any parameter is blank or failed an uniqueness check on the server
14	    alert(`Error! ${error}`);
15	    return false;
16	  }
17	};
```

TypeScript

```typescript
1	const doUserRegistration = async function (): Promise<boolean> {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue: string = username;
4	  const passwordValue: string = password;
5	  try {
6	    // Since the signUp method returns a Promise, we need to call it using await
7	    const createdUser: Parse.User = await Parse.User.signUp(usernameValue, passwordValue);
8	    alert(
9	      `Success! User ${createdUser.getUsername()} was successfully created!`,
10	    );
11	    return true;
12	  } catch (error: any) {
13	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
14	      alert(`Error! ${error}`);
15	      return false;
16	  };
17	};
```
:::

:::hint{type="info"}
**Note**: Creating a new user using signUp also makes it the currently logged-in user, so there is no need for your user to log in again to continue using your App.
:::

Insert this function inside the UserRegistration component, just before the return call, to be called and tested. Remember to update the form’s sign up button onClick action to () => doUserRegistration(). Your component should now look like this:

:::CodeblockTabs
UserRegistration.js

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider, Input } from 'antd';
5	
6	export const UserRegistration = () => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	
11	  // Functions used by the screen components
12	  const doUserRegistration = async function () {
13	    // Note that these values come from state variables that we've declared before
14	    const usernameValue = username;
15	    const passwordValue = password;
16	    try {
17	      // Since the signUp method returns a Promise, we need to call it using await
18	      const createdUser = await Parse.User.signUp(usernameValue, passwordValue);
19	      alert(
20	        `Success! User ${createdUser.getUsername()} was successfully created!`
21	      );
22	      return true;
23	    } catch (error) {
24	      // signUp can fail if any parameter is blank or failed an uniqueness check on the server
25	      alert(`Error! ${error}`);
26	      return false;
27	    }
28	  };
29	
30	  return (
31	    <div>
32	      <div className="header">
33	        <img
34	          className="header_logo"
35	          alt="Back4App Logo"
36	          src={
37	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
38	          }
39	        />
40	        <p className="header_text_bold">{'React on Back4App'}</p>
41	        <p className="header_text">{'User Registration'}</p>
42	      </div>
43	      <div className="container">
44	        <h2 className="heading">{'User Registration'}</h2>
45	        <Divider />
46	        <div className="form_wrapper">
47	          <Input
48	            value={username}
49	            onChange={(event) => setUsername(event.target.value)}
50	            placeholder="Username"
51	            size="large"
52	            className="form_input"
53	          />
54	          <Input
55	            value={password}
56	            onChange={(event) => setPassword(event.target.value)}
57	            placeholder="Password"
58	            size="large"
59	            type="password"
60	            className="form_input"
61	          />
62	        </div>
63	        <div className="form_buttons">
64	          <Button
65	            onClick={() => doUserRegistration()}
66	            type="primary"
67	            className="form_button"
68	            color={'#208AEC'}
69	            size="large"
70	          >
71	            Sign Up
72	          </Button>
73	        </div>
74	      </div>
75	    </div>
76	  );
77	};
```

UserRegistration.tsx

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	const Parse = require('parse/dist/parse.min.js');
5	
6	export const UserRegistration: FC<{}> = (): ReactElement => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	
11	  // Functions used by the screen components
12	  const doUserRegistration = async function (): Promise<boolean> {
13	    // Note that these values come from state variables that we've declared before
14	    const usernameValue: string = username;
15	    const passwordValue: string = password;
16	    try {
17	      // Since the signUp method returns a Promise, we need to call it using await
18	      const createdUser: Parse.User = await Parse.User.signUp(usernameValue, passwordValue);
19	      alert(
20	        `Success! User ${createdUser.getUsername()} was successfully created!`,
21	      );
22	      return true;
23	    } catch (error: any) {
24	        // signUp can fail if any parameter is blank or failed an uniqueness check on the server
25	        alert(`Error! ${error}`);
26	        return false;
27	    };
28	  };
29	
30	  return (
31	    <div>
32	      <div className="header">
33	        <img
34	          className="header_logo"
35	          alt="Back4App Logo"
36	          src={
37	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
38	          }
39	        />
40	        <p className="header_text_bold">{'React on Back4App'}</p>
41	        <p className="header_text">{'User Registration'}</p>
42	      </div>
43	      <div className='container'>
44	        <h2 className="heading">{'User Registration'}</h2>
45	        <Divider />
46	        <div className="form_wrapper">
47	          <Input
48	            value={username}
49	            onChange={(event) => setUsername(event.target.value)}
50	            placeholder="Username"
51	            size="large"
52	            className="form_input"
53	          />
54	          <Input
55	            value={password}
56	            onChange={(event) => setPassword(event.target.value)}
57	            placeholder="Password"
58	            size="large"
59	            type="password"
60	            className="form_input"
61	          />
62	        </div>
63	        <div className="form_buttons">
64	          <Button
65	            onClick={() => doUserRegistration()}
66	            type="primary"
67	            className="form_button"
68	            color={'#208AEC'}
69	            size="large"
70	          >
71	            Sign Up
72	          </Button>
73	        </div>
74	      </div>
75	    </div>
76	  );
77	};
```
:::

Also add these classes to your App.css file to fully render the layout styles:

:::CodeblockTabs
App.css

```css
1	html {
2	  box-sizing: border-box;
3	  outline: none;
4	  overflow: auto;
5	}
6	
7	*,
8	*:before,
9	*:after {
10	  margin: 0;
11	  padding: 0;
12	  box-sizing: inherit;
13	}
14	
15	h1,
16	h2,
17	h3,
18	h4,
19	h5,
20	h6 {
21	  margin: 0;
22	  font-weight: bold;
23	}
24	
25	p {
26	  margin: 0;
27	}
28	
29	body {
30	  margin: 0;
31	  background-color: #fff;
32	}
33	
34	.container {
35	  width: 100%;
36	  max-width: 500px;
37	  margin: auto;
38	  padding: 20px 0;
39	  text-align: left;
40	}
41	
42	.header {
43	  align-items: center;
44	  padding: 25px 0;
45	  background-color: #208AEC;
46	}
47	
48	.header_logo {
49	  height: 55px;
50	  margin-bottom: 20px;
51	  object-fit: contain;
52	}
53	
54	.header_text_bold {
55	  margin-bottom: 3px;
56	  color: rgba(255, 255, 255, 0.9);
57	  font-size: 16px;
58	  font-weight: bold;
59	}
60	
61	.header_text {
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 15px;
64	}
65	
66	.heading {
67	  font-size: 22px;
68	}
69	
70	.form_wrapper {
71	  margin-top: 20px;
72	  margin-bottom: 10px;
73	}
74	
75	.form_input {
76	  margin-bottom: 20px;
77	}
78	
79	.form_button {
80	  width: 100%;
81	}
```
:::

Your app now should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TlDhFG2O8SRfGcBNwNEHq_image.png" signedSrc size="70" width="598" height="547" position="center" caption}

After providing the desired user credentials, you will see this message after pressing on Sign Up if everything was successful:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/24LlXCA9cWuzpicXUKcqy_image.png" signedSrc size="70" width="596" height="548" position="center" caption}

Error handling can be tested if you try to register a user with the same username as before:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wa3yu-v-KSuCeZD4ykLas_image.png" signedSrc size="70" width="598" height="547" position="center" caption}

You will get another error if you try to sign up with no password:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zbYzOi1ufcp7cRC8rqkxW_image.png" signedSrc size="70" width="589" height="535" position="center" caption}

## Conclusion

At the end of this guide, you learned how to register new Parse users on React. In the next guide, we will show you how to log in and out users.

[title] Relationships
[path] ReactJS/Data objects/

# Relationships

## Introduction

Using Parse, you can store data objects establishing relations between them. To model this behavior, any ParseObject can be used as a value in other ParseObject. Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency. That can give you extra power when building and running complex queries. There are three main relation types:

- one-to-many, where one object can be related to many other objects;
- many-to-many, which can create many complex relations between many objects.
- one-to-one, establishing direct relations between two objects and only them;

There are two ways to create a one-to-many relation in Parse:

- (Recomended) The first is using the Parse Pointers in Child Class, which is the fastest in creation and query time. We will use this in this guide.
- The second is using Arrays of Parse Pointers in Parent Class which can lead to slow query times depending on their size. Because of this performance issue, we will use only pointers examples.

There are three ways to create a many-to-many relation in Parse.

- (Recomended) The first is using Parse Relations, which is the fastest in creation and query time. We will use this in this guide.
- The second is using Arraysof Parse Pointers which can lead to slow query times depending on their size.
- The third is using JoinTable where the idea from classical database. When there is a many-to-nany relation, we combine every objectId or Pointer from both sides together to build a new separate table in which the relationship is tracked.

In this guide, you will implement a React book registration application that contains the three main kinds of data associations. You will learn how to create and query data relations using Back4App and React.

:::hint{type="success"}
At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.

- <a href="https://github.com/templates-back4app/react-js-associations" target="_blank">JavaScript Example Repository</a>
- <a href="https://github.com/templates-back4app/react-ts-associations" target="_blank">TypeScript Example Repository</a>
:::

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

- A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
-
  If you want to run this guide’s example project, you should set up the <a href="https://ant.design/" target="_blank">Ant Design library</a>.
:::

## Goal

To perform and demonstrate database relations in React using Parse in a realistic scenario.

## 1 - Understanding the Book class

Since in this guide we will be using a book registration application example, you need to first understand how the object relations are laid out in this database. The main object class that you’ll be using is the Book class, which will store each book entry in the registration. These are the other four object classes:

- Publisher: book publisher name, one-to-many relation with Book;
- Genre: book genre, one-to-many relation with Book. Note that for this example we will consider that a book can only have one genre;
- Author: book author, many-to-many relation with Book, since a book can have more than one author and an author can have more than one book as well;
- ISDB: book ISDB identifying number, one-to-one relation with Book, since this number is unique for each book.

Here is a visual representation of these database tables:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EKOHjTsqqsWc031FJC-25_image.png)

For simplicity, we will assume that each object class has only a string type name attribute (title for the Book), apart from any additional relational attribute.

### Step 2 - Creating relations

Before going into this step we recommend you to clone and run the React app example (<a href="https://github.com/templates-back4app/react-js-associations" target="_blank">JavaScript Example Repository</a>, <a href="https://github.com/templates-back4app/react-ts-associations" target="_blank">TypeScript Example Repository</a>). This application has two main screens: one responsible for listing the registered books and the other for creating new books. In the book registration form, there are direct links to the other related objects and a text input field assigned to the book’s ISBD value, which will be used to create the one-to-one relation.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/1o5nsf4eGyxJRRflul2ZA_image.png" signedSrc size="50" width="467" height="678" position="center" caption}

Let’s take a look at the book creation method that is called when submitting this form:

:::CodeblockTabs
JavaScript

```javascript
1   const createBook = async function () {
2     try {
3       // These values come from state variables linked to
4       // the screen form fields, retrieving the user choices
5       // as a complete Parse.Object, when applicable;
6       const bookTitleValue = bookTitle;
7       const bookISBDValue = bookISBD;
8       // For example, bookPublisher holds the value from
9       // RadioGroup field with its options being every
10      // Publisher parse object instance saved on server, which is
11      // queried on screen load via useEffect
12      const bookPublisherObject = bookPublisher;
13      const bookGenreObject = bookGenre;
14      // bookAuthors can be an array of Parse.Objects, since the book
15      // may have more than one Author
16      const bookAuthorsObjects = bookAuthors;
17
18      // Creates a new parse object instance
19      let Book = new Parse.Object('Book');
20
21      // Set data to parse object
22      // Simple title field
23      Book.set('title', bookTitleValue);
24
25      // ONE-TO-ONE (1:1)
26      // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
27      let isbdQuery = new Parse.Query('ISBD');
28      isbdQuery.equalTo('name', bookISBDValue);
29      let isbdQueryResult = await isbdQuery.first();
30      if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
31        // If first returns a valid object instance, it means that there
32        // is at least one instance of ISBD with the informed value
33        alert(
34          'Error! There is already an ISBD instance with this value!',
35        );
36        return false;
37      } else {
38        // Create a new ISBD object instance to create a one-to-one relation on saving
39        let ISBD = new Parse.Object('ISBD');
40        ISBD.set('name', bookISBDValue);
41        ISBD = await ISBD.save();
42        // Set the new object to the new book object ISBD field
43        Book.set('isbd', ISBD);
44      }
45
46      // ONE-TO-MANY (1:N)
47      // One-to-many relations can be set in two ways:
48      // add direct object to field (Parse will convert to pointer on save)
49      Book.set('publisher', bookPublisherObject);
50      // or add pointer to field
51      Book.set('genre', bookGenreObject.toPointer());
52
53      // MANY-TO-MANY (N:N)
54      // Create a new relation so data can be added
55      let authorsRelation = Book.relation('authors');
56      // bookAuthorsObjects is an array of Parse.Objects,
57      // you can add to relation by adding the whole array or object by object
58      authorsRelation.add(bookAuthorsObjects);
59
60      // After setting the values, save it on the server
61      try {
62        await Book.save();
63        // Success
64        alert('Success!');
65        // Navigate back to home screen using react-router
66        history.push('/');
67        return true;
68      } catch (error) {
69        // Error can be caused by lack of Internet connection
70        alert(`Error! ${error.message}`);
71        return false;
72      }
73    } catch (error) {
74      // Error can be caused by lack of value selection
75      alert(
76        'Error! Make sure to select valid choices in Publisher, Genre and Author fields!',
77      );
78      return false;
79    }
80  };
```

TypeScript

```typescript
1   const createBook = async function (): Promise<boolean> {
2     try {
3       // These values come from state variables linked to
4       // the screen form fields, retrieving the user choices
5       // as a complete Parse.Object, when applicable;
6       const bookTitleValue: string = bookTitle;
7       const bookISBDValue: string = bookISBD;
8       // For example, bookPublisher holds the value from
9       // RadioButton.Group field with its options being every
10      // Publisher parse object instance saved on server, which is
11      // queried on screen load via useEffect
12      const bookPublisherObject: Parse.Object = bookPublisher;
13      const bookGenreObject: Parse.Object = bookGenre;
14      // bookAuthors can be an array of Parse.Objects, since the book
15      // may have more than one Author
16      const bookAuthorsObjects: [Parse.Object] = bookAuthors;
17
18      // Creates a new parse object instance
19      let Book: Parse.Object = new Parse.Object('Book');
20
21      // Set data to parse object
22      // Simple title field
23      Book.set('title', bookTitleValue);
24
25      // ONE-TO-ONE (1:1)
26      // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
27      let isbdQuery: Parse.Query = new Parse.Query('ISBD');
28      isbdQuery.equalTo('name', bookISBDValue);
29      let isbdQueryResult: Parse.Object = await isbdQuery.first();
30      if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
31        // If first returns a valid object instance, it means that there
32        // is at least one instance of ISBD with the informed value
33        alert(
34          'Error! There is already an ISBD instance with this value!',
35        );
36        return false;
37      } else {
38        // Create a new ISBD object instance to create a one-to-one relation on saving
39        let ISBD: Parse.Object = new Parse.Object('ISBD');
40        ISBD.set('name', bookISBDValue);
41        ISBD = await ISBD.save();
42        // Set the new object to the new book object ISBD field
43        Book.set('isbd', ISBD);
44      }
45
46      // ONE-TO-MANY (1:N)
47      // One-to-many relations can be set in two ways:
48      // add direct object to field (Parse will convert to pointer on save)
49      Book.set('publisher', bookPublisherObject);
50      // or add pointer to field
51      Book.set('genre', bookGenreObject.toPointer());
52
53      // MANY-TO-MANY (N:N)
54      // Create a new relation so data can be added
55      let authorsRelation = Book.relation('authors');
56      // bookAuthorsObjects is an array of Parse.Objects,
57      // you can add to relation by adding the whole array or object by object
58      authorsRelation.add(bookAuthorsObjects);
59
60      // After setting the values, save it on the server
61      try {
62        await Book.save();
63        // Success
64        alert('Success!');
65        // Navigate back to home screen using react-router
66        history.push('/');
67        return true;
68      } catch (error) {
69        // Error can be caused by lack of Internet connection
70        alert(`Error! ${error.message}`);
71        return false;
72      }
73    } catch (error) {
74      // Error can be caused by lack of value selection
75      alert(
76        'Error! Make sure to select valid choices in Publisher, Genre and Author fields!',
77      );
78      return false;
79    }
80  };
```
:::

Let’s now look separately at how the three types of associations are done when creating the Book object.

### **One-to-many relation**

Note how the bookPublisherObject and bookGenreObject are set to the new book Parse.Object instance. See how simple it is in Parse to create a one-to-many relation: you could either assign the target object instance or a pointer to it using the Parse.Object.set method, which takes two arguments: the field name and the value to be set. Parse will create a pointer data type column and a direct link on your dashboard for quick access under the hood.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bFO60YCiQdEa9taTVctCT_image.png)

### **Many-to-many relation**

Look at how the bookAuthorsObjects are set to the new book Parse.Object instance. To create a many-to-many relation, you first need to create a new Parse.Object.relation and then add the related objects to it either one by one or by passing an array of Parse.Object using the Parse.Object.relation.add method. Parse will create a relation type column and also a relational table on your database. Parse will also create a link for easy access to this new table in the field column in the dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HEwkq5dNeXm4qnbQENKhm_image.png)

### **One-to-one relation**

See how the bookISBDValue is set to the new book Parse.Object instance. Creating and saving one-to-one and one-to-many relations in Parse are similar processes, in which you pass as an argument the Parse.Object instance using the Parse.Object.set method, which takes two arguments: the field name and the value to be set.

The catch here is that, before saving, you need to enforce that there are no ISBD objects containing the informed ISBD ID string value in your database and that there are no Book objects already related to it as well. The second part will always be true in this case, since you are creating a new Book object every time. Enforcing the ISBD uniqueness can be achieved by using the following highlighted query:

:::CodeblockTabs
JavaScript

```javascript
1   let isbdQuery = new Parse.Query('ISBD');
2   isbdQuery.equalTo('name', bookISBDValue);
3   let isbdQueryResult = await isbdQuery.first();
4   if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
5     // If first returns a valid object instance, it means that there
6     // is at least one instance of ISBD with the informed value
7     alert(
8       'Error! There is already an ISBD instance with this value!',
9     );
10    return false;
11  }
```

TypeScript

```typescript
1   let isbdQuery: Parse.Query = new Parse.Query('ISBD');
2   isbdQuery.equalTo('name', bookISBDValue);
3   let isbdQueryResult: Parse.Object = await isbdQuery.first();
4   if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
5     // If first returns a valid object instance, it means that there
6     // is at least one instance of ISBD with the informed value
7     alert(
8       'Error! There is already an ISBD instance with this value!',
9     );
10    return false;
11  }
```
:::

After successfully saving your objects, Parse will create a pointer data type column and a direct link on your dashboard as well.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3Zhrk3HrwLQxbQQtH-OAj_image.png)

## 3 - Querying relations

Querying related objects is pretty straightforward as much of it is handled by Parse. Take a look at the query function in the book registers list screen and after we will highlight how each relation type is queried:

:::CodeblockTabs
JavaScript

```javascript
1   const queryBooks = async function () {
2     // These values come from state variables linked to
3     // the screen query RadioButton.Group fields, with its options being every
4     // parse object instance saved on server from the referred class, which is
5     // queried on screen load via useEffect; these variables retrievie the user choices
6     // as a complete Parse.Object;
7     const queryPublisherValue = queryPublisher;
8     const queryGenreValue = queryGenre;
9     const queryAuthorValue = queryAuthor;
10    const queryIsbdValue = queryIsbd;
11
12    // Reading parse objects is done by using Parse.Query
13    const parseQuery = new Parse.Query('Book');
14
15    // One-to-many queries
16    if (queryPublisherValue !== '') {
17      parseQuery.equalTo('publisher', queryPublisherValue);
18    }
19    if (queryGenreValue !== '') {
20      parseQuery.equalTo('genre', queryGenreValue);
21    }
22
23    // One-to-one query
24    if (queryIsbdValue !== '') {
25      parseQuery.equalTo('isbd', queryIsbdValue);
26    }
27
28    // Many-to-many query
29    // In this case, we need to retrieve books related to the chosen author
30    if (queryAuthorValue !== '') {
31      parseQuery.equalTo('authors', queryAuthorValue);
32    }
33
34    try {
35      let books = await parseQuery.find();
36      // Many-to-many objects retrieval
37      // In this example we need to get every related author Parse.Object
38      // and add it to our query result objects
39      for (let book of books) {
40        // This query is done by creating a relation and querying it
41        let bookAuthorsRelation = book.relation('authors');
42        book.authorsObjects = await bookAuthorsRelation.query().find();
43      }
44      setQueriedBooks(books);
45      return true;
46    } catch (error) {
47      // Error can be caused by lack of Internet connection
48      alert(`Error! ${error.message}`);
49      return false;
50    }
51  };
```

TypeScript

```typescript
1   const queryBooks = async function (): Promise<boolean> {
2     // These values come from state variables linked to
3     // the screen query RadioButton.Group fields, with its options being every
4     // parse object instance saved on server from the referred class, which is
5     // queried on screen load via useEffect; these variables retrievie the user choices
6     // as a complete Parse.Object;
7     const queryPublisherValue: Parse.Object = queryPublisher;
8     const queryGenreValue: Parse.Object = queryGenre;
9     const queryAuthorValue: Parse.Object = queryAuthor;
10    const queryIsbdValue: Parse.Object = queryIsbd;
11
12    // Reading parse objects is done by using Parse.Query
13    const parseQuery: Parse.Query = new Parse.Query('Book');
14
15    // One-to-many queries
16    if (queryPublisherValue !== '') {
17      parseQuery.equalTo('publisher', queryPublisherValue);
18    }
19    if (queryGenreValue !== '') {
20      parseQuery.equalTo('genre', queryGenreValue);
21    }
22
23    // One-to-one query
24    if (queryIsbdValue !== '') {
25      parseQuery.equalTo('isbd', queryIsbdValue);
26    }
27
28    // Many-to-many query
29    // In this case, we need to retrieve books related to the chosen author
30    if (queryAuthorValue !== '') {
31      parseQuery.equalTo('authors', queryAuthorValue);
32    }
33
34    try {
35      let books: [Parse.Object] = await parseQuery.find();
36      // Many-to-many objects retrieval
37      // In this example we need to get every related author Parse.Object
38      // and add it to our query result objects
39      for (let book of books) {
40        // This query is done by creating a relation and querying it
41        let bookAuthorsRelation = book.relation('authors');
42        book.authorsObjects = await bookAuthorsRelation.query().find();
43      }
44      setQueriedBooks(books);
45      return true;
46    } catch (error) {
47      // Error can be caused by lack of Internet connection
48      alert(`Error! ${error.message}`);
49      return false;
50    }
51  };
```
:::

### **One-to-many query**

To query any books related to a specific publisher or genre, you need to perform a Parse.Query.equalTo method passing the Parse.Object instance as the parameter. After querying, Parse will store inside the resulting objects the complete instances of any one-to-many relational fields. To retrieve and show data from these object instances, you can chain the Parse.Object.get method like this: BookParseObject.get(('publisher').get('name').

### **Many-to-many query**

To query any books related to a specific author, the query will also use only a Parse.Query.equalTo method. However, after querying, Parse will not store Parse.Object instances from many-to-many relational fields, only a reference to the related class name, such as \{"\_\_type": "Relation", "className": "Author"}. To retrieve and show data from these object instances, you need to create a relation and query it again, storing the results in an object array of your own.

### **One-to-one query**

Just as before, to query any books related to a specific ISBD, you need to perform a Parse.Query.equalTo method passing the Parse.Object instance as the parameter. After querying, Parse will store inside the resulting objects the complete instances of any one-to-one relational fields, the same way that it is done with one-to-many relational fields.

## Conclusion

At the end of this guide, you learned how to create and query relations in Parse on React. In the next guide, we will show you how to register users.

[title] Untitled
[path] /


[title] Sign in with Facebook
[path] Android/Users/

# How to add Facebook login to your Android App

## Introduction

In this guide, you will learn how to login using Facebook Login and Parse User class through Back4App. This tutorial uses a basic app created in Android Studio 4.1.1 with buildToolsVersion=30.0.3 , Compile SDK Version = 30 and targetSdkVersion 30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

Create a Login with Facebook feature to your Android App using Parse and Back4App.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TGg2Jd2Zr3RMJsaHj8Eqk_facebook-login-v2.gif" signedSrc size="50" width="405" height="880" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## 1 - Facebook Set up

To start using Facebook functions, you need to:

1. Go to the <a href="https://developers.facebook.com/" target="_blank">Facebook Developer Website </a>and create an account and an app.
2. Follow Facebook’s Quickstart Guide by <a href="https://developers.facebook.com/docs/facebook-login/android" target="_blank">clicking here </a>and pay attention to the following recommendations:

:::hint{type="info"}
**Here are the steps included in Facebook’s Quickstart Guide, which you need to follow carefully, as you are not going to follow them precisely as Facebook suggests:**

- In Step 3 instead of adding

> implementation 'com.facebook.android:facebook-login:[8.1)'

in the dependencies\{} section at build.gradle (Module\:app), add the following code in the dependencies\{} section at build.gradle (Module\:app)

> *1    // update the versions to the latest ones*
>
>
> 2    implementation "com.github.parse-community:ParseFacebookUtils-Android:latest.version.here"
> 3    implementation 'com.facebook.android:facebook-android-sdk:latest.version.here'

- Remember to update the version of Parse Facebook Utils SDK for Android to the latest one. You can find out which is the latest version at the <a href="https://jitpack.io/" target="_blank">JitPack website</a>, following these steps:

1. At JitPack website paste parse-community\:ParseFacebookUtils-Android in the Git repo URL box.
2. After doing that, click on the Look up button. Then you should see the available versions of Parse SDK for Android, as shown in the following image.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jpJmidWu5uQY7-GKoeSS6_image.png)

- In Step 4, you’ll be asked to add internet permission at the application element in /app/manifest/AndroidManifest.xml file, but you have already added it while following <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK</a> Tutorial so you don’t need to do this.
- In Step 6, you’ll need to provide key hashes, so you must have <a href="https://www.openssl.org/" target="_blank">Open SSL</a> installed. Facebook’s Guide doesn’t provide command lines to generate key hashes in Linux, but doing that is simple, as all it requires from you is to open a terminal window and run the following command:

> keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64

- Don’t follow the steps of Facebook’s Quickstart Guide right after **Step 6**.
:::

:::hint{type="info"}
&#x20; <a href="https://en.wikipedia.org/wiki/SHA-1" target="_blank">Whats is SHA-1</a> (Secure Hashing Algorithm)  &#x20;

SHA-1, called the Secure Hashing Algorithm, is the most common encryption algorithm. SHA-1, designed by United States National Security Agency. SHA-1 fingerprint is a unique key generated for your PC that can be used for signing. Its mainly used for submitting for using the some APIs (Like the Facebook api we will use in this guide.). If you want to learn more details, you can visit the SHA-1 Wikipedia page.
:::

:::hint{type="danger"}
You should follow the other steps described on Facebook’s Quickstart Guide and not mentioned here.
:::

## 2 - Link your Facebook App with Back4App

1. Go to your App dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App Website </a>and click on Server Settings.
2. Find the “Facebook Login” block and click on Settings. The “Facebook Login” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wzY4k1lITNJdcCZ-hY9ou_image.png)

&#x20;    3\. We need to add our facebook\_id, which we got from Facebook Guide to string.xml(You should have followed the Facebook Guide and did this before). Go to your Android Studio Project, open your strings file: .../app/src/main/res/values/strings.xml, copy your facebook\_app\_id and paste it in the Facebook appId field of the last page of Back4App that you opened. Lastly, press the + button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Enivtee1WosjYCAgG3T5L_image.png)

## 3 - Add provider element in the Manifest file

Go back to your Android Studio Project and inside the application element in /app/manifest/AndroidManifest.xml file, right after the meta data element, add the following code.

```java
1  <provider
2      android:name="com.facebook.FacebookContentProvider"
3      <!--don't forget to put your Facebook App ID in the following link-->
4      android:authorities="com.facebook.app.FacebookContentProviderYOUR_FACEBOOK_APP_ID"
5      android:exported="true" />
```

:::hint{type="danger"}
**Don’t forget to put your Facebook App ID in the code above.**
:::

## 4 - Initialize Parse Facebook Util SDKs

In your Android Studio Project, in the Java file called App that extends Application that you created to initialize the Parse SDK, on its onCreate method, right after Parse.initialize() call, use the following code to initialize Parse Facebook Utils SDK.

> **1   ParseFacebookUtils.**
>
> initialize
>
> **(this);**

:::hint{type="danger"}
If you don’t have an App.java file as described in this step, access the [Install Parse SDK for Android](https://www.back4app.com/docs/android/parse-android-sdk) documentation and make sure that you have followed all the steps required to install Parse SDK correctly. If you do not install Parse SDK properly your facebook login with Parse will not work.
:::

## 5 - Set up

At the beginning of each Parse activity, import the following:

> 1   import androidx.appcompat.app.AppCompatActivity
>
> **;**
>
>
> 2   import android.app.AlertDialog
>
> **;**
>
>
> 3   import android.app.ProgressDialog
>
> **;**
>
>
> 4   import android.content.Intent
>
> **;**
>
>
> 5   import android.os.Bundle
>
> **;**
>
>
> 6   import android.widget.Button
>
> **;**
>
>
> 7   import com.facebook.login.LoginManager
>
> **;**
>
>
> 8   import com.parse.ParseUser
>
> **;**

## 6 - Log In

To implement your Login Activity, do the following:

1. Import into your LoginActivity, in addition to the dependencies imported in **Step 4**:

> 1    import android.app.AlertDialog
>
> **;**
>
>
> 2    import android.app.ProgressDialog
>
> **;**
>
>
> 3    import android.content.Intent
>
> **;**
>
>
> 4    import android.os.Bundle
>
> **;**
>
>
> 5    import android.util.Log
>
> **;**
>
>
> 6    import android.widget.Button
>
> **;**
>
>
> 7    import android.widget.Toast
>
> **;**
>
>
> 8    import com.facebook.AccessToken
>
> **;**
>
>
> 9    import com.facebook.GraphRequest
>
> **;**
>
>
> 10   import com.parse.ParseUser
>
> **;**
>
>
> 11   import com.parse.facebook.ParseFacebookUtils
>
> **;**
>
>
> 12   import org.json.JSONException
>
> **;**
>
>
> 13   import java.util.Arrays
>
> **;**
>
>
> 14   import java.util.Collection
>
> **;**

&#x20;    2\. To implement Facebook Login, simply use the following code:

:::CodeblockTabs
```java
1    final ProgressDialog dialog = new ProgressDialog(this);
2    dialog.setTitle("Please, wait a moment.");
3    dialog.setMessage("Logging in...");
4    dialog.show();
5    Collection<String> permissions = Arrays.asList("public_profile", "email");
6    ParseFacebookUtils.logInWithReadPermissionsInBackground(this, permissions, (user, err) -> {
7        dialog.dismiss();
8        if (err != null) {
9            Log.e("FacebookLoginExample", "done: ", err);
10           Toast.makeText(this, err.getMessage(), Toast.LENGTH_LONG).show();
11       } else if (user == null) {
12           Toast.makeText(this, "The user cancelled the Facebook login.", Toast.LENGTH_LONG).show();
13           Log.d("FacebookLoginExample", "Uh oh. The user cancelled the Facebook login.");
14       } else if (user.isNew()) {
15           Toast.makeText(this, "User signed up and logged in through Facebook.", Toast.LENGTH_LONG).show();
16           Log.d("FacebookLoginExample", "User signed up and logged in through Facebook!");
17           getUserDetailFromFB();
18       } else {
19           Toast.makeText(this, "User logged in through Facebook.", Toast.LENGTH_LONG).show();
20           Log.d("FacebookLoginExample", "User logged in through Facebook!");
21           showAlert("Oh, you!", "Welcome back!");
22       }
23   });
```

```kotlin
1        val dlg = ProgressDialog(this)
2        dlg.setTitle("Please, wait a moment.")
3        dlg.setMessage("Logging in...")
4        dlg.show()
5        val permissions: Collection<String> = listOf("public_profile", "email")
6        ParseFacebookUtils.logInWithReadPermissionsInBackground(this, permissions) { user: ParseUser?, err: ParseException? ->
7             dlg.dismiss()
8             when {
9                err != null -> {
10                   Log.e("FacebookLoginExample", "done: ", err)
11                   Toast.makeText(this, err.message, Toast.LENGTH_LONG).show()
12               }
13               user == null -> {
14                   Toast.makeText(this, "The user cancelled the Facebook login.", Toast.LENGTH_LONG).show()
15                   Log.d("FacebookLoginExample", "Uh oh. The user cancelled the Facebook login.")
16               }
17               user.isNew -> {
18                   Toast.makeText(this, "User signed up and logged in through Facebook.", Toast.LENGTH_LONG).show()
19                   Log.d("FacebookLoginExample", "User signed up and logged in through Facebook!")
20                   getUserDetailFromFB()
21               }
22               else -> {
23                   Toast.makeText(this, "User logged in through Facebook.", Toast.LENGTH_LONG).show()
24                   Log.d("FacebookLoginExample", "User logged in through Facebook!")
25                   showAlert("Oh, you!", "Welcome back!")
26               }
27            }
28       }
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a LOGIN VIA FACEBOOK button callback.
:::

&#x20;    3\. After a successful login through Facebook to our App, we can now get some basic logged user information.. As you can see, there are many more methods included in the code above. The getUserDetailFromFB method is responsible for fetching user details. Here’s the code for this method:

:::CodeblockTabs
```java
1    private void getUserDetailFromFB() {
2        GraphRequest request = GraphRequest.newMeRequest(AccessToken.getCurrentAccessToken(), (object, response) -> {
3            ParseUser user = ParseUser.getCurrentUser();
4            try {
5                if (object.has("name"))
6                    user.setUsername(object.getString("name"));
7                if (object.has("email"))
8                    user.setEmail(object.getString("email"));
9            } catch (JSONException e) {
10               e.printStackTrace();
11           }
12           user.saveInBackground(e -> {
13             if (e == null) {
14                 showAlert("First Time Login!", "Welcome!");
15             } else
16                 showAlert("Error", e.getMessage());
17         });
18     });
19
20     Bundle parameters = new Bundle();
21     parameters.putString("fields", "name,email");
22     request.setParameters(parameters);
23     request.executeAsync();
24  }
```

```kotlin
1    private fun getUserDetailFromFB() {
2        val request =
3            GraphRequest.newMeRequest(AccessToken.getCurrentAccessToken()) { `object`: JSONObject, _: GraphResponse? ->
4                val user = ParseUser.getCurrentUser()
5                try {
6                    user.username = `object`.getString("name")
7                } catch (e: JSONException) {
8                    e.printStackTrace()
9                }
10               try {
11                   user.email = `object`.getString("email")
12               } catch (e: JSONException) {
13                   e.printStackTrace()
14               }
15               user.saveInBackground {
16                   if (it == null)
17                       showAlert("First Time Login!", "Welcome!")
18                   else
19                       showAlert("Error", it.message)
20               }
21           }
22       val parameters = Bundle()
23       parameters.putString("fields", "name,email")
24       request.parameters = parameters
25       request.executeAsync()
26   }
```
:::

&#x20;    4\. It’s interesting to add a method to display Alert Dialogs and make the process look more professional. In this function, we also get a user parameter. When going to the MainAtivity page, we send this user parameter in the intent, and in the MainActivity, we pull the information in this user and print it on the screen. The method below does this:

:::CodeblockTabs
```java
1    private void showAlert(String title, String message) {
2        AlertDialog.Builder builder = new AlertDialog.Builder(this)
3                .setTitle(title)
4                .setMessage(message)
5                .setPositiveButton("OK", (dialog, which) -> {
6                    dialog.cancel();
7                    Intent intent = new Intent(this, MainActivity.class);
8                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
9                    startActivity(intent);
10               });
11       AlertDialog ok = builder.create();
12       ok.show();
13   }
```

```kotlin
1    private fun showAlert(title: String, message: String?) {
2        val builder = AlertDialog.Builder(this)
3            .setTitle(title)
4            .setMessage(message)
5            .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
6                dialog.cancel()
7                val intent = Intent(this@LoginActivity, MainActivity::class.java)
8                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
9                startActivity(intent)
10           }
11       val ok = builder.create()
12       ok.show()
13   }
```
:::

&#x20;    5\. If you want to associate an existing ParseUser to a Facebook account, you can link it like so:

:::CodeblockTabs
```java
1    Collection<String> permissions = Arrays.asList("public_profile", "email");
2    if (!ParseFacebookUtils.isLinked(ParseUser.getCurrentUser())) {
3        ParseFacebookUtils.linkWithReadPermissionsInBackground(ParseUser.getCurrentUser(), this, permissions, ex -> {
4            if (ParseFacebookUtils.isLinked(ParseUser.getCurrentUser())) {
5                Toast.makeText(this, "Woohoo, user logged in with Facebook.", Toast.LENGTH_LONG).show();
6                Log.d("FacebookLoginExample", "Woohoo, user logged in with Facebook!");
7            }
8        });
9    } else {
10       Toast.makeText(this, "You have already linked your account with Facebook.", Toast.LENGTH_LONG).show();
11   }
```

```kotlin
1    val permissions= listOf("public_profile","email")
2        if (!ParseFacebookUtils.isLinked(ParseUser.getCurrentUser())){
3            ParseFacebookUtils.linkWithReadPermissionsInBackground(ParseUser.getCurrentUser(),this,permissions) {
4                if (ParseFacebookUtils.isLinked(ParseUser.getCurrentUser())){
5                    Toast.makeText(this, "Woohoo, user logged in with Facebook.", Toast.LENGTH_LONG).show()
6                    Log.d("FacebookLoginExample", "Woohoo, user logged in with Facebook!")
7                }
8            }
9        } else {
10           Toast.makeText(this, "You have already linked your account with Facebook.", Toast.LENGTH_LONG).show()
11       }
12   }
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a LINK YOUR ACCOUNT TO FACEBOOK button callback.
:::

&#x20;    6\. If you want to unlink Facebook from a user, simply do this:

:::CodeblockTabs
```java
1    ParseFacebookUtils.unlinkInBackground(ParseUser.getCurrentUser(), ex -> {
2        if (ex == null) {
3            Toast.makeText(this, "The user is no longer associated with their Facebook account.", Toast.LENGTH_LONG).show();
4            Log.d("MyApp", "The user is no longer associated with their Facebook account.");
5        } else {
6            Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
7        }
8    });
```

```kotlin
1    ParseFacebookUtils.unlinkInBackground(ParseUser.getCurrentUser()) {
2        if (it == null) {
3            Toast.makeText(this,"The user is no longer associated with their Facebook account.",Toast.LENGTH_LONG).show()
4            Log.d("MyApp", "The user is no longer associated with their Facebook account.")
5        } else {
6            Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
7        }
8    }
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a UNLINK YOUR ACCOUNT FROM FACEBOOK button callback.
:::

&#x20;    7\. It’s **very important** to use the following as a method outside the onCreate() method of your LoginActivity to pass login results to the LoginManager via callbackManager and avoid errors.

:::CodeblockTabs
```java
1   @Override
2   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
3       super.onActivityResult(requestCode, resultCode, data);
4       ParseFacebookUtils.onActivityResult(requestCode, resultCode, data);
5   }
```

```kotlin
1    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
2        super.onActivityResult(requestCode, resultCode, data)
3        ParseFacebookUtils.onActivityResult(requestCode, resultCode, data)
4    }
```
:::

## 7 - Log out

To implement Facebook Logout, simply use the code mentioned below:

:::CodeblockTabs
```java
1     final ProgressDialog dialog = new ProgressDialog(this);
2     dialog.setTitle("Please, wait a moment.");
3     dialog.setMessage("Logging out...");
4     dialog.show();
5     LoginManager.getInstance().logOut();
6     ParseUser.logOutInBackground(e -> {
7         if (e == null)
8             showAlert("So, you're going...", "Ok...Bye-bye then", true);
9         else
10            showAlert("Error...", e.getMessage(), false);
11    });
```

```kotlin
1    val dlg = ProgressDialog(this)
2     dlg.setTitle("Please, wait a moment.")
3     dlg.setMessage("Logging out...")
4     dlg.show()
5     LoginManager.getInstance().logOut()
6     ParseUser.logOutInBackground { e->
7         if (e == null)
8             showAlert("So, you're going...", "Ok...Bye-bye then", true)
9         else
10            showAlert("Error...", e.message, false)
11    }
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a LOG OUT button callback.
:::

:::hint{type="info"}
The method alertDisplayer is the same that you added in theLoginActivity, don’t forget to change its Intent arguments though.
:::

## It’s done!

At this stage, you can log in, register and log out of your app with Facebook using Parse Server core features through Back4App!


[title] Untitled
[path] Flutter/


[title] Files
[path] Flutter/Parse SDK (REST)/

# Save Files from a Flutter App

## Introduction

Sometimes applications need to store data that is often too large to be stored inside a ParseObject. The most common use case is storing images, but you can also use it for documents, videos, music, and other binary data.

To store a file on Parse, you should always associate the file with another data object so you can retrieve this file path when querying the object. If you do not associate, the file will be stored, but you will not find them on the Cloud.
Another important tip is to give a name to the file that has a file extension. This extension lets Parse figure out the file type and handle it accordingly. We should also mention that each upload gets a unique identifier, so there’s no problem uploading multiple files using the same name.

In Flutter, ParseFile and ParseWebFile let you store and retrieve application files in the Cloud that would. This guide explains how to store and retrieve files in your Flutter App to manage Back4app cloud storage.

:::hint{type="danger"}
- If you do not associate your file to a data object the file will become an orphan file and you wont be able to find it on Back4App Cloud.
:::

::embed[]{url="https://www.youtube.com/embed/C2XQGks2ivA"}

## Prerequisites

:::hint{type="info"}
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
- In order to run this guide example you should set up the plugin <a href="https://pub.dev/packages/image_picker" target="_blank">image\_picker</a> properly. Do not forget to add permissions for iOS in order to access images stored in device. \{: .btn target=”\_blank” rel=”nofollow”}. Carefully read the instructions for setting up the Android and iOS project.
:::

## Goal

Create an Flutter Gallery App that uploads and displays images from Back4app.

## 1 - Understanding ParseFile and ParseWebFile class

There are three different file classes in this Parse SDK for Flutter

- ParseFileBaseis an abstract class, the foundation of every file class that this SDK can handle.
- ParseFileextends ParseFileBase and is by default used as the file class on every platform (not valid for web). This class uses a File from dart\:io for storing the raw file.
- ParseWebFileis the equivalent to ParseFile used at Flutter Web. This class uses an Uint8List for storing the raw file.

The methods available on ParseFileBase to manipulate files:

- save()or upload() for save file on Cloud
- download()for retrive file and store in local storage

There are properties to get information from the saved file:

- url: Gets the file url. It is only available after you save the file or after you get the file from a Parse.Object.
- name: Gets the file name. This is the filename given by the user before calling the save() method. After callint the method, the property receives a unique identifier.

## 2 - Uploading an Image

To upload an image, you will only need to create a ParseFileBase instance and then call the save method.

Let’s do that in our upload function:

```javascript
1         ParseFileBase? parseFile;
2
3         if (kIsWeb) {
4           //Flutter Web
5           parseFile = ParseWebFile(
6               await pickedFile!.readAsBytes(),
7               name: 'image.jpg'); //Name for file is required
8         } else {
9           //Flutter Mobile/Desktop
10          parseFile = ParseFile(File(pickedFile!.path));
11        }
12        await parseFile.save();
```

The above snippet creates and saves the image, and after the save completes, we associate it with a ParseObject called Gallery.

```javascript
1      final gallery = ParseObject('Gallery')
2        ..set('file', parseFile);
3      await gallery.save();
```

## 3 - Displaying Images

To display images, you need to get the image’s URL.

To upload an image, you will only need to create a ParseFileBase instance and then call the save method.

```javascript
1       ParseFileBase? varFile = parseObject.get<ParseFileBase>('file');
2
3       return Image.network(
4         varFile!.url!,
5         width: 200,
6         height: 200,
7         fit: BoxFit.fitHeight,
8       );
```

## 4 - Upload and Retrieve from Flutter App

Let’s now use our example for uploading and displaying images in Flutter App, with a simple interface.

Open your Flutter project, go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'dart:io';
2
3   import 'package:flutter/cupertino.dart';
4   import 'package:flutter/foundation.dart';
5   import 'package:flutter/material.dart';
6   import 'package:image_picker/image_picker.dart';
7   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
8
9   void main() async {
10    WidgetsFlutterBinding.ensureInitialized();
11
12    final keyApplicationId = 'YOUR_APP_ID_HERE';
13    final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
14
15    final keyParseServerUrl = 'https://parseapi.back4app.com';
16
17    await Parse().initialize(keyApplicationId, keyParseServerUrl,
18        clientKey: keyClientKey, debug: true);
19
20    runApp(MaterialApp(
21      title: 'Flutter - Storage File',
22      debugShowCheckedModeBanner: false,
23      home: HomePage(),
24    ));
25  }
26  
27  class HomePage extends StatefulWidget {
28    @override
29    _HomePageState createState() => _HomePageState();
30  }
31  
32  class _HomePageState extends State<HomePage> {
33    PickedFile? pickedFile;
34 
35    List<ParseObject> results = <ParseObject>[];
36    double selectedDistance = 3000;
37
38    @override
39    Widget build(BuildContext context) {
40      return Scaffold(
41          body: Padding(
42        padding: const EdgeInsets.all(16.0),
43        child: Column(
44          crossAxisAlignment: CrossAxisAlignment.stretch,
45          children: [
46            Container(
47              height: 200,
48              child: Image.network(
49                  'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
50            ),
51            SizedBox(
52              height: 16,
53            ),
54            Center(
55              child: const Text('Flutter on Back4app - Save File',
56                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
57            ),
58            SizedBox(
59              height: 16,
60            ),
61            Container(
62              height: 50,
63              child: ElevatedButton(
64                child: Text('Upload File'),
65                style: ElevatedButton.styleFrom(primary: Colors.blue),
66                onPressed: () {
67                  Navigator.push(
68                    context,
69                    MaterialPageRoute(builder: (context) => SavePage()),
70                  );
71                },
72              ),
73            ),
74            SizedBox(
75              height: 8,
76            ),
77            Container(
78                height: 50,
79                child: ElevatedButton(
80                  child: Text('Display File'),
81                  style: ElevatedButton.styleFrom(primary: Colors.blue),
82                  onPressed: () {
83                    Navigator.push(
84                      context,
85                      MaterialPageRoute(builder: (context) => DisplayPage()),
86                    );
87                  },
88                ))
89          ],
90        ),
91      ));
92    }
93  }
94  
95  class SavePage extends StatefulWidget {
96    @override
97    _SavePageState createState() => _SavePageState();
98  }
99
100 class _SavePageState extends State<SavePage> {
101   PickedFile? pickedFile;
102   bool isLoading = false;
103 
104   @override
105   Widget build(BuildContext context) {
106     return Scaffold(
107       appBar: AppBar(
108         title: Text('Upload Fie'),
109       ),
110       body: Padding(
111         padding: const EdgeInsets.all(12.0),
112         child: Column(
113           crossAxisAlignment: CrossAxisAlignment.stretch,
114           children: [
115             SizedBox(height: 16),
116             GestureDetector(
117               child: pickedFile != null
118                   ? Container(
119                       width: 250,
120                       height: 250,
121                       decoration:
122                           BoxDecoration(border: Border.all(color: Colors.blue)),
123                       child: kIsWeb
124                           ? Image.network(pickedFile!.path)
125                           : Image.file(File(pickedFile!.path)))
126                   : Container(
127                       width: 250,
128                       height: 250,
129                       decoration:
130                           BoxDecoration(border: Border.all(color: Colors.blue)),
131                       child: Center(
132                         child: Text('Click here to pick image from Gallery'),
133                       ),
134                     ),
135               onTap: () async {
136                 PickedFile? image =
137                     await ImagePicker().getImage(source: ImageSource.gallery);
138 
139                 if (image != null) {
140                   setState(() {
141                     pickedFile = image;
142                   });
143                 }
144               },
145             ),
146             SizedBox(height: 16),
147             Container(
148                 height: 50,
149                 child: ElevatedButton(
150                   child: Text('Upload file'),
151                   style: ElevatedButton.styleFrom(primary: Colors.blue),
152                   onPressed: isLoading || pickedFile == null
153                       ? null
154                       : () async {
155                           setState(() {
156                             isLoading = true;
157                           });
158                           ParseFileBase? parseFile;
159 
160                           if (kIsWeb) {
161                             //Flutter Web
162                             parseFile = ParseWebFile(
163                                 await pickedFile!.readAsBytes(),
164                                 name: 'image.jpg'); //Name for file is required
165                           } else {
166                             //Flutter Mobile/Desktop
167                             parseFile = ParseFile(File(pickedFile!.path));
168                           }
169                           await parseFile.save();
170 
171                           final gallery = ParseObject('Gallery')
172                             ..set('file', parseFile);
173                           await gallery.save();
174
175                           setState(() {
176                             isLoading = false;
177                             pickedFile = null;
178                           });
179 
180                           ScaffoldMessenger.of(context)
181                             ..removeCurrentSnackBar()
182                             ..showSnackBar(SnackBar(
183                               content: Text(
184                                 'Save file with success on Back4app',
185                                 style: TextStyle(
186                                   color: Colors.white,
187                                 ),
188                               ),
189                               duration: Duration(seconds: 3),
190                               backgroundColor: Colors.blue,
191                             ));
192                         },
193                 ))
194           ],
195         ),
196       ),
197     );
198   }
199 }
200
201 class DisplayPage extends StatefulWidget {
202   @override
203   _DisplayPageState createState() => _DisplayPageState();
204 }
205
206 class _DisplayPageState extends State<DisplayPage> {
207   @override
208   Widget build(BuildContext context) {
209     return Scaffold(
210       appBar: AppBar(
211         title: Text("Display Gallery"),
212       ),
213       body: FutureBuilder<List<ParseObject>>(
214           future: getGalleryList(),
215           builder: (context, snapshot) {
216             switch (snapshot.connectionState) {
217               case ConnectionState.none:
218               case ConnectionState.waiting:
219                 return Center(
220                   child: Container(
221                       width: 100,
222                       height: 100,
223                       child: CircularProgressIndicator()),
224                 );
225               default:
226                 if (snapshot.hasError) {
227                   return Center(
228                     child: Text("Error..."),
229                   );
230                 } else {
231                   return ListView.builder(
232                       padding: const EdgeInsets.only(top: 8),
233                       itemCount: snapshot.data!.length,
234                       itemBuilder: (context, index) {
235                         //Web/Mobile/Desktop
236                        ParseFileBase? varFile =
237                             snapshot.data![index].get<ParseFileBase>('file');
238 
239                         //Only iOS/Android/Desktop
240                         /*
241                         ParseFile? varFile =
242                             snapshot.data![index].get<ParseFile>('file');
243                         */
244                         return Image.network(
245                           varFile!.url!,
246                           width: 200,
247                           height: 200,
248                           fit: BoxFit.fitHeight,
249                         );
250                       });
251                 }
252             }
253           }),
254     );
255   }
256 
257   Future<List<ParseObject>> getGalleryList() async {
258     QueryBuilder<ParseObject> queryPublisher =
259         QueryBuilder<ParseObject>(ParseObject('Gallery'))
260           ..orderByAscending('createdAt');
261     final ParseResponse apiResponse = await queryPublisher.query();
262 
263     if (apiResponse.success && apiResponse.results != null) {
264       return apiResponse.results as List<ParseObject>;
265     } else {
266       return [];
267     }
268   }
269 }
```

Find your ApplicationId and Client Key credentials navigating to your app Dashboard->Settings->Security and Keys at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with BOTH values of your project’s ApplicationId and ClientKey in Back4app.

- keyApplicationId = AppId
- keyClientKey = Client Key

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SeiDfLamRqJFys0hfz8xh_image.png" signedSrc size="50" width="320" height="638" position="center" caption}

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vkn0AimenNqVuC0LWgWVP_image.png" signedSrc size="50" width="320" height="638" position="center" caption}

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gVi8ZXJg-N0luCP83yayN_image.png" signedSrc size="50" width="320" height="638" position="center" caption}

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SJhjGyiemfZxKoOMICKBE_image.png" signedSrc size="50" width="320" height="638" position="center" caption}

## Conclusion

At this point, you have uploaded image on Back4App and displayed it in a Flutter application.

[title] Data Types
[path] Flutter/Parse SDK (REST)/Data Objects/

This guide introduces Parse data types in Flutter, using examples from the `main.dart` file. Parse data storage revolves around ParseObject, which contains key-value pairs of JSON-compatible data, making it schemaless. This means you can store any data without defining a schema in advance.

You can set whatever key-value pairs you want, and our backend will store them.

For example, let’s say you’re tracking high scores for a game. A single Parse Object could contain:

> score: 1337, playerName: "Sean Plott", cheatMode: false

:::hint{type="info"}
We suggest you `NameYourClassesLikeThis` and `nameYourKeysLikeThis` to enhance code readability and maintainability.
:::

## Understanding our App

To better understand Back4app, let's explore code examples of Parse operations in a Flutter application with the major supported data types. This guide won’t explain any Flutter app code as the primary focus is using Parse with Flutter.

## Prerequisites

To complete this tutorial, you will need:

:::hint{type="info"}
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4app:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4app.
- A Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on the Flutter project</a> to create a Flutter Project connected to Back4app.
- A device (or virtual device) running Android or iOS.
:::

## 1 - Working with Parse Objects

Each ParseObject has a class name (e.g., GamePoint) used to distinguish different data types. Here’s how you can create and save a new Parse object with data types `string`, `int`, & `boolean`:

```dart
final gamePoint = ParseObject('GamePoint')
  ..set('score', 1337)
  ..set('playerName', 'Sean Plott')
  ..set('cheatMode', false);
await gamePoint.save();
```

**To query the new Parse object and retrieve the data types:**

```dart
  final QueryBuilder<ParseObject> queryGamePoints = QueryBuilder<ParseObject>(ParseObject('GamePoint'));

  final ParseResponse response = await queryGamePoints.query();

  if (response.success && response.results != null) {
    for (var gamePoint in response.results!) {
      final score = gamePoint.get<int>('score');
      final playerName = gamePoint.get<String>('playerName');
      final cheatMode = gamePoint.get<bool>('cheatMode');
      
    }
```

## 2 - Counters

You can increment or decrement an integer field in a ParseObject using the `set()` method.

However, this isn't effective and can lead to problems if multiple clients are trying to update the same counter. Parse provides two methods that automatically increment and decrement any number field to store counter-type data.

`setIncrement()`

`setDecrement()`

An update to increment a counter `int` value will be written as:

```dart
 final gamePoint = ParseObject('GamePoint')
  ..objectId = 'yourObjectId'
  ..setIncrement('intField', 1);
 await gamePoint.save();
```

To decrement:

```dart
 final gamePoint = ParseObject('GamePoint')
  ..objectId = 'yourObjectId'
  ..setDecrement('intField', 1);
 await gamePoint.save();
```

Using `setIncrement()` and `setDecrement()` with the `save()` call allows you to update a value as part of a larger save operation where you may be modifying multiple fields. This is better to avoid extra network requests.

## 3 - Lists

Parse provides methods to work with list data, including `setAdd`, `setAddUnique`, `setRemove`, and their respective `All` versions.

List: `["a","b","c"]`

### 3.1 - setAdd

```dart
final gamePoint = ParseObject('GamePoint')
..objectId = 'yourObjectId'
..setAdd('listStringField', 'd');
await gamePoint.save();
```

Result: `["a","b","c","d"]`

### 3.2 - setAddAll

```dart
final gamePoint = ParseObject('GamePoint')
..objectId = 'yourObjectId'
..setAddAll('listStringField', ['e','f']);
await gamePoint.save();
```

Result: `["a","b","c","d","e","f"]`

`setAddAll` does not add duplicate elements if the list already contains them.

### 3.3 - setAddUnique

```dart
final gamePoint = ParseObject('GamePoint')
..objectId = 'yourObjectId'
..setAddUnique('listStringField', ['a', 'e', 'g']);
await gamePoint.save();
```

Result: `["a","b","c","d","e","f","g"]`

### 3.4 - setRemove

```dart
final gamePoint = ParseObject('GamePoint')
 ..objectId = 'yourObjectId'
 ..setRemove('listStringField', 'd');
 await gamePoint.save();
```

Result: `["a","b","c","e","f","g"]`

## 4 - Remove field from ParseObject

You can delete a single field from an object by using the unset operation:

```dart
final gamePoint = ParseObject('GamePoint')
..objectId = 'yourObjectId'
..unset("listStringField");
```

## 5 -  Files

ParseFile lets you store and retrieve application files in the Cloud.
Below is a basic example of uploading a local image file to Back4App:

```dart
// Create ParseFile object
 final parseFile = ParseFile(File(image.path));

 final response = await parseFile.save();

 if (response.success) {
   print('File uploaded successfully: ${parseFile.url}');

   // Save this file in an object 
   final gamePoint = ParseObject('GamePoint')
     ..set('title', 'Post with an Image')
     ..set('imageFile', parseFile); // Saving the file as a field

   final postResponse = await gamePoint.save();
   }
```

This code snippet shows how to retrieve the image file:

```dart
final query = QueryBuilder<ParseObject>(ParseObject('GamePoint'));

  final response = await query.query();

  if (response.success && response.results != null) {
    for (var gamePoint in response.results!) {
      ParseFileBase? varFile = gamePoint.get<ParseFileBase>('file');
      if (varFile != null) {
        final fileUrl = varFile.url;
        print('Game Title: Image URL: $fileUrl');
      } else {
        print('No file found for this object.');
      }
    }
  }
```

Starting from `Parse Server 5.2.3` they are breaking changes that could cause errors during an attempt to upload files. Follow [this guide](https://www.back4app.com/docs/platform/parse-server-version#O-CiL) to fix any upload issues you may experience.

A later guide will further discuss and show templates on how to [save and display Files with Parse](https://www.back4app.com/docs/flutter/parse-sdk/flutter-save-file).

## 6 - GeoPoint

Parse allows you to associate real-world latitude and longitude coordinates with an object with its GeoPoint data type. Adding a ParseGeoPoint to a ParseObject will enable queries to consider the proximity of an object to a reference point.

Example:

```dart
 // Request permission here and get the current location: the `position` object.
 
 // Create GeoPoint object
  final geoPoint = ParseGeoPoint(latitude: position.latitude, longitude: position.longitude);

 // Create an object with the GeoPoint
  final gamePoint = ParseObject('GamePoint')
    ..set('name', 'Current Location')
    ..set('location', geoPoint); // Save the GeoPoint in a field

  final response = await gamePoint.save();
```

You can find a standalone example of saving Geopoint location in this [main.dart](https://github.com/templates-back4app/Flutter_DataTypes/blob/18c4c372eb49c1cd950eeaac355564134a5e70c4/lib/src/main.dart) file.

## Full App Example

This example demonstrates how to:

- Create, delete, and update a `GamePoint` Parse object.
- Handle various data types, including strings, doubles, booleans, files, geopoint, lists, and dates.

On iOS, you will need to grant your simulator the necessary access permissions.

:::hint{type="info"}
- iOS provides a developer guide on protected resources. The example applications in this guide will require the [Location](https://developer.apple.com/documentation/bundleresources/information-property-list/nslocationwheninuseusagedescription) and  [Photos](https://developer.apple.com/documentation/bundleresources/information-property-list/nsphotolibraryaddusagedescription) permission keys found in that guide to work. Add the keys to the `Info.plist` file, located at `/ios/Runner/Info.plist`:
:::

You can skip the above step if you are running your Flutter app on the web:

```dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/foundation.dart';
import 'dart:io';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  const keyParseApplicationId = 'YOUR_APPLICATION_ID';
  const keyParseClientKey = 'YOUR_CLIENT_KEY';
  const keyParseServerUrl = 'https://parseapi.back4app.com';
  await Parse().initialize(
    keyParseApplicationId,
    keyParseServerUrl,
    clientKey: keyParseClientKey,
    autoSendSessionId: true,
    debug: true,
  );

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'DataTypes',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _stringController = TextEditingController();
  final TextEditingController _doubleController = TextEditingController();
  final TextEditingController _listStringController = TextEditingController();
  final TextEditingController _listIntController = TextEditingController();
  final TextEditingController _pointerController = TextEditingController();
  final TextEditingController _objectIdController = TextEditingController();
  final TextEditingController _uniqueValueController = TextEditingController();
  final TextEditingController _removeValueController = TextEditingController();

  bool _boolValue = false;
  DateTime _selectedDate = DateTime.now();
  final String _responseMessage = '';
  XFile? pickedFile;
  bool isLoading = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DataTypes'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              _buildTextField('String Field', _stringController),
              const SizedBox(height: 20),
              _buildTextField('Double Field', _doubleController,
                  isNumeric: true),
              const SizedBox(height: 20),
              _buildSwitch('Bool Field', _boolValue, (value) {
                setState(() {
                  _boolValue = value;
                });
              }),
              const SizedBox(height: 20),
              TextButton(
                onPressed: () => _selectDate(context),
                child: Text(
                    'Select Date: ${_selectedDate.toLocal()}'.split(' ')[0]),
              ),
              const SizedBox(height: 20),
              _buildTextField(
                  'List String Field (comma-separated)', _listStringController),
              const SizedBox(height: 20),
              _buildTextField(
                  'List Int Field (comma-separated)', _listIntController),
              const SizedBox(height: 20),
              _buildTextField('Pointer Field (Object ID)', _pointerController),
              const SizedBox(height: 20),
              _buildImagePicker(),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: _saveData,
                child: const Text('Save'),
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () async {
                  await _deleteData(_objectIdController.text);
                  // await dummyFunction();
                },
                child: const Text('Delete'),
              ),
              const SizedBox(height: 20),
              _buildTextField(
                  'Object ID to Delete or Update', _objectIdController),
              ElevatedButton(
                onPressed: () async {
                  await _updateData(_objectIdController.text);
                },
                child: const Text('Update'),
              ),
              const SizedBox(height: 20),
              _buildIncrementDecrementButtons(),
              const SizedBox(height: 20),
              _buildListOperations(),
              const SizedBox(height: 20),
              Text(_responseMessage),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildTextField(String label, TextEditingController controller,
      {bool isNumeric = false}) {
    return TextField(
      controller: controller,
      keyboardType: isNumeric ? TextInputType.number : TextInputType.text,
      decoration: InputDecoration(
        border: const OutlineInputBorder(),
        labelText: label,
      ),
    );
  }

  Widget _buildSwitch(String label, bool value, Function(bool) onChanged) {
    return SwitchListTile(
      title: Text(label),
      value: value,
      onChanged: onChanged,
    );
  }

  Widget _buildImagePicker() {
    return GestureDetector(
      child: pickedFile != null
          ? Container(
              width: 250,
              height: 250,
              decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
              child: kIsWeb
                  ? Image.network(pickedFile!.path)
                  : Image.file(File(pickedFile!.path)),
            )
          : Container(
              width: 250,
              height: 250,
              decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
              child: const Center(
                  child: Text('Click here to pick image from GamePoint')),
            ),
      onTap: () async {
        final picker = ImagePicker();
        XFile? image = (await picker.pickImage(source: ImageSource.gallery));
        if (image != null) {
          setState(() {
            pickedFile = image;
          });
        }
      },
    );
  }

  Widget _buildIncrementDecrementButtons() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Expanded(
          child: ElevatedButton(
            onPressed: () async {
              await _incrementField(_objectIdController.text);
            },
            child: const Text('+ Double'),
          ),
        ),
        const SizedBox(width: 10), // Adding some space between buttons
        Expanded(
          child: ElevatedButton(
            onPressed: () async {
              await _decrementField(_objectIdController.text);
            },
            child: const Text('- Double'),
          ),
        ),
      ],
    );
  }

  Widget _buildListOperations() {
    return Column(
      children: [
        _buildTextField('Unique Value to Add', _uniqueValueController),
        ElevatedButton(
          onPressed: () async {
            await _addUniqueToList(
                _objectIdController.text, _uniqueValueController.text);
          },
          child: const Text('Add Unique'),
        ),
        const SizedBox(height: 20),
        _buildTextField('Value to Remove', _removeValueController),
        ElevatedButton(
          onPressed: () async {
            await _removeFromList(
                _objectIdController.text, _removeValueController.text);
          },
          child: const Text('Remove'),
        ),
      ],
    );
  }

  Future<void> _saveData() async {
    //Parse values
    String stringValue = _stringController.text;
    List<String> listStringValue = _listStringController.text.isNotEmpty
        ? _listStringController.text
            .split(',') // Split by comma
            .map((e) =>
                e.trim()) // Remove any surrounding whitespace from each element
            .toList()
        : [];
    List<int> listtIntValue = _listIntController.text.isNotEmpty
        ? _listIntController.text
            .split(',') // Split by comma
            .map(
                (e) => int.parse(e.trim())) // Convert each string to an integer
            .toList()
        : [];
    double? doubleValue;
    if (_doubleController.text.isNotEmpty) {
      doubleValue = double.parse(_doubleController.text);
    }
    bool boolValue = _boolValue;
    DateTime selectedDate = _selectedDate;

    final gamePoint = ParseObject('GamePoint')
      ..set('bool', boolValue)
      ..set('date', selectedDate);
    if (stringValue.isNotEmpty) gamePoint.set('string', stringValue);
    if (doubleValue != null) gamePoint.set('double', doubleValue);
    if (listStringValue.isNotEmpty) {
      gamePoint.setAddAll('listString', listStringValue);
    }
    if (listtIntValue.isNotEmpty) gamePoint.setAddAll('listint', listtIntValue);
    if (_pointerController.text.isNotEmpty) {
      gamePoint.set('pointer', _parsePointer(_pointerController.text));
    }

    if (pickedFile != null) {
      setState(() {
        isLoading = true;
      });

      ParseFileBase? parseFile;

      if (kIsWeb) {
        parseFile =
            ParseWebFile(await pickedFile!.readAsBytes(), name: 'file.jpg');
      } else {
        parseFile = ParseFile(File(pickedFile!.path));
      }
      await parseFile.save();

      gamePoint.set('file', parseFile);
    }

    var apiResponse = await gamePoint.save();

    if (apiResponse.success && apiResponse.results != null) {
      setState(() {
        isLoading = false;
        pickedFile = null;
      });

      ScaffoldMessenger.of(context)
        ..removeCurrentSnackBar()
        ..showSnackBar(const SnackBar(
          content: Text(
            'File saved successfully on Back4app',
            style: TextStyle(color: Colors.white),
          ),
          duration: Duration(seconds: 3),
          backgroundColor: Colors.blue,
        ));
    } else {
      print("This is your request error: ${apiResponse.error}");
    }
  }

  Future<void> _updateData(String objectId) async {
    final gamePoint = ParseObject('GamePoint')..objectId = objectId;
    // Update fields with new values
    if (_stringController.text.isNotEmpty) {
      gamePoint.set('string', _stringController.text);
    }

    if (_doubleController.text.isNotEmpty) {
      gamePoint.set('double', double.parse(_doubleController.text));
    }

    gamePoint.set('bool', _boolValue);
    gamePoint.set('date', _selectedDate);

    if (_listStringController.text.isNotEmpty) {
      List<String> listStringValue =
          _listStringController.text.split(',').map((e) => e.trim()).toList();
      gamePoint.setAddAll('listString', listStringValue);
    }

    if (_listIntController.text.isNotEmpty) {
      List<int> listIntValue = _listIntController.text
          .split(',')
          .map((e) => int.parse(e.trim()))
          .toList();
      gamePoint.setAddUnique('listint', listIntValue);
    }

    if (_pointerController.text.isNotEmpty) {
      gamePoint.set('pointer', _parsePointer(_pointerController.text));
    }

    if (pickedFile != null) {
      ParseFileBase? parseFile;
      if (kIsWeb) {
        parseFile =
            ParseWebFile(await pickedFile!.readAsBytes(), name: 'file.jpg');
      } else {
        parseFile = ParseFile(File(pickedFile!.path));
      }
      await parseFile.save();
      gamePoint.set('file', parseFile);
    }

    // Save the updated object
    var response = await gamePoint.save();

    if (response.success) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Data updated successfully!')),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
            content: Text('Error updating data: ${response.error!.message}')),
      );
    }
  }

  Future<void> _incrementField(String objectId) async {
    final gamePoint = ParseObject('GamePoint')
      ..objectId = objectId
      ..setIncrement('double', 1);
    await gamePoint.save();
  }

  Future<void> _decrementField(String objectId) async {
    final gamePoint = ParseObject('GamePoint')
      ..objectId = objectId
      ..setDecrement('double', -1);
    await gamePoint.save();
  }

  Future<void> _addUniqueToList(String objectId, String value) async {
    final gamePoint = ParseObject('GamePoint')
      ..objectId = objectId
      ..setAddUnique('listString', value);
    await gamePoint.save();
  }

  Future<void> _removeFromList(String objectId, String value) async {
    final gamePoint = ParseObject('GamePoint')
      ..objectId = objectId
      ..setRemove('listString', value);
    await gamePoint.save();
  }

  Future<void> _selectDate(BuildContext context) async {
    final DateTime? picked = await showDatePicker(
      context: context,
      initialDate: _selectedDate,
      firstDate: DateTime(2000),
      lastDate: DateTime(2101),
    );
    if (picked != null && picked != _selectedDate) {
      setState(() {
        _selectedDate = picked;
      });
    }
  }

  Future<void> _deleteData(String objectId) async {
    // Delete data logic
    final parseObject = ParseObject('GamePoint')
      ..objectId = objectId
      ..unset("listString");

    await parseObject.save();
  }

  // Convert Pointer field to a suitable format
  ParseObject _parsePointer(String objectId) {
    // There should be an existing Game class with objects
    final pointer = ParseObject('Game')..objectId = objectId;
    return pointer;
  }
}
```

This code initializes the Parse SDK in Flutter, sets up the main application, and displays a simple home page with a title.

## Conclusion

In this guide, you learned about ParseObjects and the various datatypes available to Parse. You also learned how to handle operations like saving and retrieving the datatypes to and from your back4app backend.

[title] Untitled
[path] Android/


[title] Quickstart
[path] ReactJS/

# Quickstart



## Introduction

In this section, you will learn how to get started with Back4App using an existing or new project using React. You will install the Parse SDK, initialize Parse using your App keys, and make your first API call saving and reading data objects from Back4App.

## Prerequisites

:::hint{type="info"}
- To complete this tutorial, you will need:

  An <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">app created</a> on Back4App;
  A recent version of<a href="https://nodejs.org/" target="_blank"> Node.js</a>, including yarn and npx
:::

## 1 - Creating a React project

If you already have a working React project, you can skip to the next step.

Run the following command on the directory in which you would want to store the project, informing its name as well, in this case, back4app-guide-react:

> npx create-react-app back4app-guide-react

If Node.js is properly configured, you should see the project being created in your terminal prompt. After completion, you will see a message informing you that the process was successful.

Open the project in your favorite code editor and let’s start integrating Parse.

## 2 - Install dependencies

Let’s now install the only needed dependency, Parse JavaScript SDK, to integrate your App with Back4App servers. Run the following command on your project root directory:

> yarn add parse

## 3 - Get your App Keys

After creating your App on Back4App, go to your App’s Dashboard and get your App Keys under App Settings->Security & Keys(check the image below). Note that you will always need two keys to connect with Back4App, the Application ID and Javascript KEY.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AT6Q_UOcRE73pvLmreIA9_image.png)

## 4 - Initialize Parse and connect to Back4App

Go to your App.js and initialize the Parse SDK using both the Application ID and the Javascript KEY (check the previous step).

```javascript
1   // Import Parse minified version
2   import Parse from 'parse/dist/parse.min.js';
3
4   // Your Parse initialization configuration goes here
5   const PARSE_APPLICATION_ID = 'YOUR_APPLICATION_ID_HERE';
6   const PARSE_HOST_URL = 'https://parseapi.back4app.com/';
7   const PARSE_JAVASCRIPT_KEY = 'YOUR_JAVASCRIPT_KEY_HERE';
8   Parse.initialize(PARSE_APPLICATION_ID, PARSE_JAVASCRIPT_KEY);
9   Parse.serverURL = PARSE_HOST_URL;
```

## 5 - Save and Read a simple Data Object

Your App is initialized and can securely connect to Back4app cloud services. Let’s now create a component containing two simple functions inside to save and retrieve data from your Back4App database. Create a new file named PersonComponent.js in your src directory and add the following code:

```javascript
1   import React, { useState } from 'react';
2   import Parse from 'parse/dist/parse.min.js';
3
4   export const PersonComponent = () => {
5     // State variables
6     const [person, setPerson] = useState(null);
7
8     async function addPerson() {
9       try {
10        // create a new Parse Object instance
11        const Person = new Parse.Object('Person');
12        // define the attributes you want for your Object
13        Person.set('name', 'John');
14        Person.set('email', 'john@back4app.com');
15        // save it on Back4App Data Store
16        await Person.save();
17        alert('Person saved!');
18      } catch (error) {
19        console.log('Error saving new person: ', error);
20      }
21    }
22
23    async function fetchPerson() {
24      // create your Parse Query using the Person Class you've created
25      const query = new Parse.Query('Person');
26      // use the equalTo filter to look for user which the name is John. this filter can be used in any data type
27      query.equalTo('name', 'John');
28      // run the query
29      const Person = await query.first();
30      // access the Parse Object attributes
31      console.log('person name: ', Person.get('name'));
32      console.log('person email: ', Person.get('email'));
33      console.log('person id: ', Person.id);
34      setPerson(Person);
35    }
36
37    return (
38      <div>
39        <button onClick={addPerson}>Add Person</button>
40        <button onClick={fetchPerson}>Fetch Person</button>
41        {person !== null && (
42          <div>
43            <p>{`Name: ${person.get('name')}`}</p>
44            <p>{`Email: ${person.get('email')}`}</p>
45          </div>
46        )}
47      </div>
48    );
49  };
```

The addPerson method creates a new Parse.Object representing a Person class, sets its properties then saves it on the Back4App cloud data store. The method fetchPerson retrieves a Parse.Object which has the attribute name equals to John. When the query resolves, you will be able to access the person’s attributes using the get method.

Note also the interface elements in this component, they consist of two buttons calling the methods and also two paragraphs retrieving the fetched Person through a state variable.

We now need to import and use this component in your main App.js file. Your App.js file should look something like this, after removing most of the placeholder code in it.

```javascript
1   import './App.css';
2   import Parse from 'parse/dist/parse.min.js';
3   import { PersonComponent } from './PersonComponent';
4
5   // Your Parse initialization configuration goes here
6   const PARSE_APPLICATION_ID = 'YOUR_PARSE_APPLICATION_ID';
7   const PARSE_HOST_URL = 'https://parseapi.back4app.com/';
8   const PARSE_JAVASCRIPT_KEY = 'YOUR_PARSE_JAVASCRIPT_KEY';
9   Parse.initialize(PARSE_APPLICATION_ID, PARSE_JAVASCRIPT_KEY);
10  Parse.serverURL = PARSE_HOST_URL;
11
12  function App() {
13    return (
14      <div className="App">
15        <header className="App-header">
16          <PersonComponent />
17        </header>
18      </div>
19    );
20  }
21
22  export default App;
```

## 6 - Test your App

1. Open your project’s terminal.
2. Run yarn start. Your browser should open after building with the app running. Click the button to add a new Person first, then click to fetch the same Person.
3. You’ve saved and retrieved a data object from Back4App. You can also check the data on your <a href="https://parse-dashboard.back4app.com/apps/" target="_blank">App Dashboard</a> and clicking on Person class.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xv98ndOU435vt5WzmJ7Oq_image.png)

## What to do next

As you have seen, the easiest way to integrate Back4App into your React project is through the [Parse Javascript SDK](https://www.npmjs.com/package/parse). The Parse SDK delivers an excellent development experience through a lightweight and easy-to-use layer that provides minimal configuration and code re-usability.

[title] Untitled
[path] ReactJS/


[title] Untitled
[path] Flutter/Parse SDK (REST)/


[title] Basic Queries
[path] Android/Data objects/

# Basic Queries on Android

## Introduction

In this guide, you will perform basic queries in Parse and implement an Android app using these queries. You will learn how to set up and query realistic data using Back4App and Android.

This tutorial uses an app created in Android Studio 4.1.1 with buildToolsVersion=30.0.2 , Compile SDK Version = 30.0.2 and targetSdkVersion=30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

Our goal is query data stored on Back4App from an Android app.

Here is a preview of what we are gonna achieve:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2P-b0YNHro11-ifKLt8YQ_image.png" signedSrc size="50" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Let’s get started!

:::hint{type="info"}
Before next steps, we need to connect Back4App to our application. You should save the appId and clientKey from the Back4App to string.xml file and then init Parse in our App.java or App.kt file.
Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">New Parse App tutorial</a> if you don’t know how to init Parse to your app.

Or you can download the projects we shared the github links above and edit only the appId and clientKey parts according to you.
:::

## 1 - Understanding the Parse.Query class

Any Parse query operation uses the ParseQuery object type, which will help you retrieve specific data from your database throughout your app. It is crucial to know that a ParseQuery will only resolve after calling a retrieve method (like query.findInBackground), so a query can be set up and several modifiers can be chained before actually being called.

To create a new ParseQuery, you need to pass as a parameter the desired ParseObject 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.

:::CodeblockTabs
```java
1   // This will create your query
2   ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
3   // The query will resolve only after calling this method
4   query.findInBackground();
```

```kotlin
1   // This will create your query
2    val query = ParseQuery<ParseObject>("Profile")
3   // The query will resolve only after calling this method
4   query.findInBackground()
```
:::

You can read more about the Parse.Query class [here at the official documentation](https://www.back4app.com/docs/javascript/parse-javascript-sdk).&#x20;

## 2 - Save some data on Back4App

:::hint{type="info"}
In this step, we will create a Class with the JS Console and Javascript codes provided by Parse and we will create queries for this Class.
:::

Let’s create a Profile class, which will be the target of our queries in this guide. On Parse Dashboard Javascript 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:



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Wlu7TX7N6ZuOhocNdNmrc_image.png)

Go ahead and create the userProfileclass with the following example content:

```java
1   // Add Profile objects and create table
2   // Adam Sandler
3   let Profile = new Parse.Object('Profile');
4   Profile.set('name', 'Adam Sandler');
5   Profile.set('birthDay', new Date('09/09/1966'));
6   Profile.set('friendCount', 2);
7   Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8   await Profile.save();
9
10  // Adam Levine
11  Profile = new Parse.Object('Profile');
12  Profile.set('name', 'Adam Levine');
13  Profile.set('birthDay', new Date('03/18/1979'));
14  Profile.set('friendCount', 52);
15  Profile.set('favoriteFoods', ['Cake', 'Bread']);
16  await Profile.save();
17
18  // Carson Kressley
19  Profile = new Parse.Object('Profile');
20  Profile.set('name', 'Carson Kressley');
21  Profile.set('birthDay', new Date('11/11/1969'));
22  Profile.set('friendCount', 12);
23  Profile.set('favoriteFoods', ['Fish', 'Cookies']);
24  await Profile.save();
25
26  // Dan Aykroyd
27  Profile = new Parse.Object('Profile');
28  Profile.set('name', 'Dan Aykroyd');
29  Profile.set('birthDay', new Date('07/01/1952'));
30  Profile.set('friendCount', 66);
31  Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
32  await Profile.save();
33
34  // Eddie Murphy
35  Profile = new Parse.Object('Profile');
36  Profile.set('name', 'Eddie Murphy');
37  Profile.set('birthDay', new Date('04/03/1961'));
38  Profile.set('friendCount', 49);
39  Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
40  await Profile.save();
41
42  // Fergie
43  Profile = new Parse.Object('Profile');
44  Profile.set('name', 'Fergie');
45  Profile.set('birthDay', new Date('03/27/1975'));
46  Profile.set('friendCount', 55);
47  Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
48  await Profile.save();
49  
50  console.log('Success!');
```

### &#x20;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:

```java
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   // `contains` is a basic query method that checks if string field
5   // contains a specific substring
6   parseQuery.contains('name', 'Adam');
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(result.get('name'));
16  };
```

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.

```java
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   // `greaterThan` is a basic query method that does what it
5   // says on the tin
6   parseQuery.greaterThan('friendCount', 20);
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}`);
16  };
```

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.

```java
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   // `descending` and `ascending` can and should be chained
5   // with other query methods to improve your queries
6   parseQuery.descending('birthDay');
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11 
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(`name: ${result.get('name')}, birthday: ${result.get('birthDay')}`);
16  };
```

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:

```java
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   parseQuery.contains('name', 'Adam');
5   parseQuery.greaterThan('friendCount', 20);
6   parseQuery.descending('birthDay');
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}, birthday: ${result.get('birthDay')}`);
16  };
```

## 4 - Query from our Android App

We will now do the operations we did above from the JS Console with Java and Kotlin in our Android application. We will list the profiles by making 4 different queries.

:::CodeblockTabs
```java
1   private void doQueryByName() {
2           ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
3           query.whereContains("name", "Adam");
4           progressDialog.show();
5           query.findInBackground((objects, e) -> {
6               progressDialog.hide();
7               if (e == null) {
8                   adapter = new ResultAdapter(this, objects);
9                   resultList.setLayoutManager(new LinearLayoutManager(this));
10                  resultList.setAdapter(adapter);
11              } else {
12                  Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
13              }
14          });
15      }
16
17      private void doQueryByFriendCount() {
18          ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
19          query.whereGreaterThan("friendCount", 20);
20          progressDialog.show();
21          query.findInBackground((objects, e) -> {
22              progressDialog.hide();
23              if (e == null) {
24                  adapter = new ResultAdapter(this, objects);
25                  resultList.setLayoutManager(new LinearLayoutManager(this));
26                  resultList.setAdapter(adapter);
27              } else {
28                  Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
29              }
30          });
31      }
32 
33      private void doQueryByOrdering() {
34          ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
35          query.orderByDescending("birthDay");
36          progressDialog.show();
37          query.findInBackground((objects, e) -> {
38              progressDialog.hide();
39              if (e == null) {
40                  adapter = new ResultAdapter(this, objects);
41                  resultList.setLayoutManager(new LinearLayoutManager(this));
42                  resultList.setAdapter(adapter);
43              } else {
44                  Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
45              }
46          });
47      }
48  
49      private void doQueryByAll() {
50          ParseQuery<ParseObject> query = new ParseQuery<>("Profile");
51          query.whereContains("name", "Adam");
52          query.whereGreaterThan("friendCount", 20);
53          query.orderByDescending("birthDay");
54          progressDialog.show();
55
56          query.findInBackground((objects, e) -> {
57              progressDialog.hide();
58
59              if (e == null) {
60                  adapter = new ResultAdapter(this, objects);
61                  resultList.setLayoutManager(new LinearLayoutManager(this));
62                  resultList.setAdapter(adapter);
63                  resultList.setNestedScrollingEnabled(false);
64              } else {
65                  Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
66              }
67          });
68      }
```

```kotlin
1   private fun doQueryByName() {
2           val query = ParseQuery<ParseObject>("Profile")
3           query.whereContains("name", "Adam")
4           progressDialog!!.show()
5           query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
6               progressDialog!!.hide()
7               if (e == null) {
8                   adapter = ResultAdapter(this, objects)
9                   resultList!!.layoutManager = LinearLayoutManager(this)
10                  resultList!!.adapter = adapter
11              } else {
12                  Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
13              }
14          }
15      }
16
17      private fun doQueryByFriendCount() {
18          val query = ParseQuery<ParseObject>("Profile")
19          query.whereGreaterThan("friendCount", 20)
20          progressDialog!!.show()
21          query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
22              progressDialog!!.hide()
23              if (e == null) {
24                  adapter = ResultAdapter(this, objects)
25                  resultList!!.layoutManager = LinearLayoutManager(this)
26                  resultList!!.adapter = adapter
27              } else {
28                  Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
29              }
30          }
31      }
32
33      private fun doQueryByOrdering() {
34          val query = ParseQuery<ParseObject>("Profile")
35          query.orderByDescending("birthDay")
36          progressDialog!!.show()
37          query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
38              progressDialog!!.hide()
39              if (e == null) {
40                  adapter = ResultAdapter(this, objects)
41                  resultList!!.layoutManager = LinearLayoutManager(this)
42                  resultList!!.adapter = adapter
43              } else {
44                  Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
45              }
46          }
47      }
48  
49      private fun doQueryByAll() {
50          val query = ParseQuery<ParseObject>("Profile")
51          query.whereContains("name", "Adam")
52          query.whereGreaterThan("friendCount", 20)
53          query.orderByDescending("birthDay")
54          progressDialog!!.show()
55          query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
56              progressDialog!!.hide()
57              if (e == null) {
58                  adapter = ResultAdapter(this, objects)
59                  resultList!!.layoutManager = LinearLayoutManager(this)
60                  resultList!!.adapter = adapter
61                  resultList!!.isNestedScrollingEnabled = false
62              } else {
63                  Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
64              }
65          }
66      }
```
:::

## It’s done!

At the end of this guide, you learned how basic data queries work on Parse and how to perform them on Back4App from an Android App.

[title] User registration - login
[path] Android/Users/

# Login and User registration tutorial

## Introduction

In this section we will take a look to how to create an app with a simple user registration using <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

This tutorial uses a basic app created in Android Studio 4.1.1 with buildToolsVersion=30.0.2 , Compile SDK Version = 30.0.2 and targetSdkVersion 30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

We will learn how to log in and register using Parse.

Here is a preview of what we are gonna achive :

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aN4ASiBVNZ8ziFhqTP4Mb_user-registrationv2.gif" signedSrc size="50" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## 1 - Import Library

In this step we will import the libraries which we are gonna use in our project:

1. We will add following Parse Classes to our Activities.

> 1    import com.parse.Parse
>
> **;**
>
>
> 2    import com.parse.ParseException
>
> **;**
>
>
> 3    import com.parse.ParseUser
>
> **;**

&#x20;    2\. We will use lambda functions frequently in our project because of that we need to add Java 1.8 to our project via build.gradle(Module\:App)

> 1    compileOptions 
>
> **{**
>
>
> 2        sourceCompatibility 
>
> **JavaVersion.**
>
> VERSION_1_8
> 3        targetCompatibility 
>
> **JavaVersion.**
>
> VERSION_1_8
> 4    
>
> **}**

## 2 - Sign Up

Signing up basically creates a new Parse.User Object in User Class, shown as “User” in your app Dashboard. We need to set at least two properties when creating a new user => ParseUser.setUsername() and ParseUser.setPassword().

The method used for saving the new user on Android is ParseUser.signUpInBackground(), which may come together with a callback function.

:::hint{type="info"}
**Note**: Objects of this special class are not saved on the Dashboard with ParseObject.save() method.
:::

To make SignUpActivity work, follow these steps:

1. Import&#x20;

> 1   import com.parse.SignUpCallback
>
> **;**

into your SignUpActivity, in addition to the dependencies imported in **Step 1**.

&#x20;    2\. To implement user registration, simply use the following code in theonCreate() method:

:::CodeblockTabs
```java
1   ParseUser user = new ParseUser();
2        // Set the user's username and password, which can be obtained by a forms
3        user.setUsername( "<Your username here>");
4        user.setPassword( "<Your password here>");
5        user.signUpInBackground(new SignUpCallback() {
6            @Override
7            public void done(ParseException e) {
8                if (e == null) {
9                    showAlert("Successful Sign Up!", "Welcome" + "<Your username here>" +"!");
10               } else {
11                   ParseUser.logOut();
12                   Toast.makeText(SignUpActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
13               }
14           }
15       });
```

```kotlin
1   val user = ParseUser();
2        // Set the user's username and password, which can be obtained by a forms
3        user.setUsername("<Your username here>");
4        user.setPassword("<Your password here>");
5        user.signUpInBackground(SignUpCallback() {
6            if (it == null) {
7                showAlert("Successful Sign Up!", "Welcome" + "<Your username here>" + "!");
8            } else {
9                ParseUser.logOut();
10               Toast.makeText(this, it.message, Toast.LENGTH_LONG).show();
11           }
12       });
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a SIGN UP button callback.
Also, username and password are caught using **Edit Texts**.
:::

&#x20;    3\. It’s interesting to add an additional method to display Alert Dialogs and make the process look more professional. The method below do this:

:::CodeblockTabs
```java
1   private void showAlert(String title,String message){
2        AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this)
3                .setTitle(title)
4                .setMessage(message)
5                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
6                    @Override
7                    public void onClick(DialogInterface dialog, int which) {
8                        dialog.cancel();
9                        // don't forget to change the line below with the names of your Activities
10                       Intent intent = new Intent(SignUpActivity.this, LogoutActivity.class);
11                       intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
12                       startActivity(intent);
13                   }
14               });
15       AlertDialog ok = builder.create();
16       ok.show();
17   }
```

```kotlin
1   private fun showAlert(title: String, message: String) {
2        val builder = AlertDialog.Builder(this)
3                .setTitle(title)
4                .setMessage(message)
5                .setPositiveButton("OK") { dialog, which ->
6                    dialog.cancel()
7                    // don't forget to change the line below with the names of your Activities
8                    val intent = Intent(this, LogoutActivity::class.java)
9                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
10                   startActivity(intent)
11               }
12       val ok = builder.create()
13       ok.show()
14   }
```
:::

## 3 - Log in

Logging in creates a Session object, which points to the User logged in. If login is successful, ParseUser.getCurrentUser() returns a User object, and a Session object which created in the Dashboard. Otherwise, if the target username does not exist, or the password is wrong, it returns null.

The method used to perform the login action is ParseUser.logInInBackground(), which requires as many arguments as the strings of username and password, and may call a callback function.

:::hint{type="info"}
**Note:** After signing up, login is performed automatically.
:::

To make LoginActivity work, follow these steps:

1. Import into yourLoginActivity, in addition to the dependencies imported in the **Step 1:**

> 1    import com.parse.LogInCallback
>
> **;**

&#x20;     2\. To implement user login function, simply use the code:

:::CodeblockTabs
```java
1   private void login(String username, String password) {
2        progressDialog.show();
3        ParseUser.logInInBackground(username, password, (parseUser, e) -> {
4            progressDialog.dismiss();
5            if (parseUser != null) {
6                showAlert("Successful Login", "Welcome back " + username + " !");
7            } else {
8                ParseUser.logOut();
9                Toast.makeText(LoginActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
10           }
11       });
12   }
```

```kotlin
1   fun login(username: String, password: String) {
2        progressDialog?.show()
3        ParseUser.logInInBackground(username,password) { parseUser: ParseUser?, parseException: ParseException? ->
4            progressDialog?.dismiss()
5            if (parseUser != null) {
6                showAlert("Successful Login", "Welcome back " + username + " !")
7            } else {
8                ParseUser.logOut()
9                if (parseException != null) {
10                   Toast.makeText(this, parseException.message, Toast.LENGTH_LONG).show()
11               }
12           }
13       }
14   }
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a LOG IN button callback.
Also, username and password are caught using **Edit Texts**.
:::

:::hint{type="info"}
The method showAlert is the same that you added in the SignUpActivity, don’t forget to change its Intent arguments though.
:::

## 4 - Log Out

Logging out deletes the active Session object for the logged User. The method used to perform log out is ParseUser.logOutInBackground().

To implement user log out, simply use the code below, in the LogoutActivity:

:::CodeblockTabs
```java
1       ParseUser.logOutInBackground(e -> {
2               progressDialog.dismiss();
3               if (e == null)
4                   showAlert("So, you're going...", "Ok...Bye-bye then");
5       });
```

```kotlin
1   fun login(username: String, password: String) {
2        progressDialog?.show()
3        ParseUser.logInInBackground(username,password) { parseUser: ParseUser?, parseException: ParseException? ->
4            progressDialog?.dismiss()
5            if (parseUser != null) {
6                showAlert("Successful Login", "Welcome back " + username + " !")
7            } else {
8                ParseUser.logOut()
9                if (parseException != null) {
10                   Toast.makeText(this, parseException.message, Toast.LENGTH_LONG).show()
11               }
12           }
13       }
14   }
```
:::

:::hint{type="info"}
In the example project, this code is placed inside a LOG OUT button callback.
:::

:::hint{type="info"}
The method showAlert is the same that you added in the loginActivityandSignUpActivity, don’t forget to change its Intent arguments though.
:::

## 5 - Test your app

1. Run your app and create a couple of users, also try logging in again after registering them.
2. Login at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.
3. Find your app and click on Dashboard>Core>Browser>User.

At this point, you should see your users as displayed below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/A9nIfbXRgRJQrRuLJDOzG_image.png)

:::hint{type="info"}
**Note**: Using the codes displayed above, every time you log in with a user, a Session is opened in your Dashboard, but when the user logs out that particular Session ends. Also, whenever an unsuccessful login or sign up attempt occurs, the Session opened in Parse Server Dashboard is deleted.
:::

## It’s done!

Congrats ! Now you can log in, register or log out of your app using Parse Server core features through Back4App!

[title] Intro to GraphQL
[path] Flutter/GraphQL/

## Flutter GraphQL on Back4App

## Introduction

Since you are here delving into Back4app docs, you know that Back4app is a flexible low code Backend. It allows you to interact with your backend in several different ways, the way you like it. If you want you can use the REST API, or you can directly use a [Native SDK](https://pub.dev/packages/parse_server_sdk) for flutter. In this tutorial series, we are going to explore using the new shiny GraphQL API with Flutter.

This guide was written by Chinmay, a [Back4App Partner](https://www.back4app.com/partners/software-development-company) at Hybrowlabs

## Goals

- **Get an overview of GraphQL;**
- **Understand how GraphQL and Flutter fit in together;**
- **Understand Flutter tooling and libraries available for GraphQL;**
- **Understanding the general architecture and key concepts in a Flutter GraphQL application;**

## Prerequisites

:::hint{type="info"}
- We require that the user has some basic understanding of Dart and Flutter;
- Though not necessary, GraphQL cookbook will be useful in understanding the <a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">Back4App GraphQL spec</a>
:::

## What is GraphQL

GraphQL is an alternative architecture to the REST API standard. GraphQL was born when the Facebook team was rebuilding their native applications from scratch and they were in heavy need to optimize the data they received from the backend.

While they were refactoring their services as a set of resources, they got frustrated because it was taking them nowhere.

They took a step back and rethought the data as an interconnected graph of objects instead of isolated REST resources. This made a big difference to their application architecture and that’s how GraphQL was born. Since GraphQL was released out in the wild in 2015, it has been getting huge popularity and buzz and has been adopted for use in teams like Twitter, Airbnb, Atlassian, Github.

## Why GraphQL?

At Hybrowlabs, a [Back4App Partner](https://www.back4app.com/partners/software-development-company), we build modern, data-intensive web and mobile applications. We adopted Back4app as our backend service of choice because of its ease of use, optimization, and schema definition features that are provided on top of the already awesome databases.

Most of the time the applications in the applications we engineer data that gets displayed or manipulated are not just from a single Back4App class. Rather this data comes from multiple classes. The Back4App-GraphQL combo makes it very easy for us to tackle such scenarios.

All the while it allows us to reduce the network footprint to 2-3 calls per screen load, just bringing the data we want. On the other hand cloud code that we define easily gets documented because of the strongly typed nature of GraphQL. This makes our code maintainable and easy to understand.

**TLDR; here are the advantages of using GraphQL**

1. Network footprint gets minimized
2. Efficient queries and query batching
3. Better code management
4. Strongly typed nature brings predictability.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Vw4PxvzPMzvFcRagwTaOD_image.png)

## Flutter and Back4App GraphQL

It is often very difficult to architect a GraphQL backend solution combining the flexibility of NoSQL databases while preserving structure and organization provided by having a type and schema structure of GraphQL.
Back4app has always had this structured schema-based design and now GraphQL takes it to a whole new level by creating an API knowing semantics of the type system.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qDVwsETOs-0QgePh1AsEH_image.png)

On the other hand, as we know Dart (which is what Flutter is based on) is a strongly typed language. Yes, Dart is very particular of the type of safety.
With GraphQL we have a schema that can be reused on the client-side. This makes life a lot easier. Also knowing the type-schema that can be fetched in the future really helps as it allows us to implement advanced caching and query invalidation algorithms on this data.

## Flutter toolkit for Back4App GraphQL

For interacting with our GraphQL API we will leverage the awesome [graphql-flutter](https://github.com/zino-app/graphql-flutter) library. So let’s understand the features the library provides:

- Support for Queries and Mutations.
- All the schema defined on the Back4App dashboard is directly converted to the Back4app GraphQL endpoints and available either as Queries or Mutations.
- Queries allow us to get nested data from multiple classes in a single API call.
- Queries are mainly done for the purpose of data fetching, and they are cached by
- Back4app GraphQL allows rich query methods, supporting geolocation, time, regex, and full-text search features.
- Mutations allow us to make an API call that can update multiple class entries.
- Mutations consist of mainly creating, editing, deleting data.

Following is a ProgramingLanguage Class that I have created on Back4App:

When navigating into the GraphQL section of the API for Back4App, you will see the previously illustrated console of Back4App. This console will now have all the mutations and queries automatically created!

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cG1rrBLhH48XHFJUWFnZI_image.png)

When i go to the GraphQL section of the API for Back4App, we will see the previously illustrated console of Back4App. This console will now have all the mutations and queries automatically created!

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/847tlfr2XUmWrZQVyqiJo_image.png)

In the above picture I have used Back4App GraphQL for the listing of all my languages from the ProgrammingLanguage class.
Similarly we can create an entry in our ProgrammingLanguage Back4App class by leveraging mutations.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AaG6rap9yOmL72UdhPMhF_image.png)

### **Query polling**

```graphql
1   Query(
2   Query(  
3   options: QueryOptions(
4       document: GET_ALL_PROGRAMMING_LANGUAGES, // this is the query string you just created
5       pollInterval: 10, //setting up polling
6       ),
7     builder: (QueryResult result) {
8       if (result.errors != null) {
9         return Text(result.errors.toString());
10      }
11
12      if (result.loading) {
13        return Text('Loading');
14      }
15
16      // it can be either Map or List
17      List programmingLanguages = result.data['programmingLanguages'];
18
19      return ListView.builder(
20        itemCount: programmingLanguages.length,
21        itemBuilder: (context, index) {
22          final repository = programmingLanguages[index];
23          return Text(programmingLanguages['name']);
24      });
25    },
26  );
```

- For some use cases, wherein subscriptions might be an overkill. We need to refresh data periodically, we can use the query polling feature of the client.
- This allows us to fetch the latest data in a periodic manner. For example, we can do a background data fetch every 1 minute.

### **In-memory cache**

```graphql
1   static Future<QueryResult> callQuery(
2         {dynamic documentNode, dynamic variables, FetchPolicy fetchPolicy}) {
3       return _singleton._client.query(
4         QueryOptions(
5           context: _singleton.context,
6           documentNode: documentNode,
7           variables: variables,
8           fetchPolicy: fetchPolicy ?? FetchPolicy.cacheAndNetwork,
9         ),
10      );
11    }
```

The Flutter GraphQL client implements various ways of caching the data, they are:

- Cache while Revalidate: Let’s consider a simple example of fetching a list of articles. We want the list data to be cached, when we move from a list to a specific article and then back. Cache while revalidating mechanism implements this method of querying the data, this means that even though an API call is being made to fetch the data, old data is used to display while new data is being fetched, when the new data becomes available, it is then shown.
- Cache-Only: In this method, a network call is only made if there is no data on our device. This is worthwhile when coupled with offline sync facilities for data points that are not going to change. This optimizes the network and saves resources.
- Network-Only: In rare cases when you don’t want to keep the cache of data, then you can use the Network Only mode of the Flutter GraphQL library.

### **Offline cache sync**

Mobile experience expects that it is offline enabled by default. Doing this with Flutter and Rest API is very tedious. You have to store the data into the SQLite database, check if the data is present or not, write SQL queries to fetch the data, and then also do the network calls to update the data.

That is a lot of work, which is totally bypassed with the use of this plugin and offline support for Queries comes baked into our Flutter application.

### **File Storage**

```graphql
1   mutation($files: [Upload!]!) {
2     multipleUpload(files: $files) {
3       id
4       filename
5       mimetype
6       path
7     }
8   }
9
10  import 'package:http/http.dart';
11  
12  // ...
13
14  String filePath = '/aboslute/path/to/file.ext';
15  final file = await multipartFileFrom(File(filePath));
16  
17  final QueryResult r = await graphQLClientClient.mutate(
18    MutationOptions(
19      documentNode: gql(uploadMutation),
20      variables: {
21        'files': [file],
22      },
23    )
24  );gr
```

Many GraphQL server libraries do not support a file upload facility using the GraphQL queries. For uploading a file we have to call a separate REST endpoint. Upload the file, get the URL, then pass it to the GraphQL API in the form of a string. Also, we have to manually maintain the file metadata like filename, etc.

Back4App has support for file upload standards backed into the Back4App GraphQL API. When the file is uploaded, it is uploaded as a FILE type of GraphQL, which corresponds to a Back4app FILE in GraphQL. Automatically, we have stored the file into highly scalable Back4app File storage backed by AWS S3.

### **Optimistic results**

```graphql
1   FlutterWidget(
2     onTap: () {
3       toggleStar(
4         { 'starrableId': repository['id'] },
5         optimisticResult: {
6           'action': {
7             'starrable': {'viewerHasStarred': !starred}
8           }
9         },
10      );
11    },
12  )
```

Users have come to expect a very engaging UI, and instantaneous feedback. Optimistic UI is a UI/UX principle which in essence says that we should not for the action to become over. But instead, we show feedback to the user that we have completed the action!

This is done with the assumption that the action will mostly succeed 99% of the time. If the action unfortunately fails we show a failure message saying we could not complete the action.

Flutter GraphQL comes with baked in support for defining the optimistic mutations which makes it far easier to implement this complex UI interaction system.
Thus with this, we have a very well-optimized base for building modern-day applications.

## Conclusion

In this guide we introduced you to some important advantages of using GraphQL on your Flutter project on Back4App. In the next guide we are going to put our hands on code and start by setting up our Flutter GraphQL Client for using Back4App API.

[title] Data types
[path] ReactJS/Data objects/

# &#x20;Parse Data Types in a React component

## Introduction

In the heart of Parse Core feature is the data objects management. Parse allows you to store and query its data straightforwardly using its SDKs or APIs (REST or GraphQL). All the data object features are built using the Parse.Object class, which fields may contain key-value pairs of several JSON-compatible data types. The primary data types that can be assigned to the object fields are the following:

- Number: integer (42) or floating-point (42.5) numbers, as long as ‘.’ is the decimal separator;
- boolean: true or false values;
- string: a string that can be as long as 2147483647 characters. Be aware that values this huge will slow down data operations;
- DateTime: DateTimeobjects stored in UTC format as default. If you need to use another timezone, conversion should be done manually;
- array: an array containing data in any Parse compatible data.
- object: a JSON object also containing any Parse data. When available in SDK, an include() call will bring details from the Object property.

:::hint{type="success"}
When you choose to use the Array type, we recommend keeping array objects small as this can affect your data operations’ overall performance. Our recommendation is to use the Array type if it does not exceed 20 elements and does not grow over time. Instead of the Array type, you can use the Pointer and Relations types as an alternative.
:::

In this guide, you will learn how to store data in each of the basic data types listed above. You will build a product registration component on React, which will show you how to format, convert and save data to your Parse Server in React.

Parse also offers the datatypes GeoPoint to use the power of geolocation resources, and the Parse-specific relational data using the types Pointer or Relation. You will see both covered in the next following guides.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library</a>.
:::

## Goal

To understand the Parse-compatible basic data types, and to store each data type on Parse from a React component.

## 1 - The Product Creation Component

Let’s first create the component structure. Let’s make it simple and create a form screen with one text input to each data type, one checkbox, and a submit button to save the object. These inputs will collect your Product field values: name (string), quantity (number), price (number), available (boolean), expiration date (DateTime), and categories(array). Also, you will save an additional object type field in your saving method as well, but this one won’t need an input field.

Create a separate component in a file called ProductCreation.js/ProductCreation.tsx including the following code, or add it to your main application file (App.js/App.tsx). You can use this layout with complete stylings using Ant Design and adding the CSS code to your App.css file or set up your own custom form.

:::CodeblockTabs
ProductCreation.js

```javascript
1   import React, { useState } from 'react';
2   import Parse from 'parse/dist/parse.min.js';
3   import './App.css';
4   import { Button, Checkbox, Input } from 'antd';
5   import { PlusOutlined } from '@ant-design/icons';
6
7   export const ProductCreation = () => {
8     // State variables
9     const [productName, setProductName] = useState('');
10    const [productQuantity, setProductQuantity] = useState('');
11    const [productPrice, setProductPrice] = useState('');
12    const [productAvailable, setProductAvailable] = useState(false);
13    const [productExpirationDate, setProductExpirationDate] = useState('');
14    const [productCategories, setProductCategories] = useState('');
15
16    return (
17      <div>
18        <div className="header">
19          <img
20            className="header_logo"
21            alt="Back4App Logo"
22            src={
23              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
24            }
25          />
26          <p className="header_text_bold">{'React on Back4App'}</p>
27          <p className="header_text">{'Product Creation'}</p>
28        </div>
29        <div className="container">
30          {/* Product field inputs */}
31          <div className="flex_between">
32            <h2 className="list_heading">Available?</h2>
33            <Checkbox
34              onChange={(e) => setProductAvailable(e.target.checked)}
35            ></Checkbox>
36          </div>
37          <div className="form_wrapper">
38            <Input
39              className="form_input"
40              value={productName}
41              onChange={(event) => setProductName(event.target.value)}
42              placeholder="Name"
43              size="large"
44            />
45            <Input
46              className="form_input"
47              value={productQuantity}
48              onChange={(event) => setProductQuantity(event.target.value)}
49              placeholder="Quantity"
50              size="large"
51            />
52            <Input
53              className="form_input"
54              value={productPrice}
55              onChange={(event) => setProductPrice(event.target.value)}
56              placeholder="Price"
57              size="large"
58            />
59            <Input
60              className="form_input"
61              value={productExpirationDate}
62              onChange={(event) => setProductExpirationDate(event.target.value)}
63              placeholder="Expiration Date (mm/dd/yyyy)"
64              size="large"
65            />
66            <Input
67              className="form_input"
68              value={productCategories}
69              onChange={(event) => setProductCategories(event.target.value)}
70              placeholder="Categories (separated by comma)"
71              size="large"
72            />
73            {/* Add product button */}
74            <Button
75              type="primary"
76              className="form_button"
77              color={'#208AEC'}
78              size={'large'}
79              onClick={createProduct}
80              icon={<PlusOutlined />}
81            >
82              CREATE PRODUCT
83            </Button>
84          </div>
85        </div>
86      </div>
87    );
88 };
```

ProductCreation.tsx

```typescript
1   import React, { useState, FC, ReactElement } from 'react';
2   import './App.css';
3   import { Button, Checkbox, Input } from 'antd';
4   import { PlusOutlined } from '@ant-design/icons';
5   const Parse = require('parse/dist/parse.min.js');
6
7   export const ProductCreation: FC<{}> = (): ReactElement => {
8     // State variables
9     const [productName, setProductName] = useState('');
10    const [productQuantity, setProductQuantity] = useState('');
11    const [productPrice, setProductPrice] = useState('');
12    const [productAvailable, setProductAvailable] = useState(false);
13    const [productExpirationDate, setProductExpirationDate] = useState('');
14    const [productCategories, setProductCategories] = useState('');
15
16    const createProduct = async function (): Promise<boolean> {
17      try {
18        // These values come from state variables
19        // Convert data values to corresponding data types
20        const productNameValue: string = productName;
21        const productQuantityValue: number = Number(productQuantity);
22        const productPriceValue: number = Number(productPrice);
23        const productAvailableValue: boolean = productAvailable;
24        const productExpirationDateValue: Date = new Date(productExpirationDate);
25        const productCategoriesValue: string[] = productCategories.split(',');
26
27        // Creates a new Product parse object instance
28        let Product: Parse.Object = new Parse.Object('Product');
29
30        // Set data to parse object
31        Product.set('name', productNameValue);
32        Product.set('quantity', productQuantityValue);
33        Product.set('price', productPriceValue);
34        Product.set('available', productAvailableValue);
35        Product.set('expirationDate', productExpirationDateValue);
36        Product.set('categories', productCategoriesValue);
37        Product.set('completeData', {
38          name: productNameValue,
39          quantity: productQuantityValue,
40          price: productPriceValue,
41          available: productAvailableValue,
42          expirationDate: productExpirationDateValue,
43          categories: productCategoriesValue,
44        });
45
46        // After setting the values, save it on the server
47        try {
48          let savedProduct: Parse.Object = await Product.save();
49          // Success
50          alert(`Success! ${JSON.stringify(savedProduct)}`);
51          return true;
52        } catch (error) {
53          // Error can be caused by lack of Internet connection
54          alert(`Error! ${error.message}`);
55          return false;
56        }
57      } catch (error: any) {
58        // Error can be caused by wrong type of values in fields
59        alert(`Error! ${error.message}`);
60        return false;
61      }
62    };
63
64    return (
65      <div>
66        <div className="header">
67          <img
68            className="header_logo"
69            alt="Back4App Logo"
70            src={
71              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
72            }
73          />
74          <p className="header_text_bold">{'React on Back4App'}</p>
75          <p className="header_text">{'Product Creation'}</p>
76        </div>
77        <div className="container">
78          {/* Product field inputs */}
79          <div className="flex_between">
80            <h2 className="list_heading">Available?</h2>
81            <Checkbox
82              onChange={(e) => setProductAvailable(e.target.checked)}
83            ></Checkbox>
84          </div>
85          <div className="form_wrapper">
86            <Input
87              className="form_input"
88              value={productName}
89              onChange={(event) => setProductName(event.target.value)}
90              placeholder="Name"
91              size="large"
92            />
93            <Input
94              className="form_input"
95              value={productQuantity}
96              onChange={(event) => setProductQuantity(event.target.value)}
97              placeholder="Quantity"
98              size="large"
99            />
100           <Input
101             className="form_input"
102             value={productPrice}
103             onChange={(event) => setProductPrice(event.target.value)}
104             placeholder="Price"
105             size="large"
106           />
107           <Input
108             className="form_input"
109             value={productExpirationDate}
110             onChange={(event) => setProductExpirationDate(event.target.value)}
111             placeholder="Expiration Date (mm/dd/yyyy)"
112             size="large"
113           />
114           <Input
115             className="form_input"
116             value={productCategories}
117             onChange={(event) => setProductCategories(event.target.value)}
118             placeholder="Categories (separated by comma)"
119             size="large"
120           />
121           {/* Add product button */}
122           <Button
123             type="primary"
124             className="form_button"
125             color={'#208AEC'}
126             size={'large'}
127             onClick={createProduct}
128             icon={<PlusOutlined />}
129           >
130             CREATE PRODUCT
131           </Button>
132         </div>
133       </div>
134     </div>
135   );
136 };
```
:::

```css
1   html {
2     box-sizing: border-box;
3     outline: none;
4     overflow: auto;
5   }
6
7   *,
8   *:before,
9   *:after {
10    margin: 0;
11    padding: 0;
12    box-sizing: inherit;
13  }
14
15  h1,
16  h2,
17  h3,
18  h4,
19  h5,
20  h6 {
21    margin: 0;
22  }
23
24  p {
25    margin: 0;
26  }
27
28  body {
29    margin: 0;
30    background-color: #fff;
31  }
32
33  .container {
34    width: 100%;
35    max-width: 600px;
36    margin: auto;
37    padding: 20px 0;
38  }
39
40  .header {
41    align-items: center;
42    padding: 25px 0;
43    background-color: #208AEC;
44  }
45
46  .header_logo {
47    height: 55px;
48    margin-bottom: 20px;
49    object-fit: contain;
50  }
51
52  .header_text_bold {
53    margin-bottom: 3px;
54    color: rgba(255, 255, 255, 0.9);
55    font-size: 16px;
56    font-weight: bold;
57  }
58
59  .header_text {
60    color: rgba(255, 255, 255, 0.9);
61    font-size: 15px;
62  }
63
64  .flex_between {
65    display: flex;
66    align-items: center;
67    justify-content: space-between;
68  }
69
70  .list_heading {
71    font-weight: bold;
72  }
73
74  .form_wrapper {
75    margin-top: 20px;
76    margin-bottom: 10px;
77  }
78
79  .form_input {
80    margin-bottom: 20px;
81  }
82
83  .form_button {
84    width: 100%;
85  }
```

After setting up this screen, your application should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/k_tQsJUhWUD_0s458TFty_image.png)

Note that each Product attribute has its text input field, except for the boolean checkbox input, meaning that the data in them needs conversion to the corresponding data type before saving.

## 2 - Converting Input Data

Before saving your data to the Parse.object, you need to correctly format the number, DateTime, and array inputs. Let’s now create a saving function, which will retrieve data from your state variables and apply the suitable data conversion:

:::CodeblockTabs
ProductCreation.js

```javascript
1   const createProduct = async function () {
2     try {
3       // These values come from state variables
4       // Convert data values to corresponding data types
5       const productNameValue = productName;
6       const productQuantityValue = Number(productQuantity);
7       const productPriceValue = Number(productPrice);
8       const productAvailableValue = productAvailable;
9       const productExpirationDateValue = new Date(productExpirationDate);
10      const productCategoriesValue = productCategories.split(',');
11    } catch (error) {
12      // Error can be caused by wrong type of values in fields
13      alert(`Error! ${error.message}`);
14      return false;
15    }
16  };
```

ProductCreation.tsx

```typescript
1   const createProduct = async function (): Promise<boolean> {
2     try {
3       // These values come from state variables
4       // Convert data values to corresponding data types
5       const productNameValue: string = productName;
6       const productQuantityValue: number = Number(productQuantity);
7       const productPriceValue: number = Number(productPrice);
8       const productAvailableValue: boolean = productAvailable;
9       const productExpirationDateValue: Date = new Date(productExpirationDate);
10      const productCategoriesValue: string[] = productCategories.split(',');
11    } catch (error: any) {
12      // Error can be caused by wrong type of values in fields
13      alert(`Error! ${error.message}`);
14      return false;
15    }
16  };
```
:::

The number data conversion is done casting the value as a Number JavaScript object. DateTime is converted using the Date JavaScript object constructor; the array one is created by using the String.split method in JavaScript, creating an array containing each entry of the categories field separated by commas.

Note that your data is now contained inside a single object, which can be set in a new Parse.object instance to be saved to the server using the Parse.Object.set() method, which takes two arguments: the field name and the value to be set. Let’s also set a new field called completeData, which will be your object type field, assigning the same data object to it.

Go ahead and complete the createProduct function with the following:

:::CodeblockTabs
ProductCreation.js

```javascript
1   const createProduct = async function () {
2     try {
3       // These values come from state variables
4       // Convert data values to corresponding data types
5       const productNameValue = productName;
6       const productQuantityValue = Number(productQuantity);
7       const productPriceValue = Number(productPrice);
8       const productAvailableValue = productAvailable;
9       const productExpirationDateValue = new Date(productExpirationDate);
10      const productCategoriesValue = productCategories.split(',');
11
12      // Creates a new Product parse object instance
13      let Product = new Parse.Object('Product');
14
15      // Set data to parse object
16      Product.set('name', productNameValue);
17      Product.set('quantity', productQuantityValue);
18      Product.set('price', productPriceValue);
19      Product.set('available', productAvailableValue);
20      Product.set('expirationDate', productExpirationDateValue);
21      Product.set('categories', productCategoriesValue);
22      Product.set('completeData', {
23        name: productNameValue,
24        quantity: productQuantityValue,
25        price: productPriceValue,
26        available: productAvailableValue,
27        expirationDate: productExpirationDateValue,
28        categories: productCategoriesValue,
29      });
30
31      // After setting the values, save it on the server
32      try {
33        let savedProduct = await Product.save();
34        // Success
35        alert(`Success! ${JSON.stringify(savedProduct)}`);
36        return true;
37      } catch (error) {
38        // Error can be caused by lack of Internet connection
39        alert(`Error! ${error.message}`);
40        return false;
41      }
42    } catch (error) {
43      // Error can be caused by wrong type of values in fields
44      alert(`Error! ${error.message}`);
45      return false;
46    }
47  };
```

ProductCreation.tsx

```typescript
1   const createProduct = async function (): Promise<boolean> {
2     try {
3       // These values come from state variables
4       // Convert data values to corresponding data types
5       const productNameValue: string = productName;
6       const productQuantityValue: number = Number(productQuantity);
7       const productPriceValue: number = Number(productPrice);
8       const productAvailableValue: boolean = productAvailable;
9       const productExpirationDateValue: Date = new Date(productExpirationDate);
10      const productCategoriesValue: string[] = productCategories.split(',');
11
12      // Creates a new Product parse object instance
13      let Product: Parse.Object = new Parse.Object('Product');
14    
15      // Set data to parse object
16      Product.set('name', productNameValue);
17      Product.set('quantity', productQuantityValue);
18      Product.set('price', productPriceValue);
19      Product.set('available', productAvailableValue);
20      Product.set('expirationDate', productExpirationDateValue);
21      Product.set('categories', productCategoriesValue);
22      Product.set('completeData', {
23        name: productNameValue,
24        quantity: productQuantityValue,
25        price: productPriceValue,
26        available: productAvailableValue,
27        expirationDate: productExpirationDateValue,
28        categories: productCategoriesValue,
29      });
30
31      // After setting the values, save it on the server
32      try {
33        let savedProduct: Parse.Object = await Product.save();
34        // Success
35        alert(`Success! ${JSON.stringify(savedProduct)}`);
36        return true;
37      } catch (error: any) {
38        // Error can be caused by lack of Internet connection
39        alert(`Error! ${error.message}`);
40        return false;
41      };
42    } catch (error: any) {
43      // Error can be caused by wrong type of values in fields
44      alert(`Error! ${error.message}`);
45      return false;
46    }
47  };
```
:::

You can now test the component, inserting the createProduct function in it, and calling it inside your form submit button onClick property. After creating a product, you should see an alert containing its data like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/j2KECnAWhxB-N37QGU9Tv_image.png)

To certify that your data was saved on the server using the correct data types, you can look at your Parse dashboard. Click on the Product data table and note that every column has its data type written at the header. Your class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Mf_H4HBZ1ANY7LKMT0hsT_image.png)

## Conclusion

At the end of this guide, you learned how to save each of the basic data types available on Parse using a React component. In the next guide, you will learn about the relational data on Parse.

[title] Geoqueries
[path] ReactJS/Data objects/

# Using React geolocation to perform geoqueries with Parse

## Introduction

In this guide, you will perform geoqueries in Parse using the React geolocation. You will implement a React component using these queries and learn how to set up and query realistic data using Back4App and React.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

- A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
-
  If you want to run this guide’s example project, you should set up the <a href="https://ant.design/" target="_blank">Ant Design library</a>.
:::

## Goal

Perform Geoqueries using GeoPoints stored on Back4App and React geolocation.

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

```javascript
1	// This will create your query
2	let parseQuery = new Parse.Query("Profile");
3	// The query will resolve only after calling this method
4	let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).&#x20;

## 2 - Save some data on Back4App

Let’s create a City class, which will be the target of our queries in this guide. On Parse JS Console it 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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AtMm91wuyTyzgJJHSeznp_image.png)

Go ahead and create the Cityc class with the following example content:

```javascript
1	// Add City objects and create table
2	// Note how GeoPoints are created, passing latitude and longitude as arguments
3	// Montevideo
4	var City = new Parse.Object('City');
5	City.set('name', 'Montevideo - Uruguay');
6	City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
7	await City.save();
8	
9	// Brasília
10	City = new Parse.Object('City');
11	City.set('name', 'Brasília - Brazil');
12	City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
13	await City.save();
14	
15	// Bogotá
16	City = new Parse.Object('City');
17	City.set('name', 'Bogotá - Colombia');
18	City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
19	await City.save();
20	
21	// Mexico City
22	City = new Parse.Object('City');
23	City.set('name', 'Mexico City - Mexico');
24	City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
25	await City.save();
26	
27	// Washington, D.C.
28	City = new Parse.Object('City');
29	City.set('name', 'Washington, D.C. - USA');
30	City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
31	await City.save();
32	
33	// Ottawa
34	City = new Parse.Object('City');
35	City.set('name', 'Ottawa - Canada');
36	City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
37	await City.save();
38	
39	console.log('Success!');
```

## 3 - Query the data

Now that you have a populated class, we can now perform some GeoPoint queries in it. Let’s begin by ordering City results by the nearest from Kingston in Jamaica (latitude 18.01808695059913 and longitude -76.79894232253473), using the Parse.Query.near method:

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('City');
3	
4	// Create our GeoPoint for the query
5	let kingstonGeoPoint = new Parse.GeoPoint(18.018086950599134, -76.79894232253473);
6	
7	// `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
8	parseQuery.near('location', kingstonGeoPoint);
9	
10	// The query will resolve only after calling this method, retrieving
11	// an array of `Parse.Objects`
12	let queryResults = await parseQuery.find();
13	
14	// Let's show the results
15	for (let result of queryResults) {
16	  // You access `Parse.Objects` attributes by using `.get`
17	  console.log(result.get('name'));
18	};
```

Let’s now query using the method Parse.Query.withinKilometers, which will retrieve all results whose GeoPoint field is located within the max distance. Kingston will be used once again as a reference and the distance limit will be 3000 km.

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('City');
3	
4	// Create our GeoPoint for the query
5	let kingstonGeoPoint = new Parse.GeoPoint(18.018086950599134, -76.79894232253473);
6	
7	// You can also use `withinMiles` and `withinRadians` the same way,
8	// but with different measuring unities
9	parseQuery.withinKilometers('location', kingstonGeoPoint, 3000);
10	
11	// The query will resolve only after calling this method, retrieving
12	// an array of `Parse.Objects`
13	let queryResults = await parseQuery.find();
14	
15	// Let's show the results
16	for (let result of queryResults) {
17	  // You access `Parse.Objects` attributes by using `.get`
18	  console.log(result.get('name'));
19	};
```

Another useful query method is Parse.Query.withinPolygon, which will query results whose GeoPoint field value is within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.

For this example, you will be using a simple polygon that roughly contains the South American continent, composed of 5 distant GeoPoints in the ocean.

```javascript
1	// Create your query
2	let parseQuery = new Parse.Query('City');
3	
4	// Create our GeoPoint polygon for the query
5	let geoPoint1 = new Parse.GeoPoint(15.822238344514378, -72.42845934415942);
6	let geoPoint2 = new Parse.GeoPoint(-0.7433770196268968, -97.44765968406668);
7	let geoPoint3 = new Parse.GeoPoint(-59.997149373299166, -76.52969196322749);
8	let geoPoint4 = new Parse.GeoPoint(-9.488786415007201, -18.346101586021952);
9	let geoPoint5 = new Parse.GeoPoint(15.414859532811047, -60.00625459569375);
10	
11	// Note that the polygon is merely an array of GeoPoint objects and that the first and last are not connected, so Parse connects them for you
12	parseQuery.withinPolygon('location', [geoPoint1, geoPoint2, geoPoint3, geoPoint4, geoPoint5]);
13	
14	// The query will resolve only after calling this method, retrieving
15	// an array of `Parse.Objects`
16	let queryResults = await parseQuery.find();
17	
18	// Let's show the results
19	for (let result of queryResults) {
20	  // You access `Parse.Objects` attributes by using `.get`
21	  console.log(result.get('name'));
22	};
```

## 4 - Query from a React component

Let’s now use our example queries inside a component in React, with a simple interface having a list showing results and also 3 buttons for calling the queries. The component also retrieves the device’s current location using the navigator.geolocation JavaScript API, so the queries will be using real data, provided that the user didn’t disabled location access in the browser.

This is how the component code is laid out, note the doQueryfunctions, containing the example code form before.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider } from 'antd';
5	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
6	
7	export const QueryGeo = () => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState();
10	
11	  const doQueryNear = async function () {
12	    // Check if geolocation API is available in the browser
13	    if ('geolocation' in navigator) {
14	      // Get current location and create the GeoPoint for the query
15	      navigator.geolocation.getCurrentPosition(
16	        async function (position) {
17	          const currentPosition = {
18	            latitude: position.coords.latitude,
19	            longitude: position.coords.longitude,
20	          };
21	          // Create our GeoPoint
22	          let currentLocationGeoPoint = new Parse.GeoPoint(
23	            currentPosition.latitude,
24	            currentPosition.longitude
25	          );
26	
27	          // Create our query
28	          let parseQuery = new Parse.Query('City');
29	
30	          // `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
31	          parseQuery.near('location', currentLocationGeoPoint);
32	
33	          try {
34	            let results = await parseQuery.find();
35	            // Set query results to state variable
36	            setQueryResults(results);
37	            return true;
38	          } catch (error) {
39	            // Error can be caused by lack of Internet connection
40	            alert(`Error! ${error.message}`);
41	          }
42	        },
43	        function (_error) {
44	          alert(
45	            'You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.'
46	          );
47	        }
48	      );
49	    } else {
50	      alert('Geolocation services are not supported by your browser.');
51	    }
52	    return false;
53	  };
54	
55	  const doQueryWithinKilometers = async function () {
56	    // Check if geolocation API is available in the browser
57	    if ('geolocation' in navigator) {
58	      // Get current location and create the GeoPoint for the query
59	      navigator.geolocation.getCurrentPosition(
60	        async function (position) {
61	          const currentPosition = {
62	            latitude: position.coords.latitude,
63	            longitude: position.coords.longitude,
64	          };
65	          // Create our GeoPoint
66	          let currentLocationGeoPoint = new Parse.GeoPoint(
67	            currentPosition.latitude,
68	            currentPosition.longitude
69	          );
70	
71	          // Create our query
72	          let parseQuery = new Parse.Query('City');
73	
74	          // You can also use `withinMiles` and `withinRadians` the same way,
75	          // but with different measuring unities
76	          parseQuery.withinKilometers(
77	            'location',
78	            currentLocationGeoPoint,
79	3000
80	          );
81	
82	          try {
83	            let results = await parseQuery.find();
84	            // Set query results to state variable
85	            setQueryResults(results);
86	          } catch (error) {
87	            // Error can be caused by lack of Internet connection
88	            alert(`Error! ${error.message}`);
89	          }
90	        },
91	        function (_error) {
92	          alert(
93	            'You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.'
94	          );
95	        }
96	      );
97	    } else {
98	      alert('Geolocation services are not supported by your browser.');
99	    }
100	    return false;
101	  };
102	
103	  const doQueryWithinPolygon = async function () {
104	    // Create our GeoPoint polygon points
105	    let geoPoint1 = new Parse.GeoPoint(15.822238344514378, -72.42845934415942);
106	    let geoPoint2 = new Parse.GeoPoint(-0.7433770196268968, -97.44765968406668);
107	    let geoPoint3 = new Parse.GeoPoint(-59.997149373299166, -76.52969196322749);
108	    let geoPoint4 = new Parse.GeoPoint(-9.488786415007201, -18.346101586021952);
109	    let geoPoint5 = new Parse.GeoPoint(15.414859532811047, -60.00625459569375);
110	
111	    // Create our query
112	    let parseQuery = new Parse.Query('City');
113	
114	    // Note that the polygon is merely an array of GeoPoint objects and that the first and
115	    // last are not connected, so Parse connects them for you
116	    parseQuery.withinPolygon('location', [
117	      geoPoint1,
118	      geoPoint2,
119	      geoPoint3,
120	      geoPoint4,
121	      geoPoint5,
122	    ]);
123	
124	    try {
125	      let results = await parseQuery.find();
126	      // Set query results to state variable
127	      setQueryResults(results);
128	    } catch (error) {
129	      // Error can be caused by lack of Internet connection
130	      alert(`Error! ${error}`);
131	      return false;
132	    }
133	    return true;
134	  };
135	
136	  const clearQueryResults = async function () {
137	    setQueryResults(undefined);
138	    return true;
139	  };
140	
141	  return (
142	    <div>
143	      <div className="header">
144	        <img
145	          className="header_logo"
146	          alt="Back4App Logo"
147	          src={
148	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
149	          }
150	        />
151	        <p className="header_text_bold">{'React on Back4App'}</p>
152	        <p className="header_text">{'GeoPoint Queries'}</p>
153	      </div>
154	      <div className="container">
155	        <div className="flex_between">
156	          <h2 className="heading">{'Query List'}</h2>
157	          <div className="flex">
158	            <Button
159	              onClick={() => doQueryNear()}
160	              type="primary"
161	              className="heading_button"
162	              color={'#208AEC'}
163	              icon={<SearchOutlined />}
164	            >
165	              QUERY NEAR
166	            </Button>
167	            <Button
168	              onClick={() => doQueryWithinKilometers()}
169	              type="primary"
170	              className="heading_button"
171	              color={'#208AEC'}
172	              icon={<SearchOutlined />}
173	            >
174	              QUERY WITHIN KM
175	            </Button>
176	            <Button
177	              onClick={() => doQueryWithinPolygon()}
178	              type="primary"
179	              className="heading_button"
180	              color={'#208AEC'}
181	              icon={<SearchOutlined />}
182	            >
183	              QUERY WITHIN POLYGON
184	            </Button>
185	            <Button
186	              onClick={() => clearQueryResults()}
187	              type="primary"
188	              className="heading_button"
189	              color={'#208AEC'}
190	              icon={<CloseOutlined />}
191	            >
192	              CLEAR RESULTS
193	            </Button>
194	          </div>
195	        </div>
196	        <Divider />
197	        <div className="flex_between">
198	          <div className="flex_child">
199	            {/* Query list */}
200	            {queryResults !== undefined &&
201	              queryResults.map((result, index) => (
202	                <div className="list_item" key={`${index}`}>
203	                  <p className="list_item_title">{`${result.get('name')}`}</p>
204	                </div>
205	              ))}
206	            {queryResults !== undefined && queryResults.length <= 0 ? (
207	              <p>{'No results here!'}</p>
208	            ) : null}
209	          </div>
210	        </div>
211	      </div>
212	    </div>
213	  );
214	};
```

TypeScript

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider } from 'antd';
4	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
5	const Parse = require('parse/dist/parse.min.js');
6	
7	type LocationObject = {latitude: number, longitude: number}; 
8	
9	export const QueryGeo: FC<{}> = (): ReactElement => {
10	  // State variable
11	  const [queryResults, setQueryResults] = useState<Parse.Object[]>();
12	
13	  const doQueryNear = async function (): Promise<boolean> {
14	    // Check if geolocation API is available in the browser
15	    if ("geolocation" in navigator) {
16	      // Get current location and create the GeoPoint for the query
17	      navigator.geolocation.getCurrentPosition(async function (position) { 
18	        const currentPosition: LocationObject = { latitude: position.coords.latitude, longitude: position.coords.longitude };
19	        // Create our GeoPoint
20	        let currentLocationGeoPoint: Parse.GeoPoint = new Parse.GeoPoint(
21	          currentPosition.latitude,
22	          currentPosition.longitude,
23	        );
24	
25	        // Create our query
26	        let parseQuery: Parse.Query = new Parse.Query('City');
27	
28	        // `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
29	        parseQuery.near('location', currentLocationGeoPoint);
30	
31	        try {
32	          let results: Parse.Object[] = await parseQuery.find();
33	          // Set query results to state variable
34	          setQueryResults(results);
35	          return true;
36	        } catch (error) {
37	          // Error can be caused by lack of Internet connection
38	          alert(`Error! ${error.message}`);
39	        }
40	      }, function (_error: any) {
41	        alert("You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.");
42	      });
43	    } else {
44	      alert("Geolocation services are not supported by your browser.");
45	    }
46	    return false;
47	  };
48	
49	  const doQueryWithinKilometers = async function (): Promise<boolean> {
50	    // Check if geolocation API is available in the browser
51	    if ("geolocation" in navigator) {
52	      // Get current location and create the GeoPoint for the query
53	      navigator.geolocation.getCurrentPosition(async function (position) { 
54	        const currentPosition: LocationObject = { latitude: position.coords.latitude, longitude: position.coords.longitude };
55	      // Create our GeoPoint
56	      let currentLocationGeoPoint: Parse.GeoPoint = new Parse.GeoPoint(
57	        currentPosition.latitude,
58	        currentPosition.longitude,
59	      );
60	
61	      // Create our query
62	      let parseQuery: Parse.Query = new Parse.Query('City');
63	
64	      // You can also use `withinMiles` and `withinRadians` the same way,
65	      // but with different measuring unities
66	      parseQuery.withinKilometers('location', currentLocationGeoPoint, 3000);
67	
68	      try {
69	        let results: Parse.Object[] = await parseQuery.find();
70	        // Set query results to state variable
71	        setQueryResults(results);
72	      } catch (error) {
73	          // Error can be caused by lack of Internet connection
74	          alert(`Error! ${error.message}`);
75	        }
76	      }, function (_error: any) {
77	        alert("You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.");
78	      });
79	    } else {
80	      alert("Geolocation services are not supported by your browser.");
81	    }
82	    return false;
83	  };
84	
85	  const doQueryWithinPolygon = async function (): Promise<boolean> {
86	    // Create our GeoPoint polygon points
87	    // In React TypeScript types ('@types/parse'), Parse.Query.withinPolygon expects
88	    // an array of number arrays, so you need to change the declarations 
89	    let geoPoint1: number[] = [
90	-7.242.845.934.415.940,00
91	15.822.238.344.514.300,00
92	    ];
93	    let geoPoint2: number[] = [
94	-9.744.765.968.406.660,00
95	      -0.7433770196268968,
96	    ];
97	    let geoPoint3: number[] = [
98	-7.652.969.196.322.740,00
99	-59.997.149.373.299.100,00
100	    ];
101	    let geoPoint4: number[] = [
102	-18.346.101.586.021.900,00
103	-9.488.786.415.007.200,00
104	    ];
105	    let geoPoint5: number[] = [
106	-6.000.625.459.569.370,00
107	15.414.859.532.811.000,00
108	    ];
109	
110	    // Create our query
111	    let parseQuery: Parse.Query = new Parse.Query('City');
112	
113	    // Note that the polygon is merely an array of GeoPoint objects and that the first and
114	    // last are not connected, so Parse connects them for you
115	    parseQuery.withinPolygon('location', [
116	      geoPoint1,
117	      geoPoint2,
118	      geoPoint3,
119	      geoPoint4,
120	      geoPoint5,
121	    ]);
122	
123	    try {
124	      let results: Parse.Object[] = await parseQuery.find();
125	      // Set query results to state variable
126	      setQueryResults(results);
127	    } catch (error) {
128	      // Error can be caused by lack of Internet connection
129	      alert(`Error! ${error}`);
130	      return false;
131	    }
132	    return true;
133	  };
134	
135	  const clearQueryResults = async function (): Promise<boolean> {
136	    setQueryResults(undefined);
137	    return true;
138	  };
139	
140	  return (
141	    <div>
142	      <div className="header">
143	        <img
144	          className="header_logo"
145	          alt="Back4App Logo"
146	          src={
147	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
148	          }
149	        />
150	        <p className="header_text_bold">{'React on Back4App'}</p>
151	        <p className="header_text">{'GeoPoint Queries'}</p>
152	      </div>
153	      <div className="container">
154	        <div className="flex_between">
155	          <h2 className="heading">{'Query List'}</h2>
156	          <div className="flex">
157	            <Button
158	              onClick={() => doQueryNear()}
159	              type="primary"
160	              className="heading_button"
161	              color={'#208AEC'}
162	              icon={<SearchOutlined />}
163	            >
164	              QUERY NEAR
165	            </Button>
166	            <Button
167	              onClick={() => doQueryWithinKilometers()}
168	              type="primary"
169	              className="heading_button"
170	              color={'#208AEC'}
171	              icon={<SearchOutlined />}
172	            >
173	              QUERY WITHIN KM
174	            </Button>
175	            <Button
176	              onClick={() => doQueryWithinPolygon()}
177	              type="primary"
178	              className="heading_button"
179	              color={'#208AEC'}
180	              icon={<SearchOutlined />}
181	            >
182	              QUERY WITHIN POLYGON
183	            </Button>
184	            <Button
185	              onClick={() => clearQueryResults()}
186	              type="primary"
187	              className="heading_button"
188	              color={'#208AEC'}
189	              icon={<CloseOutlined />}
190	            >
191	              CLEAR RESULTS
192	            </Button>
193	          </div>
194	        </div>
195	        <Divider />
196	        <div className="flex_between">
197	          <div className="flex_child">
198	            {/* Query list */}
199	            {queryResults !== undefined &&
200	              queryResults.map((result: Parse.Object, index: number) => (
201	                <div className="list_item" key={`${index}`}>
202	                  <p className="list_item_title">{`${result.get('name')}`}</p>
203	                </div>
204	              ))}
205	            {queryResults !== undefined &&
206	            queryResults.length <= 0 ? (
207	              <p>{'No results here!'}</p>
208	            ) : null}
209	          </div>
210	        </div>
211	      </div>
212	    </div>
213	  );
214	};
```
:::

Also add these classes to you App.css file to fully render the component layout:

:::CodeblockTabs
App.css

```css
1	html {
2	  box-sizing: border-box;
3	  outline: none;
4	  overflow: auto;
5	}
6	
7	*,
8	*:before,
9	*:after {
10	  margin: 0;
11	  padding: 0;
12	  box-sizing: inherit;
13	}
14	
15	h1,
16	h2,
17	h3,
18	h4,
19	h5,
20	h6 {
21	  margin: 0;
22	  font-weight: bold;
23	}
24	
25	p {
26	  margin: 0;
27	}
28	
29	body {
30	  margin: 0;
31	  background-color: #fff;
32	}
33	
34	.container {
35	  width: 100%;
36	  max-width: 900px;
37	  margin: auto;
38	  padding: 20px 0;
39	  text-align: left;
40	}
41	
42	.header {
43	  align-items: center;
44	  padding: 25px 0;
45	  background-color: #208AEC;
46	}
47	
48	.header_logo {
49	  height: 55px;
50	  margin-bottom: 20px;
51	  object-fit: contain;
52	}
53	
54	.header_text_bold {
55	  margin-bottom: 3px;
56	  color: rgba(255, 255, 255, 0.9);
57	  font-size: 16px;
58	  font-weight: bold;
59	}
60	
61	.header_text {
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 15px;
64	}
65	
66	.heading {
67	  font-size: 22px;
68	}
69	
70	.flex {
71	  display: flex;
72	}
73	
74	.flex_between {
75	  display: flex;
76	  justify-content: space-between;
77	}
78	
79	.flex_child {
80	  flex: 0 0 45%;
81	}
82	
83	.heading_button {
84	  margin-left: 12px;
85	}
86	
87	.list_item {
88	  padding-bottom: 15px;
89	  margin-bottom: 15px;
90	  border-bottom: 1px solid rgba(0,0,0,0.06);
91	  text-align: left;
92	}
93	
94	.list_item_title {
95	  color: rgba(0,0,0,0.87);
96	  font-size: 17px;
97	}
```
:::

This is how the component should look like after rendering and querying one of the query functions:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/q8DX6E88uqYqY4tCWzwPF_image.png)

## Conclusion

At the end of this guide, you learned how GeoPoint data queries work on Parse and how to perform them on Back4App from a React application. In the next guide, you will check how to create and manage users in Parse.

[title] SignIn with Google
[path] ReactJS/Users/

# React Google Login

## Introduction

In the last tutorial, you built a User login/logout feature to your App using the Parse.User class. Now you will learn how to use Google Sign-in to retrieve user data from Google and log in, sign up or link existent users with it. You will also install and configure react-google-login lib to achieve that.

The Parse.User.linkWith method is responsible for signing up and logging in users using any third-party authentication method, as long as you pass the right parameters requested by each different provider. After linking the user data to a new or existent Parse.User, Parse will store a valid user session on your device. Future calls to methods like current will successfully retrieve your User data, just like with regular logins.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- Complete the previous guide so you can have a better understanding of [the Parse.User class and the Parse.User.logIn method](https://www.back4app.com/docs/react/working-with-users/react-user-login)
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library.</a>
:::

## Goal

To build a User LogIn feature using Google Sign-in on Parse for a React App.

## 1 - Installing dependencies

The most popular way to enable Google Sign-in on React is using react-google-login to handle it. Set it up following the <a href="https://github.com/anthonyjgrove/react-google-login" target="_blank">official docs</a>.

Make sure to create an OAuth clientId in your Google Credentials page, adding your local development address to the authorized URLs, which generally is http\://localhost:3000.

## 2 - Usign Google Sign-in with Parse

Let’s now create a new method inside the UserLogIn component that will handle the response when calling a Google Sign-in authentication modal. If the user signs in with Google, this call will retrieve the user data from Google and you need to store the id, idToken, and Google email. Then, the function will try to log in on Parse using the Parse.User.linkWith method and these credentials. Note that if your user had already signed up using this Google authentication, linkWith will log him in using the existent account.

:::CodeblockTabs
JavaScript

```javascript
1	const handleGoogleLoginLoginResponse = async function(response) {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Google user info
9	      const userGoogleId = response.googleId;
10	      const userTokenId = response.tokenId;
11	      const userEmail = response.profileObj.email;
12	      // Try to login on Parse using linkWith and these credentials
13	      // Create a new Parse.User object
14	      const userToLogin = new Parse.User();
15	      // Set username and email to match google email
16	      userToLogin.set('username', userEmail);
17	      userToLogin.set('email', userEmail);
18	      try {
19	        let loggedInUser = await userToLogin
20	        .linkWith('google', {
21	          authData: {id: userGoogleId, id_token: userTokenId},
22	        });
23	        // logIn returns the corresponding ParseUser object
24	        alert(
25	          `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
26	        );
27	        // Update state variable holding current user
28	        getCurrentUser();
29	        return true;
30	      } catch (error) {
31	        // Error can be caused by wrong parameters or lack of Internet connection
32	        alert(`Error! ${error.message}`);
33	        return false;
34	      }
35	    } catch (error) {
36	      console.log("Error gathering Google user info, please try again!")
37	      return false;
38	    }
39	  }
40	}
```

```typescript
1	const handleGoogleLoginLoginResponse = async function(response: any): Promise<boolean> {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Google user info
9	      const userGoogleId: string = response.googleId;
10	      const userTokenId: string = response.tokenId;
11	      const userEmail: string = response.profileObj.email;
12	      // Try to login on Parse using linkWith and these credentials
13	      // Create a new Parse.User object
14	      const userToLogin: Parse.User = new Parse.User();
15	      // Set username and email to match google email
16	      userToLogin.set('username', userEmail);
17	      userToLogin.set('email', userEmail);
18	      try {
19	        let loggedInUser: Parse.User = await userToLogin
20	        .linkWith('google', {
21	          authData: {id: userGoogleId, id_token: userTokenId},
22	        });
23	        // logIn returns the corresponding ParseUser object
24	        alert(
25	          `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
26	        );
27	        // Update state variable holding current user
28	        getCurrentUser();
29	        return true;
30	      } catch (error: any) {
31	        // Error can be caused by wrong parameters or lack of Internet connection
32	        alert(`Error! ${error.message}`);
33	        return false;
34	      }
35	    } catch (error: any) {
36	      console.log("Error gathering Google user info, please try again!")
37	      return false;
38	    }
39	  }
40	}
```
:::

After that, you need to use the react-google-login GoogleLogin component to call the Google SignIn modal, adding it to your JSX code. You can use Google’s default styling or create a custom one, which is the way followed by this guide. Here is the full UserLogin component code, note the react-google-login button and how it is tied to the modal response method created before.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import GoogleLogin from 'react-google-login';
4	import './App.css';
5	import { Button, Divider, Input } from 'antd';
6	
7	export const UserLogin = () => {
8	  // State variables
9	  const [username, setUsername] = useState('');
10	  const [password, setPassword] = useState('');
11	  const [currentUser, setCurrentUser] = useState(null);
12	
13	  const doUserLogIn = async function () {
14	    // Note that these values come from state variables that we've declared before
15	    const usernameValue = username;
16	    const passwordValue = password;
17	    try {
18	      const loggedInUser = await Parse.User.logIn(usernameValue, passwordValue);
19	      // logIn returns the corresponding ParseUser object
20	      alert(
21	        `Success! User ${loggedInUser.get(
22	          'username'
23	        )} has successfully signed in!`
24	      );
25	      // To verify that this is in fact the current user, `current` can be used
26	      const currentUser = await Parse.User.current();
27	      console.log(loggedInUser === currentUser);
28	      // Clear input fields
29	      setUsername('');
30	      setPassword('');
31	      // Update state variable holding current user
32	      getCurrentUser();
33	      return true;
34	    } catch (error) {
35	      // Error can be caused by wrong parameters or lack of Internet connection
36	      alert(`Error! ${error.message}`);
37	      return false;
38	    }
39	  };
40	
41	  const doUserLogOut = async function () {
42	    try {
43	      await Parse.User.logOut();
44	      // To verify that current user is now empty, currentAsync can be used
45	      const currentUser = await Parse.User.current();
46	      if (currentUser === null) {
47	        alert('Success! No user is logged in anymore!');
48	      }
49	      // Update state variable holding current user
50	      getCurrentUser();
51	      return true;
52	    } catch (error) {
53	      alert(`Error! ${error.message}`);
54	      return false;
55	    }
56	  };
57	
58	  // Function that will return current user and also update current username
59	  const getCurrentUser = async function () {
60	    const currentUser = await Parse.User.current();
61	    // Update state variable holding current user
62	    setCurrentUser(currentUser);
63	    return currentUser;
64	  };
65	
66	  const handleGoogleLoginLoginResponse = async function (response) {
67	    // Check if response has an error
68	    if (response.error !== undefined) {
69	      console.log(`Error: ${response.error}`);
70	      return false;
71	    } else {
72	      try {
73	        // Gather Google user info
74	        const userGoogleId = response.googleId;
75	        const userTokenId = response.tokenId;
76	        const userEmail = response.profileObj.email;
77	        // Try to login on Parse using linkWith and these credentials
78	        // Create a new Parse.User object
79	        const userToLogin = new Parse.User();
80	        // Set username and email to match google email
81	        userToLogin.set('username', userEmail);
82	        userToLogin.set('email', userEmail);
83	        try {
84	          let loggedInUser = await userToLogin.linkWith('google', {
85	            authData: { id: userGoogleId, id_token: userTokenId },
86	          });
87	          // logIn returns the corresponding ParseUser object
88	          alert(
89	            `Success! User ${loggedInUser.get(
90	              'username'
91	            )} has successfully signed in!`
92	          );
93	          // Update state variable holding current user
94	          getCurrentUser();
95	          return true;
96	        } catch (error) {
97	          // Error can be caused by wrong parameters or lack of Internet connection
98	          alert(`Error! ${error.message}`);
99	          return false;
100	        }
101	      } catch (error) {
102	        console.log('Error gathering Google user info, please try again!');
103	        return false;
104	      }
105	    }
106	  };
107	
108	  return (
109	    <div>
110	      <div className="header">
111	        <img
112	          className="header_logo"
113	          alt="Back4App Logo"
114	          src={
115	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
116	          }
117	        />
118	        <p className="header_text_bold">{'React on Back4App'}</p>
119	        <p className="header_text">{'User Login'}</p>
120	      </div>
121	      {currentUser === null && (
122	        <div className="container">
123	          <h2 className="heading">{'User Login'}</h2>
124	          <Divider />
125	          <div className="form_wrapper">
126	            <Input
127	              value={username}
128	              onChange={(event) => setUsername(event.target.value)}
129	              placeholder="Username"
130	              size="large"
131	              className="form_input"
132	            />
133	            <Input
134	              value={password}
135	              onChange={(event) => setPassword(event.target.value)}
136	              placeholder="Password"
137	              size="large"
138	              type="password"
139	              className="form_input"
140	            />
141	          </div>
142	          <div className="form_buttons">
143	            <Button
144	              onClick={() => doUserLogIn()}
145	              type="primary"
146	              className="form_button"
147	              color={'#208AEC'}
148	              size="large"
149	              block
150	            >
151	              Log In
152	            </Button>
153	          </div>
154	          <Divider />
155	          <div className="login-social">
156	            <div className="login-social-item login-social-item--facebook">
157	              <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
158	            </div>
159	            <GoogleLogin
160	              clientId="108490793456-0flm4qh8ek4cb4krt7e06980o4sjvado.apps.googleusercontent.com"
161	              render={renderProps => (
162	                  <div className="login-social-item">
163	                    <img onClick={renderProps.onClick} className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
164	                  </div>
165	              )}
166	              buttonText="Login"
167	              onSuccess={handleGoogleLoginLoginResponse}
168	              onFailure={handleGoogleLoginLoginResponse}
169	              cookiePolicy={'single_host_origin'}
170	            />
171	            <div className="login-social-item">
172	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
173	            </div>
174	          </div>
175	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
176	        </div>
177	      )}
178	      {currentUser !== null && (
179	        <div className="container">
180	          <h2 className="heading">{'User Screen'}</h2>
181	          <Divider />
182	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
183	          <div className="form_buttons">
184	            <Button
185	              onClick={() => doUserLogOut()}
186	              type="primary"
187	              className="form_button"
188	              color={'#208AEC'}
189	              size="large"
190	              block
191	            >
192	              Log Out
193	            </Button>
194	          </div>
195	        </div>
196	      )}
197	    </div>
198	  );
199	};
```

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	import GoogleLogin from 'react-google-login';
5	const Parse = require('parse/dist/parse.min.js');
6	
7	export const UserLogin: FC<{}> = (): ReactElement => {
8	  // State variables
9	  const [username, setUsername] = useState('');
10	  const [password, setPassword] = useState('');
11	  const [currentUser, setCurrentUser] = useState<Parse.Object | null>(null);
12	
13	  const doUserLogIn = async function (): Promise<boolean> {
14	    // Note that these values come from state variables that we've declared before
15	    const usernameValue: string = username;
16	    const passwordValue: string = password;
17	    try {
18	      const loggedInUser: Parse.User = await Parse.User.logIn(usernameValue, passwordValue);
19	      // logIn returns the corresponding ParseUser object
20	      alert(
21	        `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
22	      );
23	      // To verify that this is in fact the current user, `current` can be used
24	      const currentUser: Parse.User = await Parse.User.current();
25	      console.log(loggedInUser === currentUser);
26	      // Clear input fields
27	      setUsername('');
28	      setPassword('');
29	      // Update state variable holding current user
30	      getCurrentUser();
31	      return true;
32	    } catch (error: any) {
33	      // Error can be caused by wrong parameters or lack of Internet connection
34	      alert(`Error! ${error.message}`);
35	      return false;
36	    }
37	  };
38	
39	  const doUserLogOut = async function (): Promise<boolean> {
40	    try {
41	      await Parse.User.logOut();
42	      // To verify that current user is now empty, currentAsync can be used
43	      const currentUser: Parse.User = await Parse.User.current();
44	      if (currentUser === null) {
45	        alert('Success! No user is logged in anymore!');
46	      }
47	      // Update state variable holding current user
48	      getCurrentUser();
49	      return true;
50	    } catch (error: any) {
51	      alert(`Error! ${error.message}`);
52	      return false;
53	    }
54	  };
55	
56	  // Function that will return current user and also update current username
57	  const getCurrentUser = async function (): Promise<Parse.User | null> {
58	    const currentUser: (Parse.User | null) = await Parse.User.current();
59	    // Update state variable holding current user
60	    setCurrentUser(currentUser);
61	    return currentUser;
62	  }
63	
64	  const handleGoogleLoginLoginResponse = async function(response: any): Promise<boolean> {
65	    // Check if response has an error
66	    if (response.error !== undefined) {
67	      console.log(`Error: ${response.error}`);
68	      return false;
69	    } else {
70	      try {
71	        // Gather Google user info
72	        const userGoogleId: string = response.googleId;
73	        const userTokenId: string = response.tokenId;
74	        const userEmail: string = response.profileObj.email;
75	        // Try to login on Parse using linkWith and these credentials
76	        // Create a new Parse.User object
77	        const userToLogin: Parse.User = new Parse.User();
78	        // Set username and email to match google email
79	        userToLogin.set('username', userEmail);
80	        userToLogin.set('email', userEmail);
81	        try {
82	          let loggedInUser: Parse.User = await userToLogin
83	          .linkWith('google', {
84	            authData: {id: userGoogleId, id_token: userTokenId},
85	          });
86	          // logIn returns the corresponding ParseUser object
87	          alert(
88	            `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
89	          );
90	          // Update state variable holding current user
91	          getCurrentUser();
92	          return true;
93	        } catch (error: any) {
94	          // Error can be caused by wrong parameters or lack of Internet connection
95	          alert(`Error! ${error.message}`);
96	          return false;
97	        }
98	      } catch (error: any) {
99	        console.log("Error gathering Google user info, please try again!")
100	        return false;
101	      }
102	    }
103	  }
104	
105	  return (
106	    <div>
107	      <div className="header">
108	        <img
109	          className="header_logo"
110	          alt="Back4App Logo"
111	          src={
112	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
113	          }
114	        />
115	        <p className="header_text_bold">{'React on Back4App'}</p>
116	        <p className="header_text">{'User Login'}</p>
117	      </div>
118	      {currentUser === null && (
119	        <div className="container">
120	          <h2 className="heading">{'User Login'}</h2>
121	          <Divider />
122	          <div className="form_wrapper">
123	            <Input
124	              value={username}
125	              onChange={(event) => setUsername(event.target.value)}
126	              placeholder="Username"
127	              size="large"
128	              className="form_input"
129	            />
130	            <Input
131	              value={password}
132	              onChange={(event) => setPassword(event.target.value)}
133	              placeholder="Password"
134	              size="large"
135	              type="password"
136	              className="form_input"
137	            />
138	          </div>
139	          <div className="form_buttons">
140	            <Button
141	              onClick={() => doUserLogIn()}
142	              type="primary"
143	              className="form_button"
144	              color={'#208AEC'}
145	              size="large"
146	              block
147	            >
148	              Log In
149	            </Button>
150	          </div>
151	          <Divider />
152	          <div className="login-social">
153	            <div className="login-social-item login-social-item--facebook">
154	              <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
155	            </div>
156	            <GoogleLogin
157	              clientId="108490793456-0flm4qh8ek4cb4krt7e06980o4sjvado.apps.googleusercontent.com"
158	              render={renderProps => (
159	                  <div className="login-social-item">
160	                    <img onClick={renderProps.onClick} className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
161	                  </div>
162	              )}
163	              buttonText="Login"
164	              onSuccess={handleGoogleLoginLoginResponse}
165	              onFailure={handleGoogleLoginLoginResponse}
166	              cookiePolicy={'single_host_origin'}
167	            />
168	            <div className="login-social-item">
169	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
170	            </div>
171	          </div>
172	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
173	        </div>
174	      )}
175	      {currentUser !== null && (
176	        <div className="container">
177	          <h2 className="heading">{'User Screen'}</h2>
178	          <Divider />
179	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
180	          <div className="form_buttons">
181	            <Button
182	              onClick={() => doUserLogOut()}
183	              type="primary"
184	              className="form_button"
185	              color={'#208AEC'}
186	              size="large"
187	              block
188	            >
189	              Log Out
190	            </Button>
191	          </div>
192	        </div>
193	      )}
194	    </div>
195	  );
196	};
```
:::

Add these classes to your App.css file if you want to fully render this component’s layout.

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 900px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.flex {
77	  display: flex;
78	}
79	
80	.flex_between {
81	  display: flex;
82	  justify-content: space-between;
83	}
84	
85	.flex_child {
86	  flex: 0 0 45%;
87	}
88	
89	.heading_button {
90	  margin-left: 12px;
91	}
92	
93	.list_item {
94	  padding-bottom: 15px;
95	  margin-bottom: 15px;
96	  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
97	  text-align: left;
98	}
99	
100	.list_item_title {
101	  color: rgba(0, 0, 0, 0.87);
102	  font-size: 17px;
103	}
104	
105	.list_item_description {
106	  color: rgba(0, 0, 0, 0.5);
107	  font-size: 15px;
108	}
109	
110	.form_input {
111	  margin-bottom: 20px;
112	}
113	
114	.login-social {
115	  display: flex;
116	  justify-content: center;
117	  margin-bottom: 30px;
118	}
119	
120	.login-social-item {
121	  width: 54px;
122	  height: 54px;
123	  border-radius: 54px;
124	  padding: 12px;
125	  margin: 0 12px;
126	  border: 1px solid #e6e6e6;
127	  box-shadow: 0 2px 4px #d6d6d6;
128	}
129	
130	.login-social-item--facebook {
131	  padding: 4px;
132	  background-color: #3C5B9B;
133	}
134	
135	.login-social-item__image {
136	  width: 100%;
137	}
138	
139	.form__hint {
140	  color: rgba(0, 0, 0, 0.5);
141	  font-size: 16px;
142	  text-align: center;
143	}
```
:::

Go ahead and test your new function. If you were able to sign in to Google and the Parse linkWith call was successful, you should see a success message like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/U9_FkFhkpOZFrNVKLAxHP_image.png)

## 3 - Verifying user sign in and session creation

To make sure that the Google sign-in worked, you can look at your Parse dashboard and see your new User (if your Google authentication data didn’t belong to another user), containing the Google authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eiy2ar9vgp4LLNWphxp5c_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to that User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qqJZAEzdAuG5eezgbbnz__image.png)

## 4 - Linking an existing User to Google Sign-in

Another linkWith possible use is to link an existing user with another auth provider, in this case, Google. Add this function that calls linkWith the same way as logging in to your UserLogIn component. The only difference here is that instead of calling the method from an empty Parse.User, you will use it from the currently logged-in user object.

:::CodeblockTabs
JavaScript

```javascript
1	const handleGoogleLoginLinkResponse = async function(response) {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Google user info
9	      const userGoogleId = response.googleId;
10	      const userTokenId = response.tokenId;
11	      // Try to link current Parse user using linkWith and these credentials
12	      // Get current user
13	      const userToLink = await Parse.User.current();
14	      try {
15	        let loggedInUser = await userToLink
16	        .linkWith('google', {
17	          authData: {id: userGoogleId, id_token: userTokenId},
18	        });
19	        // logIn returns the corresponding ParseUser object
20	        alert(
21	          `Success! User ${loggedInUser.get(
22	            'username',
23	          )} has successfully linked his Google account!`,
24	        );
25	        // Update state variable holding current user
26	        getCurrentUser();
27	        return true;
28	      } catch (error) {
29	        // Error can be caused by wrong parameters or lack of Internet connection
30	        alert(`Error! ${error.message}`);
31	        return false;
32	      }
33	    } catch (error) {
34	      console.log("Error gathering Google user info, please try again!")
35	      return false;
36	    }
37	  }
38	}
```

```typescript
1	const handleGoogleLoginLinkResponse = async function(response: any): Promise<boolean> {
2	  // Check if response has an error
3	  if (response.error !== undefined) {
4	    console.log(`Error: ${response.error}`);
5	    return false;
6	  } else {
7	    try {
8	      // Gather Google user info
9	      const userGoogleId: string = response.googleId;
10	      const userTokenId: string = response.tokenId;
11	      // Try to link current Parse user using linkWith and these credentials
12	      // Get current user
13	      const userToLink: Parse.User = await Parse.User.current();
14	      try {
15	        let loggedInUser: Parse.User = await userToLink
16	        .linkWith('google', {
17	          authData: {id: userGoogleId, id_token: userTokenId},
18	        });
19	        // logIn returns the corresponding ParseUser object
20	        alert(
21	          `Success! User ${loggedInUser.get(
22	            'username',
23	          )} has successfully linked his Google account!`,
24	        );
25	        // Update state variable holding current user
26	        getCurrentUser();
27	        return true;
28	      } catch (error: any) {
29	        // Error can be caused by wrong parameters or lack of Internet connection
30	        alert(`Error! ${error.message}`);
31	        return false;
32	      }
33	    } catch (error: any) {
34	      console.log("Error gathering Google user info, please try again!")
35	      return false;
36	    }
37	  }
38	}
```
:::

Assign this function to another react-google-login component in your home screen, which is shown only when there is a current user logged in in your app. Test your new function, noting that the Parse.User object authData value will be updated with the new auth provider data. Verify if the user has indeed updated in your Parse server dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5yaYl2fSUKDlkWO-HiZV9_image.png)

## Conclusion

At the end of this guide, you learned how to log in, sign up or link existing Parse users on React using Google Sign-in with react-google-login. In the next guide, we will show you how to use Apple sign-in.

[title] Real Time
[path] ReactJS/


[title] useParseQuery
[path] ReactJS/Real Time/

# Getting started with the Parse React hook for real time updates using Parse

## Introduction

The easiest way to integrate Parse/Back4App into your JavaScript-based project is through the <a href="https://www.npmjs.com/package/parse" target="_blank">Parse Javascript SDK</a>. This library works on multiple JavaScript environments such as NodeJS, ReactJS, VueJS, AngularJS, React-Native, and gives you access to the Back4App features.

Parse React’s hook goal is to make this experience even better for ReactJS developers by delivering a light-weight and easy-to-use layer that provides minimal configuration and code re-usability.

Using this package will ensure that items like setting up credentials, HTTP requests, real-time synchronization, offline-first interaction are automatically available to your React App. The lib is written entirely in TypeScript, on top of <a href="https://www.npmjs.com/package/parse" target="_blank">Parse Javascript SDK</a>, and is currently on the Alpha version.

In this initial guide, you will install and set up the @parse/react library on your React project.

:::hint{type="danger"}
@parse/react is currently on the Alpha version. The lib is under testing, so we recommend to proceed with caution. Your feedback is very appreciated, so feel free to use the lib and send us your questions and first impressions by dropping an email to community\@back4app.com.
:::

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:


- An <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">app created</a> on Back4App;
- Follow the <a href="https://www.back4app.com/docs/platform/parse-live-query" target="_blank">Enable Live Query</a> tutorial.
- <a href="https://www.npmjs.com/get-npm?utm_source=house&utm_medium=homepage&utm_campaign=free%20orgs&utm_term=Install%20npm" target="_blank">Npm </a>or yarn installed;
- <a href="https://www.npmjs.com/package/npx" target="_blank">Npx </a>package runner installed.
:::

## 1 - Installation

Install @parse/react and its peer dependency parse in your React application:

> \# Using yarn
> yarn add @parse/react parse
>
> \# Using npm
> npm install --save @parse/react parse

## 2 - Application Setup

To allow the App to connect to Back4App servers securely, you must provide Parse JavaScript SDK with App’s credentials in your App.js (or App.tsx) file. Remember to use your Back4App subdomain created when you enabled you app to perform Live Queries.

:::CodeblockTabs
App.js or App.tsx

```javascript
1	import { initializeParse } from  '@parse/react';
2	
3	initializeParse(
4	  'YOUR_BACK4APP_SUBDOMAIN', // e.g. YOUR_APP_NAME.b4a.io
5	  'YOUR_APPLICATION_ID',
6	  'YOUR_JAVASCRIPT_KEY'
7	);
```
:::

Note that the initializeParse method replaces the usual Parse.initialize one. You can find your App Id and JavaScript Key credentials by opening your app Dashboard at [Back4App Website](https://www.back4app.com/) and clicking on Core Settings, under Server Settings.

## 3 - Creating your first Query

Next, you will build your first Query and display it in your App. The @parse/react library exports a useParseQuery hook, so you don’t need to waste time looking into how to implement features like offline support, real-time changes, and so on.

It takes a Parse.Query and returns an object with some props that you can use to access data returned by queries:

:::CodeblockTabs
App.js or App.tsx

```javascript
1	import React from 'react';
2	import Parse from 'parse';
3	import { initializeParse, useParseQuery } from  '@parse/react';
4	
5	initializeParse(
6	  'YOUR_BACK4APP_SUBDOMAIN', // e.g. YOUR_APP_NAME.b4a.io
7	  'YOUR_APPLICATION_ID',
8	  'YOUR_JAVASCRIPT_KEY'
9	);
10	
11	export default function App() {
12	  //make sure your class is enabled for Real Time Notifications (Live Query) checking the menu -> App Settings -> Server Settings -> Server URL and Live Query
13	 const parseQuery = new Parse.Query('YOUR_CLASS_NAME_HERE');
14	 const {
15	    isLive,
16	    isLoading,
17	    isSyncing,
18	    results,
19	    count,
20	    error,
21	    reload
22	  } = useParseQuery(parseQuery);
23	
24	  return (
25	    <div>
26	      {isLoading && (
27	        <p>Loading...</p>
28	      )}
29	      {isLive && (
30	        <p>Live!</p>
31	      )}
32	      {isSyncing && (
33	        <p>Syncing...</p>
34	      )}
35	      {results && (
36	        <ul>
37	          {results.map(result => (
38	            <li key={result.id}>
39	              {result.get('CLASS_COLUMN_NAME_HERE')}
40	            </li>
41	          ))}
42	        </ul>
43	      )}
44	      <p>{count}</p>
45	      {error && (
46	        <p>{error.message}</p>
47	      )}
48	      <button
49	        onClick={reload}
50	      >
51	        Reload
52	      </button>
53	    </div>
54	  );
55	}
```
:::

When passing a query to the hook, it will first look for cached results that it might have stored. Then, it creates a WebSocket connection to communicate with the Back4app LiveQuery server, which synchronizes automatically. In other words, the offline-first approach and real-time changes are enabled by default.

To check the query state use the props returned by the hook:

- isLive: If true, indicates the query have subscribed to realtime updates.
- isLoading: If true, the query is fetching the results.
- isSyncing: if true, the query is getting updated results from Back4app servers.
  results: This is the data returned from the query.
- count: Indicates the number of objects that matched the query.
- error: When something goes wrong with the query it returns an error.
- reload: Reload your query results.

You can see the <a href="https://github.com/parse-community/parse-react/tree/master/packages/parse-react" target="_blank">full documentation</a> for more details on how to set up and use the @parse/react library.

## 4 - Test the App Hook

Now you should be able to run your React app and see the results.

> yarn start

Keep in mind that you should add some data to your Back4App project to see some items in your App.

## It’s done!

At this point, you have installed @parse/react on your project, configured the connections with Back4App, and written your first Query. In the next guide, you will see one of the main features of this lib how to use it by creating a sample Live Chat app.

[title] Offline Database
[path] Flutter/GraphQL/

# Implementing the offline first database using the GraphQL API



## Introduction

Since you are here, you must have gone through the rest of the tutorials and are familiar with executing GraphQL queries and mutations to fetch and mutate the data. In this docs, we are going to explore how to implement an offline first user interface with Flutter and GraphQL.

## Goals

- Understand internal architecture of the flutter graphql offline client
- Allowing application to run graphql queries even though application is offline
- Implement Offline data persistence

## Prerequisites

:::hint{type="info"}
- We require that the user has some basic understanding of Dart and Flutter.
- Though not necessary, the GraphQL cookbook will be useful in understanding some of the <a href="https://www.back4app.com/docs/parse-graphql/graphql-getting-started" target="_blank">GraphQL concepts</a>.
- We require that you have completed the prerequisite topic <a href="https://www.back4app.com/docs/flutter/graphql/flutter-graphql-setup" target="_blank">Flutter Graphql Setup</a> and have previous code setup and back4app backend implemented.
:::

## 1 - Setting up offline cache

Flutter GraphQl client supports “offline queries” by default, that is it will not throw errors if we query some GraphQL data when offline and would fetch the data from cache.

We have to note that this is different from persisting the cache across app sessions and specifically the flutter graphql client does not have cache persistence to disk. So if the app closed from the system tray and reopened the data would still need to be fetched.

To enable the same we have to enable the offline cache:

Go to <a href="https://github.com/templates-back4app/Flutter-GraphQL/blob/772c058c74d870798af1cce7a29a5046f9dda456/lib/main.dart#L32" target="_blank">main.dart</a>:

```dart
1   return MaterialApp(
2     home: GraphQLProvider(
3       child: CacheProvider(   // cache provider widget that provides the offline queries support.
4         child: MyHomePage(),
5       ),
6       client: client,
7     ),
8   );
```

## 2: Setting up stored preferences

One caveat while using the flutter-graphql client is that it does not store any cache of its own when the application is closed, nor does it hydrate the cache when the application is opened again.

For implementing the same we would be leveraging the flutter shared\_prefrencces library. It wraps platform-specific persistent storage for simple data (NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.), essentially allowing to store data offline in a very simple manner.

For installing the library please add in the [pubspec.yml](https://github.com/templates-back4app/Flutter-GraphQL/blob/772c058c74d870798af1cce7a29a5046f9dda456/pubspec.yaml#L34) file

>  shared_preferences: 
>
> **^**
>
> 0.5.12
>
> **+**
>
> 4

In <a href="https://github.com/templates-back4app/Flutter-GraphQL/blob/772c058c74d870798af1cce7a29a5046f9dda456/lib/main.dart#L142" target="_blank">main.dart</a> add the following:

```dart
1   import 'package:shared_preferences/shared_preferences.dart';
2
3   class SharedPreferencesHelper {
4     static final String _offline_cache_key = 'programmingLanguageListResponse';
5
6     static Future<ProgrammingLanguageList> getCache() async {
7       final SharedPreferences prefs = await SharedPreferences.getInstance();
8       final cache = prefs.getString(_offline_cache_key);
9       final offlineData =
10          cache != null ? programmingLanguageListFromJson(cache) : null;
11
12      return offlineData;
13    }
14  
15    static Future<bool> setCache(dynamic value) async {
16      final SharedPreferences prefs = await SharedPreferences.getInstance();
17  
18      return prefs.setString(_offline_cache_key, jsonEncode(value));
19    }
20  }
```

Shared Preferences library stores data in a key-value form where value gets stringified into a JSON string. We will need to parse this data to our data model.

## 3 - Parsing the locally stored data

We will create a new file called programing\_languages\_model.dart. Which will store the parsing logic. We will generate this logic by pasting our graphql response in the JSON to dart model converter at [https://app.quicktype.io/](https://app.quicktype.io/)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BFcGNKTus1yY8fHPQUau8_image.png)

We will copy the generated code and create a file programing\_languages\_model.dart [https://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-offline/lib/programing\_languages\_model.dart](https://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-offline/lib/programing_languages_model.dart)

## 4 - Integrating Offline Storage logic

If the data does not exist we would be using the data from shared preferences. If the data is also not in the shared preferences we would simply show a loading icon.

We will now implement changes to integrate all the changes together, in the build method of our \_MyHomePageState we would change our build method. We would use the FutureBuilder widget to consume data from the SharedPreferencesHelper class.

```dart
1   return FutureBuilder<ProgrammingLanguageList>(
2     future: SharedPreferencesHelper.getCache(),
3     builder: (prefs, snapshot) {
4       final offlineData = snapshot.data;
5       if (!snapshot.hasError) {
6         return SafeArea(
7           ….
```

Using the FutureBuilder widget allows us to write code without having to use state. It is a relatively quick process to get the data from shared preferences. We could also show a loader while we are initialising the shared preferences and are getting data from an offline store.

We now use this offline data object and render while data from GraphQL is not available. We will also refactor the code a little bit. Following will be our code for the Query<a href="https://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-offline/lib/main.dart" target="_blank">https\://github.com/templates-back4app/Flutter-GraphQL/blob/flutter-graphql-offline/lib/main.dart</a> widget.

```dart
1   body: Query(
2       options: QueryOptions(
3         documentNode: gql(query),
4       ),
5       builder: (
6         QueryResult result, {
7           Refetch refetch,
8           FetchMore fetchMore,
9         }) {
10          final data = result.data == null
11          ? offlineData
12          : programmingLanguageListFromJson(
13            jsonEncode(result.data));
14            if (data == null) {
15              return Center(
16                child: Text(
17                  "Loading...",
18                  style: TextStyle(fontSize: 20.0),
19                ));
20              } else {
21                SharedPreferencesHelper.setCache(data);
22                return ListView.builder(
23                  itemBuilder: (BuildContext context, int index) {
24                    if (index == 0) {
25                      return Center(
26                        child: RaisedButton(
27                          onPressed: refetch,
28                          child: result.loading == true
29                          ? Text("Loading...")
30                          : Text("Refetch"),
31                        ),
32                      );
33                    }
34                    return ListTile(
35                      title: Text(data.programmingLanguages
36                      .edges[index - 1].node.name),
37                      trailing: Text(data.programmingLanguages
38                      .edges[index - 1].node.stronglyTyped
39                      ? "Strongly Typed"
40                      : "Weekly Typed"),
41                    );
42                  },
43                  itemCount: data.programmingLanguages.edges.length + 1,
44                );
45                }
46              },
47    ),
48          ),
```

We should get the following:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZqmUgda8xfGDIT05CbTra_flutter-graphql-offline.gif" signedSrc size="50" width="240" height="506" position="center" caption}

## Conclusion

We are now able to ensure a very good mobile experience by storing the data offline and revalidating the data when the application gets connected to the internet. Also, one important aspect that is enhancing the user experience is that the flutter-graphql client caches the old response and while sending a new request automatically. Because of which we don’t have to keep showing clumsy loading screens, while re-fetching data.

The code for the article is available at: [https://github.com/templates-back4app/Flutter-GraphQL/tree/flutter-graphql-offline](https://github.com/templates-back4app/Flutter-GraphQL/tree/flutter-graphql-offline)

[title] Geoqueries
[path] Android/Data objects/

# Geoqueries

## Introduction

In this guide, you’ll learn how to perform GeoPoint querying on Parse in an Android application.

This tutorial uses an app created in Android Studio Arctic Fox -2020.3.1 with compileSdk = 30 , minSdk = 23 and targetSdk = 30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

Perform Geoqueries using geopoints stored on Back4App and Android geolocation.

Here is a preview of what we are gonna achieve:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/lxHemJkbJ5XFw2D12fph8_image.png" signedSrc size="50" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Let’s get started!

:::hint{type="info"}
Before next steps, we need to connect Back4App to our application. You should save the appId and clientKey from the Back4App to string.xml file and then init Parse in our App.java or App.kt file.
Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">New Parse App tutorial</a> if you don’t know how to init Parse to your app.

Or you can download the projects we shared the github links above and edit only the appId and clientKey parts according to you.
:::

## 1 - Save some data on Back4App

:::hint{type="info"}
In this step, we will create a Class with the JS Console and Javascript codes provided by Parse and we will create queries for this Class.
:::

Let’s create a City 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:



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/DCDrBaQtQqrMGcKXZ6hsO_image.png)

Go ahead and create theCityclass with the following example content:

```java
1   // Add City objects and create table
2   // Note how GeoPoints are created, passing latitude and longitude as arguments
3   // Montevideo
4   City = new Parse.Object('City');
5   City.set('name', 'Montevideo - Uruguay');
6   City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
7   await City.save();
8
9   // Brasília
10  City = new Parse.Object('City');
11  City.set('name', 'Brasília - Brazil');
12  City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
13  await City.save();
14
15  // Bogotá
16  City = new Parse.Object('City');
17  City.set('name', 'Bogotá - Colombia');
18  City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
19  await City.save();
20
21  // Mexico City
22  City = new Parse.Object('City');
23  City.set('name', 'Mexico City - Mexico');
24  City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
25  await City.save();
26
27  // Washington, D.C.
28  City = new Parse.Object('City');
29  City.set('name', 'Washington, D.C. - USA');
30  City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
31  await City.save();
32
33  // Ottawa
34  City = new Parse.Object('City');
35  City.set('name', 'Ottawa - Canada');
36  City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
37  await City.save();
38
39  console.log('Success!');
```

## &#x20;2 - Query the data from Android app

Now that you have a populated class, we can now perform some GeoPoint queries in it. Let’s begin by ordering City results by the nearest from Kingston in Jamaica (latitude 18.01808695059913 and longitude -76.79894232253473), using the ParseQuery.whereNear method:

:::CodeblockTabs
```java
1       ParseQuery<ParseObject> query = new ParseQuery<>("City");
2       query.whereNear("location",new ParseGeoPoint(18.018086, -76.798942));
3       query.findInBackground((objects, e) -> {
4           if (e==null){
5               initData(objects);
6           } else {
7               Toast.makeText(MainActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
8           }
9       });
```

```kotlin
1      val query = ParseQuery<ParseObject>("City")
2      query.whereNear("location", ParseGeoPoint(18.018086, -76.798942))
3      query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
4          if (e == null) {
5              initData(objects!!)
6          } else {
7              Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT)
8                  .show()
9          }
1 0    }
```
:::

Let’s now query using the method ParseQuery.whereWithinKilometers, which will retrieve all results whose GeoPoint field is located within the max distance. Kingston will be used once again as a reference and the distance limit will be 3000 km.

:::CodeblockTabs
```java
1       ParseQuery<ParseObject> query = new ParseQuery<>("City");
2       query.whereWithinKilometers("location",new ParseGeoPoint(18.018086, -76.798942),3000);
3       query.findInBackground((objects, e) -> {
4           if (e==null){
5              initData(objects);
6           } else {
7               Toast.makeText(MainActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
8           }
9       });
```

```kotlin
1      val query = ParseQuery<ParseObject>("City")
2     query.whereWithinKilometers(
3         "location",
4         ParseGeoPoint(18.018086, -76.798942),
5         3000.0
6     )
7     query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
8         if (e == null) {
9             initData(objects!!)
10        } else {
11            Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT)
12                .show()
13        }
14    }
```
:::

Another useful query method is ParseQuery.whereWithinPolygon, which will query results whose GeoPoint field value is within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.
For this example, you will be using a simple polygon that roughly contains the South American continent, composed of 5 distant GeoPoints in the ocean.

:::CodeblockTabs
```java
1       ParseQuery<ParseObject> query = new ParseQuery<>("City");
2
3       ParseGeoPoint geoPoint1 = new ParseGeoPoint(15.822238344514378, -72.42845934415942);
4       ParseGeoPoint geoPoint2 = new ParseGeoPoint(-0.7433770196268968, -97.44765968406668);
5       ParseGeoPoint geoPoint3 = new ParseGeoPoint(-59.997149373299166, -76.52969196322749);
6       ParseGeoPoint geoPoint4 = new ParseGeoPoint(-9.488786415007201, -18.346101586021952);
7       ParseGeoPoint geoPoint5 = new ParseGeoPoint(15.414859532811047, -60.00625459569375);
8       ParseGeoPoint geoPoint6 = new ParseGeoPoint(41.015137, 28.97953);
9    
10      List<ParseGeoPoint> list = new ArrayList<>();
11      list.add(geoPoint1);
12      list.add(geoPoint2);
13      list.add(geoPoint3);
14      list.add(geoPoint4);
15      list.add(geoPoint5);
16      list.add(geoPoint6);
17      query.whereWithinPolygon("location",list);
18
19      query.findInBackground((objects, e) -> {
20          if (e==null){
21              initData(objects);
22          } else {
23              Toast.makeText(MainActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
24          }
25      });
```

```kotlin
1      val query = ParseQuery<ParseObject>("City")
2      val geoPoint1 = ParseGeoPoint(15.822238344514378, -72.42845934415942)
3      val geoPoint2 = ParseGeoPoint(-0.7433770196268968, -97.44765968406668)
4      val geoPoint3 = ParseGeoPoint(-59.997149373299166, -76.52969196322749)
5      val geoPoint4 = ParseGeoPoint(-9.488786415007201, -18.346101586021952)
6      val geoPoint5 = ParseGeoPoint(15.414859532811047, -60.00625459569375)
7      val geoPoint6 = ParseGeoPoint(41.015137, 28.97953)
8      val list: MutableList<ParseGeoPoint> =
9          ArrayList()
10     list.add(geoPoint1)
11     list.add(geoPoint2)
12     list.add(geoPoint3)
13     list.add(geoPoint4)
14     list.add(geoPoint5)
15     list.add(geoPoint6)
16     query.whereWithinPolygon("location", list)
17     query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
18         if (e == null) {
19             initData(objects!!)
20         } else {
21             Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT)
22                .show()
23         }
24     }
```
:::

## It’s done!

At the end of this guide, you learned how GeoPoint data queries work on Parse and how to perform them on Back4App from an Android app. In the next guide, you will check how to create and manage users in Parse.

[title] Untitled
[path] /


[title] Untitled
[path] Flutter/Parse SDK (REST)/


[title] User Login
[path] React Native/Relay (GraphQL)/Users/

# React Native Login sample using Relay

## Introduction

In the last tutorial, you’ve implemented the User Sign Up to your React Native App using Back4App and Relay. In this guide, you’ll build the login mechanism complementing your App auth feature.

As you may know, Parse already provides by default a User class User, which already has a ready-to-use GraphQL Mutation to login in users when it is necessary for your app.

The flow here will be very similar to the User Sign Up tutorial. You’‘ll build a Login screen using formik, then this form will call the Relay Mutation. The Relay Mutation will communicate with the Back4App Server handling the whole process of authentication.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## Goal

At the end of this guide, you will have a React Native application with the user login feature implemented, as shown below.

### Prerequisites

:::hint{type="info"}
- An app created at Back4App using the Parse Server Version 3.10 or above.
- You have to conclude the <a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">Relay Environment setup tutorial</a>:
- Expect an app with a simple sign in form. Here we are using an Expo app having a Form with the username and password.
- For this tutorial, we are going to use the Expo as a React Native framework;
- For this tutorial, we are going to use Javascript as our default implementation language;
- For this tutorial we are going to use our Style css sample;
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HLS0YNmG6UzTVGL_mVB82_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

## 1 - Creating Sign In Form

If the application already has a Form component, go to step 2. Otherwise, feel free to follow our boilerplate. The form is similar to the form used in the Sign-Up doc. You can also use it as a basis for login. Please go to User <a href="https://www.back4app.com/docs/react-native/graphql/user-sign-up-relay" target="_blank">User Sign Up</a> if you want to learn how to implement it. The Login form code should look like this:

```javascript
1	import React, {useState} from 'react';
2	import environment from '../../relay/environment';
3	import {FormikProvider, useFormik} from 'formik';
4	import { Button, Text, TextInput, View, TouchableOpacity } from 'react-native';
5	import Styles from "../../Style"
6	
7	const SignIn = () => {
8	  const [userLogged, setUserLogged] = useState(null);
9	
10	  const onSubmit = async (values) => {
11	    // @todo the mutation will be implemented here
12	  };
13	
14	  const formikbag = useFormik({
15	    initialValues: {
16	      username: '',
17	      password: '',
18	    },
19	    onSubmit,
20	  });
21	
22	  const {handleSubmit, setFieldValue} = formikbag;
23	
24	  if (userLogged?.id) {
25	    return (
26	      <View style={ {marginTop: 15, alignItems: 'center'} }>
27	        <Text>User {userLogged.name} logged</Text>
28	      </View>
29	    );
30	  }
31	
32	  return (
33	    <FormikProvider value={formikbag}>
34	        <View style={Styles.login_wrapper}>
35	            <View style={Styles.form}>
36	                <Text>Username</Text>
37	                <TextInput
38	                    name={"username"}
39	                    style={Styles.form_input}
40	                    autoCapitalize="none"
41	                    onChangeText={(text) => setFieldValue("username", text)}
42	                />
43	                <Text>Password</Text>
44	                <TextInput
45	                    style={Styles.form_input}
46	                    name={"password"}
47	                    autoCapitalize="none"
48	                    secureTextEntry
49	                    onChangeText={(text) => setFieldValue("password", text)}
50	                />
51	                <TouchableOpacity onPress={() => handleSubmit()}>
52	                    <View style={Styles.button}>
53	                        <Text style={Styles.button_label}>{"Sign in"}</Text>
54	                    </View>
55	                </TouchableOpacity>
56	            </View>
57	        </View>
58	    </FormikProvider>
59	  );
60	};
61	
62	export default SignIn;
```

Run your application, and you’ll see a screen as shown below.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xp1JZ8tdTWLrt9vgHL6TX_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

Please, look at the onSubmit function. Note that the Relay Mutation will be inside of this function. Again, it is not a problem if the application is not using Formik. Once you’re implementing a Form Component, the Relay Mutation only needs to be called inside the submit function.

## 2 - Creating the Mutation

Using the Colocation principle, let’s create a new folder called mutations the most closely to the Form Component. If you want to learn more about colocation please go to <a href="https://www.back4app.com/docs/react-native/graphql/get-started-relay-graphql" target="_blank">Getting Started</a> guide.

In the image below, you can see the colocation principle in practice. Everything related to the component is close to it. A folder wraps the LogIn component, and inside of it, you’ll create another folder called mutations. In the mutations’ folder, you will create the Relay Mutation.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Lm6WApFKYgSa8kzB7Wqaj_image.png" signedSrc size="80" width="290" height="100" position="center" caption}

:::hint{type="success"}
This pattern works perfectly on big projects. Every time you have a new mutation, put it close to the component that will use it.
:::

Inside this folder, you will create a new file called LogInMutation.js. According to our Working with users guide, where we explained the Relay Mutations, you will create a commit function, as shown below.

```javascript
1	function commit({ environment, input, onCompleted, onError }) {
2	  const variables = { input };
3	
4	  commitMutation(environment, {
5	    mutation,
6	    variables,
7	    onCompleted,
8	    onError,
9	  });
10	}
11	
12	export default {
13	  commit,
14	};
```

Before going back to the form component, let’s create our variable that will receive the GraphQL Fragment, representing the Mutation. The GraphQL Fragment is what the Relay Compiler will read and match with schema.graphql.

Before the commitMutation, copy and paste the following code:

```javascript
1	const mutation = graphql`
2	  mutation LogInMutation($input: LogInInput!) {
3	    logIn(input: $input) {
4	      viewer {
5	        user {
6	          id
7	          username
8	          createdAt
9	        }
10	        sessionToken
11	      }
12	    }
13	  }
14	`;
```

Final file:

```javascript
1	import { commitMutation, graphql } from 'react-relay';
2	
3	const mutation = graphql`
4	  mutation LogInMutation($input: LogInInput!) {
5	    logIn(input: $input) {
6	      viewer {
7	        user {
8	          id
9	          createdAt
10	          updatedAt
11	          username
12	        }
13	        sessionToken
14	      }
15	    }
16	  }
17	`;
18	
19	function commit({ environment, input, onCompleted, onError }) {
20	  const variables = { input };
21	
22	  commitMutation(environment, {
23	    mutation,
24	    variables,
25	    onCompleted,
26	    onError,
27	  });
28	}
29	
30	export default {
31	  commit,
32	};
```

:::hint{type="success"}
Since the GraphQL Fragment represents the backend, to get the code of Relay Mutation, you can go to the <a href="https://www.back4app.com/docs/react-native/graphql/users/back4app.com/docs/parse-graphql/graphql-sign-in" target="_blank">Back4App GraphQL Cookbook</a> and find the Fragment.
:::

Run yarn relay to generate the new mutation and update the files. If everything is okay the types of mutation it will be generated and you can go forward.

## 3 - Implement On Submit Function

The submit step is the most important. Here is where the Relay Mutation magic happens.

:::hint{type="info"}
this step gets the values of the form from the formik. If the application is not using formik, the values need to be available here independent of the way they get it.
:::

Back to Form Component, let’s start the implementation of the Relay Mutation.

Import the mutation

```javascript
1	import LogInMutation from './mutations/LogInMutation';
```

Inside of OnSubmit function, stars creating the input variables:

```javascript
1	const onSubmit = (values) => {
2	    const {username, password} = values;
3	    const input = {
4	        username,
5	        password,
6	    };
7	}
```

:::hint{type="info"}
The values are injected by Formik. Here, if you are not using formik, the values will likely come via the form’s native oSubmit or as you prefer.
:::

At last, call the Mutation passing all props (remember to import them).

```javascript
1	    LogInMutation.commit({
2	      environment,
3	      input,
4	      onCompleted: (response) => {
5	        if(!response?.logIn || response?.logIn === null) {
6	          alert('Error while logging');
7	          return;
8	        }
9	
10	        const { viewer } = response?.logIn;
11	        const { sessionToken, user } = viewer;
12	
13	        if (sessionToken !== null) {
14	          setUserLogged(user);
15	          alert(`user ${user.username} successfully logged`);
16	          return;
17	        }
18	      },
19	      onError: (errors) => {
20	        alert(errors[0].message);
21	      },
22	    });
```

Final result of onSubmit

```javascript
1	const onSubmit = (values) => {
2	    const { username, password } = values;
3	    
4	    const input = {
5	      username,
6	      password,
7	    };
8	
9	    LogInMutation.commit({
10	      environment,
11	      input,
12	      onCompleted: (response) => {
13	        if(!response?.logIn || response?.logIn === null) {
14	          alert('Error while logging');
15	          return;
16	        }
17	
18	        const { viewer } = response?.logIn;
19	        const { sessionToken, user } = viewer;
20	
21	        if (sessionToken !== null) {
22	          setUserLogged(user);
23	          alert(`user ${user.username} successfully logged`);
24	          return;
25	        }
26	      },
27	      onError: (errors) => {
28	        alert(errors[0].message);
29	      },
30	    });
31	};
```

Run your project, register your User and then check it on Back4App Dashboard. The Mutation will return the values from the server. Once the session token is returned, the application can start to manage it.

Testing using the user created on the last tutorial. If everything works ok, it will be showed an alert like below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0_SLXED_VK1LnAADDzUuy_image.png" signedSrc size="70" width="828" height="1792" position="center" caption}

## Handling Errors

On commit mutation, the application can handle errors on onError. Always will receive an array of errors. The most common is this array has only one object containing the error message. See the example below:

> 1	{
> 2	  "errors": [
> 3	    {
> 4	      "message": "Invalid username/password.",
> 5	      "locations": [
> 6	        {
> 7	          "line": 2,
> 8	          "column": 3
> 9	        }
> 10	      ],
> 11	      "path": [
> 12	        "logIn"
> 13	      ],
> 14	      "extensions": {
> 15	        "code": 202
> 16	      }
> 17	    }
> 18	  ],
> 19	  "data": {
> 20	    "logIn": null
> 21	  }
> 22	}

Based on this example feel free to create your our error handle. By now, if some error is returned we just show it by an alert:

```javascript
1   onError: (errors) => {
2     alert(errors[0].message);
3   },
```

## Conclusion

You now have an application with a sign-in feature fully working. In the next guide, you will understand how to log out him using the same approach. You will also use Relay Mutations to call our backend.

[title] Start from template
[path] Android/

# Download an Android project with source code and start using Back4App

## Introduction

In this guide, you will learn how to get started with an Android application written in Java or Kotlin and connect it to Back4App.

If you want a detailed Quickstart guide or connect Back4App to an existing project, go to our <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutorial</a>

## Goal

Download an Android Template and connect it to Back4App

## Prerequisites

:::hint{type="info"}
- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio version 4.1 or newer</a>
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
:::

## 1 - Download the template

There are 2 Android templates, one written in Java and the other on Kotlin:

- <a href="https://github.com/templates-back4app/android-kotlin-starter-template" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/android-java-starter-template" target="_blank">Java Example Repository</a>

Choose the template that suits you, and proceed to download or import your project on Android Studio. Android Studio.

### **1.1 - Download Directly from GitHub**

Use the following commands to download and unzip your project template:

::::ExpandableHeading
**MacOS and Linux**

:::CodeblockTabs
```java
$ curl -LOk https://github.com/templates-back4app/android-java-starter-template/archive/master.zip && unzip master.zip
```

```kotlin
$ curl -LOk https://github.com/templates-back4app/android-kotlin-starter-template/archive/master.zip && unzip master.zip
```
:::
::::

:::ExpandableHeading
**Windows**

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rwKky1GhnCrMeLd1Fqv_h_image.png)


:::

### **1.2 - Open the project on Android Studio**

After downloading the files, unzip them. Let’s open Android Studio

In the welcoming screen of Android Studio, choose **‘Open an Existing Project’** and select the project’s folder.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YTGfM9yPvulBzq16XurNv_image.png)

Choose your downloaded and unzipped folder’s location and open it.

Please wait until the finish of the Gradle Run process. Now you can see Gradle console bottom tabs in Android Studio.

### **1.3 - Import from GitHub(Optional Path)**

You can import the repository link directly to Android Studio. On Android Studio welcoming screen, choose **‘Get from Version Control’**

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9-PUyUTdaJPyxMaQ8YhKD_image.png)

Android Studio will ask you for the Git repository link and the desired project path. You can find repository links at the start of this section.

You can find repository links in the start of this section

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Hzljs-WMmNnIWQQjAsofd_image.png)

After filling the URL and Directory fields, click on the **Clone** button. Then Android Studio will copy and open the project for you. Please wait until the finish of the Gradle Run process. Now you can see Gradle console bottom tabs in Android Studio

Android Studio will copy and open project for you

Please wait until gradle run is finished.You can see gradle console bottom tabs in Android Studio

## 2 - Get your App Keys

In this guide we will use following files in project :

AndroidManifest.xml - We will set our Back4App credentials as \<meta-data> and app permissions
App.java (App.kt for kotlin) - We will modify our initialization code in here
MainActivity.java (MainActivity.kt for kotlin) - Will contain our very first code for creating a Parse Object
strings.xml - We will store and read Back4App setup credentails from here
build.gradle - We will set our Parse Android SDK version in here

In order to connect your App project to Back4App’s server, you’ll need three primary information the server URL, the Application ID, and the Client Key.
In an Android project, strings.xml is a perfect place to set this information. It is where Parse Android SDK reads Application key values to make a connection with your Back4App App.
The server URL is already on the project. You’‘ll need now go to Back4App, copy your App keys, and update your strings.xml with those values:

1. Open your strings file: .../app/src/main/res/values/strings.xml

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/KJ-smC7t6unznvL7h0gsv_image.png)

&#x20;    2\. Go to your App Dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App Website.</a>

&#x20;    3\. Find you keys on: App Settings > Security & Keys.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5mOgO_acno_d2_SrvkCQH_image.png)

&#x20;    4\. Return to your strings.xml file and paste your applicationId and clientKey.

```xml
1  <resources>
2      <string name="app_name">Back4AppExample</string>
3      <string name="back4app_server_url" translatable="false">https://parseapi.back4app.com/</string>
4        
5      <!-- Paste BOTH keys here  -->
6      <string name="back4app_app_id" translatable="false">PASTE_YOUR_APPLICATION_ID_HERE</string>
7      <string name="back4app_client_key" translatable="false">PASTE_YOUR_CLIENT_KEY_HERE</string>
8  </resources>
```

&#x20;    5\. Open your build.gradle (Module\:Back4AppExample.app) file in Gradle Scripts from Project Explorer

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nMxeKFNVGzA9mivKlP4wU_image.png)

In dependencies section change the Parse-SDK-Android value with version of your choice.

```java
 implementation "com.github.parse-community.Parse-SDK-Android:parse:latest-version-here"
```

After saving build.gradle run ‘Sync Now’

:::hint{type="info"}
You can see current version of SDK in here <a href="https://jitpack.io/#parse-community/Parse-SDK-Android" target="_blank">SDK Versions</a>.

:::

## 3 - Connect to Back4App

After setting up your App credentials, you are ready to connect with your Parse Server instance on Back4App.

This is the initialization code you’re going to use:

You can reach initialization code in project in App.java (App.kt for kotlin)

We are using App.java for our initialization because we need to establish connection before app takes any other action. App.java is the first Context to be created before any other Activity and Service and last to be destroyed.

Below initilization code gets App Keys from strings.xml and try to establish a connection with our Back4App server. We put our code onCreate() method because we want to connect to our server first before taking any other action.

:::CodeblockTabs
App.java

```java
1   public class App extends Application {
2       @Override
3       public void onCreate() {
4           super.onCreate();
5           Parse.initialize(new Parse.Configuration.Builder(this)
6                   .applicationId(getString(R.string.back4app_app_id))
7                   .clientKey(getString(R.string.back4app_client_key))
8                   .server(getString(R.string.back4app_server_url))
9                   .build());
10      }
11  }
```

App.kt

```kotlin
1   class App : Application() {
2       override fun onCreate() {
3           super.onCreate()
4           Parse.initialize(
5               Parse.Configuration.Builder(this)
6                       .applicationId(getString(R.string.back4app_app_id))
7                       .clientKey(getString(R.string.back4app_client_key))
8                       .server(getString(R.string.back4app_server_url))
9                       .build());
10      }
11  }
```
:::

Now it is time to add some codes for interacting with the server. Let’s open our MainActivity file.

Activity files are great for interacting with user. They are main purpose providing a User Interface.

You can choose which activity to show in launch in AndroidManifest.xml

```xml
1    <activity android:name=".MainActivity">
2        <intent-filter>
3            <action android:name="android.intent.action.MAIN" />
4            <category android:name="android.intent.category.LAUNCHER" />
5        </intent-filter>
6    </activity>
```

In our project MainActivity is set to open on launch.

In this code sample we have a Parse SDK code for saving a Parse Object to server and showing objectId of saved Parse Object to user with a TextView

:::CodeblockTabs
MainActivity.java

```java
1   public class MainActivity extends AppCompatActivity {
2       @Override
3       protected void onCreate(Bundle savedInstanceState) {
4           super.onCreate(savedInstanceState);
5           setContentView(R.layout.activity_main);
6           TextView textView = findViewById(R.id.textView);
7           ParseObject firstObject = new  ParseObject("FirstClass");
8           firstObject.put("message","Hey ! First message from android. Parse is now connected");
9           firstObject.saveInBackground(e -> {
10              if (e != null){
11                  Log.e("MainActivity", e.getLocalizedMessage());
12              }else{
13                  Log.d("MainActivity","Object saved.");
14                  textView.setText(String.format("Object saved. %s", firstObject.getObjectId()));
15              }
16          });
17      }
18  }
```

MainActivity.kt

```kotlin
1   class MainActivity : AppCompatActivity() {
2       override fun onCreate(savedInstanceState: Bundle?) {
3           super.onCreate(savedInstanceState)
4           setContentView(R.layout.activity_main)
5           val textView = findViewById<TextView>(R.id.textView)
6           val firstObject = ParseObject("FirstClass")
7           firstObject.put("message","Hey ! First message from android. Parse is now connected")
8           firstObject.saveInBackground {
9               if (it != null){
10                  it.localizedMessage?.let { message -> Log.e("MainActivity", message) }
11              }else{
12                  Log.d("MainActivity","Object saved.")
13                  textView.text = String.format("Object saved. %s", firstObject.objectId)
14              }
15          }
16      }
17  }
```
:::

## 4 - Test the connection

1. Build your app in a device or virtual device (Shift+F10).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PWVvmanCmRcSYI9Ui3GO0_image.png)

:::hint{type="info"}
If you don’t have any virtual device to run app. You can create a new one from AVD Manager in Android Studio
:::

1. Wait until the Hello Word! screen appears. After Hello Word! you will see `Object saved`. Message this message will include saved object’s id.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/oGS_6wcZQJ-oLzJpxj54v_image.png" signedSrc size="50" width="340" height="726" position="center" caption}

&#x20;    2\. Login at [Back4App Website](https://www.back4app.com/).&#x20;

&#x20;    3\. Find your app and click on Dashboard > Database > Browser.

If everything works properly, you should find a class named FirstClass as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QYDpDyCwN9GNk-7HxLQFG_image.png)

## It’s done!

You can see objectId in dashboard and your app’s screen is matches !

At this point, you have learned how to get started with Android apps.

:::hint{type="success"}
Learn more by walking around our <a href="https://www.back4app.com/docs/android/android-project-with-source-code-download" target="_blank">Android Tutorials </a>or check <a href="http://docs.parseplatform.org/android/guide/" target="_blank">Parse open source documentation for Android SDK.</a>
:::


[title] Basic Data Operations
[path] Flutter/Parse SDK (REST)/

This guide demonstrates how to manage Parse Objects on Back4App using the Flutter plugin for Parse Server. You'll learn the basic CRUD operations: Create, Read, Update, and Delete. This tutorial uses a simple ToDo app to illustrate these operations.

Back4app Backend data storage revolves around the `ParseObject`, which holds key-value pairs of JSON-compatible data. The Back4App Data Storage accommodates a wide range of common data types, including strings, numbers, booleans, DateTime, GeoPoints, Pointers, Relations, as well as lists and objects. Essentially, it supports any data that can be encoded in JSON format, providing a flexible and robust solution for various data storage needs.

## Prerequisites

To complete this tutorial, you will need:

:::hint{type="info"}
- Flutter version 3.x.x or later
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## 1. Create Object

The `saveTodo` function creates a new task with a title and a `done` status set to false. Here’s how it works:

1. **Initialize Parse Object setting its Attributes**: Create an instance of `ParseObject` for your class (e.g., 'Todo'). Use the `set` method to define the key-value pairs.
2. **Save the Object**: Call the `save` method to store the object in the database.

```dart
Future<void> saveTodo(String title) async {
  final todo = ParseObject('Todo')
    ..set('title', title)
    ..set('done', false);
  await todo.save();
}
```

## 2. Read Object

The `getTodo` function queries the database and returns a list of tasks. Here’s how it works:

1. **Initialize the Query**: Create an instance of `QueryBuilder` for your class.
2. **Execute the Query**: Use the `query` method to retrieve data.
3. **Handle the Response**: Check if the query was successful and process the results.

```dart
Future<List<ParseObject>> getTodo() async {
  QueryBuilder<ParseObject> queryTodo =
      QueryBuilder<ParseObject>(ParseObject('Todo'));
  final ParseResponse apiResponse = await queryTodo.query();

  if (apiResponse.success && apiResponse.results != null) {
    return apiResponse.results as List<ParseObject>;
  } else {
    return [];
  }
}
```

Update the `ListView.builder` function to extract and display Parse object values:

```dart
// Get Parse Object Values
final varTodo = snapshot.data![index];
final varTitle = varTodo.get<String>('title')!;
final varDone = varTodo.get<bool>('done')!;
```

## 3. Update Object

The `updateTodo` function updates the status of an existing task. Here’s how it works:

1. **Initialize the Parse Object and Set Attributes**: Create an instance of `ParseObject` and set its `objectId`. Use the `set` method to update key-value pairs.
2. **Save the Object**: Call the `save` method to update the object in the database.

```dart
Future<void> updateTodo(String id, bool done) async {
  var todo = ParseObject('Todo')
    ..objectId = id
    ..set('done', done);
  await todo.save();
}
```

## 4. Delete Object

The `deleteTodo` function removes an existing task from the database. Here’s how it works:

1. **Initialize the Parse Object**: Create an instance of `ParseObject` and set its `objectId`.
2. **Delete the Object**: Call the `delete` method to remove the object from the database.

```dart
Future<void> deleteTodo(String id) async {
  var todo = ParseObject('Todo')..objectId = id;
  await todo.delete();
}
```

## Full Example Code

Here’s the complete code for a simple ToDo app integrated with Back4app Backend.

```dart
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  const keyApplicationId = 'YOUR_APP_ID_HERE';
  const keyClientKey = 'YOUR_CLIENT_KEY_HERE';
  const keyParseServerUrl = 'https://parseapi.back4app.com';

  await Parse().initialize(keyApplicationId, keyParseServerUrl,
      clientKey: keyClientKey, autoSendSessionId: true);

  runApp(const MaterialApp(home: TodoApp()));
}

class TodoApp extends StatefulWidget {
  const TodoApp({super.key});

  @override
  // ignore: library_private_types_in_public_api
  _TodoAppState createState() => _TodoAppState();
}

class _TodoAppState extends State<TodoApp> {
  List<ParseObject> tasks = [];
  TextEditingController taskController = TextEditingController();

  @override
  void initState() {
    super.initState();
    getTodo();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
          primaryColor: Colors.white,
          hintColor: Colors.black,
          scaffoldBackgroundColor: Colors.white,
          appBarTheme:
              AppBarTheme(backgroundColor: Color.fromARGB(255, 68, 122, 246))),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Todo List'),
        ),
        body: Container(
          decoration: BoxDecoration(
            border: Border.all(color: Colors.black),
          ),
          child: Column(
            children: [
              const SizedBox(height: 20),
              _buildTaskInput(),
              const SizedBox(height: 20),
              Expanded(child: _buildTaskList()),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildTaskInput() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 20),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: taskController,
              decoration: InputDecoration(
                hintText: 'Enter tasks',
                filled: true,
                fillColor: Colors.grey[200],
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(8),
                  borderSide: BorderSide.none,
                ),
              ),
            ),
          ),
          const SizedBox(width: 10),
          ElevatedButton(
            onPressed: addTodo,
            style: ButtonStyle(
                foregroundColor:
                    MaterialStateProperty.all<Color>(Colors.black)),
            child: const Text('Add'),
          ),
        ],
      ),
    );
  }

  Widget _buildTaskList() {
    return ListView.builder(
      itemCount: tasks.length,
      itemBuilder: (context, index) {
        final varTodo = tasks[index];
        final varTitle = varTodo.get<String>('title') ?? '';
        bool done = varTodo.get<bool>('done') ?? false;

        return ListTile(
          title: Row(
            children: [
              Checkbox(
                value: done,
                onChanged: (newValue) {
                  updateTodo(index, newValue!);
                },
              ),
              Expanded(child: Text(varTitle)),
            ],
          ),
          trailing: IconButton(
            icon: const Icon(Icons.delete),
            onPressed: () {
              deleteTodo(index, varTodo.objectId!);
            },
          ),
        );
      },
    );
  }

  Future<void> addTodo() async {
    String task = taskController.text.trim();
    if (task.isNotEmpty) {
      var todo = ParseObject('Todo')
        ..set('title', task)
        ..set('done', false);

      var response = await todo.save();

      if (response.success) {
        setState(() {
          tasks.add(todo);
        });
        taskController.clear();
      } else {
        // Handle error
      }
    }
  }

  Future<void> updateTodo(int index, bool done) async {
    final varTodo = tasks[index];
    final String id = varTodo.objectId.toString();

    var todo = ParseObject('Todo')
      ..objectId = id
      ..set('done', done);

    var response = await todo.save();

    if (response.success) {
      setState(() {
        tasks[index] = todo;
      });
    } else {
      // Handle error
    }
  }

  Future<void> getTodo() async {
    var queryBuilder = QueryBuilder<ParseObject>(ParseObject('Todo'));
    var apiResponse = await queryBuilder.query();

    if (apiResponse.success && apiResponse.results != null) {
      setState(() {
        tasks = apiResponse.results as List<ParseObject>;
      });
    } else {
      // Handle error
    }
  }

  Future<void> deleteTodo(int index, String id) async {
    var todo = ParseObject('Todo')..objectId = id;
    var response = await todo.delete();

    if (response.success) {
      setState(() {
        tasks.removeAt(index);
      });
    } else {
      // Handle error
    }
  }
}
```

### Your App should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3advq0EwgQpAeq8ii9lp3_3kcgbfecvvb2myngt1ttp-image.jpg" signedSrc size="40" width="800" height="1730" position="center" caption}

## Conclusion

You have now implemented the basic CRUD operations in a Flutter app using Parse on Back4App. This tutorial demonstrated how to add, retrieve, update, and delete tasks in a ToDo app.


[title] Password Reset
[path] ReactJS/Users/

# User Password Reset for React

## Introduction

It’s a fact that as soon as you introduce passwords into a system, users will forget them. In such cases, the Parse library provides a way to let them securely reset their password.
As with email verification, Parse already has an implementation ready for this, Parse.User.requestPasswordEmail. By using this method, Parse will handle all aspects of password resetting for you seamlessly.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- Complete the previous guide so you can have a better understanding of [the Parse.User class and the Parse.User.logIn method](https://www.back4app.com/docs/react/working-with-users/react-user-login)
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library.</a>
:::

## Goal

To add a user password reset feature to a React app using Parse.

## 1 - Customizing password reset emails

Before calling the Parse.User.requestPasswordEmail method, you can customize the message that your user will get after requesting a password reset. Log in to your Parse app dashboard, go to Settings->Verification Emails and change your password reset email subject or message. Ensure that your user will receive an email containing clear instructions and indicating that it is indeed from your application.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Zj719_5Vo1biD1FXjAkqB_image.png)

## 2 - Using requestPasswordEmail

Calling the Parse.User.requestPasswordEmail method only requires your user account email as a parameter, so go ahead and add the following function to your password reset component. Remember to add a text input for your user email to your component.

:::CodeblockTabs
JavaScript

```javascript
1	const doRequestPasswordReset = async function () {
2	  // Note that this value come from state variables linked to your text input
3	  const emailValue = email;
4	  try {
5	    await Parse.User.requestPasswordReset(emailValue);
6	    alert(`Success! Please check ${email} to proceed with password reset.`);
7	    return true;
8	  } catch(error) {
9	    // Error can be caused by lack of Internet connection
10	    alert(`Error! ${error}`);
11	    return false;
12	  }
13	};
```

```typescript
1	const doRequestPasswordReset = async function (): Promise<boolean> {
2	  // Note that this value come from state variables linked to your text input
3	  const emailValue: string = email;
4	  try {
5	    await Parse.User.requestPasswordReset(emailValue);
6	    alert(`Success! Please check ${email} to proceed with password reset.`);
7	    return true;
8	  } catch(error: any) {
9	    // Error can be caused by lack of Internet connection
10	    alert(`Error! ${error}`);
11	    return false;
12	  }
13	};
```
:::

Go ahead and test your component. After requesting a password reset email, you should have received the email, so check your inbox. Note that the message will contain any changes you had set up before in your Parse dashboard.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/hoK1HstTFa37T52U5--Id_image.png)

The password reset form will look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NTHyzD0FyekyYfQFe4Cgr_image.png)

That’s it, after changing the password in this form, your user will be able to log in again to your application.

## 3 - Creating a password request component

As said before, you should create a component containing the function shown on Step 2 and also a text input field for your user account email to enable password reset in your app. Here is a complete example of this component. You can plug it in in our previous guide’s user login project if you like.

:::CodeblockTabs
UserResetPassword.js

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider, Input } from 'antd';
5	
6	export const UserPasswordReset = () => {
7	  // State variables
8	  const [email, setEmail] = useState('');
9	
10	  // Functions used by the screen components
11	  const doRequestPasswordReset = async function () {
12	    // Note that this value come from state variables linked to your text input
13	    const emailValue = email;
14	    try {
15	      await Parse.User.requestPasswordReset(emailValue);
16	      alert(`Success! Please check ${email} to proceed with password reset.`);
17	      return true;
18	    } catch (error) {
19	      // Error can be caused by lack of Internet connection
20	      alert(`Error! ${error}`);
21	      return false;
22	    }
23	  };
24	
25	  return (
26	    <div>
27	      <div className="header">
28	        <img
29	          className="header_logo"
30	          alt="Back4App Logo"
31	          src={
32	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
33	          }
34	        />
35	        <p className="header_text_bold">{'React on Back4App'}</p>
36	        <p className="header_text">{'User Password Reset'}</p>
37	      </div>
38	      <div className="container">
39	        <h2 className="heading">{'Request password reset email'}</h2>
40	        <Divider />
41	        <div className="form_wrapper">
42	          <Input
43	            value={email}
44	            onChange={(event) => setEmail(event.target.value)}
45	            placeholder="Your account email"
46	            size="large"
47	            className="form_input"
48	          />
49	        </div>
50	        <div className="form_buttons">
51	          <Button
52	            onClick={() => doRequestPasswordReset()}
53	            type="primary"
54	            className="form_button"
55	            color={'#208AEC'}
56	            size="large"
57	          >
58	            Request password reset
59	          </Button>
60	        </div>
61	      </div>
62	    </div>
63	  );
64	};
```

UserResetPassword.tsx

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	const Parse = require('parse/dist/parse.min.js');
5	
6	export const UserPasswordReset: FC<{}> = (): ReactElement => {
7	  // State variables
8	  const [email, setEmail] = useState('');
9	
10	  // Functions used by the screen components
11	  const doRequestPasswordReset = async function (): Promise<boolean> {
12	    // Note that this value come from state variables linked to your text input
13	    const emailValue: string = email;
14	    try {
15	      await Parse.User.requestPasswordReset(emailValue);
16	      alert(`Success! Please check ${email} to proceed with password reset.`);
17	      return true;
18	    } catch(error: any) {
19	      // Error can be caused by lack of Internet connection
20	      alert(`Error! ${error}`);
21	      return false;
22	    }
23	  };
24	
25	  return (
26	    <div>
27	      <div className="header">
28	        <img
29	          className="header_logo"
30	          alt="Back4App Logo"
31	          src={
32	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
33	          }
34	        />
35	        <p className="header_text_bold">{'React on Back4App'}</p>
36	        <p className="header_text">{'User Password Reset'}</p>
37	      </div>
38	      <div className="container">
39	        <h2 className="heading">{'Request password reset email'}</h2>
40	        <Divider />
41	        <div className="form_wrapper">
42	          <Input
43	            value={email}
44	            onChange={(event) => setEmail(event.target.value)}
45	            placeholder="Your account email"
46	            size="large"
47	            className="form_input"
48	          />
49	        </div>
50	        <div className="form_buttons">
51	          <Button
52	            onClick={() => doRequestPasswordReset()}
53	            type="primary"
54	            className="form_button"
55	            color={'#208AEC'}
56	            size="large"
57	          >
58	            Request password reset
59	          </Button>
60	        </div>
61	      </div>
62	    </div>
63	  );
64	};
```
:::

Add these classes to your App.css file to fully render the component layout elements:

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 500px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.form_wrapper {
77	  margin-top: 20px;
78	  margin-bottom: 10px;
79	}
80	
81	.form_input {
82	  margin-bottom: 20px;
83	}
84	
85	.form_button {
86	  width: 100%;
87	}
```
:::

This component should render in a screen like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qaZn9ihmyr8A6-rPs2adm_image.png" signedSrc size="80" width="592" height="522" position="center" caption}

## Conclusion

At the end of this guide, you learned how to allow your Parse users to reset their password on React. In the next guide, we will show you how to perform useful user queries.

[title] Relationships
[path] Android/Data objects/

# Relationships on Android

## Introduction

Using Parse, you can store data objects establishing relations between them. To model this behavior, any ParseObject can be used as a value in other ParseObject. Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency. That can give you extra power when building and running complex queries. There are three main relation types:

- one-to-one, establishing direct relations between two objects and only them;
- one-to-many, where one object can be related to many other objects;
- many-to-many, which can create many complex relations between many objects.

There are two ways to create a one-to-many relation in Parse:

- The first is using the Pointers in Child Class, which is the fastest in creation and query time.
- The second is using Arrays of Pointersin Parent Class which can lead to slow query times depending on their size. Because of this performance issue, we will use only pointers examples.

There are three ways to create a many-to-many relation in Parse.

- The first is using the Parse Relations, which is the fastest in creation and query time. We will use this in this guide.
- The second is using Arrays of Pointers which can lead to slow query times depending on their size.
- The third is using JoinTablewhere the idea from classical database. When there is a many-to-nany relation, we combine every objectId or Pointer from both sides together to build a new separate table in which the relationship is tracked.

This tutorial uses a basic app created in Android Studio 4.1.1 with buildToolsVersion=30.0.2 , Compile SDK Version = 30.0.2 and targetSdkVersion 30

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our Github repositories

- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/Android-Parse-Sdk-Java" target="_blank">Java Example Repository</a>
:::

## Goal

Our goal is, understand Parse Relations by creating a practical Book app.

Here is a preview of what we are gonna achieve:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4GItAcqwXy8pwNIso5U51_image.png" signedSrc size="30" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Understanding the Book App

The main object class you’ll be using is the Book class, storing each book entry in the registration. Also, these are the other three object classes:

- Publisher: book publisher name, one-to-many relation withBook
- Genre: book genre, one-to-many relation withBook. Note that for this example we will consider that a book can only have one genre;
- Author: book author, many-to-many relation withBook, since a book can have more than one author and an author can have more than one book as well;

A visual representation of these data model:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WTHengaTCAsXpfHkt_uVp_image.png)

## Let’s get started!

:::hint{type="info"}
Before next steps, we need to connect Back4App to our application. You should save the appId and clientKey from the Back4App to string.xml file and then init Parse in our App.java or App.kt file.
Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">New Parse App tutorial</a> if you don’t know how to init Parse to your app.

Or you can download the projects we shared the github links above and edit only the appId and clientKey parts according to you.
:::

## 1 - Save and list related objects of books

In this step we will see how to save and list the Genres, Publishers and Authors classes related with the Book class.

### **1.1 - Save and list Genres**

We can register aGenreusing the following snippet.

:::CodeblockTabs
```java
1   private void addGenre(String name) {
2     //We are taking this name parameter from the input.
3     progressDialog.show();
4     ParseObject parseObject = new ParseObject("Genre");
5     parseObject.put("name", name);
6     parseObject.saveInBackground(e -> {
7       progressDialog.dismiss();
8       if (e == null) {
9         getGenres();
10        inputGenre.setText("");
11        Toast.makeText(this, "Genre saved successfully", Toast.LENGTH_SHORT).show();
12      } else { 
13      Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14      }
15    });
16  }
```

```kotlin
1   private fun addGenre(name: String) {
2     //We are taking this name parameter from the input.
3     progressDialog.show()
4     val parseObject = ParseObject("Genre")
5     parseObject.put("name", name)
6     parseObject.saveInBackground {
7       progressDialog.dismiss()
8       if (it == null) {
9         getGenres()
10        inputGenre.setText("")
11        Toast.makeText(this, "Genre saved successfully", Toast.LENGTH_SHORT).show()
12      } else {
13          Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14      }
15    }
16  }
```
:::

We can register aGenreusing the following snippet.

:::CodeblockTabs
```java
1   private void getGenres() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Genre");
4     query.findInBackground((objects, e) -> {
5       progressDialog.dismiss();
6       List<ParseObjectModel> list = new ArrayList<>();
7       for (ParseObject parseObject : objects) {
8         list.add(new ParseObjectModel(parseObject));
9       }
10
11      GenreAdapter adapter = new GenreAdapter(list, this);
12      recyclerView.setLayoutManager(new LinearLayoutManager(this));
13      recyclerView.setAdapter(adapter);
14    });
15  }
```

```kotlin
1   private fun getGenres() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Genre")
4     query.findInBackground { objects, e ->
5     progressDialog.dismiss()
6     var list: MutableList<ParseObjectModel> = ArrayList()
7     for (parseObject in objects) {
8       list.add(ParseObjectModel(parseObject))
9     }
10    val adapter = GenreAdapter(this, list)
11    recyclerView.layoutManager = LinearLayoutManager(this)
12    recyclerView.adapter = adapter
13    }
14  }
```
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/27F_Ufr36FnQWAFtLAzkg_image.png" signedSrc size="30" width="532" height="1084" position="center" caption}

### **1.2 - Save and list Publishers**

We can register aPublisherusing the following snippet.

:::CodeblockTabs
```java
1   private void addPublisher(String name) {
2     //We are taking this name parameter from the input.
3     progressDialog.show();
4     ParseObject parseObject = new ParseObject("Publisher");
5     parseObject.put("name", name);
6     parseObject.saveInBackground(e -> {
7       progressDialog.dismiss();
8       if (e == null) {
9         getPublishers();
10        inputPublisher.setText("");
11        Toast.makeText(this, "Publisher saved successfully", Toast.LENGTH_SHORT).show();
12      } else {
13          Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14      }
15    });
16  }
```

```kotlin
1   private fun addPublisher(name: String) {
2     //We are taking this name parameter from the input.
3     progressDialog.show()
4     val parseObject = ParseObject("Publisher")
5     parseObject.put("name", name)
6     parseObject.saveInBackground {
7       progressDialog.dismiss()
8       if (it == null) {
9         getPublishers()
10        inputPublisher.setText("")
11        Toast.makeText(this, "Publisher saved successfully", Toast.LENGTH_SHORT).show()
12      } else {
13        Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14      }
15    }
16  }
```
:::

We can register aPublisherusing the following snippet.

:::CodeblockTabs
```java
1   private void getPublishers() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Publisher");
4     query.findInBackground((objects, e) -> {
5       progressDialog.dismiss();
6       List<ParseObjectModel> list = new ArrayList<>();
7         for (ParseObject parseObject : objects) {
8           list.add(new ParseObjectModel(parseObject));
9         }
10      
11        PublisherAdapter adapter = new PublisherAdapter(list, this);
12        recyclerView.setLayoutManager(new LinearLayoutManager(this));
13        recyclerView.setAdapter(adapter);
14    });
15  }
```

```kotlin
1   private fun getPublishers() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Publisher")
4     query.findInBackground { objects, e ->
5       progressDialog.dismiss()
6       val list: ArrayList<ParseObjectModel> = ArrayList()
7       for (parseObject in objects) {
8         list.add(ParseObjectModel(parseObject))
9       }
10  
11      val adapter = PublisherAdapter(this, list)
12      recyclerView.layoutManager = LinearLayoutManager(this)
13      recyclerView.adapter = adapter
14    }
15  }
```
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/As2skdoT9UlsTddqEfirE_image.png" signedSrc size="30" width="532" height="1084" position="center" caption}

### **1.3 - Save and list Authors**

We can register aAuthorusing the following snippet.

:::CodeblockTabs
```java
1   private void addAuthor(String name){
2     //We are taking this name parameter from the input.
3     progressDialog.show();
4     ParseObject parseObject = new ParseObject("Author");
5     parseObject.put("name", name);
6     parseObject.saveInBackground(e -> {
7       progressDialog.dismiss();
8       if (e == null) {
9         getAuthors();
10        inputAuthor.setText("");
11        Toast.makeText(this, "Author saved successfully", Toast.LENGTH_SHORT).show();
12      } else {
13        Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14      }
15    });
16  }
```

```kotlin
1   private fun addAuthor(name: String) {
2     //We are taking this name parameter from the input.
3     progressDialog.show()
4     val parseObject = ParseObject("Author")
5     parseObject.put("name", name)
6     parseObject.saveInBackground {
7       progressDialog.dismiss()
8       if (it == null) {
9         getAuthors()
10        inputAuthor.setText("")
11        Toast.makeText(this, "Author saved successfully", Toast.LENGTH_SHORT).show()
12      } else {
13          Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14      }
15    }
16  }
```
:::

We can register aAuthorusing the following snippet.

:::CodeblockTabs
```java
1   private void getAuthors() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Author");
4     query.findInBackground((objects, e) -> {
5       progressDialog.dismiss();
6       List<ParseObjectModel> list = new ArrayList<>();
7       for (ParseObject parseObject : objects) {
8         list.add(new ParseObjectModel(parseObject));
9       }
10
11      AuthorAdapter adapter = new AuthorAdapter(list, this);
12      recyclerView.setLayoutManager(new LinearLayoutManager(this));
13      recyclerView.setAdapter(adapter);
14    });
15  }
```

```kotlin
1   private fun getAuthors() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Author")
4     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
5       progressDialog.dismiss()
6       val list: ArrayList<ParseObjectModel> = ArrayList()
7       for (parseObject in objects) {
8          list.add(ParseObjectModel(parseObject))
9       }
10      val adapter = AuthorAdapter(this, list)
11      recyclerView.layoutManager = LinearLayoutManager(this)
12      recyclerView.adapter = adapter
13    }
14  }
```
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/KNTZxKCHMAjZAKFGtW9j5_image.png" signedSrc size="30" width="532" height="1084" position="center" caption}

In this part, we use a model class named ParseObjectModel. In this model class, we have a ParseObject variable to be able to read the data, and the isChecked variable, which we will use to save the book in the next step. We will be able to easily retrieve the selected objects with the isChecked variable.
Here is the our ParseObjectModel model.

:::CodeblockTabs
```java
1   public class ParseObjectModel {
2     ParseObject object;
3     boolean isChecked = false;
4  
5     public ParseObjectModel(ParseObject object) {
6       this.object = object;
7     }
8
9     public ParseObject getObject() {
10      return object;
11    }
12 
13    public ParseObjectModel setObject(ParseObject object) {
14      this.object = object;
15      return this;
16    }
17
18    public boolean isChecked() {
19      return isChecked;
20    }
21 
22    public ParseObjectModel setChecked(boolean checked) {
23      isChecked = checked;
24      return this;
25    }
26  }
```

```kotlin
1   class ParseObjectModel(obj: ParseObject) {
2     var obj: ParseObject? = null
3     var isChecked: Boolean = false
4  
5     init {
6       this.obj = obj
7     }
8   }
```
:::

## 2 - Save a Book object and its relations

### **2.1 - Save a Book object with 1\:N Relationship**

This function will create a new Book in Back4app database with 1\:N relations.

:::CodeblockTabs
```java
1   progressDialog.show();
2   book.put("genre", genre);
3   book.put("publisher", publisher);
4   book.put("title", title);
5   book.put("year", year);
6   book.saveInBackground(e -> {
7     progressDialog.hide();
8     if (e == null) {
9       Toast.makeText(AddBookActivity.this, "Book saved successfully", Toast.LENGTH_SHORT).show();
10      startActivity(new Intent(AddBookActivity.this, BookListActivity.class));
11      finish();
12    } else {
13      Toast.makeText(AddBookActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14    }
15  });
```

```kotlin
1   progressDialog.show()
2   book.put("genre", genre)
3   book.put("publisher", publisher!!)
4   book.put("title", title)
5   book.put("year", year)
6   book.saveInBackground {
7     progressDialog.hide()
8     if (it == null) {
9       Toast.makeText(this, "Book saved successfully", Toast.LENGTH_SHORT).show()
10      startActivity(Intent(this@AddBookActivity, BookListActivity::class.java))
11      finish()
12    } else {
13      Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14    }
15  }
```
:::

### **2.2 Save a Book object with N\:N Relationship**

This function will create a new Book in Back4app database with N\:N relations. For the Author relation, we find the selected Author/s in the adapter of the authorRecyclerView and save them as Parse Relation.

:::CodeblockTabs
```java
1   progressDialog.show();
2   book.put("genre", genre);
3   book.put("publisher", publisher);
4   book.put("title", title);
5   book.put("year", year);
6
7   //Here we are setting book relation with getSelectedItem function of BookAuthorAdapter.
8   if (recyclerViewAuthors.getAdapter() != null) {
9     relation = ((BookAuthorAdapter) recyclerViewAuthors.getAdapter()).getSelectedItems(book);
10      if (relation == null) {
11        Toast.makeText(this, "Please select Author/s", Toast.LENGTH_SHORT).show();
12        return;
13      }
14    } else {
15      Toast.makeText(this, "Something went wrong!!", Toast.LENGTH_SHORT).show();
16      return;
17    }
18
19  book.saveInBackground(e -> {
20    progressDialog.hide();
21    if (e == null) {
22      Toast.makeText(AddBookActivity.this, "Book saved successfully", Toast.LENGTH_SHORT).show();
23      startActivity(new Intent(AddBookActivity.this, BookListActivity.class));
24      finish();
25    } else {
26      Toast.makeText(AddBookActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
27    }
28  });
29
30  //This is the function for save Author/s relation of Book object.This function in BookAuthorAdapter.
31  public ParseRelation<ParseObject> getSelectedItems(ParseObject parseObject) {
32    ParseRelation<ParseObject> relation = parseObject.getRelation("author_relation");
33    for (ParseObjectModel object : this.list) {
34      if (object.isChecked())
35      relation.add(object.getObject());
36    }
37    return relation;
38  } 
```

```kotlin
1   progressDialog.show()
2   book.put("genre", genre)
3   book.put("publisher", publisher!!)
4   book.put("title", title)
5   book.put("year", year)
6
7   //Here we are setting book relation with getSelectedItem function of BookAuthorAdapter.
8
9   if (recyclerViewAuthors.adapter != null) {
10    relation = (recyclerViewAuthors.adapter as BookAuthorAdapter).getSelectedItems(book)
11    if (relation == null) {
12      Toast.makeText(this, "Please select Author/s", Toast.LENGTH_SHORT).show()
13      return
14    }
15  } else {
16    Toast.makeText(this, "Something went wrong!!", Toast.LENGTH_SHORT).show()
17    return
18  }
19
20  book.saveInBackground {
21    progressDialog.hide()
22    if (it == null) {
23      Toast.makeText(this, "Book saved successfully", Toast.LENGTH_SHORT).show()
24      startActivity(Intent(this@AddBookActivity, BookListActivity::class.java))
25      finish()
26    } else {
27      Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
28    }
29  }
30
31  //This is the function for save Author/s relation of Book object.This function in BookAuthorAdapter.
32
33  fun getSelectedItems(parseObject: ParseObject): ParseRelation<ParseObject>? {
34    var relation:ParseRelation<ParseObject>? = parseObject.getRelation("author_relation")
35    for (obj in this.list) {
36      if (obj.isChecked)
37        relation?.add(obj.obj)
38    }
39    return relation
40  }
```
:::

## 3 - Query the Book Details with Relations

With these functions, we will list our Books according to their Publishers. First, we throw a query to the Publisher class.

:::CodeblockTabs
```java
1   progressDialog.show();
2   ParseQuery<ParseObject> query = new ParseQuery<>("Publisher");
3   query.findInBackground((objects, e) -> {
4     progressDialog.hide();
5     if (e == null) {
6       BookListAdapter adapter = new BookListAdapter(objects, this);
7       recyclerView.setLayoutManager(new LinearLayoutManager(this));
8       recyclerView.setAdapter(adapter);
9     } else {
10      Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
11    }
12  });
```

```kotlin
1   progressDialog.show()
2   val query = ParseQuery<ParseObject>("Publisher")
3   query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
4     progressDialog.hide()
5     if (e == null) {
6       val adapter = BookListAdapter(this, objects!!)
7       recyclerView.layoutManager = LinearLayoutManager(this)
8       recyclerView.adapter = adapter
9     } else {
10      Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
11    }
12  }
```
:::

And then we query to list the Books that each Publisher item is related to.

:::CodeblockTabs
```java
1   ParseObject object = list.get(position);
2   holder.title.setText(object.getString("name"));
3   ParseQuery<ParseObject> query = new ParseQuery<>("Book");
4   query.whereEqualTo("publisher", object);
5   query.findInBackground((objects, e) -> {
6     if (e == null) {
7       BooksAdapter adapter = new BooksAdapter(objects, context);
8       holder.books.setLayoutManager(new LinearLayoutManager(context));
9       holder.books.setAdapter(adapter);
10    } else {
11      Toast.makeText(context, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
12    }
13  });
```

```kotlin
1   val `object` = list[position]
2   holder.title.text = `object`.getString("name")
3   val query = ParseQuery<ParseObject>("Book")
4   query.whereEqualTo("publisher", `object`)
5   query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
6     if (e == null) {
7       val adapter = BooksAdapter(context, objects!!)
8       holder.books.layoutManager = LinearLayoutManager(context)
9       holder.books.adapter = adapter
10    } else {
11      Toast.makeText(context, e.localizedMessage, Toast.LENGTH_SHORT).show()
12    }
13  }
```
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_mdZWSy6L--bhqBCF_Dz3_image.png" signedSrc size="30" width="532" height="1084" position="center" caption}

Now, when we click on any Book object, we send the Object Id of this Book with an intent to the page that will show the details of that Book. And we get all the details of the Book from the database by using this Object Id on that page.

:::CodeblockTabs
```java
1   private void getBookWithDetails() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Book");
4     query.getInBackground(getIntent().getStringExtra("objectId"), (object, e) -> {
5       if (e == null) {
6         bookTitle.setText("Title: " +object.getString("title"));
7         bookYear.setText("Year: " +object.getString("year"));
8         try {
9           bookGenre.setText("Genre: " +object.getParseObject("genre").fetchIfNeeded().getString("name"));
10        } catch (ParseException parseException) {
11            parseException.printStackTrace();
12        }
13        try {
14          bookPublisher.setText("Publisher: " + object.getParseObject("publisher").fetchIfNeeded().getString("name"));
15        } catch (ParseException parseException) {
16          parseException.printStackTrace();
17        }
18      
19        object.getRelation("author_relation").getQuery().findInBackground((objects, e1) -> {
20          progressDialog.hide();
21          if (e1 == null) {
22            BookDetailAuthorAdapter adapter = new BookDetailAuthorAdapter(objects, this);
23            authorRecyclerView.setLayoutManager(new LinearLayoutManager(this));
24            authorRecyclerView.setAdapter(adapter);
25         } else {
26            Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
27          }
28        });
29      } else {
30        progressDialog.hide();
31        Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
32      }
33    });
34  }
```

```kotlin
1   private fun getBookWithDetails() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Book")
4  
5     query.getInBackground(intent.getStringExtra("objectId")) { `object`, e ->
6     if (e == null) {
7       bookTitle.text = "Title: " + `object`.getString("title")
8       bookYear.text = "Year: " + `object`.getString("year")
9       try {
10        bookGenre.text = "Genre: " + `object`.getParseObject("genre")?.fetchIfNeeded<ParseObject>()?.getString("name")
11      } catch (parseException: ParseException) {
12        parseException.printStackTrace()
13      }
14      try {
15        bookPublisher.text =
16        "Publisher: " + `object`.getParseObject("publisher")?.fetchIfNeeded<ParseObject>()?.getString("name")
17      } catch (parseException: ParseException) {
18        parseException.printStackTrace()
19      }
20
21      `object`.getRelation<ParseObject>("author_relation").query.findInBackground { objects, e1 ->
22      progressDialog.hide()
23        if (e1 == null) {
24          val adapter = BookDetailAuthorAdapter(this, objects)
25          authorRecyclerView.layoutManager = LinearLayoutManager(this)
26          authorRecyclerView.adapter = adapter
27        } else {
28          Toast.makeText(this, e1.localizedMessage, Toast.LENGTH_SHORT).show()
29        }
30     }
31    } else {
32        progressDialog.hide()
33        Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
34      }
35    }
36  }
```
:::

## It’s done!

At this point, we have learned Parse Relationships on Android.

[title] N:N Relationship
[path] Flutter/Parse SDK (REST)/Data Objects/

# Many to Many Relationship on Flutter

## Introduction

In the previous guide, we learned how to use one-to-many relations and we will continue with our project. In this guide, we will focus on the most common relation: many-to-many. There are three ways to create a many-to-many relation in Parse.

- The first is using the Parse Relations, which is the fastest in creation and query time. We will use this in this guide.
- The second is usingArrays of Pointers which can lead to slow query times depending on their size.
- The third is usingJoinTable where the idea from classical database. When there is a many-to-many relation, we combine every objectId or Pointer from both sides together to build a new separate table in which the relationship is tracked.

In this guide you will implement a many-to-many relationship on a Flutter Book Registration App using the Parse Relations. You will learn how to create and query many-to-many data relations and how to perform queries returning data from related objects, using Back4App and the Flutter SDK.

::embed[]{url="https://www.youtube.com/embed/Vfyo-g8vjPs"}

## Prerequisites

:::hint{type="info"}
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- **Note: The Flutter app created in previous guide**
- Complete the previous guide so you can have a better understanding of the one-to-may relationship class
- A device (or virtual device) running Android or iOS.
:::

## 1 - Run the Book App Template

If you have not completed the previous guide you can clone and run the complete [Book Flutter App](https://github.com/templates-back4app/flutter_associations) project from our repository. You can also take a look at the [previous guide](https://www.back4app.com/docs/flutter/parse-sdk/data-objects/%7Bsite.baseurl%7D%7D//flutter/parse-sdk/data-objects/flutter-one-to-many-relationship) to better understand the App template. Below you can find a visual representation of Book Registration data model.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Swe2-7FvMIocPiAXjY-N-_image.png)

## 2 - Save a Book object and its Author’s

Open the Flutter project from the previous guide [One to many Relationship on Flutter](https://www.back4app.com/docs//flutter/parse-sdk/data-objects/flutter-one-to-many-relationship). Search for the function doSaveBook in file main.dart, and replace with the code below inside the Future\<void> doSaveBook() function below. This function will create a new Book in Back4app data store with relations.

```dart
1       final book = ParseObject('Book')
2         ..set('title', controllerTitle.text.trim())
3         ..set('year', int.parse(controllerYear.text.trim()))
4         //the objectId will be converted to a Pointer on the save() method
5         ..set('genre', ParseObject('Genre')..objectId = genre.objectId)
6         //you can also convert to a Pointer object before the saving using the .toPointer() method
7         ..set('publisher',
8             (ParseObject('Publisher')..objectId = publisher.objectId).toPointer())
9         //Saving a List of Authors for the Book
10        ..addRelation(
11            'authors',
12            authors
13                .map((o) => ParseObject('Author')..objectId = o.objectId)
14                .toList());
15 
16      await book.save();
```

To build this function, follow these steps:

- 1\. Create a new instance of the ParseBook class with the command ParseObject('Book').
- 2\. Use the set function to set the fields for this object.
  - 2.1.titleis a text attributes that receive value from the text controller.
  - 2.2.genre receives the value by defining a ParseObject with the objectId of the selected Genre. (*Parse will convert to pointer on save*)
  - 2.3.publisher receives the value by defining a ParseObject with the objectId of the selected Publisher. (*Note that we can specify for Parse that we want to save as a *pointer* using the *toPointer()* method*)
  - 2.4.authors. We call the addRelation method of ParseObject, sending a list of ParseObject with the objectId of the selected Authors.
- 3\. Call thesave function in ParseObject, which will effectively register the object to your database in the Back4app Dashboard.

Run the App and test the new doSaveBook() function

- First, access the dashboard and delete the books that were previously registered in the previous guide.
- Click on the Add Book button.
- Fill book information with Authors.
- Click on Save Book button

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/O0kajxCDCm8k3Q2aBeI_u_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

To confirm that the new object is save in the database with relations, you can access the Back4app Dashboard and access Book class.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/lE7tRKvGzi1YioqFBkxZb_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

Clicking on the object pointer/relation value in your dashboard will take you to the referenced object entry. It may seem like a harmless feature, but this makes debugging and error tracing much quicker than searching for it manually.

## 3 - Query the Book Details with Relations

This function will query Book Details in Back4app database, returning relationship data. In some situations, you want to return multiple types of related objects in one query. You can do this with the includeObject method. In our example, we want to return the books, with information from Genre and Publishers.

Search for the function getBookDetail in the file main.dart, then replace the code below inside getBookDetail(ParseObject book) function:

```dart
1       QueryBuilder<ParseObject> queryBook =
2           QueryBuilder<ParseObject>(ParseObject('Book'))
3             ..whereEqualTo('objectId', book.objectId)
4             ..includeObject(['publisher', 'genre']);
5   
6       final ParseResponse responseBook = await queryBook.query();
7
8       if (responseBook.success && responseBook.results != null) {
9         final book = (responseBook.results.first) as ParseObject;
10        bookTitle = book.get<String>('title');
11        bookYear = book.get<int>('year');
12        bookGenre = book.get<ParseObject>('genre').get<String>('name');
13        bookPublisher = book.get<ParseObject>('publisher').get<String>('name');
14        loadedData = true;
15      }
16
17      QueryBuilder<ParseObject> queryAuthors =
18          QueryBuilder<ParseObject>(ParseObject('Author'))
19            ..whereRelatedTo('authors', 'Book', book.objectId);
20
21      final ParseResponse responseAuthors = await queryAuthors.query();
22
23      if (responseAuthors.success && responseAuthors.results != null) {
24        bookAuthors = responseAuthors.results
25            .map((e) => (e as ParseObject).get<String>('name'))
26            .toList();
27      }
```

To build this function, follow these steps:

1. Create an instance ofParseQuery object for Book class. Insert a condition in the query, to search Books where objectId field is equal objectId of the selected book.
2. We use the includeObject method, informing the fields of the pointers that we want to return the data in the same query: Genre and Publisher. You can also do multi level includeObject using dot notation. Exemple : \`..includeObject(\[‘post’, ‘post.authors’]);
3. Do a Query’s search method using query() method.
4. If the operations succeed, object in Book will be returned.
5. We use the get method to retrieve the data. For fields that are pointers, we will first need to retrieve the pointer, then obtain its data. Example\:bookGenre = book.get\<ParseObject>('genre').get\<String>('name');

In the second stage of processing, we need to recover the Authors associated with the Book.

To build this function, follow these steps:

1. Create an instance ofParseQuery object for Authors class. Insert a condition in the query, using whereRelatedTo operator to search Authors relationship with Book, where Book is equal objectId of the selected book.
2. Do a Query’s search method using query() method.
3. If the operations succeed, object in Book will be returned.
4. We use the get method to retrieve the data.

Run the App and test the new Query.

First, click on the List Publisher/Book button.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MtZZXEKd2vUJ7afLiv-wR_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

Select a book from the list. The next screen will display the data for the books and their relationships.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IkAfHVHlwA-8sTmhK7cxh_image.png" signedSrc size="30" width="328" height="637" position="center" caption}

## It’s done!

At this point, you learned how to create and query many-to-many relations and how to perform queries returning data from related objects in Parse on Flutter.

[title] Relationships
[path] iOS/Parse Swift SDK/Data Objects/

# Relationships

## Introduction

Relations are a fundamental feature for organizing data objects stored on a database. **ParseSwift** does provide the necessary tools and methods to establish relations between classes in your Back4App Database. Depending on the use case, we can identify the following type of relations

- **1:1:** A relation that only connects two data objects.
- **1\:N: **A relation between one data object andNdata objects
- **N\:N: **A relation beweenNdata objects toNdata objects.

As we will see below, implementing **1:1** relations are relatively straightforward. For **1\:N** and **N\:N** relations, the implementation involves the ParseRelation object provided by **ParseSwift SDK**. There are additional alternatives for implementing **1\:N** and **N\:N** relations. However, due to efficiency and performance, we recommend following our approach

This tutorial uses a basic app created in Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

## Goal

- To understand how relations are implemented in a Back4App Database.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
:::

## Understanding our Books App

The project template is a Book App where the user enters a book details to save it on a Back4App Database. On the app’s homescreen you will find the form to do so

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CATQz6OXhv8MKsTYp2MQs_image.png" signedSrc size="50" width="1242" height="2208" position="center" caption}

Using the + button located on the top-right side of the navigation bar, we can add as many **Publishers**, **Genres** and **Authors** as needed. Once the user enters a book, they can use the Add book button to save the book on their Back4App Database. Additionally, the List books button will allow us to show all the books the user added and also to see their relationship with the publishers and authors.

## Quick reference of commands we are going to use

We make use of the objects Author, Publisher, ISBN and Book:

:::CodeblockTabs
Genre

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Genre: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

Author

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Author: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

Publisher

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Publisher: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

ISBN

```swift
1   import Foundation
2   import ParseSwift
3
4   struct ISBN: ParseObject {
5       ...
6    
7       var name: String?
8    
9       ...
10  }
```

Book

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Book: ParseObject {
5       ...
6
7       var title: String?
8       var publishingYear: Int?
9       var genre: Genre?
10      var isbn: ISBN?
11    
12      ...
13  }
```
:::

Before storing instances of these objects in a Back4App Database, all their properties must conform to the Codable and Hashable protocols.

We make use of the following methods for managing these objects on the Back4App Database:

:::CodeblockTabs
Create

```swift
   //When creating a new instance of Author we use
1   var newAuthor: Author = Author(name: "John Doe")
2
3   // Saves newAuthor on your Back4App Database synchronously and returns the new saved Item. It throws and error if something went wrong.
4   let savedAuthor = try? newAuthor.save()
5
6   // Saves newAuthor on your Back4App Database asynchronously, and passes a Result<Author, ParseError> object to the completion block to handle the save process.
7   newAuthor.save { result in
8       // Handle the result to check wether the save process was successfull or not
9   }
```

Read

```swift
   //For reading any of the objects introduced above, we construct the corresponding queries for each of them. For Author we have
1   let authorQuery = Author.query() // A query to fetch all Author items on your Back4App Database.
2
3   // Fetches the items synchronously or throws an error if found.
4   let fetchedAuthors = try? query.find()
5
6   // Fetches the items asynchronously and calls a completion block passing a result object containing the result of the operation.
7   query.find { result in
8       // Handle the result
9   }
```

Add relations

```swift
//In 1:1 relations, it is sufficient to have the child object as a property of the parent object. Back4App automatically saves the child object when the parent object is saved. For the remaining relations, we use the add(_:objects:) method via the relation property available in the parent ParseObject. Adding a relation called authors on a Book object would look like this
1   let someBook: Book
2   let authors: [Author]
3
4   // Adds the relation between someBook and authors under the name 'authors'
5   let bookToAuthorsRelation = try? someBook.relation?.add("authors", objects: authors)
6
7   // Saves the relation synchronously
8   let updatedSomeBook = try? bookToAuthorsRelation.save()
9
10  // Saves the relation asynchronously
11  bookToAuthorsRelation.save { result in
12      // Handle the result
13  }
```

Query relations

```swift
//For 1:1 relations, it is enough to append the include() method in the query. For instance, to query all the books together with their ISBN relation, we use
1   var query = Book.query().include("isbn")
2
3   // Fetches all books synchronously
4   let books = try? query.find()
5
6   // Fetches all books asynchronously
7   query.find { result in
8       // Handle the result
9   }
```

Remaining relations

```swift
//For the remaining relations, we create a query by using the static method queryRelation(_,parent:) provided by the ParseObject protocol. Querying the authors related to a book can be implemented in the following way
1   let someBook: Book
2   let authors: [Author]
3   ...
4
5   // We create a relation (identified by the name 'authors') betwee someBook and a set of authors
6   let bookToAuthorsRelation = 
7   guard let bookToAuthorsRelation = try someBook.relation?.add("authors", objects: authors) // Book -> Author 
8   else {
9       fatalError("Failed to add relation")
10  }
11
12  let savedRelation = try bookToAuthorsRelation.save() // Saves the relation synchronously
13
14  bookToAuthorsRelation.save { result in // Saves the relation asynchronously
15      // Handle the result
16  }
```
:::

## 1 - Download the Books App Template

The XCode project has the following structure

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xkHn5bbpziA7whDKS1IDY_image.png)

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

## **2 - Additional CRUD flow**


Before going further, it is necessary to implement some CRUD functions for us to be able to save the Author, Publisher and Genre objects. In theMainController+ParseSwift.swift file, under an extension for the MainController class, we implemented the following methods

```swift
1   // MainController+ParseSwift.swift file
2   extension MainController {
3       /// Collects the data to save an instance of Book on your Back4App database.
4       func saveBook() {
5           view.endEditing(true)
6        
7           // 1. First retrieve all the information for the Book (title, isbn, etc)
8           guard let bookTitle = bookTitleTextField.text else {
9               return presentAlert(title: "Error", message: "Invalid book title")
10          }
11        
12          guard let isbnValue = isbnTextField.text else {
13              return presentAlert(title: "Error", message: "Invalid ISBN value.")
14          }
15        
16          let query = ISBN.query("value" == isbnValue)
17        
18          guard (try? query.first()) == nil else {
19              return presentAlert(title: "Error", message: "The entered ISBN already exists.")
20          }
21          
22          guard let genreObjectId = genreOptionsView.selectedOptionIds.first,
23                let genre = genres.first(where: { $0.objectId == genreObjectId})
24          else {
25              return presentAlert(title: "Error", message: "Invalid genre.")
26          }
27        
28          guard let publishingYearString = publishingYearTextField.text, let publishingYear = Int(publishingYearString) else {
29              return presentAlert(title: "Error", message: "Invalid publishing year.")
30          }
31          
32          let authors: [Author] = self.authorOptionsView.selectedOptionIds.compactMap { [weak self] objectId in
33             self?.authors.first(where: { objectId == $0.objectId })
34          }
35        
36          let publishers: [Publisher] = self.publisherOptionsView.selectedOptionIds.compactMap { [weak self] objectId in
37             self?.publishers.first(where: { objectId == $0.objectId })
38          }
39        
40          // Since we are making multiple requests to Back4App, it is better to use synchronous methods and dispatch them on the background queue
41          DispatchQueue.global(qos: .background).async {
42              do {
43                  let isbn = ISBN(value: isbnValue) // 2. Instantiate a new ISBN object
44                
45                  let savedBook = try Book( // 3. Instantiate a new Book object with the corresponding input fields
46                      title: bookTitle,
47                     publishingYear: publishingYear,
48                      genre: genre,
49                      isbn: isbn
50                  ).save() // 4. Save the new Book object
51                
52                  ... // Here we will implement the relations
53                  
54                  DispatchQueue.main.async {
55                      self.presentAlert(title: "Success", message: "Book saved successfully.")
56                  }
57              } catch {
58                  DispatchQueue.main.async {
59                     self.presentAlert(title: "Error", message: "Failed to save book: \((error as! ParseError).message)")
60                  }
61              }
62          }
63      }
64    
65      /// Retrieves all the data saved under the Genre class in your Back4App Database
66      func fetchGenres() {
67          let query = Genre.query()
68          
69          query.find { [weak self] result in
70              switch result {
71              case .success(let genres):
72                  self?.genres = genres // When setting self?.genres, it triggers the corresponding UI update
73              case .failure(let error):
74                  self?.presentAlert(title: "Error", message: error.message)
75              }
76          }
77      }
78    
79      /// Presents a simple alert where the user can enter the name of a genre to save it on your Back4App Database
80      func handleAddGenre() {
81          // Displays a form with a single input and executes the completion block when the user presses the submit button
82          presentForm(
83              title: "Add genre",
84              description: "Enter a description for the genre",
85              placeholder: nil
86          ) { [weak self] name in
87              guard let name = name else { return }
88              let genre = Genre(name: name)
89              
90              let query = Genre.query("name" == name)
91            
92              guard ((try? query.first()) == nil) else {
93                  self?.presentAlert(title: "Error", message: "This genre already exists.")
94                  return
95              }
96              
97              genre.save { [weak self] result in
98                  switch result {
99                  case .success(let addedGenre):
100                     self?.presentAlert(title: "Success", message: "Genre added!")
101                     self?.genres.append(addedGenre)
102                 case .failure(let error):
103                     self?.presentAlert(title: "Error", message: "Failed to save genre: \(error.message)")
104                 }
105             }
106         }
107     }
108     
109     /// Retrieves all the data saved under the Publisher class in your Back4App Database
110     func fetchPublishers() {
111         let query = Publisher.query()
112         
113         query.find { [weak self] result in
114             switch result {
115             case .success(let publishers):
116                 self?.publishers = publishers
117             case .failure(let error):
118                 self?.presentAlert(title: "Error", message: error.message)
119             }
120         }
121     }
122    
123     /// Presents a simple alert where the user can enter the name of a publisher to save it on your Back4App Database
124     func handleAddPublisher() {
125         // Displays a form with a single input and executes the completion block when the user presses the submit button
126         presentForm(
127             title: "Add publisher",
128             description: "Enter the name of the publisher",
129             placeholder: nil
130         ) { [weak self] name in
131             guard let name = name else { return }
132             
133             let query = Publisher.query("name" == name)
134             
135             guard ((try? query.first()) == nil) else {
136                 self?.presentAlert(title: "Error", message: "This publisher already exists.")
137                 return
138             }
139             
140             let publisher = Publisher(name: name)
141             
142             publisher.save { [weak self] result in
143                 switch result {
144                 case .success(let addedPublisher):
145                     self?.presentAlert(title: "Success", message: "Publisher added!")
146                     self?.publishers.append(addedPublisher)
147                 case .failure(let error):
148                     self?.presentAlert(title: "Error", message: "Failed to save publisher: \(error.message)")
149                 }
150             }
151         }
152     }
153    
154     /// Retrieves all the data saved under the Genre class in your Back4App Database
155     func fetchAuthors() {
156         let query = Author.query()
157        
158         query.find { [weak self] result in
159             switch result {
160             case .success(let authors):
161                 self?.authors = authors
162             case .failure(let error):
163                 self?.presentAlert(title: "Error", message: error.message)
164             }
165         }
166     }
167    
168     /// Presents a simple alert where the user can enter the name of an author to save it on your Back4App Database
169     func handleAddAuthor() {
170         // Displays a form with a single input and executes the completion block when the user presses the submit button
171         presentForm(
172             title: "Add author",
173             description: "Enter the name of the author",
174             placeholder: nil
175         ) { [weak self] name in
176             guard let name = name else { return }
177            
178             let query = Author.query("name" == name)
179            
180             guard ((try? query.first()) == nil) else {
181                 self?.presentAlert(title: "Error", message: "This author already exists.")
182                 return
183             }
184             
185             let author = Author(name: name)
186             
187             author.save { [weak self] result in
188                 switch result {
189                 case .success(let addedAuthor):
190                     self?.presentAlert(title: "Success", message: "Author added!")
191                     self?.authors.append(addedAuthor)
192                 case .failure(let error):
193                     self?.presentAlert(title: "Error", message: "Failed to save author: \(error.message)")
194                 }
195             }
196         }
197     }
198 }sw
```

:::hint{type="success"}
For more details about this step, you can go to the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/swift-crud-database-operations" target="_blank">basic operations guide</a>.
:::

## 3 - Setup the relations

Before starting to create relations, take a look at the [quick reference](https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/relationships#quick-reference) section to have an idea about the objects we want to relate to each other. In the figure below we show how these objects are related

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HMgGaGgW7i8acqRI5eFuN_image.png)

As can be seen, the relations are created by putting the Book object in the middle. The arrows show how each object is related to a Book object.

## 4 - Implementing relations

**1:1 case**

Adding **1:1** relations can easlity be achieved by adding a property in the Book object, i.e.,

```swift
1   struct Book: ParseObject {
2       ...
3
4       var isbn: ISBN? // Esablishes a 1:1 relation between Book and ISBN
5    
6       ...
7   }
```

In this case, Book and ISBN share a **1:1** relation where Book is identified as the parent and ISBN as the child. Internally, when Back4App saves an instance of Book, it saves the ISBN object (under the ISBN class name) first. After this process is complete, Back4App continues with the object Book. The new Book object is saved in a way that its isbn property is represented by a Pointer\<ISBN> object. A Pointer\<> object allows us to store a unique instance of the ISBN object related to its corresponding parent.

**1\:N case**

For **1\:N** relations, the most efficient way to implement them is via a ParseRelation\<Book> object. **ParseSwift** provides a set of methods to add these types of relationships for any object conforming to the ParseObject protocol. For instance, if we want to create a 1\:N relation between Book and Author we can use

```swift
1   let someBook: Book
2   let authors: [Author]
3   ...
4
5   // We create a relation (identified by the name 'authors') between someBook and a set of authors
6   let bookToAuthorsRelation = 
7   guard let bookToAuthorsRelation = try someBook.relation?.add("authors", objects: authors) // Book -> Author 
8   else {
9       fatalError("Failed to add relation")
10  }
11
12  let savedRelation = try bookToAuthorsRelation.save() // Saves the relation synchronously
13
14  bookToAuthorsRelation.save { result in // Saves the relation asynchronously
15      // Handle the result
16  }
```

It is straightforward to adapt this snippet for the other relations we have for Book.

**- Putting all together**

Once we established the basic idea to implement relations, we now complete the saveBook() method. We enumerate the key points to have in mind during this process

```swift
1   extension MainController {
2       /// Collects the data to save an instance of Book on your Back4App database.
3       func saveBook() {
4           ...        
5           // 1. First retrieve all the information for the Book (bookTitle, isbnValue, etc)
6           ...
7
8           // Since we are making multiple requests to Back4App, it is better to use synchronous methods and dispatch them on the background queue
9           DispatchQueue.global(qos: .background).async {
10              do {
11                  let isbn = ISBN(value: isbnValue) // 2. Instantiate a new ISBN object
12                
13                  let savedBook = try Book( // 3. Instantiate a new Book object with the corresponding input fields
14                      title: bookTitle,
15                      publishingYear: publishingYear,
16                      genre: genre,
17                      isbn: isbn
18                  ).save() // 4. Save the new Book object
19                
20                  // 5. Add the corresponding relations for new Book object
21                  guard let bookToAuthorsRelation = try savedBook.relation?.add("authors", objects: authors), // Book -> Author
22                        let bootkToPublishersRelation = try savedBook.relation?.add("publishers", objects: publishers), // Book -> Publisher
23                        let genreRelation = try genre.relation?.add("books", objects: [savedBook]) // Genre -> Book
24                  else {
25                      return DispatchQueue.main.async {
26                          self.presentAlert(title: "Error", message: "Failed to add relations")
27                      }
28                  }
29                                 
30                  // 6. Save the relations
31                  _ = try bookToAuthorsRelation.save()
32                  _ = try bootkToPublishersRelation.save()
33                  _ = try genreRelation.save()
34                 
35                  DispatchQueue.main.async {
36                      self.presentAlert(title: "Success", message: "Book saved successfully.")
37                  }
38              } catch {
39                  DispatchQueue.main.async {
40                      self.presentAlert(title: "Error", message: "Failed to save book: \((error as! ParseError).message)")
41                  }
42              }
43          }
44      }
45
46      ...
47  }
```

## 5 - Querying relations

For **1:1** relations, given the parent Book and its child ISBN, we query the corresponding child by including it in the Book query

```swift
1   let query = Book.query().include("isbn")
2
3   let books = try query.find() // Retrieves synchronously all the books together with its isbn
4
5   query.find { result in // Retrieves asynchronously all the books together with its isbn
6       // Handle the result
7   }
8
```

With this, all the books from the **query** will also have the **isbn** property set properly with the related **ISBN** object.

On the other hand, in order to retrieve objects with a **1\:N** relation, the **ParseObject** protocol provides the static method **queryRelation(\_,parent:)**. By providing the name of the relation (as the first parameter) and the parent, this method allows us to create the required query. For instance, to retrieve all the **Author**’s related to a specific **Book** we can use the following snippet

```swift
1   let book: Book // Book from which we are trying to retrieve its related authors
2
3   do {
4       let authorsQuery = try Author.queryRelations("authors", parent: book) // 'authors' is the name of the relation it was saved with
5        
6       authorsQuery.find { [weak self] result in
7           switch result {
8           case .success(let authors):
9               self?.authors = authors
10                    
11              DispatchQueue.main.async {
12                  // Update the UI
13              }
14              case .failure(let error):
15                  DispatchQueue.main.async {
16                      self?.presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
17                  }
18              }
19          }
20      }
21  } catch {
22      if let error = error as? ParseError {
23          presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
24      } else {
25          presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.localizedDescription)")
26      }
27  }
```

Similarly, we can query other related objects such as **Publisher**.

In the **BookDetailsController.swift** file we implement these queries to display the relation a book has with authors and publishers

```swift
1  // BookDetailsController.swift file
2  class BookDetailsController: UITableViewController {
3      ...
4    
5      /// Retrieves the book's details, i.e., its relation with authors and publishers
6      private func fetchDetails() {
7          do {
8              // Constructs the relations you want to query
9              let publishersQuery = try Publisher.queryRelations("publishers", parent: book)
10              let authorsQuery = try Author.queryRelations("authors", parent: book)
11            
12              // Obtains the publishers related to book and display them on the tableView, it presents an error if happened.
13              publishersQuery.find { [weak self] result in
14                  switch result {
15                  case .success(let publishers):
16                      self?.publishers = publishers
17                    
18                      // Update the UI
19                      DispatchQueue.main.async {
20                          self?.tableView.reloadSections(IndexSet([Section.publisher.rawValue]), with: .none)
21                      }
22                  case .failure(let error):
23                      DispatchQueue.main.async {
24                          self?.presentAlert(title: "Error", message: "Failed to retrieve publishers: \(error.message)")
25                      }
26                  }
27              }
28            
29              // Obtains the authors related to book and display them on the tableView, it presents an error if happened.
30              authorsQuery.find { [weak self] result in
31                  switch result {
32                  case .success(let authors):
33                      self?.authors = authors
34                    
35                      // Update the UI
36                      DispatchQueue.main.async {
37                          self?.tableView.reloadSections(IndexSet([Section.author.rawValue]), with: .none)
38                      }
39                  case .failure(let error):
40                      DispatchQueue.main.async {
41                          self?.presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
42                      }
43                  }
44              }
45          } catch { // If there was an error during the creation of the queries, this block should catch it
46              if let error = error as? ParseError {
47                  presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.message)")
48              } else {
49                  presentAlert(title: "Error", message: "Failed to retrieve authors: \(error.localizedDescription)")
50              }
51          }
52      }
53    
54      ...
55  }
```

## 6 - Run the app!

:::hint{type="info"}
Before pressing the run button on XCode, do not forget to configure your Back4App application in the AppDelegate** **class!
:::

You have to add a couple of **Genre**’s, **Publisher**’s and **Author**’s before adding a new book. Then, you can start entering a book’s information to save it on your Back4App Database. Once you have saved one Book, open your [Back4App dashboard](https://parse-dashboard.back4app.com/apps) and go to your application linked to the **XCode** project. In the Database section, you will find the class Book where all the books created by the iOS App are stored.

Additionally, you can see that Back4App automatically created the class ISBN in order to relate it with its corresponding Book object. If you go back to the Book class, you can identify the data types for each type of relation. In the case of ISBN and Genre, the data type is a Pointer\<>. On the other hand, for relations like Author and Publisher, the data type is Relation\<>. This is a key difference to have in mind when constructing relations.

[title] Cloud Code Functions
[path] Android/

# How to create and deploy your Parse Cloud Code

## Introduction

For complex apps, sometimes you need a bit of logic that isn’t running on the mobile device. **Cloud Code** makes it possible.

Cloud Code is built on the same JavaScript SDK that powers thousands of apps. The only
difference is that this code runs in your Parse Server rather than running on the user’s
mobile device. When you update the Cloud Code, it becomes available to all mobile environments
instantly and you don’t have to wait until a new release of your application comes up. This lets you change
app behavior on the fly and also lets you add new features on your app faster.

This section explains how to create and deploy Cloud Code, followed by how to call a cloud function
in Android projects through Back4App.

Even if you’re only familiar with mobile development, we hope you’ll find Cloud Code
straightforward and easy to use.

:::hint{type="info"}
You can find more in-depth information in <a href="https://docs.parseplatform.org/cloudcode/guide/" target="_blank">Parse Official Cloud Code Documentation</a>.
:::

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.0 (Ice Cream Sandwich) or newer.
:::

## 1 - Create a Cloud Code File

Create a new file and name it main.js and add the following Parse.Cloud.define function, which has its name and a callback as arguments.

:::hint{type="info"}
You can pass parameters to your Cloud function from your Android App and access then within the
&#x20;request.params object.
:::

:::CodeblockTabs
Parse Server 3.X

```javascript
//main.js
1   Parse.Cloud.define("test", (request) => {
2       var text = "hello world";
3       var jsonObject = {
4           "answer": text
5       };
6       return jsonObject
7   });
```

Parse Server 2.X

```javascript
//main.js
1   Parse.Cloud.define("test", function(request, response) {
2       var text = "hello world";
3       var jsonObject = {
4           "answer": text
5       };
6       response.success(jsonObject);
7   });
```
:::

## 2 - Upload to Cloud Code

1. Go to your App at <a href="https://www.back4app.com/" target="_blank">Back4App website </a>and click on Dashboard.
2. Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0DZPwtZliAPrFFFKX5qZB_image.png)

3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click at Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AQkVxTJ2saNB_PnsEAvHs_image.png)

## 3 - Add Android Code

Import the following dependecies:

> *1   // Front End Dependencies*
>
>
> 2   import android.widget.Toast
>
> **;**
>
>
>
>
> *3   // Parse Dependencies*
>
>
> 4   import com.parse.FunctionCallback
>
> **;**
>
>
> 5   import com.parse.ParseCloud
>
> **;**
>
>
> 6   import com.parse.ParseException
>
> **;**
>
>
>
>
> *7   // Java Dependencies*
>
>
> 8   import java.util.HashMap
>
> **;**
>
>
> 9   import java.util.Map
>
> **;**

To call your Cloud Code function, you need to call a special android function: ParseCloud.callFunctionInBackground.
Its first parameter is the **function name on Cloud Code** and the second one is the **HashMap**
that has every parameter that will be passed to the function. The third argument is the **callback**
that will be executed after the function has been called.

The following code calls the function:

```javascript
1   // Use this map to send parameters to your Cloud Code function
2   // Just push the parameters you want into it
3   Map<String, String> parameters = new HashMap<String, String>();
4
5   // This calls the function in the Cloud Code
6   ParseCloud.callFunctionInBackground("test", parameters, new FunctionCallback<Map<String, Object>>() {
7       @Override
8       public void done(Map<String, Object> mapObject, ParseException e) {
9           if (e == null) {
10              // Everything is alright
11              Toast.makeText(MainActivity.this, "Answer = " + mapObject.get("answer").toString(), Toast.LENGTH_LONG).show();
12          }
13          else {
14              // Something went wrong
15          }
16      }
17  });
```

:::hint{type="info"}
In this function, the mapObject has a key called answer, which contains the value hello world,
which will be printed on the screen by the Toast class when the code is executed.
:::

## It’s done!

At this stage, you are able to code and call your own Cloud Code in your Android App using Parse Server Core features through Back4App!

[title] Start from Template
[path] Flutter/GraphQL/

# Download a Flutter GraphQL project with source code and start using Back4App



## Introduction

In this tutorial, we are going to build an application that would parse data from Back4App backend through GraphQL. As you may know, GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. Back4App is a low code backend as a Service(based on Open Source Parse Platform) which is helping developers to build extensible and scalable mobile and web applications at a rapid pace.

## Goals

The main goal is to build a simple app that is going to show a list of programming languages and their save type format.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_-4cKpBMuxZgozn0f7GK8_image.png" signedSrc size="50" width="249" height="441" position="center" caption}

At the end of this article we expect that you will be able to:

- **Make an API call on Back4App using GraphQL;**
- **Get the data from GraphQL API;**
- **Fetch data in your Flutter App.**

### Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An IDE for writing Flutter code like Android Studio or Flutter. However we will be using Android Studio for this tutorial.
- A Back4App account that can be created here: <a href="https://www.back4app.com/" target="_blank">Back4App</a>
- [GraphQL\_flutter](https://pub.dev/packages/graphql_flutter)
:::

## 1 -  Clone project from Github to Android Studio

Go to the [Github repo](https://github.com/templates-back4app/Flutter-GraphQL), and download the ZIP file, extract it, and open it in your flutter IDE.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pWqIPMznW1Jcd-AyBCc3C_image.png)

To complete this step, drag the folder inside the zip file into your desktop, open Android Studio and then click on Open existing Android Studio Project. The project directory over would be usually ‘*C:\Users\Username\Desktop\back4App\_GraphQL-starting\_code*’.

But this may be different for different users.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NPwmVfaQbmfUk6XN_XTkq_image.png)

Open the project and go to the lib\main.dart file. You will see so many errors there, don’t worry, just press all ‘*Get dependencies*’ and all the errors would perish. If doesn’t then just press ‘*Upgrade dependencies*’.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Zot364iTdLBDGfAi7pihY_image.png)

The code in your main.dart should look like this.

```dart
1   import 'package:flutter/material.dart';
2   import 'package:graphql_flutter/graphql_flutter.dart';
3   import 'consonents.dart';
4
5   void main() {
6     runApp(MyApp());
7   }
8
9   class MyApp extends StatelessWidget {
10    @override
11    Widget build(BuildContext context) {
12      return MaterialApp(
13        home: MyHomePage(),
14        ),
15      );
16    }
17  }
18  class MyHomePage extends StatefulWidget {
19    @override
20    _MyHomePageState createState() => _MyHomePageState();
21  }
22
23  class _MyHomePageState extends State<MyHomePage> {
24  
25    @override
26    Widget build(BuildContext context) {
27      return SafeArea(
28        child: Scaffold(
29          appBar: AppBar(
30            title: Text('Parsing data using GraphQL',
31            ),
32          ),
33          body: Container(),
34      ),);
35    }
36  }
```

If you open Run the app now you would only see an empty Screen in your emulator/device with only an AppBar titled *‘Parsing data using GraphQL’*.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gvYh8hgqNlREnr-l-q-KR_image.png" signedSrc size="50" width="247" height="442" position="center" caption}

We will work on main.dart file and our important values are stored in consonents.dart and we can directly use it from there. We will require the graphql\_flutter dependency to use graphQL in our app.

:::hint{type="info"}
The graphql\_flutter is most popular GraphQL cliet for flutter. It helps us to use *GraphQL* queries directly in our code. It provides us with GraphQLClient, GraphQLProvider and many other useful widgets that helps us to parse data from our database directly with the help of GraphQL without even using StreamBuilder. The package provides us with plenty of features including:

- Subscriptions
- In memory cache
- Offline cache sync
- Optimistic results
- GraphQl file uploads
:::

You can import it by writing the following code in pubspec.yaml file:

> dependencies:
>   graphql_flutter: 
>
> **^**
>
> 3.1.0

:::hint{type="info"}
See more about graphql\_flutter at <a href="https://pub.dev/packages/graphql_flutter" target="_blank">GraphQL Flutter Documentation</a>
:::

All dependencies are already pre-installed and you are good to proceed to the next step now.

## 2 - Creating backend in Back4App

After you have signed up at [Back4App](https://www.back4app.com/) website, u can proceed to the next step and create a new app. Click on *‘Build new App’*. Give it the same name as your project name which over here is *‘back4app\_Graphql’*.

Now scroll down to *Server settings* on your left and select *Settings* Manage Parse Server Card, then select the option *‘3.10.0 - Parse Server 3.10.0’* from the list. Now press the save button below and wait until it gets saved.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IvwhiBtWYUgBFYASsT2z-_image.png)

Now come back to *Core* (on the left), select the *API console*, and select *GraphQL Console* from it.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/h900ERbA5Rem8OAJb9c8q_image.png)

This is the window where you can write and test your GraphQL queries/mutations code. Let’s proceed to the next step.

## 3 - Creating and getting data through GraphQL

Now, let’s test the GraphQL API on Back4App using the GraphQL API Console. First, paste the following query on the left code-box:

```graphql
1       mutation CreateClass {
2   		    createClass(input:{
3   	    name: "Language"
4    	    schemaFields: {
5   	      addStrings: [{name: "name"}{name: "saveFormat"}]
6   	    }
7   	  }){
8   	    class{
9   	      schemaFields{
10  		name
11   		__typename
12  	      }
13  	    }
14  	  }
15  	}
16
```

The code above will create a class named “Language”. Let’s populate this new class with some rows:

```graphql
1       mutation CreateObject{
2   	  createLanguage(input:{fields: {name: "Python", saveFormat:".py"}}){
3   	    language{
4   	      name,
5   	      saveFormat
6   	    }
7   	  }
8   	}
```

If your operation is successful you’ll see this message on the right code-box on GraphQL PlayGround:

```graphql
1       {
2   	  "data": {
3   	    "createLanguage": {
4    	      "language": {
5    		    "name": "Python",
6   		      "saveFormat": ".py"
7   	      }
8   	    }
9   	  }
10   	}
```

Mutations are used to create or make changes on a class. By running the above mutation, we’ll create a new class named Language with data fields:

- **name: “Python”**
- **saveFormat: “.py”**

Repeat the process and create two more objects in the same Class for:

- **name: “C” and saveFormat: “.c”**
- **name: “java” and saveFormat: “.java”.**

The mutation for this will be such:

```graphql
1       mutation CreateObject{
2   	  createLanguage(input:{fields: {name: "C", saveFormat:".c"}}){
3    	    language{
4    	      name,
5    	      saveFormat
6    	    }
7   	  }
8    	}
```

Java:

```graphql
1       mutation CreateObject{
2   	  createLanguage(input:{fields: {name: "Java", saveFormat:".java"}}){
3   	    language{
4   	      name,
5    	      saveFormat
6    	    }
7   	  }
8   	}
```

Now let’s see all the data in our class *Languages*. For reading data we use*query*. So go ahead and type the below command.

```graphql
1       query FindLanguages{
2   	  languages{
3   	    count,
4   	    edges{
5   	      node{
6   		name,
7    		saveFormat
8   	      }
9   	    }
10  	  }
11  	}
```

In query FindLanguage ‘FindLanguage’ is just a name for your query command and you could even name it anything else. We use the find(className: "") command to find all the elements of the specific Class. count, returns the number of elements in the Class and all the elements are shown inside the result object.

The above query is going to return this:

```graphql
1       {
2   	  "data": {
3   	    "languages": {
4   	      "count": 3,
5   	      "edges": [
6   		{
7   		  "node": {
8   		    "name": "Python",
9   		    "saveFormat": ".py"
10  		  }
11  		},
12  		{
13  		  "node": {
14  		    "name": "C",
15  		    "saveFormat": ".c"
16  		  }
17  		},
18  		{
19  		  "node": {
20  		    "name": "Java",
21  		    "saveFormat": ".java"
22  		  }
23  		}
24  	      ]
25  	    }
26  	  }
27  	}
```

You can see all other queries in the following link:
[GraphQL queries - Back4App](https://blog.back4app.com/graphql-on-parse/)

Now, let’s proceed to the next step.

## 4 - Setting up GraphQL in our App

Let’s start coding our app. Before this, you must do few things in your lib\consonents.dart file:

1. Copy graphql link which is next to the history button in the top of GraphQL window, and paste it in as kUrl string datatype.
2. Now move to the bottom of the page and copy the codes from HTTP Headers, copy only the codes on the right of the colon(:) and paste them with their respective names in the lib\consonents.dart file:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ysPVIOchEBFZ182itNksQ_image.png)

The file should have following code:

```graphql
1       String kParseApplicationId= "APPLICATION_ID_COPIED_FROM_HEADERS";
2    	String kParseClientKey = "CLIENT_KEY_COPIED_FROM_HEADER";
3   	String kUrl= "URL_COPIED";
4   	// replace "APPLICATION_ID_COPIED_FROM_HEADERS", "CLIENT_KEY_COPIED_FROM_HEADER", "URL_COPIED" with real keys/ids copied 
5   	//from Http Headers tab.
```

Now move to main.dart file and head over to MyApp Stateless Widget and add the following code just above the return MaterialApp():

```graphql
1       final HttpLink httpLink = HttpLink(
2         uri: kUrl,
3         headers: {
4           'X-Parse-Application-Id' : kParseApplicationId,
5           'X-Parse-Client-Key' : kParseClientKey,    
6         },
7       );
```

**HttpLink** is from *flutter\_graphql.dart* and takes the widget HttpLink() with two parameters.The first is the GraphQL API Url on Back4App. The second are the headers necessary to authenticate on Back4App API. After this section you need to include the GraphQL Client Code (explain what is), copy the code below and then paste under the HttpLink section:

```graphql
1       ValueNotifier<GraphQLClient> client = ValueNotifier(
2   	      GraphQLClient(
3   		    cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
4   		    link: httpLink,
5   	      ),
6    	    );
```

Now we’ve provided the link and cache method to our GraphQLClient. We have done this through our ValueNotifier and named it as client. Let’s wrap the MyHomePage() widget which is a child of MaterialApp with GraphQLProvider and pass MyHomePage() as its client. Add an another parameter inside GraphQLProvider namely client and pass client(name of our ValueNotifier) in it. This is how your MyApp class should look now:

```graphql
1   class MyApp extends StatelessWidget {
2     @override
3     Widget build(BuildContext context) {
4       final HttpLink httpLink = HttpLink(
5         uri: kUrl,
6         headers: {
7           'X-Parse-Application-Id' : kParseApplicationId,
8           'X-Parse-Client-Key' : kParseClientKey,        
9           //'X-Parse-REST-API-Key' : kParseRestApiKey,
10        },//getheaders()
11      );
12      ValueNotifier<GraphQLClient> client = ValueNotifier(
13        GraphQLClient(
14          cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
15          link: httpLink,
16        ),
17      );
18      return MaterialApp(
19        home: GraphQLProvider(
20          child: MyHomePage(),
21          client: client,
22        ),
23      );
24    }
25  }
```

Let’s call the API and get the data

## 5 - Making an API call

Now we will work on the MyHomePageState. We’re going to start by initialising a String data type named ‘query’ and assign/pass the query statement for finding all the data from our Language class to it. Since the query is multi-line, we will pass the query in triple quotes. Here’s how it looks like:

```graphql
1   String query='''
2   query FindLanguages{
3   languages{
4     count,
5     edges{
6       node{
7         name,
8         saveFormat
9       }
10    }
11  }
12 }
13  ''';
```

Now go to Query() widget, inside the body parameter of the Scaffold which have two properties:

- ***options***
- ***builder***

And then pass null for both. This is how your build method would look like:

```graphql
1     Widget build(BuildContext context) {
2       return SafeArea(
3         child: Scaffold(
4           appBar: AppBar(
5             title: Text('Parsing data using GraphQL',
6             ),
7           ),
8             body: Query(
9                 options: null,
10                builder: null
11            ),
12      ),);
13    }
```

The Query() widget helps us write queries for GraphQL and will help us read and get data. We will pass the query statement which we have taken as a string in the options and build the widget with the builder parameter.

So pass the QueryOptions() widget in options as follows:

```graphql
1   options: QueryOptions(
2               documentNode: gql(query),
3             ),
4
```

The query is passed through documentNode parameter in QueryOptions. Now let’s build with the help of builder Parameter. The builder method accepts a function with three parameters namely,

- **QueryResult result;**
- **Refetch refetch;**
- **FetchMore fetchmore.**

Right now, we only need to worry about the QueryResult which gives us the result of our query and we can access the data through result.data.
So let’s code the following below:

```graphql
1   builder : (QueryResult result, { Refetch refetch,FetchMore fetchMore,})
2            {
3              if(result.data==null){
4                return Center(child: Text("Loading...",style: TextStyle(fontSize: 20.0),));
5              }else{
6                return Text('SUCCESS');
7              }
8            },
```

In the above code we are accessing the data. If there’s no data we return a text widget reading *‘Loading…’* else we will return a Text() widget reading *‘SUCCESS’*

Here’s how your myHomePage class in main.dart should look like:

```dart
1   class MyHomePage extends StatefulWidget {
2     @override
3     _MyHomePageState createState() => _MyHomePageState();
4   }
5
6   class _MyHomePageState extends State<MyHomePage> {
7     String name;
8     String saveFormat;
9     String objectId;
10   
11    String query = '''
12    query FindLanguages{
13    languages{
14      count,
15      edges{
16        node{
17          name,
18          saveFormat
19        }
20      }
21    }
22  }
23    ''';
24  
25    @override
26    Widget build(BuildContext context) {
27      return SafeArea(
28        child: Scaffold(
29          appBar: AppBar(
30            title: Text(
31              'Parsing data using GraphQL',
32            ),
33          ),
34          body: Query(
35              options: QueryOptions(
36                documentNode: gql(query),
37              ),
38              builder: (
39                  QueryResult result, {
40                    Refetch refetch,
41                    FetchMore fetchMore,
42                  }) {
43                if (result.data == null) {
44                  return Center(
45                    child: Text(
46                      "Loading...",
47                      style: TextStyle(fontSize: 20.0),
48                    ),
49                  );
50                } else{
51                  return Text('SUCCESS');
52                }
53              }
54          ),
55        ),
56      );
57    }
58  }
```

Now, start the App and wait for a few seconds after it restarts. If you see *‘SUCCESS’* on the screen then ***Congrats! You have established your connection and called the API.***

## 6 - Getting and showing Data from API

Instead of returning the Text widget, we will return the *ListView\.builder()* widget with original data. Write the following code instead of return Text('SUCCESS').

```graphql
1   return ListView.builder(
2                       itemBuilder: (BuildContext context, int index){
3                         return ListTile(
4                           title: Text(result.data["languages"]["edges"][index]["node"]['name']),
5                           trailing: Text(result.data["languages"]["edges"][index]["node"]['saveFormat']),
6                         );
7                       },
8                       itemCount: result.data["languages"]["edges"].length,
9                     );
```

Now if you look back to your GraphQL result screen in your API console of Back4App where we typed our find method, the results were such:

```graphql
1   {
2     "data": {
3       "languages": {
4         "count": 3,
5         "edges": [
6           {
7             "node": {
8               "name": "Python",
9               "saveFormat": ".py"
10            }
11          },
12          {
13            "node": {
14              "name": "C",
15              "saveFormat": ".c"
16            }
17          },
18          {
19            "node": {
20              "name": "Java",
21              "saveFormat": ".java"
22            }
23          }
24        ]
25      }
26    }
27  }
```

So from above code the location of “Python” was:

- *“data”-> *“*languages” -> “count” -> “edges” -> “node” -> “name”*. Also notice name is inside square brackets ‘\[]’ of edges which symbolizes it is the first element of edges list/array.

So we need to enter into this location to get “Python” and same for everything else. When we write result.data, we enter the *“data”* location. So to give the other locations we add \["location\_name"] to it. So the location of “Python” will be result.data\["languages"]\["edges"]\[0]\["node"]\["name"].

When using ListView, it takes two parameters, itemCount, it tells the number of elements in the API call, itemBuilder, it takes a function with parameters (BuildContext context, int index) and returns a list widgets in which we will be showing the data. Here, we will use List of ListTile to show the data:

```dart
1   return ListView.builder(
2                        itemCount: result.data["languages"]["edges"].length,
3                       itemBuilder: (BuildContext context, int index){
4                         return ListTile(
5                           title: Text(result.data["languages"]["edges"][index]["node"]['name']),
6                           trailing: Text(result.data["languages"]["edges"][index]["node"]['saveFormat']),
7                         );
8                       },
9                       
10                    );
```

When we replace Text('SUCCESS') with the above `ListView.builder()` widget, we first pass the itemCount where we pass the number of elements in the results List and so we don’t need to worry about that anymore. In itemBuilder we are returning a list of ListTiles which will have the "name" of the "Languages" class and in "saveFormat" in the trailing. Notice we used index instead of any number after result, this is what itemCount took care of.

This is how your main.dart should look like now:

```dart
1   import 'package:flutter/material.dart';
2   import 'package:graphql_flutter/graphql_flutter.dart';
3   import 'consonents.dart';
4   import 'dart:ui';
5
6   void main() {
7     runApp(MyApp());
8   }
9 
10  class MyApp extends StatelessWidget {
11    @override
12    Widget build(BuildContext context) {
13      final HttpLink httpLink = HttpLink(
14        uri: 'https://parseapi.back4app.com/graphql',
15        headers: {
16          'X-Parse-Application-Id': kParseApplicationId,
17          'X-Parse-Client-Key': kParseClientKey,        
18        }, //getheaders()
19      );
20  
21      ValueNotifier<GraphQLClient> client = ValueNotifier(
22        GraphQLClient(
23          cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
24          link: httpLink,
25        ),
26      );
27 
28      return MaterialApp(
29        home: GraphQLProvider(
30          child: MyHomePage(),
31          client: client,
32        ),
33      );
34    }
35  }
36 
37  class MyHomePage extends StatefulWidget {
38    @override
39    _MyHomePageState createState() => _MyHomePageState();
40  }
41
42  class _MyHomePageState extends State<MyHomePage> {
43    String name;
44    String saveFormat;
45    String objectId;
46  
47    String query = '''
48    query FindLanguages{
49    languages{
50      count,
51      edges{
52        node{
53          name,
54          saveFormat
55        }
56      }
57    }
58  }
59    ''';
60
61    @override
62    Widget build(BuildContext context) {
63      return SafeArea(
64        child: Scaffold(
65          appBar: AppBar(
66            title: Text(
67              'Parsing data using GraphQL',
68            ),
69          ),
70          body: Query(
71            options: QueryOptions(
72              documentNode: gql(query),
73            ),
74            builder: (
75              QueryResult result, {
76              Refetch refetch,
77              FetchMore fetchMore,
78            }) {
79              if (result.data == null) {
80                return Center(
81                    child: Text(
82                  "Loading...",
83                  style: TextStyle(fontSize: 20.0),
84                ));
85              } else {
86                return ListView.builder(
87                  itemBuilder: (BuildContext context, int index) {
88                    return ListTile(
89                      title: Text(result.data["languages"]["edges"][index]["node"]
90                          ['name']),
91                      trailing: Text(result.data["languages"]["edges"][index]
92                          ["node"]['saveFormat']),
93                    );
94                  },
95                  itemCount: result.data["languages"]["edges"].length,
96                );
97              }
98            },
99          ),
100       ),
101     );
102   }
103 }
```

And our final App Screen:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4US1H2nsHrXZvtZzrZw3k_image.png" signedSrc size="50" width="249" height="441" position="center" caption}

## Conclusion

Now you have a Flutter App connected to a GraphQL API that can store and retrieve data on Back4App.

We did not require to encode or decode the json data separately which makes our work easier and faster using few lines of codes.

**Have a great day!**

[title] User Registration
[path] iOS/Parse Swift SDK/Users/

# User Registration

## Introduction

Most real-world applications often utilize user-based features to provide a more personalized service to clients. These functionalities require the client to sign up on the app. With the **Back4App** platform and the ParseSwift SDK, you will be able to implement those features in your apps simply and quickly.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
- A basic iOS App to test queries
:::

## Goal

To implement a user registration feature on an iOS App using the ParseSwift SDK.

## 1 - Understanding the user registration flow

In order to integrate a signup option on an iOS app, it is necessary to create an object that conforms to the ParseUser protocol. This protocol implements the main required properties so that **Back4App** is able to store and manage login information. The following snippet shows how a user object can be implemented:

```swift
1   import Foundation
2   import ParseSwift
3
4   struct User: ParseUser {
5     // Additional properties required by the ParseUser protocol
6     var authData: [String : [String : String]?]?
7     var originalData: Data?
8     var objectId: String?
9     var createdAt: Date?
10    var updatedAt: Date?
11    var ACL: ParseACL?
12  
13    // Main properties linked to the user's information
14    var username: String?
15    var email: String?
16    var emailVerified: Bool?
17    var password: String?
18
19    // A custom property
20    var age: Int?
21  }
```

As it can be seen in the above snippet, ParseSwift allows us to have a very flexible implementation for a user object. Similar to the age field, we can add as many additional fields as needed.

Once we have the User object ready, the ParseUser protocol gives this object a set of methods to handle all the necessary user operations such as **sign up**, **log in**, **log out**, etc.

In the following step, we first take a look at how to implement a signup request.

## 2 - Creating a signup request

We start by adding the corresponding form where the user enters their account information. Let ViewController (a subclass of UIViewController) be the class where we implement the form. In the snippet below, we remark the key elements a basic signup form should have:

```swift
1   import UIKit
2   import ParseSwift
3
4   class ViewController: UIViewController {
5     // User inputs
6     @IBOutlet weak var usernameTextField: UITextField!
7     @IBOutlet weak var emailTextField: UITextField!
8     @IBOutlet weak var passwordTextField: UITextField!
9  
10    // Sign up button
11   @IBOutlet weak var signUpButton: UIButton!
12     
13    override func viewDidLoad() {
14      super.viewDidLoad()
15        
16      // Add additional code if needed
17    }
18
19    // Called when the user taps on the signUpButton
20    @IBAction func handleSignUp(_ sender: Any) {
21      guard let username = usernameTextField.text, let password = passwordTextField.text else {
22        return showMessage(title: "Error", message: "The credentials are not valid.")
23      }
24        
25      signUp(username: username, email: emailTextField.text, password: password)
26    }
27    
28    // This method prepares and registers the new user
29    private func signUp(username: String, email: String?, password: String) {
30      // TODO: Here we will implement the signup action
31    }
32    
33    // Presents an alert with a title, a message and a back button
34    func showMessage(title: String, message: String) {
35      let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
36          
37      alertController.addAction(UIAlertAction(title: "Back", style: .cancel))
38        
39      present(alertController, animated: true)
40    }
41  }
```

We leave the layout part to the developer. You can integrate and set up the visual components according to your needs.

Next, in the following step, we implement the signUp(username\:email\:password:) method.

## 3 - Implementing the signup function

The first step for signing up a user is to have an instance of a User object with its corresponding credentials. The username and the password fields are mandatory to register a new user, the remaining fields are optional. Therefore, a typical way to instantiate a User object would be:

```swift
1   var newUser = User(username: "aCoolUsername", email: "myEmail@domain.net", password: "mySecurePassword")
2   newUser.age = 25
```

Additionally, we should also provide the initial values for the custom fields, like the age field in our case.

The next step is to perform the signup action. The ParseUser protocol implements the method signup(...) that will allow us to send the signup request to the **Back4App** application. There are three types of implementations for signup(...). Depending on the use case, one should pick the appropriate one.
Now, we can complete the signUp(username\:email\:password:) in ViewController:

```swift
1   class ViewController: UIViewController {
2     ...
3
4     private func signUp(username: String, email: String?, password: String) {
5       var newUser = User(username: username, email: email, password: password)
6       newUser.age = 25 // WARNING: This should be entered by the user, not hardcoded
7
8      //WARNING: ONLY USE ONE OF THE FOLLOWING CASES, THE SYNCHRONOUS OR THE ASYNCHRONOUS CASE  
9
10     // The below registers the user synchronously and returns the updated User object (stored on your Back4App application)
11    do {
12        let signedUpUser = try newUser.signup()
13        showMessage(title: "Signup succeeded", message: "\(user)")
14        usernameTextField.text = nil
15        emailTextField.text = nil
16        passwordTextField.text = nil
17      } catch let error as ParseError {
18        showMessage(title: "Error", message: error.message)
19      } catch {
20        showMessage(title: "Error", message: error.localizedDescription)
21      }
22  
23      // The below registers the user asynchronously and returns the updated User object (stored on your Back4App application) wrapped in a Result<User, ParseError> object
24      newUser.signup { [weak self] result in
25        switch result {
26          case .success(let signedUpUser):
27            self?.showMessage(title: "Signup succeeded", message: "\(signedUpUser)")
28            self?.usernameTextField.text = nil
29            self?.emailTextField.text = nil
30            self?.passwordTextField.text = nil
31          case .failure(let error):
32            self?.showMessage(title: "Error", message: error.message)
33        }
34      }
35    }
36  }
```

:::hint{type="info"}
**Note:**Registering a new user using thesignup(...)method automatically logs in the user, so there is no need for the user to log in again to continue using your app.
:::

At any time during your app’s lifecycle, you can have access to the currently logged-in user from a **static** property implemented in the ParseUser protocol

```swift
1   let loggedInUser: User? = User.current
```

In <a href="https://github.com/templates-back4app/ios-user-registration-app" target="_blank">this repository</a> you can find a simple user registration app that follows the steps we detailed above.

## Conclusion

The **Back4App** platform together with the ParseSwift SDK offers a quick and staightforward way to integrate a signup flow onto your iOS apps. Furthermore, in the following guides, we will explore the remaining procedures like log in, log out, etc.

[title] Current User
[path] Flutter/Parse SDK (REST)/User Authentication/Authentication/

# Get current User on session

## Introduction

It would not be enjoyable if the user had to log in every time they open your app. You can avoid this by using the cached current ParseUser object. Whenever you use any signup or login methods, the user is cached locally. You can manage this cache as a session, and automatically assume the user is logged in.
In this guide, you will learn how to use the **Flutter plugin for Parse Server** to get current User on session using ParseUser class for your Flutter App.

## Goal

Get current User on session using Parse for a Flutter App.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- A Flutter app created and connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- Complete the previous guide so ou can have a better understanding of the ParseUser class.
- A device (not Simulator) running Android or iOS.
:::

## Understanding the Get Current User App

To better understand the get current User on session process, we will create an app to signUp, login e logout a user. The application is similar to the previous guide, where we perform the signup, login, and logout. As we’re going to use the same project in the following guides, you can find some not yet available functions.
We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse. Following the next steps, you will build a Login e Logout App at Back4App Database.

## Let’s get started!

In the following steps, you will be able to build app a get current user.

## 1 - Create the Login/Logout App Template

Open your Flutter project from the previous guide **Flutter plugin for Parse Server**. Go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/material.dart';
2   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
3
4   void main() async {
5     WidgetsFlutterBinding.ensureInitialized();
6
7     final keyApplicationId = 'YOUR_APP_ID_HERE';
8     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
9     final keyParseServerUrl = 'https://parseapi.back4app.com';
10
11    await Parse().initialize(keyApplicationId, keyParseServerUrl,
12        clientKey: keyClientKey,
13        debug: true);
14
15    runApp(MyApp());
16  }
17
18  class MyApp extends StatelessWidget {
19    Future<bool> hasUserLogged() async {
20      return Future.value(false);
21    }
22
23    @override
24    Widget build(BuildContext context) {
25      return MaterialApp(
26        title: 'Flutter - Parse Server',
27        theme: ThemeData(
28          primarySwatch: Colors.blue,
29          visualDensity: VisualDensity.adaptivePlatformDensity,
30        ),
31        home: FutureBuilder<bool>(
32            future: hasUserLogged(),
33            builder: (context, snapshot) {
34              switch (snapshot.connectionState) {
35                case ConnectionState.none:
36                case ConnectionState.waiting:
37                  return Scaffold(
38                    body: Center(
39                      child: Container(
40                          width: 100,
41                          height: 100,
42                          child: CircularProgressIndicator()),
43                    ),
44                  );
45                default:
46                  if (snapshot.hasData && snapshot.data!) {
47                    return UserPage();
48                  } else {
49                    return LoginPage();
50                  }
51              }
52            }),
53      );
54    }
55  }
56
57  class LoginPage extends StatefulWidget {
58    @override
59    _LoginPageState createState() => _LoginPageState();
60  }
61
62  class _LoginPageState extends State<LoginPage> {
63    final controllerUsername = TextEditingController();
64    final controllerPassword = TextEditingController();
65    bool isLoggedIn = false;
66
67    @override
68    Widget build(BuildContext context) {
69      return Scaffold(
70          appBar: AppBar(
71            title: const Text('Flutter - Parse Server'),
72          ),
73          body: Center(
74            child: SingleChildScrollView(
75              padding: const EdgeInsets.all(8),
76              child: Column(
77                crossAxisAlignment: CrossAxisAlignment.stretch,
78                children: [
79                  Container(
80                    height: 200,
81                    child: Image.network(
82                        'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
83                  ),
84                  Center(
85                    child: const Text('Flutter on Back4App',
86                        style:
87                            TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
88                  ),
89                  SizedBox(
90                    height: 16,
91                  ),
92                  TextField(
93                    controller: controllerUsername,
94                    enabled: !isLoggedIn,
95                    keyboardType: TextInputType.text,
96                    textCapitalization: TextCapitalization.none,
97                    autocorrect: false,
98                    decoration: InputDecoration(
99                        border: OutlineInputBorder(
100                           borderSide: BorderSide(color: Colors.black)),
101                       labelText: 'Username'),
102                 ),
103                 SizedBox(
104                   height: 8,
105                 ),
106                 TextField(
107                   controller: controllerPassword,
108                   enabled: !isLoggedIn,
109                   obscureText: true,
110                   keyboardType: TextInputType.text,
111                   textCapitalization: TextCapitalization.none,
112                   autocorrect: false,
113                   decoration: InputDecoration(
114                       border: OutlineInputBorder(
115                           borderSide: BorderSide(color: Colors.black)),
116                       labelText: 'Password'),
117                 ),
118                 SizedBox(
119                   height: 16,
120                 ),
121                 Container(
122                   height: 50,
123                   child: ElevatedButton(
124                     child: const Text('Login'),
125                     onPressed: isLoggedIn ? null : () => doUserLogin(),
126                   ),
127                 ),
128                 SizedBox(
129                   height: 16,
130                 ),
131                 Container(
132                   height: 50,
133                   child: ElevatedButton(
134                     child: const Text('Sign Up'),
135                     onPressed: () => navigateToSignUp(),
136                   ),
137                 ),
138                 SizedBox( 
139                   height: 16,
140                 ),
141                 Container(
142                   height: 50,
143                   child: ElevatedButton(
144                     child: const Text('Reset Password'),
145                     onPressed: () => navigateToResetPassword(),
146                   ),
147                 )
148               ),
149            ),
150          ),
151        ));
152  }
153
154  void doUserLogin() async {
155    final username = controllerUsername.text.trim();
156    final password = controllerPassword.text.trim();
157
158    final user = ParseUser(username, password, null);
159
160    var response = await user.login();
161
162    if (response.success) {
163      navigateToUser();
164    } else {
165      Message.showError(context: context, message: response.error!.message);
166    }
167  }
168
169  void navigateToUser() {
170    Navigator.pushAndRemoveUntil(
171      context,
172      MaterialPageRoute(builder: (context) => UserPage()),
173      (Route<dynamic> route) => false,
174    );
175  }
176
177  void navigateToSignUp() {
178    Navigator.push(
179      context,
180      MaterialPageRoute(builder: (context) => SignUpPage()),
181    );
182  }
183
184  void navigateToResetPassword() {
185    Navigator.push(
186      context,
187      MaterialPageRoute(builder: (context) => ResetPasswordPage()),
188    );
189  }
190 }
191
192 class SignUpPage extends StatefulWidget {
193  @override
194  _SignUpPageState createState() => _SignUpPageState();
195 }
196
197 class _SignUpPageState extends State<SignUpPage> {
198  final controllerUsername = TextEditingController();
199  final controllerPassword = TextEditingController();
200  final controllerEmail = TextEditingController();
201
202  @override
203  Widget build(BuildContext context) {
204    return Scaffold(
205        appBar: AppBar(
206          title: const Text('Flutter Sign Up'),
207        ),
208        body: Center(
209          child: SingleChildScrollView(
210            padding: const EdgeInsets.all(8),
211            child: Column(
212              crossAxisAlignment: CrossAxisAlignment.stretch,
213              children: [
214                Container(
215                  height: 200,
216                  child: Image.network(
217                      'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
218                ),
219                Center(
220                  child: const Text('Flutter on Back4App',
221                      style:
222                          TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
223                ),
224                SizedBox(
225                  height: 16,
226                ),
227                Center(
228                  child: const Text('User registration',
229                      style: TextStyle(fontSize: 16)),
230                ),
231                SizedBox(
232                  height: 16,
233                ),
234                TextField(
235                  controller: controllerUsername,
236                  keyboardType: TextInputType.text,
237                  textCapitalization: TextCapitalization.none,
238                  autocorrect: false,
239                  decoration: InputDecoration(
240                      border: OutlineInputBorder(
241                          borderSide: BorderSide(color: Colors.black)),
242                      labelText: 'Username'),
243                ),
244                SizedBox(
245                  height: 8,
246                ),
247                TextField(
248                  controller: controllerEmail,
249                  keyboardType: TextInputType.emailAddress,
250                  textCapitalization: TextCapitalization.none,
251                  autocorrect: false,
252                  decoration: InputDecoration(
253                      border: OutlineInputBorder(
254                          borderSide: BorderSide(color: Colors.black)),
255                      labelText: 'E-mail'),
256                ),
257                SizedBox(
258                  height: 8,
259                ),
260                TextField(
261                  controller: controllerPassword,
262                  obscureText: true,
263                  keyboardType: TextInputType.text,
264                  textCapitalization: TextCapitalization.none,
265                  autocorrect: false,
266                  decoration: InputDecoration(
267                      border: OutlineInputBorder(
268                          borderSide: BorderSide(color: Colors.black)),
269                      labelText: 'Password'),
270                ),
271                SizedBox(
272                  height: 8,
273                ),
274                Container(
275                  height: 50,
276                  child: ElevatedButton(
277                    child: const Text('Sign Up'),
278                    onPressed: () => doUserRegistration(),
279                  ),
280                )
281              ],
282            ),
283          ),
284        ));
285  }
286
287  void doUserRegistration() async {
288    final username = controllerUsername.text.trim();
289    final email = controllerEmail.text.trim();
290    final password = controllerPassword.text.trim();
291
292    final user = ParseUser.createUser(username, password, email);
293
294    var response = await user.signUp();
295
296    if (response.success) {
297      Message.showSuccess(
298          context: context,
299          message: 'User was successfully created!',
300          onPressed: () async {
301            Navigator.pushAndRemoveUntil(
302              context,
303              MaterialPageRoute(builder: (context) => UserPage()),
304              (Route<dynamic> route) => false,
305            );
306          });
307    } else {
308      Message.showError(context: context, message: response.error!.message);
309    }
310  }
311 }
312
313 class UserPage extends StatelessWidget {
314  ParseUser? currentUser;
315
316  Future<ParseUser?> getUser() async {
317  }
318
319  @override
320  Widget build(BuildContext context) {
321    void doUserLogout() async {
322      var response = await currentUser!.logout();
323      if (response.success) {
324        Message.showSuccess(
325            context: context,
326            message: 'User was successfully logout!',
327            onPressed: () {
328              Navigator.pushAndRemoveUntil(
329                context,
330                MaterialPageRoute(builder: (context) => LoginPage()),
331                (Route<dynamic> route) => false,
332              );
333            });
334      } else {
335        Message.showError(context: context, message: response.error!.message);
336      }
337    }
338
339    return Scaffold(
340        appBar: AppBar(
341          title: Text('User logged in - Current User'),
342        ),
343        body: FutureBuilder<ParseUser?>(
344            future: getUser(),
345            builder: (context, snapshot) {
346              switch (snapshot.connectionState) {
347                case ConnectionState.none:
348                case ConnectionState.waiting:
349                  return Center(
350                    child: Container(
351                        width: 100,
352                        height: 100,
353                        child: CircularProgressIndicator()),
354                  );
355                default:
356                  return Padding(
357                    padding: const EdgeInsets.all(8.0),
358                    child: Column(
359                      crossAxisAlignment: CrossAxisAlignment.stretch,
360                      mainAxisAlignment: MainAxisAlignment.center,
361                      children: [
362                        Center(
363                            child: Text('Hello, ${snapshot.data!.username}')),
364                        SizedBox(
365                          height: 16,
366                        ),
367                        Container(
368                          height: 50,
369                          child: ElevatedButton(
370                            child: const Text('Logout'),
371                            onPressed: () => doUserLogout(),
372                          ),
373                        ),
374                      ],
375                    ),
376                  );
377              }
378            }));
379  }
380 }
381
382 class ResetPasswordPage extends StatefulWidget {
383  @override
384  _ResetPasswordPageState createState() => _ResetPasswordPageState();
385 }
386
387 class _ResetPasswordPageState extends State<ResetPasswordPage> {
388  final controllerEmail = TextEditingController();
389
390  @override
391  Widget build(BuildContext context) {
392    return Scaffold(
393        appBar: AppBar(
394          title: Text('Reset Password'),
395        ),
396        body: SingleChildScrollView(
397          padding: const EdgeInsets.all(8),
398          child: Column(
399            crossAxisAlignment: CrossAxisAlignment.stretch,
400            children: [
401              TextField(
402                controller: controllerEmail,
403                keyboardType: TextInputType.emailAddress,
404                textCapitalization: TextCapitalization.none,
405                autocorrect: false,
406                decoration: InputDecoration(
407                    border: OutlineInputBorder(
408                        borderSide: BorderSide(color: Colors.black)),
409                    labelText: 'E-mail'),
410              ),
411              SizedBox(
412                height: 8,
413              ),
414              Container(
415                height: 50,
416                child: ElevatedButton(
417                  child: const Text('Reset Password'),
418                  onPressed: () => doUserResetPassword(),
419                ),
420              )
421            ],
422          ),
423        ));
424  }
425
426  void doUserResetPassword() async {}
427 }
428
429 class Message {
430  static void showSuccess(
431      {required BuildContext context,
432      required String message,
433      VoidCallback? onPressed}) {
434    showDialog(
435      context: context,
436      builder: (BuildContext context) {
437        return AlertDialog(
438          title: const Text("Success!"),
439          content: Text(message),
440          actions: <Widget>[
441            new ElevatedButton(
442              child: const Text("OK"),
443              onPressed: () {
444                Navigator.of(context).pop();
445                if (onPressed != null) {
446                  onPressed();
447                }
448              },
449            ),
450          ],
451        );
452      },
453    );
454  }
455
456  static void showError(
457      {required BuildContext context,
458      required String message,
459      VoidCallback? onPressed}) {
460    showDialog(
461      context: context,
462      builder: (BuildContext context) {
463        return AlertDialog(
464          title: const Text("Error!"),
465          content: Text(message),
466          actions: <Widget>[
467            new ElevatedButton(
468              child: const Text("OK"),
469              onPressed: () {
470                Navigator.of(context).pop();
471                if (onPressed != null) {
472                  onPressed();
473                }
474              },
475            ),
476          ],
477        );
478      },
479    );
480  }
481 }
482
```

:::hint{type="info"}
When debug parameter in function Parse().initialize is true, allows displaying Parse API calls on the console. This configuration can help you debug the code. It is prudent to disable debug in the release version.
:::

## 2 - Connect Template to Back4app Project

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId** **= App Id**
- **keyClientKey** **= Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4Theva5wIFKOP4aLYKfCq_image.png" signedSrc size="50" width="325" height="636" position="center" caption}

## 3 - Code for get current user on Session

The User Login or SignUp function creates a Session object, which points to the User logged in and stores in your local storage a valid user session.

Calls to methods currentUser will successfully retrieve your ParseUser data and sessionToken for Session object.

Search for the function hasUserLogged in the file main.dart.

Replace the code inside hasUserLogged with:

```javascript
1       ParseUser? currentUser = await ParseUser.currentUser() as ParseUser?;
2       if (currentUser == null) {
3         return false;
4       }
5       //Checks whether the user's session token is valid
6       final ParseResponse? parseResponse =
7           await ParseUser.getCurrentUserFromServer(currentUser.sessionToken!);
8
9       if (parseResponse?.success == null || !parseResponse!.success) {
10        //Invalid session. Logout
11        await currentUser.logout();
12        return false;
13      } else {
14        return true;
15      }
```

To build this function, follow these steps:

1. Call the ParseUser.currentUser() function, which will return a ParseUser object from local storage.
2. IfParseUser is null, we do not have any users with an active session in the app.
3. IfParseUser is not null, we have a user with an active session in our app.
4. The user’s session needs to be validated on the Parse Server, as it has a lifetime.
5. If the token is not valid, it is necessary to call the logout function to clear the current session and the user needs to log in again.

The complete function should look like this:

```javascript
1     Future<bool> hasUserLogged() async {
2       ParseUser? currentUser = await ParseUser.currentUser() as ParseUser?;
3       if (currentUser == null) {
4         return false;
5       }
6       //Checks whether the user's session token is valid
7       final ParseResponse? parseResponse =
8           await ParseUser.getCurrentUserFromServer(currentUser.sessionToken!);
9   
10      if (parseResponse?.success == null || !parseResponse!.success) {
11        //Invalid session. Logout
12        await currentUser.logout();
13        return false;
14      } else {
15        return true;
16      }
17    }
```

Look for the function getUser in the file main.dart.

Replace the code inside getUser with:

```dart
1    currentUser = await ParseUser.currentUser() as ParseUser?;
2    return currentUser;
```

To build this function, follow these steps:

1. Call the ParseUser.currentUser() function, which will return a ParseUser object from local storage.

The complete function should look like this:

```dart
1     Future<ParseUser> getUser() async {
2       currentUser = await ParseUser.currentUser() as ParseUser?;
3       return currentUser;
4     }
```

:::hint{type="info"}
To test it, click on theRunbutton in Android Studio/VSCode.
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3_Cl98oXfNlWh9xqseJ7y_image.png" signedSrc size="50" width="325" height="636" position="center" caption}

SignUp or Login and the next screen will display the username of the logged in user.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gqmLgfctsrs4P6DJpRXbQ_image.png" signedSrc size="50" width="325" height="636" position="center" caption}

Quit the application and run again.

If a valid user session is identified, the screen with the user’s username will be displayed.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YT5lGyUOJwNjkMqLAeBoT_image.png" signedSrc size="50" width="325" height="636" position="center" caption}

## It’s done!

At the end of this guide, you can get current user on Session of your app using Parse Server core features through Back4App!

[title] Data Types
[path] Android/Data objects/

# Parse Data Types on Android

## Introduction

In this guide, you will learn about the Parse Datatypes using Android. You will read and save the Parse Objects on Back4App from an Android App.

Storing data on Parse is built around the ParseObject. Each ParseObject contains key-value pairs of JSON-compatible data. This data is schemaless, which means that we don’t need to specify ahead of time what keys exist on each ParseObject. We can set whatever key-value pairs we want, and our backend will store them. For example, let’s say we’re tracking high scores for a game. A single ParseObject could contain:

> 1   score: 1337
>
> **,**
>
>  playerName: "Sean Plott"
>
> **,**
>
>  cheatMode: 
>
> **false**

Keys must be alphanumeric strings, and values can be:

- String=> String
- Number(primitive numeric values such as int,double)
- Bool=> boolean
- DateTime=> java.util.Date
- Null=> JSONObject.NULL
- Array=>JSONArray
- File=> Parse File
- Pointer=> other ParseObject
- Relation=> ParseRelation
- Geopoint=> ParseGeoPoint

Each ParseObject has a class name that we can use to distinguish different sorts of data. For example, we could call the high score object a GameScore. There are also a few fields we don’t need to specify that are provided as a convenience:

- objectIdis a unique identifier for each saved object.
- createdAtand updatedAtrepresent the time that each object was created and last modified in the cloud.

Each of these fields is automatically filled in by Back4app at the moment we save a new ParseObject.

:::hint{type="info"}
We recommend you NameYourClassesLikeThis (UpperCamelCase) and nameYourKeysLikeThis (lowerCamelCase), just to keep your code looking pretty.
:::

This tutorial uses a basic app created in Android Studio 4.1.1 with buildToolsVersion=30.0.2 , Compile SDK Version = 30.0.2 and targetSdkVersion 30

:::hint{type="success"}
**At any time, you can access the complete Project via our GitHub repositories.**

- <a href="https://github.com/templates-back4app/android_crud_operations_kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/android_crud_operations_java" target="_blank">Java Example Repository</a>
:::

## Goal

Our goal is to create an Android App that can process all data types provided by Parse Server.
Here is a preview of what we are gonna achive:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/v8KoOA0ezaDkvt7ZT1nFr_image.png" signedSrc size="30" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Understanding our App

You will create an app for a better understanding of Parse Data Types. In this Android app, you will create all data types in a class named DataTypes and assign values to this classes variables. Then we will read and update this data.

:::hint{type="info"}
**Note** The data types Pointer, Relation, File, Geopoint will be covered later in specific guides.
:::

## Let’s get started!

## 1 - Create App Template

Define the following variables in the MainActivity and replace the code in the onCreate method with the following code.

:::CodeblockTabs
```java
1     private ProgressDialog progressDialog;
2     private View popupInputDialogView;
3     private RecyclerView recyclerView;
4     private String objectId;
5     private static final String TAG = "MainActivity";
6
7     @Override
8     protected void onCreate(Bundle savedInstanceState) {
9         super.onCreate(savedInstanceState);
10        setContentView(R.layout.activity_main);
11
12        progressDialog = new ProgressDialog(MainActivity.this);
13
14        Button saveData = findViewById(R.id.saveData);
15        Button readData = findViewById(R.id.readData);
16        Button updateData = findViewById(R.id.updateData);
17
18        saveData.setOnClickListener(saveDataView -> {
19            try {
20                saveDataTypes();
21            } catch (JSONException e) {
22                e.printStackTrace();
23            }
24        });
25
26        readData.setOnClickListener(readDataView -> readObjects());
27
28        updateData.setOnClickListener(updateDataView -> updateObject());
29
30    }
```

```kotlin
1     private var progressDialog: ProgressDialog? = null
2     private var objectId: String? = null
3     private var popupInputDialogView: View? = null
4     private var recyclerView: RecyclerView? = null
5     private val TAG = "MainActivity"
6
7     override fun onCreate(savedInstanceState: Bundle?) {
8        super.onCreate(savedInstanceState)
9         setContentView(R.layout.activity_main)
10
11        progressDialog = ProgressDialog(this@MainActivity)
12        val saveData = findViewById<Button>(R.id.saveData)
13        val readData = findViewById<Button>(R.id.readData)
14        val updateData = findViewById<Button>(R.id.updateData)
15        
16        saveData.setOnClickListener {
17            try {
18                saveDataTypes()
19            } catch (e: JSONException) {
20                e.printStackTrace()
21            }
22        }
23        readData.setOnClickListener { readObjects() }
24        updateData.setOnClickListener { updateObject() }
25    }
```
:::

:::hint{type="info"}
Before next steps, we need to connect Back4App to our application. You should save the appId and clientKey from the Back4App to string.xml file and then init Parse in our App.java or App.kt file.
Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">New Parse App tutorial</a> if you don’t know how to init Parse to your app.
:::

## 2 - Code for Save Object

The create function will create a new object in Back4app database. We define the saveDataTypes function that we call in the onCreate function and use the following code in this function. We will save all data types with this function to the DataTypes classes object.

:::CodeblockTabs
```java
1  private void saveDataTypes() throws JSONException{
2      ParseObject parseObject = new ParseObject("DataTypes");
3
4         parseObject.put("stringField", "String");
5         parseObject.put("doubleField", 1.5);
6         parseObject.put("intField", 2);
7         parseObject.put("boolField", true);
8         parseObject.put("dateField", Calendar.getInstance().getTime());
9
10        JSONObject myObject = new JSONObject();
11        myObject.put("number", 1);
12        myObject.put("string", "42");
13
14        parseObject.put("jsonObject", myObject);
15
16
17        JSONArray myArray = new JSONArray();
18        myArray.put(myObject);
19        myArray.put(myObject);
20        myArray.put(myObject);
21
22        parseObject.put("jsonArray", myArray);
23
24
25        List<String> list = new ArrayList<>();
26        list.add("string1");
27        list.add("string2");
28        parseObject.put("listStringField", list);
29
30        List<Integer> listInt = new ArrayList<>();
31        listInt.add(1);
32        listInt.add(2);
33        listInt.add(3);
34        parseObject.put("listIntField", listInt);
35
36        List<Boolean> listBool = new ArrayList<>();
37        listBool.add(true);
38        listBool.add(false);
39        parseObject.put("listBoolField", listBool);
40
41        progressDialog.show();
42        parseObject.saveInBackground(e -> {
43            progressDialog.dismiss();
44            if (e == null) {
45                Toast.makeText(this, "Object created successfully...", Toast.LENGTH_SHORT).show();
46                objectId = parseObject.getObjectId();
47            } else {
48                objectId = null;
49                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
50            }
51        });
52    }
```

```kotlin
1     private fun saveDataTypes() {
2     val parseObject = ParseObject("DataTypes")
3
4         parseObject.put("stringField", "String")
5         parseObject.put("doubleField", 1.5)
6         parseObject.put("intField", 2)
7         parseObject.put("boolField", true)
8         parseObject.put("dateField", Calendar.getInstance().time)
9
10
11        val myObject = JSONObject()
12        myObject.put("number", 1)
13        myObject.put("string", "42")
14
15        parseObject.put("jsonObject", myObject)
16
17        val myArray = JSONArray()
18        myArray.put(myObject)
19        myArray.put(myObject)
20        myArray.put(myObject)
21
22        parseObject.put("jsonArray", myArray)
23
24        val list: MutableList<String> = ArrayList()
25        list.add("string1")
26        list.add("string2")
27        parseObject.put("listStringField", list)
28
29        val listInt: MutableList<Int> = ArrayList()
30        listInt.add(1)
31        listInt.add(2)
32        listInt.add(3)
33        parseObject.put("listIntField", listInt)
34
35        val listBool: MutableList<Boolean> = ArrayList()
36        listBool.add(true)
37        listBool.add(false)
38        parseObject.put("listBoolField", listBool)
39
40        progressDialog?.show()
41        parseObject.saveInBackground {
42            progressDialog?.dismiss()
43            if (it == null) {
44                Toast.makeText(this, "Object created successfully...", Toast.LENGTH_SHORT).show()
45                objectId = parseObject.objectId
46            } else {
47                objectId = null
48                Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
49            }
50        }
51   }
```
:::

## 3 - Code for Read Object

We will give the objectId variable that we have assigned in the saveDataTypes function as a parameter to the query and read the data that we have saved in the saveDataTypes function with the readObjects function.

:::CodeblockTabs
```java
1 private void readObjects() {
2    if (objectId == null) {
3             Toast.makeText(this, "No object. Click on the 'Save Data' button before.", Toast.LENGTH_SHORT).show();
4             return;
5         }
6
7         ParseQuery<ParseObject> query = new ParseQuery<>("DataTypes");
8
9         progressDialog.show();
10        query.getInBackground(objectId, (object, e) -> {
11            progressDialog.dismiss();
12            if (e == null) {
13                List<Data> list = new ArrayList<>();
14                list.add(new Data("Int list field",object.get("listIntField").toString()));
15                list.add(new Data("String field",object.get("stringField").toString()));
16                list.add(new Data("Double field",object.get("doubleField").toString()));
17                list.add(new Data("Int field",object.get("intField").toString()));
18                list.add(new Data("String list field",object.get("listStringField").toString()));
19                list.add(new Data("Date field",object.get("dateField").toString()));
20                list.add(new Data("Bool field",object.get("boolField").toString()));
21                list.add(new Data("List Bool field",object.get("listBoolField").toString()));
22                list.add(new Data("Json Object field",object.get("jsonObject").toString()));
23                list.add(new Data("Json Array field",object.get("jsonArray").toString()));
24
25                showDataTypes(list);
26
27            } else {
28                Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
29            }
30        });
31 }
```

```kotlin
1 private fun readObjects() {
2    if (objectId == null) {
3             Toast.makeText(
4                 this,
5                 "No object. Click on the 'Save Data' button before.",
6                 Toast.LENGTH_SHORT
7             ).show()
8             return
9         }
10
11        val query = ParseQuery<ParseObject>("DataTypes")
12
13
14        progressDialog?.show()
15        query.getInBackground(
16            objectId
17        ) { obj, e ->
18            progressDialog?.dismiss()
19            if (e == null) {
20
21                val list: MutableList<Data> = ArrayList()
22                list.add(Data("Int list field", obj.get("listIntField").toString()))
23                list.add(Data("String field",obj.get("stringField").toString()))
24                list.add(Data("Double field", obj.get("doubleField").toString()))
25                list.add(Data("Int field", obj.get("intField").toString()))
26                list.add(Data("String list field", obj.get("listStringField").toString()))
27                list.add(Data("Date field",obj.get("dateField").toString()))
28                list.add(Data("Bool field", obj.get("boolField").toString()))
29                list.add(Data("List Bool field", obj.get("listBoolField").toString()))
30                list.add(Data("Json Object field", obj.get("jsonObject").toString()))
31                list.add(Data("Json Array field", obj.get("jsonArray").toString()))
32                showDataTypes(list)
33            } else {
34                Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
35            }
36
37        }
38 }
```
:::

In this section, we have created a model class called Data. We use the data we get in the readObjects function to create objects from this model class. We give these objects as elements to the list we created in Data type. Then we give this list as a parameter to the showDataTypes function and list it in the AlertDialog.

This is the Data model.&#x20;

:::CodeblockTabs
```java
1   public class Data {
2    private String type;
3    private String value;
4
5    public Data(String type, String value) {
6        this.type = type;
7        this.value = value;
8    }
9
10    public String getType() {
11        return type;
12    }
13
14    public Data setType(String type) {
15        this.type = type;
16        return this;
17    }
18
19    public String getValue() {
20        return value;
21    }
22
23    public Data setValue(String value) {
24        this.value = value;
25        return this;
26    }
27   }
```

```kotlin
1   class Data(val type:String?=null,val value:String?=null) {
2
3   }
```
:::

This is the showDataTypes function.

:::CodeblockTabs
```java
1   private void showDataTypes(List<Data> list){
2        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
3        alertDialogBuilder.setTitle("Data types");
4        alertDialogBuilder.setCancelable(true);
5        initPopupViewControls(list);
6        //We are setting our custom popup view by AlertDialog.Builder
7        alertDialogBuilder.setView(popupInputDialogView);
8        final AlertDialog alertDialog = alertDialogBuilder.create();
9        alertDialog.show();
10    }
11
12    private void initPopupViewControls(List<Data> list) {
13        LayoutInflater layoutInflater = LayoutInflater.from(MainActivity.this);
14        popupInputDialogView = layoutInflater.inflate(R.layout.custom_alert_dialog, null);
15        recyclerView = popupInputDialogView.findViewById(R.id.recyclerView);
16        ItemAdapter adapter = new ItemAdapter(list,this);
17        recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
18        recyclerView.setAdapter(adapter);
19    }
```

```kotlin
1    private fun showDataTypes(list: List<Data>) {
2         val alertDialogBuilder = AlertDialog.Builder(this@MainActivity)
3         alertDialogBuilder.setTitle("Data types")
4         alertDialogBuilder.setCancelable(true)
5         initPopupViewControls(list)
6         //We are setting our custom popup view by AlertDialog.Builder
7         alertDialogBuilder.setView(popupInputDialogView)
8         val alertDialog = alertDialogBuilder.create()
9         alertDialog.show()
10    }
11
12    @SuppressLint("InflateParams")
13    private fun initPopupViewControls(list: List<Data>) {
14        val layoutInflater = LayoutInflater.from(this@MainActivity)
15        popupInputDialogView = layoutInflater.inflate(R.layout.custom_alert_dialog, null)
16        recyclerView = popupInputDialogView?.findViewById(R.id.recyclerView)
17        val adapter = ItemAdapter(this@MainActivity, list)
18        recyclerView?.layoutManager = LinearLayoutManager(
19            this,
20            LinearLayoutManager.VERTICAL,
21            false
22        )
23        recyclerView?.adapter = adapter
24    }
```
:::

## 4 - Code for Update Object

The updateObject function is responsible for updating data in the object created on the saveDataTypes function. We are using objectId again for update object.

:::CodeblockTabs
```java
1   public void updateObject() {
2      if (objectId == null) {
3           Toast.makeText(this, "No object. Click on the 'Save Data' button before.", Toast.LENGTH_SHORT).show();
4           return;
5       }
6
7       ParseObject parseObject = new ParseObject("DataTypes");
8       parseObject.setObjectId(objectId);
9       parseObject.put("intField", 5);
10      parseObject.put("stringField", "new String");
11
12      progressDialog.show();
13
14      parseObject.saveInBackground(e -> {
15          progressDialog.dismiss();
16          if (e == null) {
17              Toast.makeText(this, "Object updated successfully...", Toast.LENGTH_SHORT).show();
18          } else {
19              Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
20          }
21      });
22  }
```

```kotlin
1   private fun updateObject() {
2      if (objectId == null) {
3           Toast.makeText(
4               this,
5               "No object. Click on the 'Save Data' button before.",
6               Toast.LENGTH_SHORT
7           ).show()
8           return
9       }
10
11      val parseObject = ParseObject("DataTypes")
12      parseObject.objectId = objectId
13      parseObject.put("intField", 5)
14      parseObject.put("stringField", "new String")
15
16      progressDialog?.show()
17
18      parseObject.saveInBackground {
19          progressDialog?.dismiss()
20          if (it == null) {
21              Toast.makeText(this, "Object updated successfully...", Toast.LENGTH_SHORT).show()
22          } else {
23              Toast.makeText(this, it.message, Toast.LENGTH_SHORT).show()
24          }
25      }
26  }
```
:::

## 5 - Using Counters

The above example contains a common use case. The intField field can be a counter that we’ll need to update continually. The above solution works, but it’s cumbersome and can lead to problems if we have multiple clients trying to update the same counter. Parse provides methods that atomically increment any number field to help with storing counter-type data. So, the same update can be rewritten as:

:::CodeblockTabs
```java
1    ParseObject parseObject = new ParseObject("DataTypes");
2    parseObject.setObjectId(objectId);
3    parseObject.increment("intField",1);
```

```kotlin
1    val parseObject = ParseObject("DataTypes")
2    parseObject.objectId = objectId
3    parseObject.increment("intField",1)
```
:::

## 6 - Using Lists

Parse also provides methods to help in storing list data. There are three operations that can be used to change a list field atomically:

- setAddand setAddAll: append the given objects to the end of an array field.
- setAddUniqueand setAddAllUnique\:add only the given objects which aren’t already contained in an array field to that field. The position of the insert is not guaranteed.
- removeand removeAll: removes all instances of the given objects from an array field.

### **6.1 - Examples with add and addAll**

listStringField has the value:

> \["a","b","c","d","e","f","g","g"]

Running the code below:

:::CodeblockTabs
```java
1        ParseObject parseObject = new ParseObject("DataTypes");
2        parseObject.setObjectId(objectId);
3        parseObject.add("listStringField","e");
4        parseObject.addAll("listStringField", Arrays.asList("e", "f", "g", "g"));
5        parseObject.save();
```

```kotlin
1        val parseObject = ParseObject("DataTypes")
2        parseObject.objectId = objectId
3        parseObject.add("listStringField", "e")
4        parseObject.addAll("listStringField", Arrays.asList("e", "f", "g", "g"))
5        parseObject.save()
```
:::

After this command the result of the stringList field will be:

> \["a","b","c","d","e","e","f","g","g"]

### **6.2 - Examples with addUnique and addAllUnique**

listStringField has the value:

> \["a","b","c","d","e"]

Running the code below:

:::CodeblockTabs
```java
1        ParseObject parseObject = new ParseObject("DataTypes");
2        parseObject.setObjectId(objectId);
3        parseObject.addUnique("listStringField","e");
4        parseObject.addAllUnique("listStringField",Arrays.asList("c", "d", "e", "f"));
5        parseObject.save();
```

```kotlin
1        val parseObject = ParseObject("DataTypes")
2        parseObject.objectId = objectId
3        parseObject.addUnique("listStringField", "e")
4        parseObject.addAllUnique("listStringField", Arrays.asList("c", "d", "e", "f"))
5        parseObject.save()
```
:::

After this command the result of thestringList field will be:

> \["a","b","c","d","e","f"]

No values were repeated.

### **6.3 - Examples with removeAll**

listStringField has the value:

> \["a","b","c","d","e","f"]

Running the code below:

:::CodeblockTabs
```java
1        ParseObject parseObject = new ParseObject("DataTypes");
2        parseObject.setObjectId(objectId);
3        parseObject.removeAll("listStringField",Arrays.asList("c", "d", "e", "f"));
4        parseObject.save();
```

```kotlin
1        val parseObject = ParseObject("DataTypes")
2        parseObject.objectId = objectId
3        parseObject.removeAll("listStringField", Arrays.asList("c", "d", "e", "f"))
4        parseObject.save()
```
:::

After this command the result of thestringList field will be:

> \["a","b"]

:::hint{type="info"}
**Note** that it is not currently possible to atomically add and remove items from an array in the same save. We will have to call the save for every different array operation we want to perform separately.
:::

## 7 - Remove single field from ParseObject

You can delete a single field from an object by using the remove operation:

:::CodeblockTabs
```java
1    ParseObject parseObject = new ParseObject("DataTypes");
2    parseObject.remove("stringField");
3    parseObject.save();
```

```kotlin
1       val parseObject = ParseObject("DataTypes")
2       parseObject.remove("stringField")
3       parseObject.save()
```
:::

## It’s done!

At this point, we have learned Parse Data Types on Android.

[title] Relational Queries
[path] ReactJS/Data objects/

# Relational Query in React using Parse

## Introduction

In this guide, you will perform relational queries in Parse and implement a React component using these queries. You will learn how to set up and query realistic data using Back4App and React.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

- A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
-
  If you want to run this guide’s example project, you should set up the <a href="https://ant.design/" target="_blank">Ant Design library</a>.
:::

## Goal

Query relational data stored on Back4App from a React App.

## 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.first), 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 Book subclass is being queried.

```javascript
1	// This will create your query
2	let parseQuery = new Parse.Query("Book");
3	// The query will resolve only after calling this method
4	let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Save some data on Back4App

Let’s create an assortment of classes, which will be the target of our queries in this guide. The classes are: Author, Book, Publisher and BookStore, in which Book has a 1\:N relation with Publisher and N\:N with Author, and BookStore has an N\:N relation with Book.

On Parse JS Console it 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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MrG1tC6gVb0yyGeaOpFdm_image.png)

Go ahead and create the classes with the following example content:

```javascript
1	// Add objects and create tables
2	// Authors
3	const AuthorA = new Parse.Object('Author');
4	AuthorA.set('name', 'Aaron Writer');
5	await AuthorA.save();
6	
7	const AuthorB = new Parse.Object('Author');
8	AuthorB.set('name', 'Beatrice Novelist');
9	await AuthorB.save();
10	
11	const AuthorC = new Parse.Object('Author');
12	AuthorC.set('name', 'Casey Columnist');
13	await AuthorC.save();
14	
15	// Publishers
16	const PublisherA = new Parse.Object('Publisher');
17	PublisherA.set('name', 'Acacia Publishings');
18	await PublisherA.save();
19	
20	const PublisherB = new Parse.Object('Publisher');
21	PublisherB.set('name', 'Birch Distributions');
22	await PublisherB.save();
23	
24	// Books
25	const BookA = new Parse.Object('Book');
26	BookA.set('title', 'A Love Story');
27	BookA.set('publisher', PublisherA);
28	BookA.set('publishingDate', new Date('05/07/1998'));
29	const BookARelation = BookA.relation("authors");
30	BookARelation.add(AuthorA);
31	await BookA.save();
32	
33	const BookB = new Parse.Object('Book');
34	BookB.set('title', 'Benevolent Elves');
35	BookB.set('publisher', PublisherB);
36	BookB.set('publishingDate', new Date('11/31/2008'));
37	const BookBRelation = BookB.relation("authors");
38	BookBRelation.add(AuthorB);
39	await BookB.save();
40	
41	const BookC = new Parse.Object('Book');
42	BookC.set('title', 'Can You Believe It?');
43	BookC.set('publisher', PublisherB);
44	BookC.set('publishingDate', new Date('08/21/2018'));
45	const BookCRelation = BookC.relation("authors");
46	BookCRelation.add(AuthorA);
47	BookCRelation.add(AuthorC);
48	await BookC.save();
49	
50	// BookStore
51	const BookStoreA = new Parse.Object('BookStore');
52	BookStoreA.set('name', 'Books of Love');
53	const BookStoreARelation = BookStoreA.relation("books");
54	BookStoreARelation.add(BookA);
55	await BookStoreA.save();
56	
57	const BookStoreB = new Parse.Object('BookStore');
58	BookStoreB.set('name', 'Fantasy Books');
59	const BookStoreBRelation = BookStoreB.relation("books");
60	BookStoreBRelation.add(BookB);
61	await BookStoreB.save();
62	
63	const BookStoreC = new Parse.Object('BookStore');
64	BookStoreC.set('name', 'General Books');
65	const BookStoreCRelation = BookStoreC.relation("books");
66	BookStoreCRelation.add(BookA);
67	BookStoreCRelation.add(BookC);
68	await BookStoreC.save();
69	
70	console.log('Success');
```

## 3 - Query the data

Now that you have populated all the classes, we can now perform some relational queries in it. Let’s begin by filtering Book results by the publisher, searching for the ones that belong to the Publisher “Acacia Publishings” (or “Publisher A”) using the basic Parse.Query.equalTo method:

```javascript
1	// Get PublisherA object
2	const PublisherAQuery = new Parse.Query('Publisher');
3	PublisherAQuery.equalTo('name', 'Acacia Publishings');
4	const PublisherA = await PublisherAQuery.first();
5	
6	// Query Books with PublisherA
7	const bookQuery = new Parse.Query('Book');
8	bookQuery.equalTo('publisher', PublisherA);
9	let queryResults = await bookQuery.find();
10	
11	// Let's show the results
12	for (let result of queryResults) {
13	  // You access `Parse.Objects` attributes by using `.get`
14	  console.log(result.get('title'));
15	};
```

Let’s now query which BookStore objects contain Book objects with publishing date greater than 01/01/2010, using an inner query with the Parse.Query.greaterThan method and then the Parse.Query.matchesQuery method:

```javascript
1	// Create inner Book query
2	const bookQuery = new Parse.Query('Book');
3	bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
4	
5	// Query BookStore using inner Book query
6	const bookStoreQuery = new Parse.Query('BookStore');
7	bookStoreQuery.matchesQuery('books', bookQuery);
8	let queryResults = await bookStoreQuery.find();
9	
10	// Let's show the results
11	for (let result of queryResults) {
12	  // You access `Parse.Objects` attributes by using `.get`
13	  console.log(result.get('name'));
14	};
```

Let’s now create a more complex relational query, looking for BookStore objects that have at least one Book written by Author “Aaron Writer” (or “AuthorA”), using equalTo and matchesQuery:

```javascript
1	// Get AuthorA object
2	const AuthorAQuery = new Parse.Query('Author');
3	AuthorAQuery.equalTo('name', 'Aaron Writer');
4	const AuthorA = await AuthorAQuery.first();
5	
6	// Create inner Book query
7	const bookQuery = new Parse.Query('Book');
8	bookQuery.equalTo('authors', AuthorA);
9	
10	// Query BookStore using inner Book query
11	const bookStoreQuery = new Parse.Query('BookStore');
12	bookStoreQuery.matchesQuery('books', bookQuery);
13	let queryResults = await bookStoreQuery.find();
14	
15	// Let's show the results
16	for (let result of queryResults) {
17	  // You access `Parse.Objects` attributes by using `.get`
18	  console.log(result.get('name'));
19	};
```

## 4 - Query from a React component

Let’s now use our example queries inside a component in React, with a simple interface having a list showing results and also buttons for calling the queries. This is how the component code is laid out, note the doQuery functions, containing the example code from before.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider } from 'antd';
5	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
6	
7	export const QueryRelational = () => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState();
10	
11	  const doQueryA = async function () {
12	    // Get PublisherA object
13	    const PublisherAQuery = new Parse.Query('Publisher');
14	    PublisherAQuery.equalTo('name', 'Acacia Publishings');
15	    const PublisherA = await PublisherAQuery.first();
16	
17	    // Query Books with PublisherA
18	    const bookQuery = new Parse.Query('Book');
19	    bookQuery.equalTo('publisher', PublisherA);
20	
21	    try {
22	      let results = await bookQuery.find();
23	      setQueryResults(results);
24	      return true;
25	    } catch (error) {
26	      // Error can be caused by lack of Internet connection
27	      alert(`Error! ${error.message}`);
28	      return false;
29	    }
30	  };
31	
32	  const doQueryB = async function () {
33	    // Create inner Book query
34	    const bookQuery = new Parse.Query('Book');
35	    bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
36	
37	    // Query BookStore using inner Book query
38	    const bookStoreQuery = new Parse.Query('BookStore');
39	    bookStoreQuery.matchesQuery('books', bookQuery);
40	
41	    try {
42	      let results = await bookStoreQuery.find();
43	      setQueryResults(results);
44	      return true;
45	    } catch (error) {
46	      // Error can be caused by lack of Internet connection
47	      alert(`Error! ${error.message}`);
48	      return false;
49	    }
50	  };
51	
52	  const doQueryC = async function () {
53	    // Get AuthorA object
54	    const AuthorAQuery = new Parse.Query('Author');
55	    AuthorAQuery.equalTo('name', 'Aaron Writer');
56	    const AuthorA = await AuthorAQuery.first();
57	
58	    // Create inner Book query
59	    const bookQuery = new Parse.Query('Book');
60	    bookQuery.equalTo('authors', AuthorA);
61	
62	    // Query BookStore using inner Book query
63	    const bookStoreQuery = new Parse.Query('BookStore');
64	    bookStoreQuery.matchesQuery('books', bookQuery);
65	
66	    try {
67	      let results = await bookStoreQuery.find();
68	      setQueryResults(results);
69	      return true;
70	    } catch (error) {
71	      // Error can be caused by lack of Internet connection
72	      alert(`Error! ${error.message}`);
73	      return false;
74	    }
75	  };
76	
77	  const clearQueryResults = async function () {
78	    setQueryResults(undefined);
79	    return true;
80	  };
81	
82	  return (
83	    <div>
84	      <div className="header">
85	        <img
86	          className="header_logo"
87	          alt="Back4App Logo"
88	          src={
89	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
90	          }
91	        />
92	        <p className="header_text_bold">{'React on Back4App'}</p>
93	        <p className="header_text">{'Relational Queries'}</p>
94	      </div>
95	      <div className="container">
96	        <div className="flex_between">
97	          <h2 className="heading">{'Query List'}</h2>
98	          <div className="flex">
99	            <Button
100	              onClick={() => doQueryA()}
101	              type="primary"
102	              className="heading_button"
103	              color={'#208AEC'}
104	              icon={<SearchOutlined />}
105	            >
106	              QUERY A
107	            </Button>
108	            <Button
109	              onClick={() => doQueryB()}
110	              type="primary"
111	              className="heading_button"
112	              color={'#208AEC'}
113	              icon={<SearchOutlined />}
114	            >
115	              QUERY B
116	            </Button>
117	            <Button
118	              onClick={() => doQueryC()}
119	              type="primary"
120	              className="heading_button"
121	              color={'#208AEC'}
122	              icon={<SearchOutlined />}
123	            >
124	              QUERY C
125	            </Button>
126	            <Button
127	              onClick={() => clearQueryResults()}
128	              type="primary"
129	              className="heading_button"
130	              color={'#208AEC'}
131	              icon={<CloseOutlined />}
132	            >
133	              CLEAR RESULTS
134	            </Button>
135	          </div>
136	        </div>
137	        <Divider />
138	        <div className="flex_between">
139	          <div className="flex_child">
140	            {/* Query list */}
141	            {queryResults !== undefined &&
142	              queryResults.map((result, index) => (
143	                <div className="list_item" key={`${index}`}>
144	                  <p className="list_item_title">{`${
145	                    result.get('name') !== undefined
146	                      ? result.get('name')
147	                      : result.get('title')
148	                  }`}</p>
149	                </div>
150	              ))}
151	            {queryResults !== undefined && queryResults.length <= 0 ? (
152	              <p>{'No results here!'}</p>
153	            ) : null}
154	          </div>
155	        </div>
156	      </div>
157	    </div>
158	  );
159	};
```

TypeScript

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider } from 'antd';
4	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
5	const Parse = require('parse/dist/parse.min.js');
6	
7	export const QueryRelational: FC<{}> = (): ReactElement => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState<Parse.Object[]>();
10	
11	  const doQueryA = async function (): Promise<boolean> {
12	    // Get PublisherA object
13	    const PublisherAQuery: Parse.Query = new Parse.Query('Publisher');
14	    PublisherAQuery.equalTo('name', 'Acacia Publishings');
15	    const PublisherA: Parse.Object | undefined = await PublisherAQuery.first();
16	
17	    // Query Books with PublisherA
18	    const bookQuery: Parse.Query = new Parse.Query('Book');
19	    bookQuery.equalTo('publisher', PublisherA);
20	
21	    try {
22	      let results: Parse.Object[] = await bookQuery.find();
23	      setQueryResults(results);
24	      return true;
25	    } catch (error) {
26	      // Error can be caused by lack of Internet connection
27	      alert(`Error! ${error.message}`);
28	      return false;
29	    }
30	  };
31	
32	  const doQueryB = async function (): Promise<boolean> {
33	    // Create inner Book query
34	    const bookQuery: Parse.Query = new Parse.Query('Book');
35	    bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
36	
37	    // Query BookStore using inner Book query
38	    const bookStoreQuery: Parse.Query = new Parse.Query('BookStore');
39	    bookStoreQuery.matchesQuery('books', bookQuery);
40	
41	    try {
42	      let results: Parse.Object[] = await bookStoreQuery.find();
43	      setQueryResults(results);
44	      return true;
45	    } catch (error) {
46	      // Error can be caused by lack of Internet connection
47	      alert(`Error! ${error.message}`);
48	      return false;
49	    }
50	  };
51	
52	  const doQueryC = async function (): Promise<boolean> {
53	    // Get AuthorA object
54	    const AuthorAQuery: Parse.Query = new Parse.Query('Author');
55	    AuthorAQuery.equalTo('name', 'Aaron Writer');
56	    const AuthorA: Parse.Object | undefined = await AuthorAQuery.first();
57	
58	    // Create inner Book query
59	    const bookQuery: Parse.Query = new Parse.Query('Book');
60	    bookQuery.equalTo('authors', AuthorA);
61	
62	    // Query BookStore using inner Book query
63	    const bookStoreQuery: Parse.Query = new Parse.Query('BookStore');
64	    bookStoreQuery.matchesQuery('books', bookQuery);
65	
66	    try {
67	      let results: Parse.Object[] = await bookStoreQuery.find();
68	      setQueryResults(results);
69	      return true;
70	    } catch (error) {
71	      // Error can be caused by lack of Internet connection
72	      alert(`Error! ${error.message}`);
73	      return false;
74	    }
75	  };
76	
77	  const clearQueryResults = async function (): Promise<boolean> {
78	    setQueryResults(undefined);
79	    return true;
80	  };
81	
82	  return (
83	    <div>
84	      <div className="header">
85	        <img
86	          className="header_logo"
87	          alt="Back4App Logo"
88	          src={
89	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
90	          }
91	        />
92	        <p className="header_text_bold">{'React on Back4App'}</p>
93	        <p className="header_text">{'Relational Queries'}</p>
94	      </div>
95	      <div className="container">
96	        <div className="flex_between">
97	          <h2 className="heading">{'Query List'}</h2>
98	          <div className="flex">
99	            <Button
100	              onClick={() => doQueryA()}
101	              type="primary"
102	              className="heading_button"
103	              color={'#208AEC'}
104	              icon={<SearchOutlined />}
105	            >
106	              QUERY A
107	            </Button>
108	            <Button
109	              onClick={() => doQueryB()}
110	              type="primary"
111	              className="heading_button"
112	              color={'#208AEC'}
113	              icon={<SearchOutlined />}
114	            >
115	              QUERY B
116	            </Button>
117	            <Button
118	              onClick={() => doQueryC()}
119	              type="primary"
120	              className="heading_button"
121	              color={'#208AEC'}
122	              icon={<SearchOutlined />}
123	            >
124	              QUERY C
125	            </Button>
126	            <Button
127	              onClick={() => clearQueryResults()}
128	              type="primary"
129	              className="heading_button"
130	              color={'#208AEC'}
131	              icon={<CloseOutlined />}
132	            >
133	              CLEAR RESULTS
134	            </Button>
135	          </div>
136	        </div>
137	        <Divider />
138	        <div className="flex_between">
139	          <div className="flex_child">
140	            {/* Query list */}
141	            {queryResults !== undefined &&
142	              queryResults.map((result: Parse.Object, index: number) => (
143	                <div className="list_item" key={`${index}`}>
144	                  <p className="list_item_title">{`${result.get('name') !== undefined ? result.get('name') : result.get('title')}`}</p>
145	                </div>
146	              ))}
147	            {queryResults !== undefined &&
148	            queryResults.length <= 0 ? (
149	              <p>{'No results here!'}</p>
150	            ) : null}
151	          </div>
152	        </div>
153	      </div>
154	    </div>
155	  );
156	};
```
:::

Also add these classes to your App.css file to fully render the component layout:

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 900px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.flex {
77	  display: flex;
78	}
79	
80	.flex_between {
81	  display: flex;
82	  justify-content: space-between;
83	}
84	
85	.flex_child {
86	  flex: 0 0 45%;
87	}
88	
89	.heading_button {
90	  margin-left: 12px;
91	}
92	
93	.list_item {
94	  padding-bottom: 15px;
95	  margin-bottom: 15px;
96	  border-bottom: 1px solid rgba(0,0,0,0.06);
97	  text-align: left;
98	}
99	
100	.list_item_title {
101	  color: rgba(0,0,0,0.87);
102	  font-size: 17px;
103	}
```
:::

This is how the component should look like after rendering and querying by one of the query functions:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/y52ncClgNZmWznwkc1c4C_image.png)

## Conclusion

At the end of this guide, you learned how relational queries work on Parse and how to perform them on Back4App from a React App. In the next guide, you will learn how to work with Users in Parse.

[title] Install Parse SDK
[path] Flutter/Parse SDK (REST)/

## Introduction



In this guide, you will learn how to get started with Back4App for your Flutter project using the **Flutter plugin for Parse Server**.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
- [Flutter version 3.3.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
:::

## 1. Install Parse SDK

Open your computer's cmd or terminal (depending on your operating system) and **go** into your Flutter projects directory. Run this command to create a new Flutter app.

> flutter create flutter_parse

Launch your Flutter app by running this command:

> cd flutter_parse
> flutter run

Add the <a href="https://pub.dev/packages/parse_server_sdk_flutter" target="_blank">Parse SDK</a> to your project dependencies running:

> flutter pub add parse_server_sdk_flutter

This command will `flutter get` and add the Parse SDK to your pubspec.yaml file.

## 2. Set up Parse SDK

The next step is to import the Parse SDK inside your project's main.dart file.

Inside main.dart, you can use:

```dart
import 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';
```

### **Initialize your Parse app**

Go to main.dart, and initialize Parse in your Flutter app:

```dart
1   void main() async {
2     const keyApplicationId = 'YOUR_APP_ID_HERE';
3     const keyClientKey = 'YOUR_CLIENT_KEY_HERE';
4     const keyParseServerUrl = 'https://parseapi.back4app.com';
5
6     await Parse().initialize(keyApplicationId, keyParseServerUrl,
7       clientKey: keyClientKey, debug: true);
8   }
```

:::hint{type="info"}
**Note: **The debug parameter in function Parse().initialize is set to true,  to allow displaying Parse API calls on the console. This configuration can assist in debugging the code. It is advisable to disable debug in the release version.
:::

To successfully connect your app to the Back4App servers, you must provide Parse SDK with your backend application credentials.

Update the string values with your Application Id and Client key.

Locate your Application ID and Client Key credentials by navigating to your app Dashboard > App Settings > Security & Keys.

![Image of the backend app ID ](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IYCmXJi-0nsTFw-agj-HK_testapp-credentials.png "Flutter app backend")

Copy these credentials and replace in main.dart.

- `keyApplicationId = App Id`
- `keyClientKey = Client Key`


3\. Test your Parse connection
------------------------------

You should test the SDK to verify that Parse and your backend is working with your Flutter application.

It will create an object on your project called `First Class.`

Let’s add the test code to main.dart as follows:

```dart
1 void main() async {
2   final keyApplicationId = 'YOUR_APP_ID_HERE';
3   final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
4   final keyParseServerUrl = 'https://parseapi.back4app.com';
5
6   await Parse().initialize(keyApplicationId, keyParseServerUrl,
7        clientKey: keyClientKey, autoSendSessionId: true);
8
9  var firstObject = ParseObject('FirstClass')
10    ..set(
11        'message', 'Hey, Parse is now connected!🙂');
12  await firstObject.save();
13  
14  print('done');
15 }
```

Launch your app and go to the [Back4App Website](https://www.back4app.com/). Find your app and navigate to the Dashboard.

Now go to `Database` > `Browser` > `First Class`. You should see the First Class with an object, as shown in the image below.

![Image of a new class object created in the backend](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PLtrsfttnteqJfjxCrU8N_firstclass.png "First Class")

## You're done!

Now you know exactly how to get started using Back4app. You learned how to install the Parse SDK in your Flutter Application and connect to your Back4App backend.

[title] Untitled
[path] iOS/Parse Swift SDK/


[title] Relational Queries
[path] Flutter/Parse SDK (REST)/Data Queries/

# Parse Relational Query in Flutter

## Introduction

You’ve already seen <a href="https://www.back4app.com/docs/flutter/parse-sdk/data-objects/flutter-one-to-many-relationship" target="_blank">how to store relational data objects</a> using theParse.Pointer and Parse.Relations data types. You’ve learned that on Parse it is possible to use any ParseObject as a value in other ParseObject, establishing a relation between them. Internally, the Parse framework will store the referred-to object in just one place to maintain consistency. That can give you extra power when building and running complex queries.

Also, you’ve already <a href="https://www.back4app.com/docs/flutter/parse-sdk/data-objects/flutter-query" target="_blank">learned how to use a QueryBuilder</a> with get can retrieve a single ParseObject from Back4App. There are many other ways to retrieve data with QueryBuilder.

In this guide, you will ding deep into the QueryBuilder class and see methods you can use to build Relational Queries. You will use a simple database class with some mocked data to perform the Queries using Flutter on Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## Goal

Query relational data stored on Back4App from a Flutter App.

## 1 - Understanding the QueryBuilder class

Any Parse query operation uses the QueryBuilder object type, which will help you retrieve specific data from your database throughout your app.

To create a new QueryBuilder, you need to pass as a parameter the desired ParseObject subclass, which is the one that will contain your query results.

It is crucial to know that a QueryBuilder will only resolve after calling a retrieve method query, so a query can be set up and several modifiers can be chained before actually being called.

```dart
1	       // This will create your query
2      QueryBuilder<ParseObject> queryBook =
3           QueryBuilder<ParseObject>(ParseObject('Book'));
4        
5       	// The query will resolve only after calling this method
6      final ParseResponse apiResponse = await queryBook.query();
7	
8       if (apiResponse.success && apiResponse.results != null) {
9         ....
10      } else {
11  	   ...
12      }
```

You can read more about the QueryBuilder class <a href="https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#complex-queries" target="_blank">here at the official documentation</a>.

## Using the JavaScript Console on Back4App

Inside your Back4App application’s dashboard, you will find a very useful API console in which you can run JavaScript code directly. In this guide you will use to store data objects in Back4App. On your App main dashboard go to Core->API Console->Javascript.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uAoF6bUPQ86DeL5ddVeH-_image.png)

## 2 - Save Data on Back4app

To run the queries on this guide you’ll need first to populate your App with some data.

The classes are: `Author`, `Book`, `Publisher` and `BookStore`.
Which `Book` has a `1:N` relation with `Publisher` and `N:N` with `Author`, and `BookStore` has an `N:N` relation with Book.

Here is the `Parse.Object` classes creation code, so go ahead and run it in your API console:

```dart
1   // Add objects and create tables
2   // Authors
3   const AuthorA = new Parse.Object('Author');
4   AuthorA.set('name', 'Aaron Writer');
5   await AuthorA.save();
6
7   const AuthorB = new Parse.Object('Author');
8   AuthorB.set('name', 'Beatrice Novelist');
9   await AuthorB.save();
10
11  const AuthorC = new Parse.Object('Author');
12  AuthorC.set('name', 'Casey Columnist');
13  await AuthorC.save();
14
15  const AuthorD = new Parse.Object('Author');
16  AuthorD.set('name', 'Gary Stur');
17  await AuthorD.save();
18
19  const AuthorE = new Parse.Object('Author');
20  AuthorE.set('name', 'Mary Sue');
21  await AuthorE.save();
22
23  // Publishers
24  const PublisherA = new Parse.Object('Publisher');
25  PublisherA.set('name', 'Acacia Publishings');
26  await PublisherA.save();
27
28  const PublisherB = new Parse.Object('Publisher');
29  PublisherB.set('name', 'Birch Distributions');
30  await PublisherB.save();
31  
32  const PublisherC = new Parse.Object('Publisher');
33  PublisherC.set('name', 'Acacia Distributions');
34  await PublisherC.save();
35
36
37  // Books
38  const BookA = new Parse.Object('Book');
39  BookA.set('title', 'A Love Story');
40  BookA.set('publisher', PublisherA);
41  BookA.set('publishingDate', new Date('05/07/1998'));
42  const BookARelation = BookA.relation("authors");
43  BookARelation.add(AuthorA);
44  await BookA.save();
45 
46  const BookB = new Parse.Object('Book');
47  BookB.set('title', 'Benevolent Elves');
48  BookB.set('publisher', PublisherB);
49  BookB.set('publishingDate', new Date('11/31/2008'));
50  const BookBRelation = BookB.relation("authors");
51  BookBRelation.add(AuthorB);
52  await BookB.save();
53 
54  const BookC = new Parse.Object('Book');
55  BookC.set('title', 'Can You Believe It?');
56  BookC.set('publisher', PublisherB);
57  BookC.set('publishingDate', new Date('08/21/2018'));
58  const BookCRelation = BookC.relation("authors");
59  BookCRelation.add(AuthorA);
60  BookCRelation.add(AuthorC);
61  await BookC.save();
62
63  // BookStore
64  const BookStoreA = new Parse.Object('BookStore');
65  BookStoreA.set('name', 'Books of Love');
66  const BookStoreARelation = BookStoreA.relation("books");
67  BookStoreARelation.add(BookA);
68  await BookStoreA.save();
69  
70  const BookStoreB = new Parse.Object('BookStore');
71  BookStoreB.set('name', 'Fantasy Books');
72  const BookStoreBRelation = BookStoreB.relation("books");
73  BookStoreBRelation.add(BookB);
74  await BookStoreB.save();
75  
76  const BookStoreC = new Parse.Object('BookStore');
77  BookStoreC.set('name', 'General Books');
78  const BookStoreCRelation = BookStoreC.relation("books");
79  BookStoreCRelation.add(BookA);
80  BookStoreCRelation.add(BookC);
81  await BookStoreC.save();
82 
83  console.log('Success');
```

After running this code, you should now have a Author, Publisher, Book and BookStore class in your database.

Your new class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8xnDcV6IaJ9WNPPw9ZiSY_image.png)

Let’s now take a look at examples from every QueryBuilder method, along with brief explanations on what they do.

## 3 - Query the data

Now that you have a populated class, we can now perform some relational queries

Now that you have populated all the classes, we can now perform some relational queries in it.

Let’s begin by filtering Book results by the publisher, searching for the ones that belong to the Publisher *“Acacia Publishings”* (or “Publisher A”) with relational Pointer using the basic .whereEqualTo method:

```dart
1       // Get PublisherA object
2       final QueryBuilder<ParseObject> publisherQueryA =
3           QueryBuilder<ParseObject>(ParseObject('Publisher'))
4             ..whereEqualTo('name', 'Acacia Publishings');
5   
6       final ParseResponse publisherResponse = await publisherQueryA.query();
7       if (!publisherResponse.success) {
8         return;
9       }
10      final publisherA = publisherResponse.results?.first as ParseObject;
11
12      // Query Books with PublisherA
13      final QueryBuilder<ParseObject> bookQuery =
14          QueryBuilder<ParseObject>(ParseObject('Book'))
15            ..whereEqualTo('publisher', publisherA);
16
17      final ParseResponse bookResponse = await bookQuery.query();
18      if (!bookResponse.success) {
19        return;
20      }
21  
22      for (var book in bookResponse.results as List<ParseObject>) {
23        print(book.get('title'));
24      }
```

Let’s now query which BookStore objects contain Book objects with publishing date greater than 01/01/2010, using an inner query with the whereGreaterThan method and then the whereMatchesQuery method:

```dart
1   // Create inner Book query
2       final QueryBuilder<ParseObject> bookQuery =
3           QueryBuilder<ParseObject>(ParseObject('Book'))
4             ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
5
6       // Query BookStore using inner Book query
7       final QueryBuilder<ParseObject> bookStoreQuery =
8           QueryBuilder<ParseObject>(ParseObject('BookStore'))
9             ..whereMatchesQuery('books', bookQuery);
10  
11      final ParseResponse bookStoreResponse = await bookStoreQuery.query();
12      if (!bookStoreResponse.success) {
13        return;
14      }
15
16      for (var b in bookStoreResponse.results as List<ParseObject>) {
17        print(b.get('name'));
18      }
```

Let’s now create another query, looking for Authors that are relation with the book **Can You Believe It**?, using whereRelatedTo:

```dart
1       // Get Book object
2       final QueryBuilder<ParseObject> bookQuery =
3           QueryBuilder<ParseObject>(ParseObject('Book'))
4             ..whereEqualTo('title', 'Can You Believe It?');
5
6       final ParseResponse bookResponse = await bookQuery.query();
7       if (!bookResponse.success) {
8         return;
9       }
10
11      final book = bookResponse.results?.first as ParseObject;
12
13      // Get Author with relation with Book Can You Believe It?
14      final QueryBuilder<ParseObject> authorsQuery =
15          QueryBuilder<ParseObject>(ParseObject('Author'))
16            ..whereRelatedTo('authors', 'Book', book.objectId!);
17
18      final ParseResponse authorResponse = await authorsQuery.query();
19
20      // Let's show the results
21      if (authorResponse.success && authorResponse.results != null) {
22        for (var a in authorResponse.results! as List<ParseObject>) {
23          print(a.get('name'));
24        }
25      }
```

## 4 - Query from Flutter

Let’s now use our example queries inside a Flutter App, with a simple interface having a list showing results and also 3 buttons for calling the queries.

Open your Flutter project, go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/cupertino.dart';
2   import 'package:flutter/material.dart';
3   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
4
5   void main() async {
6     WidgetsFlutterBinding.ensureInitialized();
7
8     final keyApplicationId = 'YOUR_APP_ID_HERE';
9     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10    final keyParseServerUrl = 'https://parseapi.back4app.com';
11
12    await Parse().initialize(keyApplicationId, keyParseServerUrl,
13        clientKey: keyClientKey, debug: true);
14
15    runApp(MaterialApp(
16      title: 'Flutter - GeoPoint',
17      debugShowCheckedModeBanner: false,
18      home: HomePage(),
19    ));
20  }
21
22  class HomePage extends StatefulWidget {
23    @override
24    _HomePageState createState() => _HomePageState();
25  }
26 
27  class _HomePageState extends State<HomePage> {
28    List<ParseObject> results = <ParseObject>[];
29    double selectedDistance = 3000;
30  
31    void doQueryPointer() async {
32      // Get PublisherA object
33      final QueryBuilder<ParseObject> publisherQueryA =
34          QueryBuilder<ParseObject>(ParseObject('Publisher'))
35            ..whereEqualTo('name', 'Acacia Publishings');
36
37      final ParseResponse publisherResponse = await publisherQueryA.query();
38      if (!publisherResponse.success) {
39        return;
40      }
41      final publisherA = publisherResponse.results?.first as ParseObject;
42  
43      // Query Books with PublisherA
44      final QueryBuilder<ParseObject> bookQuery =
45          QueryBuilder<ParseObject>(ParseObject('Book'))
46            ..whereEqualTo('publisher', publisherA);
47  
48      final ParseResponse bookResponse = await bookQuery.query();
49  
50      if (!bookResponse.success) {
51        setState(() {
52          results.clear();
53        });
54      } else {
55        setState(() {
56          results = bookResponse.results as List<ParseObject>;
57        });
58      }
59    }
60
61    void doQueryMatches() async {
62      // Create inner Book query
63      final QueryBuilder<ParseObject> bookQuery =
64          QueryBuilder<ParseObject>(ParseObject('Book'))
65            ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
66
67      // Query BookStore using inner Book query
68      final QueryBuilder<ParseObject> bookStoreQuery =
69          QueryBuilder<ParseObject>(ParseObject('BookStore'))
70            ..whereMatchesQuery('books', bookQuery);
71  
72      final ParseResponse bookStoreResponse = await bookStoreQuery.query();
73      if (!bookStoreResponse.success) {
74        setState(() {
75          results.clear();
76        });
77      } else {
78        setState(() {
79          results = bookStoreResponse.results as List<ParseObject>;
80        });
81      }
82    }
83  
84    void doQueryRelatedTo() async {
85      // Get Book object
86      final QueryBuilder<ParseObject> bookQuery =
87          QueryBuilder<ParseObject>(ParseObject('Book'))
88            ..whereEqualTo('title', 'Can You Believe It?');
89  
90      final ParseResponse bookResponse = await bookQuery.query();
91      if (!bookResponse.success) {
92        return;
93      }
94
95      final book = bookResponse.results?.first as ParseObject;
96  
97      // Get Author with relation with Book Can You Believe It?
98      final QueryBuilder<ParseObject> authorsQuery =
99          QueryBuilder<ParseObject>(ParseObject('Author'))
100           ..whereRelatedTo('authors', 'Book', book.objectId!);
101 
102     final ParseResponse authorResponse = await authorsQuery.query();
103 
104     if (!authorResponse.success) {
105       setState(() {
106         results.clear();
107       });
108     } else {
109       setState(() {
110         results = authorResponse.results as List<ParseObject>;
111       });
112     }
113   }
114 
115   void doClearResults() async {
116     setState(() {
117       results.clear();
118     });
119   }
120
121   @override
122   Widget build(BuildContext context) {
123     return Scaffold(
124         body: Padding(
125       padding: const EdgeInsets.all(16.0),
126       child: Column(
127         crossAxisAlignment: CrossAxisAlignment.stretch,
128         children: [
129           Container(
130             height: 200,
131             child: Image.network(
132                 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
133           ),
134           Center(
135             child: const Text('Flutter on Back4app - GeoPoint',
136                 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
137           ),
138           SizedBox(
139             height: 8,
140           ),
141           Container(
142             height: 50,
143            child: ElevatedButton(
144                 onPressed: doQueryPointer,
145                 child: Text('Query A'),
146                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
147           ),
148           SizedBox(
149             height: 8,
150           ),
151           Container(
152             height: 50,
153             child: ElevatedButton(
154                 onPressed: doQueryMatches,
155                 child: Text('Query B'),
156                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
157           ),
158           SizedBox(
159             height: 8,
160           ),
161           Container(
162             height: 50,
163             child: ElevatedButton(
164                 onPressed: doQueryRelatedTo,
165                 child: Text('Query C'),
166                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
167           ),
168           SizedBox(
169             height: 8,
170           ),
171           Container(
172             height: 50,
173             child: ElevatedButton(
174                 onPressed: doClearResults,
175                 child: Text('Clear results'),
176                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
177           ),
178           SizedBox(
179             height: 8,
180           ),
181           Text(
182             'Result List: ${results.length}',
183           ),
184           Expanded(
185             child: ListView.builder(
186                 itemCount: results.length,
187                 itemBuilder: (context, index) {
188                   final o = results[index];
189                   return Container(
190                     padding: const EdgeInsets.all(4),
191                     decoration:
192                         BoxDecoration(border: Border.all(color: Colors.black)),
193                     child: Text('${o.toString()}'),
194                   );
195                 }),
196           )
197         ],
198       ),
199     ));
200   }
201 }
```

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = App Id**
- **keyClientKey = Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qXtf2dBOzL7kRy1zt_5Cj_image.png" signedSrc size="30" width="321" height="631" position="center" caption}

## Conclusion

At the end of this guide, you learned how relational queries work on Parse and how to perform them on Back4App from a Flutter App. In the next guide, you will learn how to work with Users in Parse.

[title] Relational Mutations
[path] Flutter/GraphQL/

# Store relational data using GraphQL



## Introduction

In the last two tutorials we have performed GraphQL queries and mutations on a Back4App database from our Flutter App Project. We’re using [GraphQL Flutter](https://pub.dev/packages/graphql_flutter/install) as our GraphQL client once this is a very robust and far used project. For those tutorials we’ve used a very simple data model with the most common types for our Classes.

Back4App is a flexible platform which allows you to create, store and query relational data. In this tutorial we’re going to give a dive deep into this capability by showing you how to use relations and pointers when saving and querying relational data on Back4App backend. Also, we are going to explore other data types that Back4App offers like: GeoPointer and Datetime.

## Goals

At the end of this article you’ll be able to:

- **Create/update and delete Relational data (using Pointers and Relations)**
- **Create/update GeoPointers**
- **Create/update Date-Time.**

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- Make sure you have read the previous two guides - <a href="https://www.back4app.com/docs/flutter/graphql/flutter-graphql-project-with-source-code-download" target="_blank">Start from template</a> and <a href="https://www.back4app.com/docs/flutter/graphql/flutter-crud-app-example" target="_blank">GraphQL Mutation</a>.
- Download the project file from <a href="https://github.com/templates-back4app/Flutter-GraphQL/tree/complex-mutations" target="_blank">GitHub Repo</a> which includes the previous code as well as the new GUI you would need.
- Open the downloaded project on a Flutter IDE like VS Code or Android Studio.
- Back4App account that can be created <a href="https://www.back4app.com/" target="_blank">here</a>.&#x20;
- Connect your tutorial to Back4App as per the previous tutorials. <a href="https://www.back4app.com/docs/flutter/graphql/flutter-graphql-project-with-source-code-download" target="_blank">Start from template</a>.
:::

## 1 - Setting up Back-end

On our [previous project](https://www.back4app.com/docs/flutter/flutter-crud-app-example) our data model was very simple with just a single class: Language. Now we’re going to make it more complex by adding 2 new classes and relating them to Language.

**Founder with format**

| **Column Name** | **Description**                     |
| --------------- | ----------------------------------- |
| name            | Name of the founder of the language |

| **Column Name** | **Description**                      |
| --------------- | ------------------------------------ |
| name            | Name of owner company                |
| date\_owned     | Date ownership gained by company     |
| headquarters    | Headquarters location of the company |

We will create a one-to-one relation between class **Language** and class **Founder** using Parse Pointers, which will tell the founder of the specific language. Then, we create will one-to-many relation between class **Language** and class **Ownership** using Parse Relations that will tell which organisation/company owned the language, their Headquarters and the date they acquired the ownership of the language. The data model will look like that:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MSTrpoeUG0ptgqYO82xGh_image.png)

Before we move to our app let’s create the classes and data in our back-end that we would require. Go to your Back4App App and then go to the GraphQL Playground. Using the mutation below, create a class **Founder** where we will store the Language’s founders names.

```dart
1   mutation CreateClass {
2   	createClass(input:{
3       name: "Founder"
4       schemaFields: {
5         addStrings: [{name: "name"}]
6       }
7     }){
8       class{
9         schemaFields{
10          name
11          __typename
12        }
13      }
14    }
15  }
```

Now let’s populate our class with some founder’s names. In the following steps we will use this data to create new languages pointing to Founder’s class. Use the mutation below on Back4App GraphQL Playground to create your founders.

```dart
1   mutation CreateObject{
2     createFounder(input: {fields: {name: "James Gosling"}}){
3       founder{
4         id
5         name
6       }
7     }
8   }
```

Here we have entered the JAVA programming language founder’s name. You can similarly for others too but right now this is enough for our guide.

Let’s create the **Ownership** class where we will store the language ownership, foundation date (Date-Time) and the Owner’s headquarter’s location (GeoPointer). Later we will create the relation between the Language class and Ownership. Proceed by running the code below for creating the class:

```dart
1   mutation CreateClass {
2   	createClass(input:{
3       name: "Ownership"
4       schemaFields: {
5         addStrings: [{name: "name"}]
6         addDates: [{name: "date_owned"}]
7       }
8     }){
9       class{
10        schemaFields{
11          name
12          __typename
13        }
14      }
15    }
16  }  
```

Now populate the **Ownership **class using the following mutations:

```dart
1   mutation CreateObject{
2     createOwnership(input: {fields: {name: "Sun Microsystems"}}){
3       ownership{
4         id
5         name
6       }
7     }
8   }
```

```dart
1   mutation CreateObject{
2     createOwnership(input: {fields: {name: "Oracle"}}){
3       ownership{
4         id
5         name
6       }
7     }
8   }
```

Now refresh the page, go to Ownership class on Database browser. Select the **Add new Column** from the top right. Select GeoPoint from the first drop down and name it **headquarters** in the second text field. And leave everything as it is and press the **Add Column** button.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Agqec5Oovt3OYFjV0n0Fe_image.png" signedSrc size="50" width="538" height="477" position="center" caption}

Then go to your Language class on Database browser. Let’s add the relations to Ownership and Founder classes. Click over **add new column** and then choose the data type Pointer and the target class: Founder. Give the column the same name - founder - then press Add Column.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/O1dj3LFxgci_OtxB898BI_image.png" signedSrc size="60" width="536" height="555" position="center" caption}

Now repeat that process by adding a new column called ownership using the data type Relation and selecting the class Ownership.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ALxoJ61kaL4mYrOdQMI6s_image.png" signedSrc size="70" width="536" height="393" position="center" caption}

Now you have your data model ready to use on your Flutter App and to start saving and updating the data.

## 2 - Creating/Adding and Deleting Pointers

Now you need to download the project boilerplate code from our [GitHub repo](https://github.com/templates-back4app/Flutter-GraphQL/tree/complex-mutations) open on your IDE. To connect your project to Back4App go to the GraphQL Playground and open then copy the Keys and the API URL as shown on the image below. Now paste them into the constants.dart and run your project.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OO8tAOtsr-U2s62pAqSyy_image.png)

Run the application in your Emulator. Got to the **M** floating Button on the bottom. That will take us to the page where we performed simple mutations. Here you will find an extra floating action button **CM**. That is hte button we’re going to use to perform our complex GraphQL mutations. Click on the **CM** button, and you will see another four buttons one for each operation we’re going to make on this guide.

Now open the databaseutils.dart file and scroll down to the addPointers() method. Here we will add the logic for adding pointers. We are going to point James Gosling as a Founder of Java Language. So first you will need to proceed to your Back4App backend and copy both Founder’s (James Gosling) and Language (Java) objectId.

**Updating/Creating Pointer to Data**

Proceed to our app inside the databaseutils.dart and inside the addPointers() method initialize a String addPointerQuery where we will assign the GraphQL query for adding Pointers as follows:

```dart
1   String addPointerQuery=
2      '''
3       mutation addPointer(\$languageId: ID!, \$founderId: UpdateLanguageFieldsInput! ){
4         updateLanguage(input : {id : \$languageId, fields : \$founderId})
5         {
6           language{
7             objectId
8           }
9         }
10      }
11     ''';
```

and initialize final variable to assign variables, notice we are taking the rowObjectId and pointersId as parameters where

- rowObjectId- The object Id of row from where we will point
- pointersId- The object Id to the row to be pointed.

So declare variable as:

```dart
1   final variable={
2     "userId" : rowObjectId,
3        "founderId": {
4          "founder": {
5            "link": pointersId
6          }
7        }
8      };
```

Now like the last guide we will initialize the GraphQLClient and send query with the help of QueryOptions() and return its instance with QueryResults():

```dart
1   GraphQlConfiguration configuration = GraphQlConfiguration();
2      GraphQLClient client = configuration.clientToQuery();
3
4      QueryResult queryResult = await client.query(
5        QueryOptions(documentNode: gql(addPointerQuery), variables: variable),
6      );
7       return queryResult;
```

This is how your addPointers() method should look like:

```dart
1   Future<QueryResult> addPointers(String rowObjectId, String pointersId) async{
2      print('addPointers');
3      //code for add/update Pointers
4      String addPointerQuery=
5      '''
6       mutation addPointer(\$languageId: ID!, \$founderId: UpdateLanguageFieldsInput! ){
7         updateLanguage(input : {id : \$languageId, fields : \$founderId})
8         {
9           language{
10            objectId
11          }
12       }
13     }
14     ''';
15     final variable={
16       "userId" : rowObjectId,
17       "founderId": {
18         "founder": {
19           "link": pointersId
20         }
21       }
22     };
23     GraphQlConfiguration configuration = GraphQlConfiguration();
24     GraphQLClient client = configuration.clientToQuery();
25
26     QueryResult queryResult = await client.query(
27       QueryOptions(documentNode: gql(addPointerQuery), variables: variable),
28     );
29      return queryResult;
30   }
```

Hot Restart your app now, go to the **CM** button on the mutation page and then press the set **Add Pointers** button. Here enter the row objectId where the pointer needs to be added in the first Text Field and objectId of row where it would point in the second Text Field and then press **Done**. Now check your Dashboard and you must be able to see a relation under the **founder** column. You could click on it where it would take you to the row pointing to the **Founder** class.

**Deleting Pointer to Data**

Now proceed to the method deletePointers() where we will write logic to delete relations. To delete relations you just have to make minor changes to the query above, that is, in the variables just change the "link" with "remove". So after initializing final variable it would be:

```dart
1   final variable={
2        "languageId" : rowObjectId,
3        "founderId": {
4          "founder": {
5            "remove": pointersId
6          }
7        }
8      };
```

Everything else will look exactly the same as the addPointers(). So your deletePointers() method looks like:

```dart
1   Future<QueryResult> deletePointers(String rowObjectId, String pointersId) async{
2      print('deletePointers');
3      //code for delete pointers
4      String removePointersQuery=
5      '''
6       mutation addPointer(\$languageId: ID!, \$founderId: UpdateLanguageFieldsInput! ){
7         updateLanguage(input : {id : \$languageId, fields : \$founderId})
8         {
9           language{
10            objectId
11          }
12        }
13      }
14     ''';
15     final variable={
16       "languageId" : rowObjectId,
17       "founderId": {
18         "founder": {
19           "remove": pointersId
20         }
21       }
22     };
23     GraphQlConfiguration configuration = GraphQlConfiguration();
24     GraphQLClient client = configuration.clientToQuery();
25  
26     QueryResult queryResult = await client.query(
27       QueryOptions(documentNode: gql(removePointersQuery), variables: variable),
28     );
29     return queryResult;
30
31   }
```

You could now proceed to your Back4App dashboard and see that the \*founder column of the specific row was deleted.

## 3 - Creating/Adding and Deleting Date-time data

If you remember we have created a class **Ownership** ealier in *Step 1* and stored some data into it. These are names of companies that owned Java Programing Language. So first let’s enter the dates the acuired the ownership.

Let’s proceed to databaseutils.dart and create or scroll down to addDateTime() function. Here we will write logic to add Data-Time datatype to our class. Initialize String addDateTimeQuery and assign it the query for creating date-time data:

```dart
1   String addDateTimeQuery=
2      '''
3      mutation addTime(\$rowId: ID!,\$dateOwned: Date!){
4         updateOwnership(input : {id : \$rowId, fields : {date_owned : \$dateOwned}})
5         {
6           ownership{
7             objectId
8           }
9         }
10      } 
11     ''';
```

And the final variable as:

```dart
1   final variable={
2        "rowId": rowObjectId,
3        "dateOwned": dateTime
4      };
```

Now initialize the GraphQLClient and pass the query throught the QueryOption(). This is how your function would look like:

```dart
1   Future<QueryResult> addDateTime(String rowObjectId, String dateTime) async{
2      print('addDateTime');
3      //code for add/update date-time
4      String addDateTimeQuery=
5       '''
6       mutation addTime(\$rowId: ID!,\$dateOwned: Date!){
7         updateOwnership(input : {id : \$rowId, fields : {date_owned : \$dateOwned}})
8         {
9           ownership{
10            objectId
11          }
12        }
13      } 
14     ''';
15     final variable={
16       "rowId": rowObjectId,
17       "dateOwned": dateTime
18     };
19     GraphQlConfiguration configuration = GraphQlConfiguration();
20     GraphQLClient client = configuration.clientToQuery();
21     QueryResult queryResult = await client.query(
22       QueryOptions(documentNode: gql(addDateTimeQuery), variables: variable),
23     );
24     return queryResult;
25   }
```

Now **Hot Restart** application and note down the **objectId** of row containing *Sun Microsystems* in **name** column. Press the **Add Date-time** button. Now enter the *objectId* noted in the first text field and **“02-24-1982”** as **MM-DD-YYYY** format at the day when Java was pubished and press the **Done** button.

Proceed to your Back4App backend and you would date in form of **24 Feb 1982 at 00:00:00 UTC** which means it has identified the datatype as Date-Time datatype. Similarly, you could add **“01-27-2010”** for the **date\_owned** for *Oracle*.

## 4 - Adding/updating GeoPointer Data

Let’s add GeoPointer data to locate Head Quateres of of the comapnies from the **Ownership** table.
Proceed to database\_utils.dart file and scroll down to addGeoPointers() function. Now initialize String addGeoPointers and assign the respective query:

```dart
1   String addGeoPointers=
2       '''
3       mutation addGeoPointer(\$objectId: ID!,\$latitude: Float!,\$longitude: Float!){
4         updateOwnership(input : {id : \$objectId, fields : { headquarters:  {latitude : \$latitude, longitude : \$longitude}}})
5         {
6           ownership{
7             objectId
8           }
9         }
10      } 
11      ''';
```

and final variable as:

```dart
1   final variable={
2        "objectId": rowObjectId,
3        "latitude": double.parse(latitude),
4        "longitude":  double.parse(longitude),
5      };
```

Since latitude and longitude are double values we need to parse them from String to double before sending it.Initialise the GrapQLClient and pass query with by QueryOption(). This is how your addGeoPointers() function would look like:

```dart
1   Future<QueryResult> addGeoPointers(String rowObjectId, String latitude, String longitude) async{
2      print('add GeoPointers');
3      //code for add/update Geopointers
4      String addGeoPointers=
5       '''
6       mutation addGeoPointer(\$objectId: ID!,\$latitude: Float!,\$longitude: Float!){
7         updateOwnership(input : {id : \$objectId, fields : { headquarters:  {latitude : \$latitude, longitude : \$longitude}}})
8         {
9           ownership{
10            objectId
11          }
12        }
13      } 
14      ''';
15     final variable={
16       "objectId": rowObjectId,
17       "latitude": double.parse(latitude),
18       "longitude":  double.parse(longitude),
19     };
20     GraphQlConfiguration configuration = GraphQlConfiguration();
21     GraphQLClient client = configuration.clientToQuery();
22     QueryResult queryResult = await client.query(
23       QueryOptions(documentNode: gql(addGeoPointers), variables: variable),
24     );
25     return queryResult;
26   }
```

Hot Restart your app and select the **Add GeoPointers** button. Enter 37.35 in the first text box and 121.95 in the second text box. Enter the **objectId** of the row with Sun Microsystems as **name** and press done. Now proceed to your Back4App Backend and you would see **(37.35, 121.95)** which are co-ordinates of the HeadQuaters and states that it identifies it as GeoPointers.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TN9iBvaF5x-BMN2ydq0DI_image.png)

## 5 - Adding/Updating and Deleting Relation

In pointers we can nly point to one row, but with the help of relations we can make connection to multiple rows. So lets make relation of our **Language** table to these two rows for **Ownership** of the **Java** language.

**Adding/Updating Relation**

Proceed to database\_utils.dart file and proceed to addRelation() function for writing the logic. Initialize String addRelationQuery and assign the query for relation as follows:

```dart
1   String addRelationQuery=
2        '''
3        mutation addRelation(\$objectId: ID!, \$relationId: OwnershipRelationInput){
4         updateLanguage(input : {id : \$objectId, fields : {ownership : \$relationId}})
5           {
6             language{
7               objectId
8             }
9           }
10         }
11       ''';
```

and the final variable would be:

```dart
1   final variable= {
2        "objectId": objectId,
3        "relationId": relationId
4      };
```

So after initializing the GraphQLClient and passing the query like before this is how your database\_utils.dart would look like:

```dart
1   Future<QueryResult> addRelation(String rowObjectId, String relationId) async{
2      //code for add/update Relation.
3      print('addRelation');
4      String addRelationQuery=
5        '''
6        mutation addRelation(\$objectId: ID!, \$relationId: OwnershipRelationInput){
7         updateLanguage(input : {id : \$objectId, fields : {ownership : \$relationId}})
8           {
9             language{
10              objectId
11            }
12          }
13        }
14       ''';
15     final variable= {
16       "objectId": rowObjectId,
17       "relationId": {
18         "add": relationId
19       }
20     };
21     GraphQlConfiguration configuration = GraphQlConfiguration();
22     GraphQLClient client = configuration.clientToQuery();
23     QueryResult queryResult = await client.query(
24       QueryOptions(documentNode: gql(addRelationQuery), variables: variable),
25     );
26     print(queryResult);
27     return queryResult;
28   }
```

***SUCCESS*** your app has finally stored relational, datetime and GeoPointers data on Back4App!

## Conclusion

In this guide we learned how to store relational data on Back4App using GraphQL form a Flutter App project. Also we worked with other GraphQL mutations like GeoPointers and Datetime, creating, updating and deleting data. On the next tutorial we are going to deep dive into queries to our flutter App.

This guide was written by Asim Junain, from [HybrowLabs](https://www.back4app.com/partners/software-development-company).

[title] Current User
[path] ReactJS/Users/

# Get current User for React

## Introduction

After implementing user registration and login to your, you need to retrieve the currently logged user data to perform different actions and requests. This data can be promptly retrieved by using Parse.User.current inside your app components.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- Complete the previous guide so you can have a better understanding of [the Parse.User class and the Parse.User.logIn method](https://www.back4app.com/docs/react/working-with-users/react-user-login)
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library.</a>
:::

## Goal

Get the current user data fetching using Parse for a React app.

## 1 - Retrieving Current User

The method Parse.User.current can be used anywhere on your code, after properly configuring your app to use Parse. Its response will be your current user’s object (Parse.User) or null if there is no logged-in user currently.

:::CodeblockTabs
JavaScript

```javascript
1	const getCurrentUser = async function () {
2	  const currentUser = await Parse.User.current();
3	  if (currentUser !== null) {
4	    Alert.alert(
5	      'Success!',
6	      `${currentUser.get('username')} is the current user!`,
7	    );
8	  }
9	  return currentUser;
10	};
```

```typescript
1	const getCurrentUser = async function (): Promise<Parse.User | null> {
2	  const currentUser: Parse.User = await Parse.User.current();
3	  if (currentUser !== null) {
4	    Alert.alert(
5	      'Success!',
6	      `${currentUser.get('username')} is the current user!`,
7	    );
8	  }
9	  return currentUser;
10	};
```
:::

This method is essential in situations where you don’t have access to your application state or your user data, making it possible to perform relevant Parse requests in any component of your app.

## 2 - Using Current User in a React component

In our previous guides, the Parse.User.current method was already used for testing showing the current username on the User Login guide. Here is the UserLogin component code, take a closer look at the getCurrentUser function:

:::CodeblockTabs
UserLogin.js

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider, Input } from 'antd';
5	
6	export const UserLogin = () => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	  const [currentUser, setCurrentUser] = useState(null);
11	
12	  const doUserLogIn = async function () {
13	    // Note that these values come from state variables that we've declared before
14	    const usernameValue = username;
15	    const passwordValue = password;
16	    try {
17	      const loggedInUser = await Parse.User.logIn(usernameValue, passwordValue);
18	      // logIn returns the corresponding ParseUser object
19	      alert(
20	        `Success! User ${loggedInUser.get(
21	          'username'
22	        )} has successfully signed in!`
23	      );
24	      // To verify that this is in fact the current user, `current` can be used
25	      const currentUser = await Parse.User.current();
26	      console.log(loggedInUser === currentUser);
27	      // Clear input fields
28	      setUsername('');
29	      setPassword('');
30	      // Update state variable holding current user
31	      getCurrentUser();
32	      return true;
33	    } catch (error) {
34	      // Error can be caused by wrong parameters or lack of Internet connection
35	      alert(`Error! ${error.message}`);
36	      return false;
37	    }
38	  };
39	
40	  const doUserLogOut = async function () {
41	    try {
42	      await Parse.User.logOut();
43	      // To verify that current user is now empty, currentAsync can be used
44	      const currentUser = await Parse.User.current();
45	      if (currentUser === null) {
46	        alert('Success! No user is logged in anymore!');
47	      }
48	      // Update state variable holding current user
49	      getCurrentUser();
50	      return true;
51	    } catch (error) {
52	      alert(`Error! ${error.message}`);
53	      return false;
54	    }
55	  };
56	
57	  // Function that will return current user and also update current username
58	  const getCurrentUser = async function () {
59	    const currentUser = await Parse.User.current();
60	    // Update state variable holding current user
61	    setCurrentUser(currentUser);
62	    return currentUser;
63	  };
64	
65	  return (
66	    <div>
67	      <div className="header">
68	        <img
69	          className="header_logo"
70	          alt="Back4App Logo"
71	          src={
72	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
73	          }
74	        />
75	        <p className="header_text_bold">{'React on Back4App'}</p>
76	        <p className="header_text">{'User Login'}</p>
77	      </div>
78	      {currentUser === null && (
79	        <div className="container">
80	          <h2 className="heading">{'User Login'}</h2>
81	          <Divider />
82	          <div className="form_wrapper">
83	            <Input
84	              value={username}
85	              onChange={(event) => setUsername(event.target.value)}
86	              placeholder="Username"
87	              size="large"
88	              className="form_input"
89	            />
90	            <Input
91	              value={password}
92	              onChange={(event) => setPassword(event.target.value)}
93	              placeholder="Password"
94	              size="large"
95	              type="password"
96	              className="form_input"
97	            />
98	          </div>
99	          <div className="form_buttons">
100	            <Button
101	              onClick={() => doUserLogIn()}
102	              type="primary"
103	              className="form_button"
104	              color={'#208AEC'}
105	              size="large"
106	            >
107	              Log In
108	            </Button>
109	          </div>
110	        </div>
111	      )}
112	      {currentUser !== null && (
113	        <div className="container">
114	          <h2 className="heading">{'User Screen'}</h2>
115	          <Divider />
116	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
117	          <div className="form_buttons">
118	            <Button
119	              onClick={() => doUserLogOut()}
120	              type="primary"
121	              className="form_button"
122	              color={'#208AEC'}
123	              size="large"
124	            >
125	              Log Out
126	            </Button>
127	          </div>
128	        </div>
129	      )}
130	    </div>
131	  );
132	};
```

HelloUser.tsx

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	const Parse = require('parse/dist/parse.min.js');
5	
6	export const UserLogin: FC<{}> = (): ReactElement => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	  const [currentUser, setCurrentUser] = useState<Parse.Object | null>(null);
11	
12	  const doUserLogIn = async function (): Promise<boolean> {
13	    // Note that these values come from state variables that we've declared before
14	    const usernameValue: string = username;
15	    const passwordValue: string = password;
16	    try {
17	      const loggedInUser: Parse.User = await Parse.User.logIn(usernameValue, passwordValue);
18	      // logIn returns the corresponding ParseUser object
19	      alert(
20	        `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
21	      );
22	      // To verify that this is in fact the current user, `current` can be used
23	      const currentUser: Parse.User = await Parse.User.current();
24	      console.log(loggedInUser === currentUser);
25	      // Clear input fields
26	      setUsername('');
27	      setPassword('');
28	      // Update state variable holding current user
29	      getCurrentUser();
30	      return true;
31	    } catch (error: any) {
32	      // Error can be caused by wrong parameters or lack of Internet connection
33	      alert(`Error! ${error.message}`);
34	      return false;
35	    }
36	  };
37	
38	  const doUserLogOut = async function (): Promise<boolean> {
39	    try {
40	      await Parse.User.logOut();
41	      // To verify that current user is now empty, currentAsync can be used
42	      const currentUser: Parse.User = await Parse.User.current();
43	      if (currentUser === null) {
44	        alert('Success! No user is logged in anymore!');
45	      }
46	      // Update state variable holding current user
47	      getCurrentUser();
48	      return true;
49	    } catch (error: any) {
50	      alert(`Error! ${error.message}`);
51	      return false;
52	    }
53	  };
54	
55	  // Function that will return current user and also update current username
56	  const getCurrentUser = async function (): Promise<Parse.User | null> {
57	    const currentUser: (Parse.User | null) = await Parse.User.current();
58	    // Update state variable holding current user
59	    setCurrentUser(currentUser);
60	    return currentUser;
61	  }
62	
63	  return (
64	    <div>
65	      <div className="header">
66	        <img
67	          className="header_logo"
68	          alt="Back4App Logo"
69	          src={
70	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
71	          }
72	        />
73	        <p className="header_text_bold">{'React on Back4App'}</p>
74	        <p className="header_text">{'User Login'}</p>
75	      </div>
76	      {currentUser === null &&
77	        (<div className="container">
78	          <h2 className="heading">{'User Login'}</h2>
79	          <Divider />
80	          <div className="form_wrapper">
81	            <Input
82	              value={username}
83	              onChange={(event) => setUsername(event.target.value)}
84	              placeholder="Username"
85	              size="large"
86	              className="form_input"
87	            />
88	            <Input
89	              value={password}
90	              onChange={(event) => setPassword(event.target.value)}
91	              placeholder="Password"
92	              size="large"
93	              type="password"
94	              className="form_input"
95	            />
96	          </div>
97	          <div className="form_buttons">
98	            <Button
99	              onClick={() => doUserLogIn()}
100	              type="primary"
101	              className="form_button"
102	              color={'#208AEC'}
103	              size="large"
104	            >
105	              Log In
106	            </Button>
107	          </div>
108	        </div>)
109	      }
110	      {currentUser !== null &&
111	        (<div className="container">
112	          <h2 className="heading">{'User Screen'}</h2>
113	          <Divider />
114	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
115	          <div className="form_buttons">
116	            <Button
117	              onClick={() => doUserLogOut()}
118	              type="primary"
119	              className="form_button"
120	              color={'#208AEC'}
121	              size="large"
122	            >
123	              Log Out
124	            </Button>
125	          </div>
126	        </div>)
127	      }
128	    </div>
129	  );
130	};
```
:::

In this component, the Parse.User.current method retrieves the username and updates the state variable that is rendered inside the component JSX.

Add these classes to your App.css file to fully render the layout styles:

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 900px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.flex {
77	  display: flex;
78	}
79	
80	.flex_between {
81	  display: flex;
82	  justify-content: space-between;
83	}
84	
85	.flex_child {
86	  flex: 0 0 45%;
87	}
88	
89	.heading_button {
90	  margin-left: 12px;
91	}
92	
93	.list_item {
94	  padding-bottom: 15px;
95	  margin-bottom: 15px;
96	  border-bottom: 1px solid rgba(0,0,0,0.06);
97	  text-align: left;
98	}
99	
100	.list_item_title {
101	  color: rgba(0,0,0,0.87);
102	  font-size: 17px;
103	}
104	
105	.list_item_description {
106	  color: rgba(0,0,0,0.5);
107	  font-size: 15px;
108	}
```
:::

## Conclusion

At the end of this guide, you learned how to retrieve the current Parse user data from local storage on React. In the next guide, we will show you how to enable your user to reset his password.

[title] Quickstart
[path] iOS/Parse Swift SDK/

# Install Parse SDK on your iOS Swift Project

## Introduction

In this section you will learn how to install Parse Swift iOS SDK into your Xcode project.

In this tutorial we will use a basic app created in Swift with Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app created at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create an app at Back4App.
- Xcode.
- Basic iOS app.
  - **Note:**If you don’t have a basic app created you can open Xcode and hit **File-> New-> Project -> iOS**. Then select **App**. After you create your basic app you are ready to follow this guide.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-iwt8golf8-OjxlZ5Y29W_image.png)



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/x2hO0A18Ip_NBwkTbH_qd_image.png)
:::

## 1 - Choose your Installation Method

::::ExpandableHeading
### Swift Package Manager

**1.1 - Add the Parse Swift SDK Package - Swift Package Manager**

:::hint{type="info"}
Follow this step if you haven’t yet installed Parse iOS SDK.
:::

Newer versions of <a href="https://developer.apple.com/xcode/" target="_blank">XCode</a> have the Swift Package Manager built in. This is the easiest and best way to install the Parse Swift SDK on your project and keep it updated.

Currently we only recommend using this method for installing the Parse Swift SDK.

Under the (File) menu, select (Swift Packages) and then (Add Package Dependency)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WNWUx2DQRnHc7zWOMZhG7_image.png)

On the (Choose Package Repository) window, paste the URL for the Parse Swift SDK github website ( https\://github.com/parse-community/Parse-Swift ) and click Next

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dE6e5umL8vdVK48XjFdGx_image.png)

On the (Repository) window, you can select a Version, Branch or specific Commit. Choose the method you prefer and click Next

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ga6Swwt7LymSKKbJiVFlL_image.png)

Wait for XCode to resolve all Parse-Swift dependencies and then click Next

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-LhzW14lQHBbbD0w9EAsJ_image.png)

Check if ht package product ParseSwift is checked and your target is correctly selected on Add to Target, then click Next

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5yvU_zsCXMc0r5bi0BKB1_image.png)

The Swift package should appear on the Dependencies tree right under your project, showing its version on the right side:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/L5Qvl3TXUkLwszlCu_qEu_image.png" signedSrc size="60" width="528" height="992" position="center" caption}

If you need to update the ParseSwift package, right click it under the Dependencies tree and choose Update Package. The process will automatically update everything for you.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Iht_rEnp6CCr3m2U_X3tT_image.png" signedSrc size="60" width="666" height="1232" position="center" caption}

Congratulations! You have now installed the Parse Swift iOS SDK
::::

::::ExpandableHeading
### Cocoapods

**1.1 - Install the Parse Swift iOS SDK**

:::hint{type="info"}
Follow this step if you haven’t yet installed Parse Swift iOS SDK.
:::

Xcode can use <a href="https://cocoapods.org/" target="_blank">CocoaPods</a> as dependency manager for Swift and Objective-C Cocoa projects.

You can refer to <a href="https://guides.cocoapods.org/using/getting-started.html" target="_blank">CocoaPods Getting Started Guide</a> for additional details.

To install CocoaPods, open your terminal, copy the following code snippet and paste it in to your terminal and hit return:

> $ sudo gem install cocoapods

CocoaPods should install automatically after you enter your password. If there’s a problem you may need to upgrade your local version of Ruby.

Next open up the Xcode Project folder and open a terminal window in that folder.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/mq-QnkfRF0Cc0sjbgN6BC_image.png)

Now you are going to create a Podfile. Copy the following code snippet and paste it in to your terminal and hit return:

> $ pod init

If your folder now shows your Podfile you did it correctly.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6ErZwMqR9QICzyzgedlRT_image.png)

:::hint{type="danger"}
**Be careful, **If you don’t see the podfile make sure your terminal is actually inside the project folder..
:::

Next open your Podfile with Xcode or any text editor and under each target add “pod ‘Parse’”.

> pod 'ParseSwift'

Your Podfile will look similar to this:

> platform :ios, '14.0'
>
> target 'Cocoapods_ParseSwift' do
>   \# Comment the next line if you don't want to use dynamic frameworks
>   use_frameworks!
>
>   \# Pods for Cocoapods_ParseSwift
>   pod 'ParseSwift'
>
> end

Now you are going to add Parse Swift to your project. Make sure your terminal is opened to your project folder. Copy the following code snippet and paste it in to your terminal and hit return:

> $ pod install

CocoaPods will rebuild the project as a workspace and your project will now look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/79GE-ZlmH8y-zEPjEKJ9e_image.png)

If you have already opened your Xcode project close it. From now on you’ll open the workspace file instead of the project file. Double click on the workspace file to open it.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xJMQtHVYYc370i3-v-yRz_image.png)

Congratulations! You have now installed the Parse iOS SDK
::::

## 2 - Connect your Parse App

1. Open your project’s AppDelegate.swift file to set up the app’s credentials.
2. Parse Swift iOS SDK uses these settings to connect to the Back4App servers.
3. At the top of the file you should see a function called ‘didFinishLaunchingWithOptions’.
4. Paste the following code snippet inside this function, and make sure it is above the line that says ‘return true’.

:::CodeblockTabs
AppDelegate.swift

```swift
1   ParseSwift.initialize(applicationId: "PASTE_YOUR_APPLICATION_ID_HERE", clientKey: "PASTE_YOUR_CLIENT_ID_HERE", serverURL: URL(string: "https://parseapi.back4app.com")!)
```
:::

At the top of your AppDelegate.swift file make sure to include Parse as a module by including the follwing code snippet right below ‘import UIKit’.

:::CodeblockTabs
AppDelegate.swift

```swift
1   import ParseSwift
```
:::

Your AppDelegate.swift file should now look like this:

:::CodeblockTabs
AppDelegate.swift

```swift
1   import UIKit
2   import ParseSwift
3
4   @main
5   class AppDelegate: UIResponder, UIApplicationDelegate {
6
7
8
9       func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
10          // Override point for customization after application launch.
11          ParseSwift.initialize(applicationId: "PASTE_YOUR_APPLICATION_ID_HERE", clientKey: "PASTE_YOUR_CLIENT_ID_HERE", serverURL: URL(string: "https://parseapi.back4app.com")!)
12          return true
13      }
14
15      // MARK: UISceneSession Lifecycle
16
17      func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
18          // Called when a new scene session is being created.
19          // Use this method to select a configuration to create the new scene with.
20          return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
21      }
22
23      func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
24          // Called when the user discards a scene session.
25          // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
26          // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
27      }
28
29
30  }
```
:::

:::hint{type="danger"}
**Be careful, **If Xcode tells you there is **No Such Module ‘Parse’ **there’s an easy solution. In Xcode open ‘Target > Build Settings > Search Paths > Framework Search Paths’ and then add two values: ‘$(PROJECT\_DIR)’ and ‘$(inherited)’. Xcode will now be able to find your Parse module.
:::

1. Go to your App Dashboard at Back4App website.
2. Navigate to app’s settings: Click on Features > Core Settingsblock> Server.
3. Return to your AppDelegate.swift file and paste your applicationId and clientKey.

:::hint{type="info"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App guide</a>.
:::

## 3 - Test your connection

Open your ViewController.swift file.

At the top of the file make sure to include Parse as a module by including the follwing code snippet right below ‘import UIKit’.

:::CodeblockTabs
ViewController.swift

```swift
1   import ParseSwift
```
:::

Inside the function called ‘viewDidLoad’ add a snippet of code below the code that configures parse.

:::CodeblockTabs
ViewController.swift

```swift
1   testParseConnection()
```
:::

Then add a function below viewDidLoad() method.

:::CodeblockTabs
ViewController.swift

```swift
1	    struct GameScore: ParseObject {
2	        //: Those are required for Object
3	        var objectId: String?
4	        var createdAt: Date?
5	        var updatedAt: Date?
6	        var ACL: ParseACL?
7	
8	        //: Your own properties.
9	        var score: Int = 0
10	
11	        //: Custom initializer.
12	        init(score: Int) {
13	            self.score = score
14	        }
15	
16	        init(objectId: String?) {
17	            self.objectId = objectId
18	        }
19	    }
20	
21	    func testParseConnection(){
22	        let score = GameScore(score: 10)
23	        let score2 = GameScore(score: 3)
24	        score.save { result in
25	            switch result {
26	            case .success(let savedScore):
27	                assert(savedScore.objectId != nil)
28	                assert(savedScore.createdAt != nil)
29	                assert(savedScore.updatedAt != nil)
30	                assert(savedScore.ACL == nil)
31	                assert(savedScore.score == 10)
32	
33	                /*: To modify, need to make it a var as the value type
34	                    was initialized as immutable.
35	                */
36	                var changedScore = savedScore
37	                changedScore.score = 200
38	                changedScore.save { result in
39	                    switch result {
40	                    case .success(var savedChangedScore):
41	                        assert(savedChangedScore.score == 200)
42	                        assert(savedScore.objectId == savedChangedScore.objectId)
43	
44	                        /*: Note that savedChangedScore is mutable since it's
45	                            a var after success.
46	                        */
47	                        savedChangedScore.score = 500
48	
49	                    case .failure(let error):
50	                        assertionFailure("Error saving: \(error)")
51	                    }
52	                }
53	            case .failure(let error):
54	                assertionFailure("Error saving: \(error)")
55	            }
56	        }
57	    }
58	}
```
:::

1. Build your app in a device or simulator (Command+R).
2. Wait until the main screen appears.
3. Login at [Back4App Website](https://www.back4app.com/)
4. Find your app and click on Dashboard
5. Click on Core.
6. Go to Browser.

If everything works properly, you should find a class named GameScore and the saved objects in it.

## Next Steps

At this point, you have learned how to get started with iOS apps. You are now ready to explore [Parse Server core features](https://www.back4app.com/product/parse-server) and [Back4App add-ons](https://www.back4app.com/product/addons).&#x20;

:::hint{type="success"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] User Logout
[path] React Native/Relay (GraphQL)/Users/

# React Native User Logout

## Introduction

In this step you will create the user Logout for React Native using Relay, the last implementation for this User’s section. This step is simple and we will follow our [GraphQL Logout Guide](https://www.back4app.com/docs/parse-graphql/graphql-logout-mutation) from our GraphQL Cookbook. You will implement the Logout mutation and call it using a button into the React Native Application.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## Goal

To implement the Logout feature to our React Native App using Relay and the Back4App GraphQL API.

## Prerequisites

- For this tutorial we will use the Parse Server in the 4.4 version. If you want to use other versions you can check the corresponding mutation code at <a href="https://www.back4app.com/docs/parse-graphql/graphql-logout-mutation" target="_blank">GraphQL Logout Guide</a> example for your respective version.
- You have to conclude the <a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">Relay Environment setup tutorial</a>:
- You have to conclude the <a href="https://www.back4app.com/docs/react-native/graphql/users/react-native-login-sample" target="_blank">React Native Login sample using Relay</a>:
- You have to conclude the <a href="https://app.archbee.com/docs/_roxIyUMXoBue9I7uv49e/6qyoyUmFLdRtjjy7dcgnr" target="_blank">React Native User Logged</a>:
- For this tutorial, we are going to use the Expo as a React Native framework;
- For this tutorial, we are going to use Javascript as our default implementation language;

## 1 - Creating the Logout Mutation

Back again to the SignIn folder, into mutations folder create a new file and call it LogOutMutation.js.

Copy and paste the following code inside:

```javascript
1	import { commitMutation, graphql } from 'react-relay';
2	
3	const mutation = graphql`
4	  mutation LogOutMutation($input: LogOutInput!) {
5	    logOut(input: $input) {
6	      clientMutationId
7	    }
8	  }
9	`;
10	
11	function commit({ environment, input, onCompleted, onError }) {
12	    const variables = { input };
13	
14	    commitMutation(environment, {
15	        mutation,
16	        variables,
17	        onCompleted,
18	        onError,
19	    });
20	}
21	
22	export default {
23	    commit,
24	};
```

Run yarn relay into your terminal to update the relay types:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SAkFpoBSkoDk028w_ShEl_image.png" signedSrc size="70" width="632" height="252" position="center" caption}

## 2 - Creating logout button

Now, open the FormSignIn.js component. Let’s add the Logout Button and call the Relay Mutation.

Import the new Relay Mutation on begin of file:

```javascript
1	import LogOutMutation from "./mutations/LogOutMutation";
```

Then, create the function responsible to call the LogOut Mutation:

```javascript
1	const handleLogout = async () => {
2	  LogOutMutation.commit({
3	    environment: environment,
4	    input: {},
5	    onCompleted: async () => {
6	      await AsyncStorage.removeItem('sessionToken');
7	      setSessionToken(null);
8	      alert('User successfully logged out');
9	    },
10	    onError: (errors) => {
11	      alert(errors[0].message);
12	    },
13	  });
14	};
```

What is happening into onCompleted:

- **it is removing the session token from Async Storage because it is not valid anymore.**
- **is cleaning the local useState that retrieves the session token for the same reason above.**
- **an alert saying the user it was logged out with success.**

After, right below the UserLoggedRenderer, let’s implement the button responsible to call the logout:

From:

```javascript
1   if (sessionToken) {
2     return <UserLoggedRenderer />;
3   }
```

To

```javascript
1	if (sessionToken) {
2	  return (
3	    <>
4	      <UserLoggedRenderer />
5	      <Button title={'logout'} onPress={() => handleLogout()} />
6	    </>
7	  )
8	}
```

:::hint{type="success"}
Import the Button from react-native lib import \{ Button, Text, TextInput, View, TouchableOpacity } from "react-native";
:::

The application screen should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vb1yq78Wc2fDQjeNNThKX_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

## 3 - Testing

Testing the button, when click should be made the logout and appears an alert:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5wX6DxsRc_vJF2_WfNbDL_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

And, after should return to Login page:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/UIlqzlmwpXlZpTXyEWXfQ_image.png" signedSrc size="60" width="828" height="1792" position="center" caption}

## Conclusion

Now you’ve implemented the main user authentication features necessary to any App. Your users can now SignUp, Login, navigate on authenticated area and LogOut from your React Native App using Relay and Back4App GraphQL API.

[title] using Cloud Code
[path] Android/Push Notifications/

# Send Parse push notifications using Cloud Code

## Introduction

This section explains how you can send push notifications using Cloud Code through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EiFlOChZLMHV5aMdWSbrr_push-cloud-code.gif" signedSrc size="50" width="290" height="580" position="center" caption}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our <a href="https://github.com/back4app/android-cloud-code-push" target="_blank">Github repository.</a>
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- Follow the **Steps 1** to **5** of <a href="https://www.back4app.com/docs/android/push-notifications/parse-server-push-notifications" target="_blank">Back4App Push Notification via Dashboard tutorial</a> carefully to set up Push Notifications to your app.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running API level 27 or newer.
:::

## 1 - Set up Android to receive push

Every Parse application installed on a device registered for push notifications has an
associated Installation object. The Installation object is where you store all the data needed to target push notifications. For example, in your app, you could store which
teams one of your users is interested in to send updates about their performance. Saving the Installation object is also required for tracking push-related app open events.

The simplest way to start sending notifications is using channels. This allows you to use a
publisher-subscriber model for sending pushes. Devices start by subscribing to one or more channels, and notifications can later be sent to these subscribers. The channels subscribed to by a given Installation are stored in the channels field of the Installation object.

To start working with push notifications, the following steps are required:

:::hint{type="info"}
If you downloaded our <a href="https://github.com/back4app/android-cloud-code-push" target="_blank">project template</a>, don’t forget to change your credentials in the app/src/main/res/values/string.xml file and the GCMSenderId that you obtained at Firebase in the AndroidManifest.xml file.
:::

1. Import the following dependecies:

> *1   // Java Dependencies*
>
>
> 2   import java.util.ArrayList
>
> **;**
>
>
>
>
> *3   // Parse Dependencies*
>
>
> 4   import com.parse.Parse
>
> **;**
>
>
> 5   import com.parse.ParseInstallation
>
> **;**

&#x20;    2\. Initialize Parse with Parse.initialize(this).

&#x20;    3\. Create a new array of channels and put the channels you would like to subscribe. In this example, the News channel is created.

&#x20;   4\.  Add to your installation your GCMSenderId, obtained from the <a href="https://console.firebase.google.com/?pli=1" target="_blank">Firebase Console</a>, through the command installation.put("GCMSenderId", "YOUR\_FIREBASE\_GCM\_SENDER\_ID\_HERE").

:::hint{type="info"}
To know how you can obtain that key, look at **Step 1** of <a href="https://www.back4app.com/docs/android/push-notifications/parse-server-push-notifications" target="_blank">Push Notifications via Dashboard tutorial</a>.
:::

&#x20;    5\. Add the channels object to the installation through the command installation.put("channels", channels).

&#x20;    6\. Save the installation to your database through installation.saveInBackground().

The following code executes these steps:

```java
1   Parse.initialize(this);
2   ArrayList<String> channels = new ArrayList<>();
3   channels.add("News");
4   ParseInstallation installation = ParseInstallation.getCurrentInstallation();
5   // don't forget to change the line below with the sender ID you obtained at Firebase
6   installation.put("GCMSenderId", "YOUR_FIREBASE_GCM_SENDER_ID_HERE");
7   installation.put("channels", channels);
8   installation.saveInBackground();
```

## 2 - Create your Cloud Code

:::hint{type="info"}
To know more about how to get started with **Cloud Code **look at <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">Cloud Code for Android Tutorial</a>.
:::

1. Create a .jsfile to put your Cloud Code into. In this example, a main.js file is created.
2. Define a Cloud function, using Parse.Cloud.Define, to call the push notification. In this example, this function is called Parse.Push.Send.

:::hint{type="danger"}
It is required to use the **master key **in this operation.
:::

The following code executes these steps:

:::CodeblockTabs
Parse Server 3.X

```javascript
//main.js
1   Parse.Cloud.define("pushsample", (request) => {
2
3       return Parse.Push.send({
5           channels: ["News"],
6           data: {
7               title: "Hello from the Cloud Code",
8               alert: "Back4App rocks!",
9           }
10      }, { useMasterKey: true });
11  });
```

Parse Server 2.X

```javascript
//main.js
1   Parse.Cloud.define("pushsample", function (request, response) {
2       Parse.Push.send({
3               channels: ["News"],
4               data: {
5                   title: "Hello from the Cloud Code",
6                   alert: "Back4App rocks!",
7               }
8          }, {
9               success: function () {
10                  // Push was successful
11                  response.success("push sent");
12                  console.log("Success: push sent");
13              },
14              error: function (error) {
15                  // Push was unsucessful
16                  response.error("error with push: " + error);
17                  console.log("Error: " + error);
18              },
19              useMasterKey: true
20         });
21  });
```
:::

## 3 - Upload to Cloud Code

1. Go to your App at <a href="https://www.back4app.com/" target="_blank">Back4App website </a>and click on Dashboard.
2. Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ncvuU4Sq-UW5g8-GZWOBi_image.png)

3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click at Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8MGNrzcB4wdH4-klh9QcY_image.png)

## 4 - Call Cloud Code from Android App

1. Import the following dependencies:

> *1   // Java Dependencies*
>
>
> 2   import java.util.HashMap
>
> **;**
>
>  
>
> *// This includes the HasMap Object that the 4       Cloud function needs to call*
>
>
>
>
> *3   // Parse Dependencies*
>
>
> 4   import com.parse.FunctionCallback
>
> **;**
>
>
> 5   import com.parse.ParseCloud
>
> **;**
>
>
> 6   import com.parse.ParseException
>
> **;**
>
>  

&#x20;    2\. Call the ParseCloud.callFunctionInBackground on the pushsample cloud function:

```java
1   final HashMap<String, String> params = new HashMap<>();
2   // Calling the cloud code function
3   ParseCloud.callFunctionInBackground("pushsample", params, new FunctionCallback<Object>() {
4    @Override
5    public void done(Object response, ParseException exc) {
6        if(exc == null) {
7            // The function was executed, but it's interesting to check its response
8            alertDisplayer("Successful Push","Check on your phone the notifications to confirm!");
9        }
10       else {
11           // Something went wrong
12           Toast.makeText(MainActivity.this, exc.getMessage(), Toast.LENGTH_LONG).show();
13       }
14   }
15  });
```

:::hint{type="info"}
The alertDisplayer method used in the example above is the following:

> **1   private**
>
>  
>
> **void**
>
>  
>
> **alertDisplayer(String**
>
>  title
>
> **,String**
>
>  message
>
> **){**
>
>
> 2      
>
> **AlertDialog.**
>
> Builder builder 
>
> **=**
>
>  
>
> **newAlertDialog.**
>
> Builder
>
> **(MainActivity.**
>
> this
>
> **)**
>
>
> 3      
>
> **.**
>
> setTitle
>
> **(**
>
> title
>
> **)**
>
>
> 4      
>
> **.**
>
> setMessage
>
> **(**
>
> message
>
> **)**
>
>
> 5      
>
> **.**
>
> setPositiveButton
>
> **(**
>
> "OK"
>
> **,**
>
>  
>
> **new**
>
>  
>
> **DialogInterface.**
>
> OnClickListener
>
> **()**
>
>  
>
> **{**
>
>
> 6                 @Override
> 7                 
>
> **public**
>
>  
>
> **void**
>
>  
>
> **onClick(DialogInterface**
>
>  dialog
>
> **,**
>
>  
>
> **int**
>
>  which
>
> **)**
>
>  
>
> **{**
>
>
> 8                     dialog
>
> **.**
>
> cancel
>
> **();**
>
>
> 9                 
>
> **}**
>
>
> 10             
>
> **});**
>
>
> 11     
>
> **AlertDialog**
>
>  ok 
>
> **=**
>
>  builder
>
> **.**
>
> create
>
> **();**
>
>
> 12     ok
>
> **.**
>
> show
>
> **();**
>
>
> 13   
>
> **}**
:::

&#x20;   3\. Test if the push notifications are being sent by calling the function above while the device is opened.

## 5 - Call Cloud Code from REST API

The REST API provides a quick and easy way to test if your Cloud function is working.
Just use the code below in your terminal or command prompt:

:::hint{type="info"}
Click on to know more about how to get started with command line in <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-the-linux-terminal" target="_blank">Linux</a>, <a href="https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line" target="_blank">MacOS</a> or <a href="https://www.bleepingcomputer.com/tutorials/windows-command-prompt-introduction/" target="_blank">Windows</a>.
:::

> curl -X POST -H "X-Parse-Application-Id: YOUR_APP_ID_HERE" \
> -H "X-Parse-REST-API-Key: YOUR_REST_API_KEY_HERE" \
> -H "Content-Type: application/json" \
> -d '{ // Put the function parameters here in JSON format }' \
> https://parseapi.back4app.com/functions/pushsample

To test the push notifications, just use the REST code while the device is opened.

## It’s done!

At this stage, you can send push notifications using Cloud Code through Back4App!

[title] Untitled
[path] Android/


[title] Basic Operations
[path] Android/Data objects/

# CRUD Parse objects in Android

## Introduction

In this section we are gonna build an Android application that performs basic CRUD operations. CRUD is abbreviation of Create-Read-Update-Delete. When you store data on Parse it is built around ParseObject and each one contains key-value pairs of JSON-compatible data.

The values you can store in Back4App Database could be in type of String, Number, Bool, Array, Object, Date, File, Pointer, Relation and null.

You can get more information about data types by clicking <a href="https://docs.parseplatform.org/js/guide/#data-types" target="_blank">here</a>.

This tutorial uses a basic app created in Android Studio 4.1.1 with buildToolsVersion=30.0.2 , Compile SDK Version = 30.0.2 and targetSdkVersion 30

:::hint{type="success"}
**At any time, you can access the complete Project via our GitHub repositories.**

- <a href="https://github.com/templates-back4app/android_crud_operations_kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/android_crud_operations_java" target="_blank">Java Example Repository</a>
:::

## Goal

Here is a preview of what we are gonna achive:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FxSZDT3Wxn0M43vVJGUkN_image.png" signedSrc size="30" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## Understanding our Todo App

To better understand Parse on Android, you will see the CRUD operations implemented on a ToDo App. The application will have a simple interface, with a title and description text field to register a task and a list of registered tasks. You can update each task’s title or description.

:::hint{type="info"}
**Note:**

- In this tutorial we will make our own custom alert dialog. Therefore, the codes will be configured to follow the template. You can adjust it to your own design.
- If you have any trouble, check the GitHub repositories for <a href="https://github.com/templates-back4app/android_crud_operations_kotlin" target="_blank">Kotlin </a>and <a href="https://github.com/templates-back4app/android_crud_operations_java" target="_blank">Java</a>.
:::

## Let’s get started!

Following the next steps, you will be able to build a Todo App that will store the tasks at Back4App Database.

## 1 - Create Todo App Template

Go to Android Studio, and find activity\_main.xml layout file from the Project (Project/app/res/layout/activity\_main.xml) and then replace the code below with your own code. This xml code will be our MainActivity's design, and we will bind this views to our MainActivity.java class and use them.

```xml
1   <?xml version="1.0" encoding="utf-8"?>
2   <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3       xmlns:app="http://schemas.android.com/apk/res-auto"
4       xmlns:tools="http://schemas.android.com/tools"
5       android:layout_width="match_parent"
6       android:layout_height="match_parent"
7       tools:context=".MainActivity">
8
9       <!--Title-->>
10      <TextView
11          android:id="@+id/textView"
12          android:layout_width="0dp"
13          android:layout_height="wrap_content"
14          android:background="@color/blue_700"
15          android:gravity="center_horizontal"
16          android:padding="24dp"
17          android:text="TODO List"
18          android:textColor="@color/white"
19          app:layout_constraintEnd_toEndOf="parent"
20          app:layout_constraintStart_toStartOf="parent"
21          app:layout_constraintTop_toTopOf="parent" />
22
23      <!--We will open a pop-up view when clicked to this button-->>
24      <com.google.android.material.floatingactionbutton.FloatingActionButton
25          android:id="@+id/fab"
26          style="@style/Widget.MaterialComponents.FloatingActionButton"
27          android:layout_width="wrap_content"
28          android:layout_height="wrap_content"
29          android:layout_margin="16dp"
30          android:scaleType="centerInside"
31          android:src="@drawable/ic_baseline_add_24"
32          app:backgroundTint="@color/blue_700"
33          app:fabSize="normal"
34          app:layout_constraintBottom_toBottomOf="parent"
35          app:layout_constraintEnd_toEndOf="parent"
36          app:tint="@color/white" />
37
38      <!--We will show this text view when data list is empty-->>
39      <TextView
40          android:id="@+id/empty_text"
41          android:layout_width="wrap_content"
42          android:layout_height="wrap_content"
43          android:text="List is empty"
44          android:layout_marginTop="32dp"
45          android:textSize="20sp"
46          android:visibility="gone"
47          app:layout_constraintEnd_toEndOf="parent"
48          app:layout_constraintStart_toStartOf="parent"
49          app:layout_constraintTop_toBottomOf="@+id/textView" />
50
51      <!--We will adapt the data list to this RecyclerView-->
52      <androidx.recyclerview.widget.RecyclerView
53          android:id="@+id/recyclerView"
54          android:layout_width="0dp"
55          android:layout_height="0dp"
56          app:layout_constraintBottom_toBottomOf="parent"
57          app:layout_constraintEnd_toEndOf="parent"
58          app:layout_constraintStart_toStartOf="parent"
59          app:layout_constraintTop_toBottomOf="@+id/textView">
60      </androidx.recyclerview.widget.RecyclerView>
61  </androidx.constraintlayout.widget.ConstraintLayout>
```

## 2 - Create Object

The create function will create a new Task with the title and description.

To add a TODO, we enter the title and description values in the pop-up that appears on the screen, set it to the ParseObject and save this object. We save this object in the database by using the function that Parse has provided us.

:::hint{type="info"}
**Note:**

- In this project we will make our own custom alert dialog. You can design your Alert dialog as you want and set it to the view of the dialog you will use later.
:::

:::CodeblockTabs
```java
1    openInputPopupDialogButton.setOnClickListener(fabButtonView -> {
2             AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
3             alertDialogBuilder.setTitle("Create a TODO");
4             alertDialogBuilder.setCancelable(true);
5             initPopupViewControls();
6             //We are setting our custom popup view by AlertDialog.Builder
7             alertDialogBuilder.setView(popupInputDialogView);
8             final AlertDialog alertDialog = alertDialogBuilder.create();
9             alertDialog.show();
10            saveTodoButton.setOnClickListener(saveButtonView -> saveTodo(alertDialog));
11            cancelUserDataButton.setOnClickListener(cancelButtonView -> alertDialog.cancel());
12        });   
13
14    //This is our saveTodo function
15    private void saveTodo(AlertDialog alertDialog) {
16        ParseObject todo = new ParseObject("Todo");
17        if (titleInput.getText().toString().length() != 0 && descriptionInput.getText().toString().length() != 0) {
18            alertDialog.cancel();
19            progressDialog.show();
20            todo.put("title", titleInput.getText().toString());
21            todo.put("description", descriptionInput.getText().toString());
22            todo.saveInBackground(e -> {
23                progressDialog.dismiss();
24                if (e == null) {
25                    //We saved the object and fetching data again
26                    getTodoList();
27                } else {
28                    //We have an error.We are showing error message here.
29                    showAlert("Error", e.getMessage());
30                }
31            });
32        } else {
33            showAlert("Error", "Please enter a title and description");
34        }
35    }
```

```kotlin
1    openInputPopupDialogButton?.setOnClickListener { fabButtonView ->
2             val alertDialogBuilder = AlertDialog.Builder(this@MainActivity)
3             alertDialogBuilder.setTitle("Create a TODO")
4             alertDialogBuilder.setCancelable(true)
5            initPopupViewControls()
6            //We are setting our custom popup view by AlertDialog.Builder
7            alertDialogBuilder.setView(popupInputDialogView)
8            val alertDialog = alertDialogBuilder.create()
9            alertDialog.show()
10            saveTodoButton?.setOnClickListener { saveButtonView ->
11                saveData(alertDialog)
12            }
13            cancelUserDataButton?.setOnClickListener { cancelButtonView ->
14                alertDialog.cancel()
15            }
16        }
17
18    //This is our saveTodo function
19    private fun saveData(alertDialog: AlertDialog) {
20        val todo = ParseObject("Todo")
21        if (titleInput?.text.toString().isNotEmpty() && descriptionInput?.text.toString().isNotEmpty()) {
22            alertDialog.cancel()
23            progressDialog?.show()
24            todo.put("title", titleInput?.text.toString())
25            todo.put("description", descriptionInput?.text.toString())
26            todo.saveInBackground { e ->
27                progressDialog?.dismiss()
28                if (e == null) {
29                    //We saved the object and fetching data again
30                    getTodoList()
31                } else {
32                    //We have an error.We are showing error message here.
33                    showAlert("Error", e.message!!)
34                }
35            }
36        } else {
37            showAlert("Error", "Please enter a title and description")
38        }
39    }
```
:::

## 3 - Read Object

With the function that Parse has provided us, we can fetch all the data in a class as ParseObject list. We create a query like the one below and fetch all the data in the Todo class.

:::CodeblockTabs
```java
1     private void getTodoList() {
2         progressDialog.show();
3         ParseQuery<ParseObject> query = ParseQuery.getQuery("Todo");
4         query.orderByDescending("createdAt");
5         query.findInBackground((objects, e) -> {
6             progressDialog.dismiss();
7             if (e == null) {
8                 //We are initializing Todo object list to our adapter
9                 initTodoList(objects);
10            } else {
11                showAlert("Error", e.getMessage());
12            }
13        });
14    }
```

```kotlin
1     private fun getTodoList() {
2          progressDialog?.show()
3          val query = ParseQuery.getQuery<ParseObject>("Todo")
4          query.orderByDescending("createdAt")
5          query.findInBackground { objects, e ->
6              progressDialog?.dismiss()
7              if (e == null) {
8                  //We are initializing Todo object list to our adapter
9                  initTodoList(objects)
10             } else {
11                 showAlert("Error", e.message!!)
12             }
13         }
14    }
```
:::

In this function, we give the returned list as a parameter to our adapter, set this adapter, to our RecyclerView, and we print the values of each object of this list into its own views. And if list has no item, we are setting our empty\_text, view’s as VISIBLE.

:::CodeblockTabs
```java
1    private void initTodoList(List<ParseObject> list) {
2         if (list == null || list.isEmpty()) {
3             empty_text.setVisibility(View.VISIBLE);
4             return;
5         }
6         empty_text.setVisibility(View.GONE);
7
8         TodoAdapter adapter = new TodoAdapter(list, this);
9
10        recyclerView.setLayoutManager(new LinearLayoutManager(this));
11        recyclerView.setAdapter(adapter);
12    }
```

```kotlin
1    private fun initTodoList(list: List<ParseObject>?) {
2         if (list == null || list.isEmpty()) {
3             empty_text!!.visibility = View.VISIBLE
4             return
5         }
6         empty_text?.visibility = View.GONE
7
8         val adapter = TodoAdapter(list as ArrayList<ParseObject>, this)
9
10        recyclerView?.layoutManager = LinearLayoutManager(this)
11        recyclerView?.adapter = adapter
12    }
```
:::

## 4 - Update Object

With the edit button in the view of our adapter, we are performing our updates. With the MutableLivedata, object provided by Android, we can listen to the click event of the edit button on our home page. When this button is clicked, we open the same pop-up and this time we populate this pop up with the title and description values of the object we clicked. Then, when the user changes this description and title and press save, first we fetch from the database with the id of this object, then we set the new variables and save object again.

:::hint{type="info"}
**Note:**

- MutableLiveData is a subclass of LiveData which is used for some of it’s properties (setValue/postValue) and using these properties we can easily notify the ui.
:::

:::CodeblockTabs
```java
1     adapter.onEditListener.observe(this, parseObject -> {
2              AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
3              alertDialogBuilder.setTitle("Update a TODO");
4              alertDialogBuilder.setCancelable(true);
5              //We are initializing PopUp Views with title and description parameters of ParseObject
6              initPopupViewControls(parseObject.getString("title"), parseObject.getString("description"));
7              alertDialogBuilder.setView(popupInputDialogView);
8              final AlertDialog alertDialog = alertDialogBuilder.create();
9              alertDialog.show();
10             saveTodoButton.setOnClickListener(saveTodoButtonView -> {
11                 if (titleInput.getText().toString().length() != 0 && descriptionInput.getText().toString().length() != 0) {
12                     alertDialog.cancel();
13                     progressDialog.show();
14                     parseObject.put("title", titleInput.getText().toString());
15                     parseObject.put("description", descriptionInput.getText().toString());
16                     parseObject.saveInBackground(e1 -> {
17                         progressDialog.dismiss();
18                         if (e1 == null) {
19                             getTodoList();
20                         } else {
21                             showAlert("Error", e1.getMessage());
22                         }
23                     });
24                 } else {
25                     showAlert("Error", "Please enter a title and description");
26                 }
27             });
28             cancelUserDataButton.setOnClickListener(cancelButtonView -> alertDialog.cancel());
29         });
```

```kotlin
1     adapter.clickListenerToEdit.observe(this@MainActivity, { parseObject ->
2              val alertDialogBuilder = AlertDialog.Builder(this@MainActivity)
3              alertDialogBuilder.setTitle("Update a TODO")
4              alertDialogBuilder.setCancelable(true)
5
6              //We are initializing PopUp Views with title and description parameters of ParseObject
7
8              initPopupViewControls(
9                  parseObject.getString("title")!!,
10                 parseObject.getString("description")!!
11             )
12
13             alertDialogBuilder.setView(popupInputDialogView)
14             val alertDialog = alertDialogBuilder.create()
15             alertDialog.show()
16
17             saveTodoButton?.setOnClickListener { saveButtonView ->
18                 if (titleInput?.text.toString().isNotEmpty() && descriptionInput?.text.toString().isNotEmpty()) {
19                     alertDialog.cancel()
20                     progressDialog?.show()
21                     parseObject.put("title", titleInput?.text.toString())
22                     parseObject.put("description", descriptionInput?.text.toString())
23                     parseObject.saveInBackground { e1 ->
24                         progressDialog?.dismiss()
25                         if (e1 == null) {
26                             getTodoList()
27                         } else {
28                             showAlert("Error", e1.message!!)
29                         }
30                     }
31                 } else {
32                     showAlert("Error", "Please enter a title and description")
33                 }
34             }
35         })
```
:::

## 5 - Delete Object

We listen to the click event of the delete button in the view of our adapter with the MutableLivedataobject from the home page, as in the edit button. When the delete button is clicked, we give the object id of the ParseObject as a parameter to the delete function that Parse has provided us and delete this object from the database.

:::CodeblockTabs
```java
1    adapter.onDeleteListener.observe(this, parseObject -> {
2             progressDialog.show();
3             parseObject.deleteInBackground(e -> {
4                 progressDialog.dismiss();
5                 if (e == null) {
6                     //We deleted the object and fetching data again.
7                     getTodoList();
8                 } else {
9                     showAlert("Error",e.getMessage());
10                }
11            });
12        });
```

```kotlin
1    adapter.onDeleteListener.observe(this@MainActivity, { parseObject ->
2             progressDialog?.show()
3             parseObject.deleteInBackground { e ->
4                 progressDialog?.dismiss()
5                 if (e == null) {
6                     //We deleted the object and fetching data again.
7                     getTodoList()
8                 } else {
9                     showAlert("Error", e.message!!)
10                }
11            }
12        })
```
:::

## It’s done!

At this point, you have learned how to do the basic CRUD operations with Parse on Android.

[title] User Registration
[path] Flutter/Parse SDK (REST)/User Authentication/Authentication/

# User Registration for Flutter using Parse Server

## Introduction

At the core of many apps, user accounts have a notion that lets users securely access their information. Parse provides a specialized User class that automatically handles much of the functionality required for user account management. With this class, you’ll be able to add user account functionality to your app.

ParseUser is a subclass of the ParseObject, and has the same features. All the methods that are on ParseObject also exist in ParseUser. The difference is that ParseUser has some special additions specific to user accounts. ParseUser has several properties that set it apart from ParseObject:

- **username: The username for the user (required).**
- **password: The password for the user (required on signup).**
- **email: The email address for the user (optional).**

You are free to use an email address as the username. Ask your users to enter their email, but fill it in the username property — ParseUser will work as normal.

In this guide, you will learn how to use the **Flutter plugin for Parse Server** to manage users using ParseUser class creating a user registration feature for your Flutter App.

::embed[]{url="https://www.youtube.com/embed/RDRyVhZ_LNg"}

## Goal

To build a User Registration feature using Parse for a Flutter App.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## Understanding the SignUp App

To better understand the SignUp process, we will create an app to register user data and create your account. We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse. Following the next steps, you will build a Todo App that will store the tasks at Back4App Database.

## Let’s get started!

Following the next steps you will be able to build a Sign App that will create user Account in Back4App Database.

## 1 - Create Sign App Template

Open your Flutter project from the previous guide **Flutter plugin for Parse Server**. Go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/material.dart';
2   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
3
4   void main() async {
5     WidgetsFlutterBinding.ensureInitialized();
6
7     final keyApplicationId = 'YOUR_APP_ID_HERE';
8     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
9     final keyParseServerUrl = 'https://parseapi.back4app.com';
10
11    await Parse().initialize(keyApplicationId, keyParseServerUrl,
12        clientKey: keyClientKey, debug: true);
13 
14    runApp(MyApp());
15  }
16
17  class MyApp extends StatelessWidget {
18    @override
19    Widget build(BuildContext context) {
20      return MaterialApp(
21        title: 'Flutter SignUp',
22        theme: ThemeData(
23          primarySwatch: Colors.blue,
24          visualDensity: VisualDensity.adaptivePlatformDensity,
25        ),
26        home: HomePage(),
27      );
28    }
29  }
30  
31  class HomePage extends StatefulWidget {
32    @override
33    _HomePageState createState() => _HomePageState();
34  }
35  
36  class _HomePageState extends State<HomePage> {
37    final controllerUsername = TextEditingController();
38    final controllerPassword = TextEditingController();
39    final controllerEmail = TextEditingController();
40  
41    @override
42    Widget build(BuildContext context) {
43      return Scaffold(
44          appBar: AppBar(
45            title: const Text('Flutter SignUp'),
46          ),
47          body: Center(
48            child: SingleChildScrollView(
49              padding: const EdgeInsets.all(8),
50              child: Column(
51                crossAxisAlignment: CrossAxisAlignment.stretch,
52                children: [
53                  Container(
54                    height: 200,
55                    child: Image.network(
56                        'http://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
57                  ),
58                  Center(
59                    child: const Text('Flutter on Back4App',
60                        style:
61                            TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
62                  ),
63                  SizedBox(
64                    height: 16,
65                  ),
66                  Center(
67                    child: const Text('User registration',
68                       style: TextStyle(fontSize: 16)),
69                  ),
70                  SizedBox(
71                    height: 16,
72                  ),
73                  TextField(
74                    controller: controllerUsername,
75                    keyboardType: TextInputType.text,
76                    textCapitalization: TextCapitalization.none,
77                    autocorrect: false,
78                    decoration: InputDecoration(
79                        border: OutlineInputBorder(
80                            borderSide: BorderSide(color: Colors.black)),
81                        labelText: 'Username'),
82                  ),
83                  SizedBox(
84                    height: 8,
85                  ),
86                  TextField(
87                    controller: controllerEmail,
88                    keyboardType: TextInputType.emailAddress,
89                    textCapitalization: TextCapitalization.none,
90                    autocorrect: false,
91                    decoration: InputDecoration(
92                        border: OutlineInputBorder(
93                            borderSide: BorderSide(color: Colors.black)),
94                        labelText: 'E-mail'),
95                  ),
96                  SizedBox(
97                    height: 8,
98                  ),
99                  TextField(
100                   controller: controllerPassword,
101                   obscureText: true,
102                   keyboardType: TextInputType.text,
103                   textCapitalization: TextCapitalization.none,
104                   autocorrect: false,
105                   decoration: InputDecoration(
106                       border: OutlineInputBorder(
107                           borderSide: BorderSide(color: Colors.black)),
108                       labelText: 'Password'),
109                 ),
110                 SizedBox(
111                   height: 8,
112                 ),
113                 Container(
114                   height: 50,
115                   child: TextButton(
116                     child: const Text('Sign Up'),
117                     onPressed: () => doUserRegistration(),
118                   ),
119                 )
120               ],
121             ),
122           ),
123         ));
124   }
125 
126   void showSuccess() {
127     showDialog(
128       context: context,
129       builder: (BuildContext context) {
130         return AlertDialog(
131           title: const Text("Success!"),
132           content: const Text("User was successfully created!"),
133           actions: <Widget>[
134             new FlatButton(
135               child: const Text("OK"),
136               onPressed: () {
137                 Navigator.of(context).pop();
138               },
139             ),
140           ],
141         );
142       },
143     );
144   }
145 
146   void showError(String errorMessage) {
147     showDialog(
148       context: context,
149       builder: (BuildContext context) {
150         return AlertDialog(
151           title: const Text("Error!"),
152           content: Text(errorMessage),
153           actions: <Widget>[
154             new FlatButton(
155               child: const Text("OK"),
156               onPressed: () {
157                 Navigator.of(context).pop();
158               },
159             ),
160           ],
161         );
162       },
163     );
164   }
165 
166   void doUserRegistration() async {
167 		//Sigup code here
168   }
169 }
170
```

:::hint{type="info"}
When debug parameter in function Parse().initialize is true, allows displaying Parse API calls on the console. This configuration can assist in debugging the code. It is advisable to disable debug in the release version.
:::

## 2 - Connect Template to Back4app Project

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = App Id**
- **keyClientKey = Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QdGj6ymZMOwKP3UmZVER-_image.png" signedSrc size="30" width="321" height="635" position="center" caption}

## 3 - Code for Sign User

The User SignUp function creates a new user in your Parse App. Before that, it checks to make sure that both the username and email are unique. If a SignUp isn’t successful, you should check the error object that is returned. The most likely cause is that another user has already taken the username or email. You should communicate this to your users and ask them to try a different username.

Search for the function doUserRegistration in the file main.dart. Replace the code inside doUserRegistration with:

```dart
1       final username = controllerUsername.text.trim();
2       final email = controllerEmail.text.trim();
3       final password = controllerPassword.text.trim();
4
5       final user = ParseUser.createUser(username, password, email);
6   
7       var response = await user.signUp();
8
9       if (response.success) {
10        showSuccess();
11      } else {
12        showError(response.error!.message);
13      }
```

To build this function, follow these steps:

1. Make a new instance of the ParseUser class with the command ParseUser.createUser(username, password, email), with data that were filled in the app.
2. Call the signUpfunction, which will register user to your database in the Parse Dashboard.
3. Check if user signup with success. If not success, show error description message.

The complete code should look like this:

```dart
1     void doUserRegistration() async {
2       final username = controllerUsername.text.trim();
3       final email = controllerEmail.text.trim();
4       final password = controllerPassword.text.trim();
5   
6       final user = ParseUser.createUser(username, password, email);
7
8       var response = await user.signUp();
9
10      if (response.success) {
11        showSuccess();
12      } else {
13        showError(response.error!.message);
14      }
15    }
```

:::hint{type="info"}
To test it, click on the Run button in Android Studio/VSCode.
:::

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FqEORppE07Iq7AQt6FFU6_image.png" signedSrc size="30" width="321" height="635" position="center" caption}

After providing the desired user credentials, you will see this message after pressing on Sign Up if everything was successful:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/t_Ntw03UeTQXfkX5CJ9A6_image.png" signedSrc size="30" width="321" height="635" position="center" caption}

Error handling can be tested if you try to register a user with the same username as before:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jPGjfA28-xVzeJhuMT87W_image.png" signedSrc size="30" width="321" height="635" position="center" caption}

You will get another error if you try to sign up with no password:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/1DJKZvpBsQyL-vf-1cPK2_image.png" signedSrc size="30" width="321" height="635" position="center" caption}

## 4 - Check User Registration on Dashboard

1. Login at [Back4App Website](https://www.back4app.com/)
2. Find your app and click on Dashboard>Core>Browser>User.

At this point, you should see your user as displayed below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cX3TvNKX8-ViA8EqWhzuf_image.png)

## It’s done!

At the end of this guide, you learned how to register new Parse users on Flutter. In the next guide, we will show you how to log in and out users.

[title] Join Queries
[path] ReactJS/Data objects/

# Join Query using Parse

## Introduction

In this guide, you will perform relational queries in Parse mimicking the behavior of SQL JOIN queries using your database.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App.</a>
:::

## Goal

Query relational data stored on Back4App in SQL JOIN query fashion.

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

```javascript
1	// This will create your query
2	let parseQuery = new Parse.Query("Profile");
3	// The query will resolve only after calling this method
4	let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Save some data on Back4App

Let’s create two example classes, TableA and TableB, which will be the targets of our queries in this guide. On Parse JS Console it 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:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8QPc1tGIBJlaZRj_dHILO_image.png)

Go ahead and create the classes with the following content:

```javascript
1	// Create TableA and its records
2	let TableARecord1 = new Parse.Object('TableA');
3	TableARecord1.set('FieldA', 'Value A 1');
4	TableARecord1 = await TableARecord1.save();
5	
6	let TableARecord2 = new Parse.Object('TableA');
7	TableARecord2.set('FieldA', 'Value A 2');
8	TableARecord2 = await TableARecord2.save();
9	
10	let TableARecord3 = new Parse.Object('TableA');
11	TableARecord3.set('FieldA', 'Value A 3');
12	TableARecord3 = await TableARecord3.save();
13	
14	// Create TableB and its records, some of them linked to TableA
15	let TableBRecord1 = new Parse.Object('TableB');
16	TableBRecord1.set('FieldB', 'Value B 1');
17	TableBRecord1.set('link', TableARecord1);
18	TableBRecord1 = await TableBRecord1.save();
19	
20	let TableBRecord2 = new Parse.Object('TableB');
21	TableBRecord2.set('FieldB', 'Value B 2');
22	TableBRecord2.set('link', TableARecord1);
23	TableBRecord2 = await TableBRecord2.save();
24	
25	let TableBRecord3 = new Parse.Object('TableB');
26	TableBRecord3.set('FieldB', 'Value B 3');
27	TableBRecord3.set('link', TableARecord3);
28	TableBRecord3 = await TableBRecord3.save();
29	
30	let TableBRecord4 = new Parse.Object('TableB');
31	TableBRecord4.set('FieldB', 'Value B 4');
32	TableBRecord4 = await TableBRecord4.save();
33	
34	console.log('Success!');
```

## 3 - Querying the data

Now that you have populated the classes, we can now perform the relational queries in it. Let’s begin by performing the INNER JOIN, introducing the join relational query that we will use in all of our examples. This query represents the results of two combined queries between tables A and B, returning all the records that are related by a specific condition using the Parse.Query.matchesQuery method.

```javascript
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// INNER JOIN, get only the records in TableA that have matching records in TableB
13	console.log("INNER JOIN");
14	console.log("TABLE A ID | FIELD A | FIELD B");
15	for (let joinResult of joinQueryResults) {
16	  console.log(
17	    `${joinResult.get("link").id} | ${joinResult
18	      .get("link")
19	      .get("FieldA")} | ${joinResult.get("FieldB")}`
20	  );
21	}
```

The INNER JOIN SQL query behavior is exactly the one achieved in our generic join relational query, so we need to print its results in the console. Remember that with a Parse.Object you can use the get method to retrieve data by using the column name.

Let’s now perform a LEFT OUTER JOIN consisting of getting all the records on TableA and showing the relational data on TableB, when available:

```javascript
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// LEFT OUTER JOIN, get records in TableA that have matching records in TableB and also every
13	// other TableA record
14	let queryTableA = new Parse.Query("TableA");
15	queryTableA.limit(10);
16	let queryTableAResults = await queryTableA.find();
17	console.log("LEFT JOIN");
18	console.log("TABLE A ID | FIELD A | FIELD B");
19	for (let result of queryTableAResults) {
20	  // Get all entries from JOIN query that have a link to this TableA entry
21	  let joinQueryResultsFiltered = joinQueryResults.filter(
22	    (joinQueryResult) =>
23	      joinQueryResult.get("link") !== undefined &&
24	      joinQueryResult.get("link").id == result.id
25	  );
26	  if (joinQueryResultsFiltered.length > 0) {
27	    for (let joinResult of joinQueryResultsFiltered) {
28	      let fieldBValue = joinResult.get("FieldB");
29	      console.log(`${result.id} | ${result.get("FieldA")} | ${fieldBValue}`);
30	    }
31	  } else {
32	    console.log(`${result.id} | ${result.get("FieldA")} | `);
33	  }
34	}
```

The RIGHT OUTER JOIN is the opposite of the left one, fetching the records from TableB.

```javascript
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// RIGHT OUTER JOIN, get records in TableA that have matching records in TableB and also every
13	// other TableB record
14	let queryTableB = new Parse.Query("TableB");
15	queryTableB.limit(10);
16	let queryTableBResults = await queryTableB.find();
17	console.log("RIGHT JOIN");
18	console.log("TABLE B ID | FIELD A | FIELD B");
19	for (let result of queryTableBResults) {
20	  // Get all entries from JOIN query that matches this TableB entry
21	  let joinQueryResultsFiltered = joinQueryResults.filter(
22	    (joinQueryResult) => joinQueryResult.id == result.id
23	  );
24	  if (joinQueryResultsFiltered.length > 0) {
25	    for (let joinResult of joinQueryResultsFiltered) {
26	      let fieldAValue = "";
27	      if (joinResult.get("link") !== undefined) {
28	        fieldAValue = joinResult.get("link").get("FieldA");
29	      }
30	      console.log(
31	        `${result.id} | ${fieldAValue} | ${joinResult.get("FieldB")}`
32	      );
33	    }
34	  } else {
35	    console.log(`${result.id} | | ${result.get("FieldB")}`);
36	  }
37	}
```

Finally, we have the FULL OUTER JOIN which is the combination of the left and right inner joins:

```javascript
1	// JOIN query, get all records in TableA that have matching records in TableB
2	let innerQueryTableA = new Parse.Query("TableA");
3	// Limit to 10 results only for example so we don't fetch too much data
4	innerQueryTableA.limit(10);
5	let joinQueryTableB = new Parse.Query("TableB");
6	// Match the TableA query by the "link" property
7	joinQueryTableB.matchesQuery("link", innerQueryTableA);
8	// Include the "link" property so we have the content of TableA as well
9	joinQueryTableB.include("link");
10	let joinQueryResults = await joinQueryTableB.find();
11	
12	// FULL OUTER JOIN, combining LEFT and RIGHT OUTER JOIN results
13	console.log("FULL JOIN");
14	console.log("TABLE ID | FIELD A | FIELD B");
15	// First print all INNER JOIN results
16	for (let joinResult of joinQueryResults) {
17	  console.log(
18	    `${joinResult.get("link").id} | ${joinResult
19	      .get("link")
20	      .get("FieldA")} | ${joinResult.get("FieldB")}`
21	  );
22	}
23	// Print LEFT JOIN leftovers
24	let outerQueryTableA = new Parse.Query("TableA");
25	outerQueryTableA.limit(10);
26	let outerQueryTableAResults = await outerQueryTableA.find();
27	// Get all entries from query that doesn't match the JOIN query results
28	let filteredOuterQueryTableAResults = outerQueryTableAResults.filter(
29	  (outerQueryTableAResult) =>
30	    joinQueryResults.find(
31	      (joinQueryResult) =>
32	        joinQueryResult.get("link") !== undefined &&
33	        joinQueryResult.get("link").id === outerQueryTableAResult.id
34	    ) === undefined
35	);
36	for (let result of filteredOuterQueryTableAResults) {
37	  console.log(`${result.id} | ${result.get("FieldA")} | `);
38	}
39	// Print RIGHT JOIN leftovers
40	let outerQueryTableB = new Parse.Query("TableB");
41	outerQueryTableB.limit(10);
42	let outerQueryTableBResults = await outerQueryTableB.find();
43	// Get all entries from query that doesn't match the JOIN query results
44	let filteredOuterQueryTableBResults = outerQueryTableBResults.filter(
45	  (outerQueryTableBResult) =>
46	    joinQueryResults.find(
47	      (joinQueryResult) => joinQueryResult.id === outerQueryTableBResult.id
48	    ) === undefined
49	);
50	for (let result of filteredOuterQueryTableBResults) {
51	  console.log(`${result.id} | | ${result.get("FieldB")}`);
52	}
```

## Conclusion

At the end of this guide, you learned how to perform on Back4App relational queries using Parse and emulating the most common SQL JOIN queries in a NoSQL database.

[title] Email verification
[path] Android/Users/

# How to implement user registration with email verification

## Introduction

In this guide, you will learn how to set up a user email verification process to a user registration feature (Sign Up). You will create an app that includes user registration with email verification using <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

This tutorial uses a basic app created in Android Studio 4.1.1 with buildToolsVersion=30.0.3 , Compile SDK Version = 30 and targetSdkVersion 30

:::hint{type="success"}
**At any time, you can access the complete Project via our GitHub repositories.**

- <a href="https://github.com/templates-back4app/android_email_verification_kt" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/android_email_verification_java" target="_blank">Java Example Repository</a>
:::

## Goal

Setup a user verification email process on Back4App on a user sign up feature.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An android app <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">created and connected to Back4App.</a>
- A device (or <a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank">virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## 1 - Import Library

In this step we will import the libraries which we are gonna use in our project:

1. Add the following Parse Classes to our Activities.

>  1   import com.parse.Parse;
>  2   import com.parse.ParseException;
>  3   import com.parse.ParseUser;

&#x20;    2\. You need to add Java 1.8 to our Project via build.gradle(Module\:App) because you will use lambda functions frequently in this Project.

```java
1   compileOptions {
2       sourceCompatibility JavaVersion.VERSION_1_8
3       targetCompatibility JavaVersion.VERSION_1_8
4   }
```

## 2 - Enable Email Verification

Let’s now enable the email verification on Back4App Dashboard. The email verification page has two properties: Verify User Emails and Prevent login if the email is not verified.
If you enable only the Verify User Emails option, the user will receive the verification email but will be able to log in and use the application normally. If you also enable the “Prevent login if email is not verified” option, the user will only log in after concluding the email verification process.

&#x20;    1\. Go to your App at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a> and click on Server Settings.

&#x20;    2\. Find the “Verification emails” card and click on Settings.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bydSzO_8krq6jisNsHd_G_image.png)



&#x20;    3\. Click on Verify User Email and Prevent login if the email is not verified.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/z3rwo9fC1_-Hd3kGf6zP7_image.png)

&#x20;    4\. Optional: Fill the empty fields and modify the ones that have already been filled based on your preferences.

&#x20;    5\. Click on the SAVE button.

## 3 - Sign Up


The two fundamental attributes of ParseUser class are username and password. There’s a third special attribute that you should also set, i.e. the email.

To implement Sign Up with Email Verification, you will use the same method you used to implement the user registration. But this time, instead of redirecting the user to a logged screen, you will ask the user to verify their email to log in.

After completing the SignUp process, the user will be saved on the database. The user data will be available on Parse Dashboard with the mailVerified Boolean attribute set to false. The verification email process consists of verifying the User’s email and setting this attribute to true so the user can entirely access all your app resources.

Your Sign Up screen will look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ilBeWCB-6fdQ9nvjJ-Bar_image.png" signedSrc size="50" width="278" height="600" position="center" caption}

Create a SignUpActivity work following these steps:

&#x20;    1\. Import into your SignUpActivity, in addition to the dependencies imported in **Step 1**:

> 1   import com.parse.ParseException
>
> **;**
>
>
> 2   import com.parse.SignUpCallback
>
> **;**

&#x20;    2\. Implement the user registration using the following code:

:::CodeblockTabs
```java
1   private void signUp(String username, String password, String email) {
2        progressDialog.show();
3        ParseUser user = new ParseUser();
4        user.setUsername(username);
5        user.setPassword(password);
6        user.setEmail(email);
7        user.signUpInBackground(e -> {
8            progressDialog.dismiss();
9            if (e == null) {
10               ParseUser.logOut();
11               showAlert("Account Created Successfully!", "Please verify your email before Login", false);
12           } else {
13               ParseUser.logOut();
14               showAlert("Error Account Creation failed", "Account could not be created" + " :" + e.getMessage(), true);
15           }
16       });
17   }
```

```kotlin
1   private fun signUp(username: String, password: String, email: String) {
2        progressDialog?.show()
3        val user = ParseUser()
4        user.username = username
5        user.setPassword(password)
6        user.email = email
7        user.signUpInBackground(SignUpCallback {
8            progressDialog?.dismiss()
9            if (it == null) {
10               ParseUser.logOut();
11               showAlert("Account Created Successfully!","Please verify your email before Login", false)
12           } else {
13              ParseUser.logOut();
14              showAlert("Error Account Creation failed","Account could not be created" + " :" + it.message,true)
15           }
16       })
17   }
```
:::

:::hint{type="info"}
In the example project, this code is available inside a SIGN UP button callback.
Also, username, password and email are caught using Edit Texts.
:::

:::hint{type="info"}
You may add your own code to verify if the email address is valid before setting it in the front end. Finally, you may add your own code to provide feedback.
:::

After conclude the sign up we will see the following message… :

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wIMP0PQxS_564oj6QW4Tm_image.png" signedSrc size="50" width="276" height="600" position="center" caption}

&#x20;    3\. It’s interesting to add an additional method to display Alert Dialogs and make the process look more professional. Here’s how you can do this:

:::CodeblockTabs
```java
1    private void showAlert(String title, String message, boolean error) {
2         AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this)
3                 .setTitle(title)
4                 .setMessage(message)
5                 .setPositiveButton("OK", (dialog, which) -> {
6                     dialog.cancel();
7                     // don't forget to change the line below with the names of your Activities
8                     if (!error) {
9                         Intent intent = new Intent(SignUpActivity.this, LoginActivity.class);
10                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
11                        startActivity(intent);
12                    }
13                });
14        AlertDialog ok = builder.create();
15        ok.show();
16    }
```

```kotlin
1    private fun showAlert(title: String, message: String, error: Boolean) {
2         val builder = AlertDialog.Builder(this)
3             .setTitle(title)
4             .setMessage(message)
5             .setPositiveButton("OK") { dialog, which ->
6                 dialog.cancel()
7                 // don't forget to change the line below with the names of your Activities
8                 if (!error) {
9                     val intent = Intent(this@SignUpActivity, LoginActivity::class.java)
10                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
11                    startActivity(intent)
12                }
13            }
14        val ok = builder.create()
15        ok.show()
16    }
```
:::

After SignUp we will receieve an email like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NXLL2hhr5j7GhVGftSUOK_image.png)

After verify the email the property will be set to true:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vhgw-H2rGUgvnKoW3vvQe_image.png)

After verify the email the property will be set to true:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QRt0_FrBwp7BmuLjZHaUG_image.png)

## 4 - Log in

To implement Log In with Email Verification, you will use the same method which you used to implement the basic <a href="https://www.back4app.com/docs/android/login-android-tutorial" target="_blank">user registration</a>. But this time, Parse will check the **emailVerified** boolean before granting further access to the user.

:::hint{type="info"}
**Note**: the user actually logs in when the function *ParseUser.logInInBackground()* is called. But he can’t access the app entirely until the email verification is done, because of a Session object which is created in the database. So it’s important to use *ParseUser.logout() *every time the user who hasn’t verified his email tries to access the application unsuccessfully, in order to not leave Sessions opened.

:::

If you have enabled the ‘Prevent login if email is not verified’ option in Step 2, you will get the following error if you try to login without verifying your email.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ngrxWmCOlDPGthFmDGrVB_image.png" signedSrc size="50" width="281" height="601" position="center" caption}

To make LoginActivity` `work, follow these steps:

&#x20;    1\. Import into your LoginActivity, in addition to the dependencies imported in **Step 1**:

> 1   import com.parse.LogInCallback
>
> **;**
>
>
> 2   import com.parse.ParseException
>
> **;**

&#x20;    2\. To implement user login function, simply use the following code:

:::CodeblockTabs
```java
1   private void login(String username, String password) {
2        progressDialog.show();
3        ParseUser.logInInBackground(username, password, (parseUser, e) -> {
4            progressDialog.dismiss();
5            if (parseUser != null) {
6                showAlert("Login Successful", "Welcome, " + username + "!", false);
7            } else {
8                ParseUser.logOut();
9                showAlert("Login Fail", e.getMessage() + " Please try again", true);
10           }
11       });
12   }
```

```kotlin
1   private fun login(username: String, password: String) {
2        progressDialog?.show()
3        ParseUser.logInInBackground(username,password) { parseUser: ParseUser?, e: ParseException? ->
4            progressDialog?.dismiss()
5            if (parseUser != null) {
6                showAlert("Login Successful", "Welcome, $username!", false)
7            } else {
8                ParseUser.logOut()
9                showAlert("Login Fail", e?.message + " Please try again", true)
10           }
11       }
12   }
```
:::

:::hint{type="info"}
In the example project, this code is placed available a LOG IN button callback.
Also, username and password are caught using Edit Texts.
:::

:::hint{type="info"}
The method alertDisplayer is the same that you added in the SignUpActivity, don’t forget to change its Intent arguments though.
:::

## 5 - Log Out

To implement user Log Out, simply use the code below, in the LogoutActivity:

:::CodeblockTabs
```java
1   progressDialog.show();
2        ParseUser.logOutInBackground(e -> {
3            progressDialog.dismiss();
4            if (e == null)
5                showAlert("So, you're going...", "Ok...Bye-bye then");
6        });
```

```kotlin
1   progressDialog!!.show()
2        ParseUser.logOutInBackground { e: ParseException? ->
3            progressDialog!!.dismiss()
4            if (e == null)
5                showAlert("So, you're going...", "Ok...Bye-bye then")
6        }
```
:::

:::hint{type="info"}
In the example project, this code is available inside a LOG OUT button callback.
:::

:::hint{type="info"}
The method alertDisplayer is the same as you added in the LoginActivity and SignUpActivity, don’t forget to change its Intent arguments .
:::

## 6 - Test your app

Run your app, create a couple of users, and try logging in after registering without

&#x20;    1\. Run your app, create a couple of users, and try logging in after registering without verifying the email to see if the error is displayed.

&#x20;    2\. Login at [Back4App Website.](https://www.back4app.com/)

&#x20;    3\. Find your app and click on Dashboard > Core > Browser > User to see the users you’ve created!

## It’s done!

At this stage, you can Log in, Sign Up or Log out of your app using email verification with Parse Server core features through Back4App!

[title] Geoqueries
[path] Flutter/Parse SDK (REST)/Data Queries/

# Using Flutter geolocation to perform geoqueries on Parse

## Introduction

In this guide, you will learn about GeoPoint data type on Parse and how to perform geo queries in Parse using the Flutter geolocation. You will create an application that will perform geo queries and retrieve records using Parse Geopoints.

We won’t explain the Flutter application code once this guide’s primary focus is on the Flutter with Parse.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
- In order to run this guide example you should set up the plugin <a href="https://pub.dev/packages/geolocator" target="_blank">Geolocator</a> properly. Do not forget to add permissions for Android and iOS in order to access the device location \{: .btn target=”\_blank” rel=”nofollow noreferer noopener”} in the project. Carefully read the instructions for setting up the Android and iOS project.
:::

## Goal

Perform Geoqueries using geopoints stored on Back4App and Flutter geolocation.

## The GeoPoint datatype

Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a ParseGeoPoint to a ParseObject will enable queries to consider the proximity of an object to a reference point. This feature allows you to easily do things like finding out what user is closest to another user or which places are nearest to a user.

To associate a point with an object, you first need to create a ParseGeoPoint. Below you can find a geopoint with a latitude of 40.0 degrees and -30.0 degrees longitude.

> **final**
>
>  point 
>
> **=**
>
>  ParseGeoPoint(latitude: 40.0, longitude: 30.0);

This point is then stored in the object as a regular field, like any other data type (string, number, date, etc.)

> placeObject
>
> **.**
>
> set("location", point);
>
>
> **await**
>
>  placeObject
>
> **.**
>
> save();

\*\* Note: Currently only one key in a class may be a ParseGeoPoint

## 1 - The QueryBuilder class

Any Parse query operation uses the QueryBuilder object type, which will help you retrieve specific data from your database throughout your app. To create a new QueryBuilder, you need to pass as a parameter the desired ParseObject subclass, which is the one that will contain your query results.

It is crucial to know that a QueryBuilder will only resolve after calling a retrieve method query, so a query can be set up and several modifiers can be chained before actually being called. You can read more about the QueryBuilder class <a href="https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#complex-queries" target="_blank">here at the official documentation</a>.

## Using the JavaScript Console on Back4App

Inside your Back4App application’s dashboard, you will find a very useful API console in which you can run JavaScript code directly. In this guide you will use to store data objects in Back4App. On your App main dashboard go to Core->API Console->Javascript.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/a_0axHyd4EviC1lkQUYl6_image.png)

## 2 - Save Data on Back4app

To run the queries on this guide you’ll need first to populate your App with some data. Let’s create a City class, which will be the target of our queries in this guide. Here is the Parse.Object classes creation code, so go ahead and run it in your API console:

```javascript
1   // Add City objects and create table
2   // Note how GeoPoints are created, passing latitude and longitude as arguments
3   // Montevideo
4   City = new Parse.Object('City');
5   City.set('name', 'Montevideo - Uruguay');
6   City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
7   await City.save();
8
9   // Brasília
10  City = new Parse.Object('City');
11  City.set('name', 'Brasília - Brazil');
12  City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
13  await City.save();
14
15  // Bogotá
16  City = new Parse.Object('City');
17  City.set('name', 'Bogotá - Colombia');
18  City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
19  await City.save();
20
21  // Mexico City
22  City = new Parse.Object('City');
23  City.set('name', 'Mexico City - Mexico');
24  City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
25  await City.save();
26
27  // Washington, D.C.
28  City = new Parse.Object('City');
29  City.set('name', 'Washington, D.C. - USA');
30  City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
31  await City.save();
32  
33  // Ottawa
34  City = new Parse.Object('City');
35  City.set('name', 'Ottawa - Canada');
36  City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
37  await City.save();
38
39  // Tokyo
40  City = new Parse.Object('City');
41  City.set('name', 'Tokyo - Japan');
42  City.set('location', new Parse.GeoPoint(35.6897, 139.6922));
43  await City.save();
44
45  // Mumbai
46  City = new Parse.Object('City');
47  City.set('name', 'Mumbai - India');
48  City.set('location', new Parse.GeoPoint(18.9667, 72.8333));
49  await City.save();
50
51  // Shanghai
52  City = new Parse.Object('City');
53  City.set('name', 'Shanghai - China');
54  City.set('location', new Parse.GeoPoint(31.1667, 121.4667));
55  await City.save();
56
57  // New York
58  City = new Parse.Object('City');
59  City.set('name', 'New York - USA');
60  City.set('location', new Parse.GeoPoint(40.6943, -73.9249));
61  await City.save();
62
63  // Moscow
64  City = new Parse.Object('City');
65  City.set('name', 'Moscow - Russia');
66  City.set('location', new Parse.GeoPoint(55.7558, 37.6178));
67  await City.save();
68 
69  // Paris
70  City = new Parse.Object('City');
71  City.set('name', 'Paris - France');
72  City.set('location', new Parse.GeoPoint(48.8566, 2.3522));
73  await City.save();
74
75  // Paris
76  City = new Parse.Object('City');
77  City.set('name', 'London - United Kingdom');
78  City.set('location', new Parse.GeoPoint(51.5072, -0.1275));
79  await City.save();
80  
81  // Luanda
82  City = new Parse.Object('City');
83  City.set('name', 'Luanda - Angola');
84  City.set('location', new Parse.GeoPoint(-8.8383, 13.2344));
85  await City.save();
86
87  // Johannesburg
88  City = new Parse.Object('City');
89  City.set('name', 'Johannesburg - South Africa');
90  City.set('location', new Parse.GeoPoint(-26.2044, 28.0416));
91  await City.save();
92
93  console.log('Success!');
```

After running this code, you should now have a City class in your database. Your new class should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/sgzV_xHxAqb687O4oBItc_image.png)

Let’s now take a look at examples from every QueryBuilder method, along with brief explanations on what they do.

## 3 - Query the data

Now that you have a populated class, we can now perform some GeoPoint queries in it.

Let’s begin by ordering City results by the nearest from Dallas in USA (latitude 32.779167, and longitude -96.808891), using the whereNear method:

```javascript
1       // Create your query
2       final QueryBuilder<ParseObject> parseQuery =
3           QueryBuilder<ParseObject>(ParseObject('City'));
4
5       // Create our GeoPoint for the query
6       final dallasGeoPoint =
7           ParseGeoPoint(latitude: 32.779167, longitude: -96.808891);
8
9       // `whereNear` will order results based on distance between the GeoPoint
10      // type field from the class and the GeoPoint argument
11      parseQuery.whereNear('location', dallasGeoPoint);
12
13      // The query will resolve only after calling this method, retrieving
14      // an array of `ParseObjects`, if success
15      final ParseResponse apiResponse = await parseQuery.query();
16
17      if (apiResponse.success && apiResponse.results != null) {
18        // Let's show the results
19        for (var o in apiResponse.results! as List<ParseObject>) {
20          print(
21              'City: ${o.get<String>('name')} - Location: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}');
22        }
23      }
```

Let’s now query using the method whereWithinKilometers, which will retrieve all results whose GeoPoint field is located within the max distance in Kilometers.
Dallas will be used once again as a reference and the distance limit will be 3000 km.

```javascript
1       // Create your query
2       final QueryBuilder<ParseObject> parseQuery =
3           QueryBuilder<ParseObject>(ParseObject('City'));
4
5       // Create our GeoPoint for the query
6       final dallasGeoPoint =
7           ParseGeoPoint(latitude: 32.779167, longitude: -96.808891);
8
9       // You can also use `withinMiles` and `withinRadians` the same way,
10      // but with different measuring unities
11      parseQuery.whereWithinKilometers('location', dallasGeoPoint, 3000);
12     //parseQuery.whereWithinMiles('location', dallasGeoPoint, 3000);
13
14      // The query will resolve only after calling this method, retrieving
15      // an array of `ParseObjects`, if success
16      final ParseResponse apiResponse = await parseQuery.query();
17
18      if (apiResponse.success && apiResponse.results != null) {
19        // Let's show the results
20        for (var o in apiResponse.results! as List<ParseObject>) {
21          print(
22              'City: ${o.get<String>('name')} - Location: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}');
23        }
24      }
```

Let’s now query using the method whereWithinMiles, which will retrieve all results whose GeoPoint field is located within the max distance in Miles.
Dallas will be used once again as a reference and the distance limit will be 3000 miles.

```javascript
1       // Create your query
2       final QueryBuilder<ParseObject> parseQuery =
3           QueryBuilder<ParseObject>(ParseObject('City'));
4
5       // Create our GeoPoint for the query
6       final dallasGeoPoint =
7           ParseGeoPoint(latitude: 32.779167, longitude: -96.808891);
8
9       // You can also use `whereWithinKilometers` and `whereWithinRadians` the same way,
10      parseQuery.whereWithinMiles('location', dallasGeoPoint, 3000);
11
12      // The query will resolve only after calling this method, retrieving
13      // an array of `ParseObjects`, if success
14      final ParseResponse apiResponse = await parseQuery.query();
15
16      if (apiResponse.success && apiResponse.results != null) {
17        // Let's show the results
18        for (var o in apiResponse.results! as List<ParseObject>) {
19          print(
20              'City: ${o.get<String>('name')} - Location: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}');
21        }
22      }
```

## 4 - Query from Flutter

Let’s now use our example queries inside a Flutter App, with a simple interface having a list showing results and also 3 buttons for calling the queries.

The app also retrieves the device’s current location using <a href="https://pub.dev/packages/geolocator" target="_blank">Geolocator</a> plugin (follow the instructions), so the queries will be using real data.

Open your Flutter project, go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/cupertino.dart';
2   import 'package:flutter/material.dart';
3   import 'package:geolocator/geolocator.dart';
4   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
5
6   void main() async {
7     WidgetsFlutterBinding.ensureInitialized();
8
9     final keyApplicationId = 'YOUR_APP_ID_HERE';
10    final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
11
12    final keyParseServerUrl = 'https://parseapi.back4app.com';
13
14    await Parse().initialize(keyApplicationId, keyParseServerUrl,
15        clientKey: keyClientKey, debug: true);
16
17    runApp(MaterialApp(
18      title: 'Flutter - GeoPoint',
19      debugShowCheckedModeBanner: false,
20      home: HomePage(),
21    ));
22  }
23  
24  class HomePage extends StatefulWidget {
25    @override
26    _HomePageState createState() => _HomePageState();
27  }
28
29  class _HomePageState extends State<HomePage> {
30    List<ParseObject> results = <ParseObject>[];
31    double selectedDistance = 3000;
32
33    Future<Position> getCurrentPosition() async {
34      bool serviceEnabled;
35      LocationPermission permission;
36  
37      // Test if location services are enabled.
38      serviceEnabled = await Geolocator.isLocationServiceEnabled();
39      if (!serviceEnabled) {
40        return Future.error('Location services are disabled.');
41      }
42
43      permission = await Geolocator.checkPermission();
44      if (permission == LocationPermission.denied) {
45        permission = await Geolocator.requestPermission();
46        if (permission == LocationPermission.denied) {
47          return Future.error('Location permissions are denied');
48        }
49      }
50
51      if (permission == LocationPermission.deniedForever) {
52        return Future.error(
53            'Location permissions are permanently denied, we cannot request permissions.');
54      }
55
56      // When we reach here, permissions are granted and we can
57      // continue accessing the position of the device.
58      return await Geolocator.getCurrentPosition();
59    }
60  
61    void doQueryNear() async {
62      // Create your query
63      final QueryBuilder<ParseObject> parseQuery =
64          QueryBuilder<ParseObject>(ParseObject('City'));
65  
66      // Get current position from device
67      final position = await getCurrentPosition();
68
69      final currentGeoPoint = ParseGeoPoint(
70          latitude: position.latitude, longitude: position.longitude);
71
72      // `whereNear` will order results based on distance between the GeoPoint
73      // type field from the class and the GeoPoint argument
74      parseQuery.whereNear('location', currentGeoPoint);
75  
76      // The query will resolve only after calling this method, retrieving
77      // an array of `ParseObjects`, if success
78      final ParseResponse apiResponse = await parseQuery.query();
79  
80      if (apiResponse.success && apiResponse.results != null) {
81        // Let's show the results
82        for (var o in apiResponse.results! as List<ParseObject>) {
83          print(
84              'City: ${o.get<String>('name')} - Location: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}');
85        }
86  
87        setState(() {
88          results = apiResponse.results as List<ParseObject>;
89        });
90      } else {
91        setState(() {
92          results.clear();
93        });
94      }
95   }
96  
97    void doQueryInKilometers() async {
98      // Create your query
99      final QueryBuilder<ParseObject> parseQuery =
100         QueryBuilder<ParseObject>(ParseObject('City'));
101 
102     // Get current position from device
103     final position = await getCurrentPosition();
104 
105     final currentGeoPoint = ParseGeoPoint(
106         latitude: position.latitude, longitude: position.longitude);
107 
108     // You can also use `whereWithinMiles` and `whereWithinRadians` the same way,
109     // but with different measuring unities
110     parseQuery.whereWithinKilometers(
111         'location', currentGeoPoint, selectedDistance);
112
113     // The query will resolve only after calling this method, retrieving
114     // an array of `ParseObjects`, if success
115     final ParseResponse apiResponse = await parseQuery.query();
116 
117     if (apiResponse.success && apiResponse.results != null) {
118       // Let's show the results
119       for (var o in apiResponse.results! as List<ParseObject>) {
120         print(
121             'City: ${o.get<String>('name')} - Location: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}');
122       }
123
124       setState(() {
125         results = apiResponse.results as List<ParseObject>;
126       });
127     } else {
128       setState(() {
129         results.clear();
130       });
131     }
132   }
133
134   void doQueryInMiles() async {
135     // Create your query
136     final QueryBuilder<ParseObject> parseQuery =
137         QueryBuilder<ParseObject>(ParseObject('City'));
138 
139     // Get current position from device
140     final position = await getCurrentPosition();
141 
142     final currentGeoPoint = ParseGeoPoint(
143         latitude: position.latitude, longitude: position.longitude);
144 
145     // You can also use `whereWithinKilometers` and `whereWithinRadians` the same way,
146     parseQuery.whereWithinMiles('location', currentGeoPoint, selectedDistance);
147 
148     // The query will resolve only after calling this method, retrieving
149     // an array of `ParseObjects`, if success
150     final ParseResponse apiResponse = await parseQuery.query();
151 
152     if (apiResponse.success && apiResponse.results != null) {
153     // Let's show the results
154       for (var o in apiResponse.results! as List<ParseObject>) {
155         print(
156             'City: ${o.get<String>('name')} - Location: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}');
157       }
158
159       setState(() {
160         results = apiResponse.results as List<ParseObject>;
161       });
162     } else {
163       setState(() {
164         results.clear();
165       });
166     }
167   }
168 
169   void doClearResults() async {
170     setState(() {
171       results = [];
172     });
173   }
174 
175   @override
176   Widget build(BuildContext context) {
177     return Scaffold(
178         body: Padding(
179       padding: const EdgeInsets.all(16.0),
180       child: Column(
181         crossAxisAlignment: CrossAxisAlignment.stretch,
182         children: [
183           Container(
184             height: 200,
185             child: Image.network(
186                 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
187           ),
188           Center(
189             child: const Text('Flutter on Back4app - GeoPoint',
190                 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
191           ),
192           SizedBox(
193             height: 8,
194           ),
195           Container(
196             height: 50,
197             child: ElevatedButton(
198                 onPressed: doQueryNear,
199                 child: Text('Query Near'),
200                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
201           ),
202           SizedBox(
203             height: 16,
204           ),
205           Center(child: Text('Distance')),
206           Slider(
207             value: selectedDistance,
208             min: 0,
209             max: 10000,
210             divisions: 10,
211             onChanged: (value) {
212               setState(() {
213                 selectedDistance = value;
214               });
215             },
216             label: selectedDistance.toStringAsFixed(0),
217           ),
218           SizedBox(
219             height: 8,
220           ),
221           Container(
222             height: 50,
223             child: ElevatedButton(
224                 onPressed: doQueryInKilometers,
225                 child: Text('Query in Kilometers'),
226                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
227           ),
228           SizedBox(
229             height: 8,
230           ),
231           Container(
232             height: 50,
233             child: ElevatedButton(
234                 onPressed: doQueryInMiles,
235                 child: Text('Query Miles'),
236                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
237           ),
238           SizedBox(
239             height: 8,
240           ),
241           Container(
242             height: 50,
243             child: ElevatedButton(
244                 onPressed: doClearResults,
245                 child: Text('Clear results'),
246                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
247           ),
248           SizedBox(
249             height: 8,
250           ),
251           Text(
252             'Result List: ${results.length}',
253           ),
254           Expanded(
255             child: ListView.builder(
256                 itemCount: results.length,
257                 itemBuilder: (context, index) {
258                   final o = results[index];
259                   return Container(
260                     padding: const EdgeInsets.all(4),
261                     decoration:
262                         BoxDecoration(border: Border.all(color: Colors.black)),
263                     child: Text(
264                         '${o.get<String>('name')} \nLocation: ${o.get<ParseGeoPoint>('location')!.latitude}, ${o.get<ParseGeoPoint>('location')!.longitude}'),
265                   );
266                 }),
267           )
268         ],
269       ),
270     ));
271   }
272 }
```

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = App Id**
- **keyClientKey = Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/m8MRUzwQA1gerzIsCY8oA_image.png" signedSrc size="30" width="318" height="629" position="center" caption}

## Conclusion

At the end of this guide, you learned how GeoPoint data queries work on Parse and how to perform them on Back4App from a Flutter App.

[title] Cloud Code Functions
[path] Flutter/Parse SDK (REST)/

# Using Cloud Functions in a Flutter App

## Introduction

For complex apps, sometimes you just need a bit of logic that isn’t running on a mobile device. Cloud Code makes this possible.

Cloud Code is easy to use because it’s built on the same Parse JavaScript SDK that powers thousands of apps. The only difference is that this code runs in your Parse Server rather than running on the user’s mobile device.

You can use Cloud Code to offload processing to the Parse servers thus increasing your app’s perceived performance. You can create hooks that run whenever an object is saved or deleted. This is useful if you want to validate or sanitize your data. You can also use Cloud Code to modify related objects or kick off other processes such as sending off a push notification.

When you update your Cloud Code, it becomes available to all mobile environments instantly. You don’t have to wait for a new release of your application. This lets you change app behavior on the fly and add new features faster.

This section explains how to create and deploy Cloud Code, followed by how to call a cloud function in Flutter projects through Back4App.

:::hint{type="info"}
In this guide, the focus is to demonstrate the use of Cloud Function through Flutter. You can find more in-depth information in <a href="https://docs.parseplatform.org/cloudcode/guide/" target="_blank">Parse Official Cloud Code Documentation</a>.
:::

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## Goal

Run Parse Cloud Code on Back4App from a Flutter App.

## 1 - Create a Cloud Code File

1. Go to your App at [Back4App Website ](https://www.back4app.com/)and click on Server Settings.
2. Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kqpZq4RaKexbIqQwkwwck_image.png)

&#x20;    3\. Upload or create a new file (you can also edit the currentmain.jsfile directly on the browser). Then, click at Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/M9T9MM0YYLdWYDVaBdEGh_image.png)

Yourmain.jsfile should look like this:

```javascript
1   Parse.Cloud.define("hello", async (request) => {
2       console.log("Hello from Cloud Code!");
3       return "Hello from Cloud Code!";
4   });
5
6   Parse.Cloud.define("sumNumbers", async (request) => {
7       return (request.params.number1 + request.params.number2);
8   });
9
10  Parse.Cloud.define("createToDo", async (request) => {
11      const title = request.params.title;
12      const done = request.params.done;
13
14      const Todo = Parse.Object.extend('ToDo');
15      const todo = new Todo();
16      todo.set('title', title);
17      todo.set('done', done);
18 
19      try {
20          await todo.save();
21          return todo;
22        } catch (error) {
23          console.log('ToDo create - Error - ' + error.code + ' ' + error.message);
24        }
25  });
26
27  Parse.Cloud.define("getListToDo", async (request) => {
28      let query = new Parse.Query("ToDo");
29      query.descending("done");
30      return await query.find();
31  });
```

:::hint{type="info"}
You pass parameters to your Cloud function from your Flutter App and access then within the request.params object.
:::

## 2 - Understanding the ParseCloudFunction class

The ParseCloudFunction class defines provides methods for interacting with Parse Cloud Functions.

A Cloud Function can be called with ParseCloudFunction.execute(\{parameters: params}) that returns a map object or ParseCloudFunction.executeObjectFunction\<>(\{parameters: params}) that returns a ParseObject.

Parameters are optional and a map object is expected.

## 3 - Call Parse Cloud function

Now that you have deployed the Cloud Functions, we can call the functions using Flutter.

### **Example 1 - Call a Cloud Function and get the return**

```javascript
1       //Executes a cloud function with no parameters that returns a Map object
2       final ParseCloudFunction function = ParseCloudFunction('hello');
3       final ParseResponse parseResponse = await function.execute();
4       if (parseResponse.success && parseResponse.result != null) {
5         print(parseResponse.result);
6       }
```

The result displayed in the console will be:

> 1

### **Example 2 - Call a Cloud Function with parameters and get the return**

```javascript
1       //Executes a cloud function with parameters that returns a Map object
2       final ParseCloudFunction function = ParseCloudFunction('sumNumbers');
3       final Map<String, dynamic> params = <String, dynamic>{
4         'number1': 10,
5         'number2': 20
6       };
7       final ParseResponse parseResponse =
8           await function.execute(parameters: params);
9       if (parseResponse.success) {
10        print(parseResponse.result);
11      }
```

The result displayed in the console will be:

>  flutter: Hello from Cloud Code
>
> **!**

### **Example 2.1 - Call a Cloud Function with parameters and get the return**

```javascript
1       //Executes a cloud function with parameters that returns a Map object
2       final ParseCloudFunction function = ParseCloudFunction('sumNumbers');
3       final Map<String, dynamic> params = <String, dynamic>{
4         'number1': 10,
5         'number2': 20
6       };
7       final ParseResponse parseResponse =
8           await function.execute(parameters: params);
9       if (parseResponse.success) {
10        print(parseResponse.result);
11      }
```

> flutter: 30

### **Example 3 - Call a Cloud Function with parameters and get ParseObject on return**

```javascript
1       //Executes a cloud function that returns a ParseObject type
2       final ParseCloudFunction function = ParseCloudFunction('createToDo');
3       final Map<String, dynamic> params = <String, dynamic>{
4         'title': 'Task 1',
5         'done': false
6       };
7       final ParseResponse parseResponse =
8           await function.executeObjectFunction<ParseObject>(parameters: params);
9       if (parseResponse.success && parseResponse.result != null) {
10        if (parseResponse.result['result'] is ParseObject) {
11          //Transforms the return into a ParseObject
12          final ParseObject parseObject = parseResponse.result['result'];
13          print(parseObject.objectId);
14        }
15      }
```

The result displayed in the console will be:

> flutter: {"className"
>
> **:**
>
> "ToDo","objectId"
>
> **:**
>
> "H0kHsIr6KT","createdAt"
>
> **:**
>
> "2021-06-25T00:21:10.023Z","updatedAt"
>
> **:**
>
> "2021-06-25T00:21:10.023Z","title"
>
> **:**
>
> "Task 1","done"
>
> **:false**
>
> }

### **Example 4 - Call a Cloud Function that returns a list of maps that can be converted to a ParseObject**

```javascript
1       //Executes a cloud function with parameters that returns a Map object    
2       final ParseCloudFunction function = ParseCloudFunction('getListToDo');
3       final ParseResponse parseResponse = await function.execute();
4       if (parseResponse.success) {
5         if (parseResponse.result != null) {
6           for (final todo in parseResponse.result) {
7             //Use fromJson method to convert map in ParseObject 
8             print(ParseObject('ToDo').fromJson(todo));
9           }
10        }
11      }
```

The result displayed in the console will be:

:::CodeblockTabs
&#x20;  &#x20;

```none
1    flutter: {"className":"ToDo","objectId":"cR3G4RccT1","createdAt":"2021-06-23T03:20:34.933Z","updatedAt":"2021-06-23T03:20:34.933Z","title":"Task 1","done":false}
2    flutter: {"className":"ToDo","objectId":"6BARcICPKe","createdAt":"2021-06-23T03:20:54.294Z","updatedAt":"2021-06-23T03:20:54.294Z","title":"Task 1","done":false}
3    flutter: {"className":"ToDo","objectId":"tYZA74l89Q","createdAt":"2021-06-23T03:39:42.049Z","updatedAt":"2021-06-23T03:39:42.049Z","title":"Task 1","done":false}
4    flutter: {"className":"ToDo","objectId":"ArjM8Q7H8D","createdAt":"2021-06-24T03:33:27.925Z","updatedAt":"2021-06-24T03:33:27.925Z","title":"Task 1","done":false}
```
:::

## 5 - Call Cloud Function from Flutter

Let’s now use our example call cloud Function in Flutter App, with a simple interface.

Open your Flutter project, go to the main.dart file, clean up all the code, and replace it with:

```javascript
1   import 'package:flutter/cupertino.dart';
2   import 'package:flutter/material.dart';
3   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
4
5   void main() async {
6     WidgetsFlutterBinding.ensureInitialized();
7
8     final keyApplicationId = 'YOUR_APP_ID_HERE';
9     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10
11    final keyParseServerUrl = 'https://parseapi.back4app.com';
12
13    await Parse().initialize(keyApplicationId, keyParseServerUrl,
14        clientKey: keyClientKey, debug: true);
15
16    runApp(MaterialApp(
17      title: 'Flutter - GeoPoint',
18      debugShowCheckedModeBanner: false,
19      home: HomePage(),
20    ));
21  }
22  
23  class HomePage extends StatefulWidget {
24    @override
25    _HomePageState createState() => _HomePageState();
26  }
27
28  class _HomePageState extends State<HomePage> {
29
30    void doCallCloudCodeHello() async {
31      //Executes a cloud function with no parameters that returns a Map object
32      final ParseCloudFunction function = ParseCloudFunction('hello');
33      final ParseResponse parseResponse = await function.execute();
34      if (parseResponse.success && parseResponse.result != null) {
35        print(parseResponse.result);
36      }
37    }
38
39    void doCallCloudCodeSumNumbers() async {
40      //Executes a cloud function with parameters that returns a Map object
41      final ParseCloudFunction function = ParseCloudFunction('sumNumbers');
42      final Map<String, dynamic> params = <String, dynamic>{
43        'number1': 10,
44        'number2': 20
45      };
46      final ParseResponse parseResponse =
47          await function.execute(parameters: params);
48      if (parseResponse.success) {
49        print(parseResponse.result);
50      }
51    }
52  
53    void doCallCloudCodeCreateToDo() async {
54      //Executes a cloud function that returns a ParseObject type
55      final ParseCloudFunction function = ParseCloudFunction('createToDo');
56      final Map<String, dynamic> params = <String, dynamic>{
57        'title': 'Task 1',
58        'done': false
59      };
60      final ParseResponse parseResponse =
61          await function.executeObjectFunction<ParseObject>(parameters: params);
62      if (parseResponse.success && parseResponse.result != null) {
63        if (parseResponse.result['result'] is ParseObject) {
64          //Transforms the return into a ParseObject
65          final ParseObject parseObject = parseResponse.result['result'];
66          print(parseObject.toString());
67        }
68      }
69    }
70
71    void doCallCloudCodeGetListTodo() async {
72      //Executes a cloud function with parameters that returns a Map object
73      final ParseCloudFunction function = ParseCloudFunction('getListToDo');
74      final ParseResponse parseResponse = await function.execute();
75      if (parseResponse.success) {
76        if (parseResponse.result != null) {
77          for (final todo in parseResponse.result) {
78            //Use fromJson method to convert map in ParseObject
79            print(ParseObject('ToDo').fromJson(todo));
80          }
81        }
82      }
83    }
84  
85    @override
86    Widget build(BuildContext context) {
87      return Scaffold(
88          body: Padding(
89        padding: const EdgeInsets.all(16.0),
90        child: Column(
91          crossAxisAlignment: CrossAxisAlignment.stretch,
92          children: [
93            Container(
94              height: 200,
95              child: Image.network(
96                  'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
97            ),
98            Center(
99              child: const Text('Flutter on Back4app - Call Clode Code',
100                 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
101           ),
102           SizedBox(
103             height: 8,
104           ),
105           Container(
106             height: 50,
107             child: ElevatedButton(
108                 onPressed: doCallCloudCodeHello,
109                 child: Text('Cloud Code - Hello'),
110                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
111           ),
112           SizedBox(
113             height: 8,
114           ),
115           Container(
116             height: 50,
117             child: ElevatedButton(
118                 onPressed: doCallCloudCodeSumNumbers,
119                 child: Text('Cloud Code - sumNumber'),
120                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
121           ),
122           SizedBox(
123             height: 8,
124           ),
125           Container(
126             height: 50,
127             child: ElevatedButton(
128                 onPressed: doCallCloudCodeCreateToDo,
129                 child: Text('Cloud Code - createToDo'),
130                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
131           ),
132           SizedBox(
133             height: 8,
134           ),
135           Container(
136             height: 50,
137             child: ElevatedButton(
138                 onPressed: doCallCloudCodeGetListTodo,
139                 child: Text('Cloud Code - getListToDo'),
140                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
141           ),
142         ],
143       ),
144     ));
145   }
146 }
```

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId** = App Id
- **keyClientKey** = Client Key

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LI5Mp1i8qw52_deqP0JE6_image.png" signedSrc size="50" width="318" height="636" position="center" caption}

### Conclusion

At this stage, you are able to code and call your own Cloud Code in your Flutter App using Parse Server Core features through Back4App!.

[title] Data Types
[path] iOS/Parse Swift SDK/Data Objects/

# Parse data types on Swift

## Introduction

When saving data on a Back4App Database, each entity is stored in a key-value pair format. The data type for the value field goes from the fundamental ones (such as **String**, **Int**, **Double**, **Float**, and **Bool**) to more complex structures. The main requirement for storing data on a Back4App Database is that the entity has to conform the **ParseSwift** protocol. On its turn, this protocol provides a set of methods to store, update and delete any instance of an entity.

In this guide, you will learn how to create and setup an entity to save it on your Back4App Database. In the project example the entity we are storing encloses information about a Recipe.

This tutorial uses a basic app created in Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

## Goal

To understand how objects are parsed and stored on a Back4App Database.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
:::

## Understanding our Recipes App

The app functionality is based on a form where one can enter information about a recipe. Depending on the information, the data type may vary. In our example the recipe has the following features:

| Field                   | Data Type         | Description                                                                                     |
| ----------------------- | ----------------- | ----------------------------------------------------------------------------------------------- |
| Name                    | String            | Name of the recipe                                                                              |
| Servings                | Int               | Number of servings                                                                              |
| Available               | Bool              | Determines whether the recipe is available or not                                               |
| Category                | Category          | A custom enumeration which classifies a recipe in three categories: Breakfast, Lunch and Dinner |
| Ingredients             | \[Ingredients]    | The set of ingredients enclosed in a customIngredient\*\* \*\*struct                            |
| Side options            | \[String]         | Names of the additional options the recipe comes with                                           |
| Nutritional information | \[String\:String] | A dictionary containing information about the recipe’s nutritional content                      |
| Release date            | Date              | A date showing when the recipe was available                                                    |

Additionally, there are more data types which are used to implement Database functionality like relation between objects. These data types are not covered in this tutorial.

## Quick reference of commands we are going to use

Given an object, say Recipe, if you want to save it on a Back4App Database, you have to first make this object to conform the ParseSwift protocol (available via the ParseSwift SDK).

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Recipe: ParseObject {
5       /// Enumeration for the recipe category
6       enum Category: Int, CaseIterable, Codable {
7           case breakfast = 0, lunch = 1, dinner = 2
8        
9           var title: String {
10              switch self {
11              case .breakfast: return "Breakfast"
12              case .lunch: return "Lunch"
13              case .dinner: return "Dinner"
14              }
15          }
16      }
17
18      ...
19
20      /// A *String* type property
21      var name: String?
22    
23      /// An *Integer* type property
24      var servings: Int?
25    
26      /// A *Double* (or *Float*) type property
27      var price: Double?
28    
29      /// A *Boolean* type property
30      var isAvailable: Bool?
31    
32      /// An *Enumeration* type property
33      var category: Category?
34    
35      /// An array of *structs*
36      var ingredients: [Ingredient]
37    
38      /// An array of *Strings*
39      var sideOptions: [String]
40    
41      /// A dictionary property
42      var nutritionalInfo: [String: String]
43    
44      /// A *Date* type property
45      var releaseDate: Date?
46  }
```

Before storing instances of this object in a Back4App Database, all its properties must conform the Codable and Hashable protocols.

We make use of the following methods for managing these objects on the Back4App Database:

:::CodeblockTabs
Create

```swift
//The procedure for reading and updating a Recipe object is similar since they rely on the save() method. How a Recipe is instantiated determines if we are creating or updating the object on the Back4App Database. When creating a new instance we use
1   var newRecipe: Recipe
2
3   // Setup newRecipe's properties
4   newRecipe.name = "My recipe's name"
5   newRecipe.servings = 4
6   newRecipe.price = 3.99
7   newRecipe.isAvailable = false
8   newRecipe.category = .breakfast
9   newRecipe.sideOptions = ["Juice"]
10  newRecipe.releaseDate = Date()
11  ...
12
13  // Saves newRecipe on your Back4App Database synchronously and returns the new saved Item. It throws and error if something went wrong.
14  let savedRecipe = try? savedRecipe.save()
15
16  // Saves savedRecipe on your Back4App Database asynchronously, and passes a Result<ToDoListItem, ParseError> object to the completion block to handle the save proccess.
17  savedRecipe.save { result in
18      // Handle the result to check the save was successfull or not
19  }
```

Update

```swift
//And to update an existing instance, we have to provide the objectId value which identifies the the object on the Back4App Database. A satandard update can be implemented in the following way
1   let recipeToUpdate = Recipe(objectId: "OBJECT_ID")
2
3   // Update the properties you need
4   recipeToUpdate.name = "My updated recipe's name"
5   recipeToUpdate.servings = 5
6   recipeToUpdate.price = 5.99
7   recipeToUpdate.isAvailable = true
8   recipeToUpdate.category = .lunch
9   recipeToUpdate.sideOptions = ["Juice", "Coffee"]
10  recipeToUpdate.releaseDate = Date().addingTimeInterval(3600 * 24)
11  ...
12
13  // Save changes synchronousty
14  try? recipeToUpdate.save()
15
16  // Or save changes asynchronously
17  recipeToUpdate.save { result in
18      // handle the result
19  }
```

Read

```swift
//For reading objects stored on your Back4App Database, Recipe now provides the query() static method which returns a Query<Recipe>. This query object can be constructed using one or more QueryConstraint objects in the following way
1   let query = Recipe.query() // A query to fetch all Recipe items on your Back4App Database.
2   let query = Recipe.query("name" == "Omelette") // A query to fetch all Recipe items with name "Omelette" on your Back4App Database.
3   let query = Recipe.query(["name" == "Omelette", "price" = 9.99]) // A query to fetch all Recipe items with name = "Omelette" and price = 9.99.
4
5   // Fetches the items synchronously or throws an error if found.
6   let fetchedRecipes = try? query.find()
7
8   // Fetches the items asynchronously and calls a completion block passing a result object containing the result of the operation.
9   query.find { result in
10      // Handle the result
11  }
```

Delete

```swift
//Any deletion process is performed by calling the method delete() on the object to be deleted
1   var recipeToDelete: Recipe
2
3   // Delete recipeToDelete synchronously
4   try? recipeToDelete.delete()
5
6   // Delete recipeToDelete asynchronously
7   recipeToDelete.delete { result in
8       // Handle the result
9   }
```
:::

## 1 - Create the Recipe App Template

We start by creating a new XCode project. This this tutorial the project should look like this

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uYHuakrS2iSPUtwmnURpp_image.png)

:::hint{type="success"}
At any time, you can access the complete Project via our GitHub repositories.

- <a href="https://github.com/templates-back4app/ios-crud-to-do-list" target="_blank">iOS Example Repository</a>
:::

Go to Xcode, and find the SceneDelegate.swift file. In order to add a navigation bar on top of the app, we setup a UINavigationController as the root view controller in the following way

```swift
1   class SceneDelegate: UIResponder, UIWindowSceneDelegate {
2
3       var window: UIWindow?
4
5       func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
6           guard let scene = (scene as? UIWindowScene) else { return }
7        
8           window = .init(windowScene: scene)
9           window?.rootViewController = UINavigationController(rootViewController: RecipesController())
10          window?.makeKeyAndVisible()
11
12          // Additional logic
13      }
14
15      ...
16  }
```

The root view controller class (RecipesController) for the navigation controller is a subclass of UIViewController in which we will layout a form to create and update Recipe objects on the Back4App Database.

## 2 - Setup the Recipe object

Objects you want to save on your Back4App Database have to conform the ParseObject protocol. On our Recipes app this object is Recipe. Therefore, you first need to create this object. Create a new file Recipe.swift and add the following

```swift
1   import Foundation
2   import ParseSwift
3
4   struct Recipe: ParseObject {
5       /// Enumeration for the recipe category
6       enum Category: Int, CaseIterable, Codable {
7           case breakfast = 0, lunch = 1, dinner = 2
8        
9           var title: String {
10              switch self {
11              case .breakfast: return "Breakfast"
12              case .lunch: return "Lunch"
13              case .dinner: return "Dinner"
14              }
15          }
16      }
17    
18      // Required properties from ParseObject protocol
19      var objectId: String?
20      var createdAt: Date?
21      var updatedAt: Date?
22      var ACL: ParseACL?
23    
24      /// A *String* type property
25      var name: String?
26    
27      /// An *Integer* type property
28      var servings: Int?
29    
30      /// A *Double* (or *Float*) type property
31      var price: Double?
32    
33      /// A *Boolean* type property
34      var isAvailable: Bool?
35    
36      /// An *Enumeration* type property
37      var category: Category?
38    
39      /// An array of *structs*
40      var ingredients: [Ingredient]
41    
42      /// An array of *Strings*
43      var sideOptions: [String]
44
45      /// A dictionary property
46      var nutritionalInfo: [String: String]
47        
48      /// A *Date* type property
49      var releaseDate: Date?
50    
51      /// Maps the nutritionalInfo property into an array of tuples
52      func nutritionalInfoArray() -> [(name: String, value: String)] {
53          return nutritionalInfo.map { ($0.key, $0.value) }
54      }
55  }
```

where we already added all the necessary properties to Recipe according to the recipes’s features table.

The Ingredient data type is a struct holding the quantity and the description of the ingredient. As mentioned before, this data type should conform the Codable and Hashable protocols to be part of Recipe’s properties

```swift
1   import Foundation
2
3   struct Ingredient: Hashable, Codable {
4       var quantity: Float
5       var description: String
6   }
```

Additionally, the property category in Recipe has an enumeration (Category) as data type which also conforms the corresponding protocols

```swift
1   struct Recipe: ParseObject {
2       /// Enumeration for the recipe category
3       enum Category: Int, CaseIterable, Codable {
4           case breakfast = 0, lunch = 1, dinner = 2
5        
6           ...
7       }
8       ...   
9   }
```

## 3 - Setting up RecipesController

In RecipesController we should implement all the necessary configuration for the navigationBar and the form used to capture all the Recipe properties. This tutorial does not cover how to implement the layout for the form. We then focus on the logic related with managing data types using ParseSwift SDK. Below we highlight the key points in RecipesController which allow us to understand how we implement the connection between the user interface and the data coming from your Back4App Database

```swift
1   class RecipesController: UIViewController {
2       enum PreviousNext: Int { case previous = 0, next = 1 }
3    
4       ...
5    
6       var recipes: [Recipe] = [] // 1: An array of recipes fetched from your Back4App Database
7    
8       // Section header labels
9       private let recipeLabel: UILabel = .titleLabel(title: "Recipe overview")
10      private let ingredientsLabel: UILabel = .titleLabel(title: "Ingredients")
11      private let nutritionalInfoLabel: UILabel = .titleLabel(title: "Nutritional information")
12    
13      // 2: A custom view containing input fields to enter the recipe's information (except nutritional info. and ingredients)
14      let recipeOverviewView: RecipeInfoView
15    
16      // 3: A  stack view containig the fields to enter the recipe's ingredients
17      let ingredientsStackView: UIStackView
18     
19      // 4: A  stack view containig the fields to enter the nutritional information
20      let nutritionalInfoStackView: UIStackView
21    
22      // 5: Buttons to handle the CRUD logic for the Recipe object currently displayed
23      private var saveButton: UIButton = UIButton(title: "Save")
24      private var updateButton: UIButton = UIButton(title: "Update")
25      private var reloadButton: UIButton = UIButton(title: "Reload")
26    
27      var currentRecipeIndex: Int? // 6: An integer containing the index of the current recipe presenten from the recipes property
28    
29      override func viewDidLoad() {
30          super.viewDidLoad()
31          setupNavigationBar()
32          setupViews()
33      }
34    
35      override func viewDidAppear(_ animated: Bool) {
36          super.viewDidAppear(animated)
37          handleReloadRecipes()
38      }
39    
40      private func setupNavigationBar() {
41          navigationController?.navigationBar.barTintColor = .primary
42          navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
43          navigationController?.navigationBar.isTranslucent = false
44          navigationController?.navigationBar.barStyle = .black
45          navigationItem.title = "Parse data types".uppercased()
46      }
47    
48      private func setupViews() {
49          ... // See the project example for more details
50
51          saveButton.addTarget(self, action: #selector(handleSaveRecipe), for: .touchUpInside)
52          updateButton.addTarget(self, action: #selector(handleUpdateRecipe), for: .touchUpInside)
53          reloadButton.addTarget(self, action: #selector(handleReloadRecipes), for: .touchUpInside)
54      }
55    
56      ...
57  }
```

## 3 - Handling user input and parsing a Recipe object

In a separate file (called RecipesController+ParseSwiftLogic.swift), using an extension we now implement the methods handleSaveRecipe(), handleUpdateRecipe() and handleUpdateRecipe() to handle the input data

```swift
1   import UIKit
2   import ParseSwift
3
4   extension RecipesController {
5       /// Retrieves all the recipes stored on your Back4App Database
6       @objc func handleReloadRecipes() {
7           view.endEditing(true)
8           let query = Recipe.query()
9           query.find { [weak self] result in // Retrieves all the recipes stored on your Back4App Database and refreshes the UI acordingly
10              guard let self = self else { return }
11              switch result {
12              case .success(let recipes):
13                  self.recipes = recipes
14                  self.currentRecipeIndex = recipes.isEmpty ? nil : 0
15                  self.setupRecipeNavigation()
16                
17                  DispatchQueue.main.async { self.presentCurrentRecipe() }
18              case .failure(let error):
19                  DispatchQueue.main.async { self.showAlert(title: "Error", message: error.message) }
20              }
21          }
22      }
23    
24      /// Called when the user wants to update the information of the currently displayed recipe
25      @objc func handleUpdateRecipe() {
26          view.endEditing(true)
27          guard let recipe = prepareRecipeMetadata(), recipe.objectId != nil else { // Prepares the Recipe object for updating
28              return showAlert(title: "Error", message: "Recipe not found.")
29          }
30        
31          recipe.save { [weak self] result in
32              switch result {
33              case .success(let newRecipe):
34                  self?.recipes.append(newRecipe)
35                  self?.showAlert(title: "Success", message: "Recipe saved on your Back4App Database! (objectId: \(newRecipe.id)")
36              case .failure(let error):
37                  self?.showAlert(title: "Error", message: "Failedto save recipe: \(error.message)")
38              }
39          }
40      }
41    
42      /// Saves the currently displayed recipe on your Back4App Database
43      @objc func handleSaveRecipe() {
44          view.endEditing(true)
45          guard var recipe = prepareRecipeMetadata() else { // Prepares the Recipe object for storing
46              return showAlert(title: "Error", message: "Failed to retrieve all the recipe fields.")
47          }
48        
49          recipe.objectId = nil // When saving a Recipe object, we ensure it will be a new instance of it.
50          recipe.save { [weak self] result in
51              switch result {
52              case .success(let newRecipe):
53                  if let index = self?.currentRecipeIndex { self?.recipes[index] = newRecipe }
54                  self?.showAlert(title: "Success", message: "Recipe saved on your Back4App Database! (objectId: \(newRecipe.id))")
55              case .failure(let error):
56                  self?.showAlert(title: "Error", message: "Failed to save recipe: \(error.message)")
57              }
58          }
59      }
60    
61      /// When called it refreshes the UI according to the content of *recipes* and *currentRecipeIndex* properties
62      private func presentCurrentRecipe() {
63          ...
64      }
65    
66      /// Adds the 'Next recipe' and 'Previous recipe' button on the navigation bar. These are used to iterate over all the recipes retreived from your Back4App Database
67      private func setupRecipeNavigation() {
68          ...
69      }
70    
71      /// Reads the information the user entered via the form and returns it as a *Recipe* object
72      private func prepareRecipeMetadata() -> Recipe? {
73          let ingredientsCount = ingredientsStackView.arrangedSubviews.count
74          let nutritionalInfoCount = nutritionalInfoStackView.arrangedSubviews.count
75        
76          let ingredients: [Ingredient] = (0..<ingredientsCount).compactMap { row in
77              guard let textFields = ingredientsStackView.arrangedSubviews[row] as? DoubleTextField,
78                    let quantityString = textFields.primaryText,
79                    let quantity = Float(quantityString),
80                    let description = textFields.secondaryText
81              else {
82                  return nil
83              }
84              return Ingredient(quantity: quantity, description: description)
85          }
86        
87          var nutritionalInfo: [String: String] = [:]
88          
89          (0..<nutritionalInfoCount).forEach { row in
90              guard let textFields = nutritionalInfoStackView.arrangedSubviews[row] as? DoubleTextField,
91                    let content = textFields.primaryText, !content.isEmpty,
92                    let value = textFields.secondaryText, !value.isEmpty
93              else {
94                  return
95              }
96              nutritionalInfo[content] = value
97          }
98         
99          let recipeInfo = recipeOverviewView.parseInputToRecipe() // Reads all the remaining fields from the form (name, category, price, serving, etc) and returns them as a tuple
100 
101         // we collect all the information the user entered and create an instance of Recipe.
102         // The recipeInfo.objectId will be nil if the currently displayed information does not correspond to a recipe already saved on your Back4App Database
103         let newRecipe: Recipe = Recipe(
104             objectId: recipeInfo.objectId,
105             name: recipeInfo.name,
106             servings: recipeInfo.servings,
107             price: recipeInfo.price,
108             isAvailable: recipeInfo.isAvailable,
109             category: recipeInfo.category,
110             ingredients: ingredients,
111             sideOptions: recipeInfo.sideOptions,
112             nutritionalInfo: nutritionalInfo,
113             releaseDate: recipeInfo.releaseDate
114         )
115        
116         return newRecipe
117     }
118    
119     /// Called when the user presses the 'Previous recipe' or 'Next recipe' button
120     @objc private func handleSwitchRecipe(button: UIBarButtonItem) {
121         ...
122     }
123 }
```

## 4 - Run the app!

Before pressing the run button on XCode, do not forget to configure your Back4App application in the AppDelegate class!

The first time you run the project you should see something like this in the simulator (with all the fields empty)

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LBSCbEJ5R0GQvtntzir12_image.png" signedSrc size="60" width="1242" height="2688" position="center" caption}

Now you can start entering a recipe to then save it on your Back4App Database. Once you have saved one recipe, go to your [Back4App dashboard](https://parse-dashboard.back4app.com/apps) and go to your application, in the Database section you will find the class Recipe where all recipes created by the iOS App.

In Particular, it is worth noting how non-fundamental data types like Ingredient, Recipe.Category or dictionaries are stored. If you navigate through the data saved under the Recipe class, you will find that

- The nutritionalInformation dictionary is stored as a JSON object.
- The \[Ingredients] array is stored as an array of JSON objects.
- The enumeration Recipe.Category, since it is has an integer data type as RawValue, it is transformed to a Number value type.
- The releaseDate property, a Date type value in **Swift**, is also stored as a Date type value.

To conclude, when retrieving data from your Back4App Database, you do not need to decode all these fields manually, ParseSwift SDK does decode them automatically. That means, when creating a query (Query\<Recipe> in case) to retrieve data, the query.find() method will parse all the data types and JSON objects to return a Recipe array, there is no additional parsing procedure to implement.

[title] Untitled
[path] iOS/Parse Swift SDK/


[title] Sign in with Twitter
[path] Android/Users/

# How to add twitter login to your Android App

## Introduction

This section explains how you can create an app with user registration using Twitter Login and <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

It will look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3Fmyms7qN4uXug_8PjCkl_twitter-login.gif" signedSrc size="50" width="274" height="477" position="center" caption}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our <a href="https://github.com/back4app/android-geopoints-tutorial" target="_blank">GitHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## 1 - Twitter Set up

To start using Twitter functions, you need to:

1. Go to <a href="https://apps.twitter.com/" target="_blank">Twitter Application Management Website</a>, sign in with a Twitter account and click on Create New App.
2. Fill in the Application Details. When asked to specify Callback URLs, please insert twittersdk://. This is **mandatory** in order to enable authentication through Twitter.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4KreLh6xuEZyCgXIVo4ge_image.png)

&#x20;    3\. Click on the Developer Agreement and then on Create your Twitter application.

&#x20;    4\. Open your Android Studio Project, find your build.gradle (Module: app) and in the dependencies\{} section add the following code to install the Parse Twitter Utils SDK for Android.

> *1  // Don't forget to change the line below with the latest version of Parse Twitter Utils SDK for Android*
>
>
> 2  implementation 'com.github.parse-community:ParseTwitterUtils-Android:latest.version.here'

:::hint{type="info"}
Remember to update the version of Parse Facebook Utils SDK for Android to the latest one. You can find out which is the latest version at the <a href="https://jitpack.io/" target="_blank">JitPack website</a>, following these steps:

1. At JitPack website paste parse-community/ParseTwitterUtils-Androidin the Git repo urlbox.
2. After doing that, click on the Look upbutton. Then you should see the available versions of Parse Twitter Utils SDK for Android, as shown in the following image.&#x20;

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/i9JXLBBrNNsgQarX_tI8u_image.png)
:::

## 2 - Link your Twitter App with Back4App

1. In your Android Studio Project, in the Java file called App that extends Application that you created to initialize the Parse SDK, on its onCreatemethod, right after Parse.initialize()call, use the following code to initialize Parse Twitter Utils SDK.

```java
1   ParseTwitterUtils.initialize(getString(R.string.twitter_consumer_key),  getString(R.string.twitter_consumer_secret));
```

:::hint{type="danger"}
**If you don’t have an **App.java** file as described in this step, access the **[**Install Parse SDK for Android**](https://www.back4app.com/docs/android/parse-android-sdk)** documentation and make sure that you have followed all the steps required to install Parse SDK correctly. If you do not install Parse SDK properly your facebook login with Parse will not work.**
:::

&#x20;    2\. Go to app > res > values > strings.xml file.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NwZ3K7fEHhCwOPW3tu19G_image.png)

1. In the strings.xml file add the following code:

> *<!-- Change the following strings as required -->
> *
>
> \<stringname="twitter_consumer_key">PASTE_YOUR_TWITTER_CONSUMER_KEY</string><string name="twitter_consumer_secret">PASTE_YOUR_TWITTER_CONSUMER_SECRET</string>

&#x20;    2\. Leave the string.xml opened and go to Back4App Website, log in and click on My Apps. Find your app and then click on SERVER SETTINGS.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/DelZzb12DpiSGO6982KlB_image.png)

1. Find the “Twitter Login” block and click on Settings. The “Twitter Login” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SVtBq40Gp_rC_p1fs-9t9_image.png)

&#x20;    2\. Leave the *Back4App Twitter Login *page you visited opened and go to <a href="https://apps.twitter.com/" target="_blank">Twitter Application Management Website</a> find your app and click on its name.

&#x20;    3\. Click on Keys and Access Tokens, copy the Consumer Key (API Key) and the Consumer Secret (API Secret) and paste it in the Back4App Twitter Login page, filling in the respective fields. To finish just click on SAVE. The Consumer Key (API Key) and the Consumer Secret (API Secret) looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wfT0e_pyIJNl7ipTqcUR4_image.png)

&#x20;    4\. Also, copy the Consumer Key (API Key) and the Consumer Secret (API Secret) and paste it in the strings.xml file of your Android Studio Project.

## 4 - Log In

1. Import to your LoginActivity:

> 1   import android.app.AlertDialog
>
> **;**
>
>
> 2   import android.app.ProgressDialog
>
> **;**
>
>
> 3   import android.content.DialogInterface
>
> **;**
>
>
> 4   import android.content.Intent
>
> **;**
>
>
> 5   import android.support.v7.app.AppCompatActivity
>
> **;**
>
>
> 6   import android.os.Bundle
>
> **;**
>
>
> 7   import android.view.View
>
> **;**
>
>
> 8   import android.util.Log
>
> **;**
>
>
> 9   import android.widget.Button
>
> **;**
>
>
> 10  import android.widget.Toast
>
> **;**
>
>
> 11
> 12  import com.parse.LogInCallback
>
> **;**
>
>
> 13  import com.parse.ParseException
>
> **;**
>
>
> 14  import com.parse.twitter.ParseTwitterUtils
>
> **;**
>
>
> 15  import com.parse.ParseUser
>
> **;**
>
>
> 16  import com.parse.SaveCallback
>
> **;**

&#x20;    2\. To implement Twitter Login, simply use below code:

```java
1   ParseTwitterUtils.logIn(LoginActivity.this, new LogInCallback() {
2
3        @Override
4        public void done(final ParseUser user, ParseException err) {
5            if (err != null) {
6                dlg.dismiss();
7                ParseUser.logOut();
8                Log.e("err", "err", err);
9            }
10           if (user == null) {
11               dlg.dismiss();
12               ParseUser.logOut();
13               Toast.makeText(LoginActivity.this, "The user cancelled the Twitter login.", Toast.LENGTH_LONG).show();
14               Log.d("MyApp", "Uh oh. The user cancelled the Twitter login.");
15           } else if (user.isNew()) {
16               dlg.dismiss();
17               Toast.makeText(LoginActivity.this, "User signed up and logged in through Twitter.", Toast.LENGTH_LONG).show();
18               Log.d("MyApp", "User signed up and logged in through Twitter!");
19               user.setUsername(ParseTwitterUtils.getTwitter().getScreenName());
20               user.saveInBackground(new SaveCallback() {
21                   @Override
22                   public void done(ParseException e) {
23                       if (null == e) {
24                           alertDisplayer("First tome login!", "Welcome!");
25                       } else {
26                           ParseUser.logOut();
27                           Toast.makeText(LoginActivity.this, "It was not possible to save your username.", Toast.LENGTH_LONG).show();
28                       }
29                   }
30               });
31           } else {
32               dlg.dismiss();
33               Toast.makeText(LoginActivity.this, "User logged in through Twitter.", Toast.LENGTH_LONG).show();
34               Log.d("MyApp", "User logged in through Twitter!");
35               alertDisplayer("Oh, you!","Welcome back!");
36           }
37       }
38   });
```

:::hint{type="info"}
In the example project, this code is placed inside aLOGIN VIA TWITTERbutton callback.
:::

&#x20;    3\. It’s interesting to add some method to display Alert Dialogs and make the process look more professional. The method below does this:

```java
1   private void alertDisplayer(String title,String message){
2           AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this)
3                   .setTitle(title)
4                   .setMessage(message)
5                   .setPositiveButton("OK", new DialogInterface.OnClickListener() {
6                       @Override
7                       public void onClick(DialogInterface dialog, int which) {
8                           dialog.cancel();
9                           // don't forget to change the line below with the names of your Activities
10                          Intent intent = new Intent(LoginActivity.this, LogoutActivity.class);
11                          intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
12                          startActivity(intent);
13                      }
14                   });
15          AlertDialog ok = builder.create();
16          ok.show();
17  }
```

## 5 - Log out

1. Import to your LoginActivity:

> 1   import android.app.AlertDialog
>
> **;**
>
>
> 2   import android.app.ProgressDialog
>
> **;**
>
>
> 3   import android.content.DialogInterface
>
> **;**
>
>
> 4   import android.content.Intent
>
> **;**
>
>
> 5   import android.support.v7.app.AppCompatActivity
>
> **;**
>
>
> 6   import android.os.Bundle
>
> **;**
>
>
> 7   import android.view.View
>
> **;**
>
>
> 8   import android.widget.Button
>
> **;**
>
>
> 9
> 10  import com.parse.ParseUser
>
> **;**

&#x20;    2\. To implement Twitter Logout, simply use the code below:

> 1
>
> **   ParseUser.**
>
> logOut
>
> **();**
>
>
> 2   alertDisplayer
>
> **(**
>
> "So, you're going..."
>
> **,**
>
>  "Ok...Bye-bye then"
>
> **);**

:::hint{type="info"}
In the example project, this code is placed inside aLOGOUT VIA TWITTERbutton callback.
:::

:::hint{type="info"}
The method alertDisplayer is the same that you added in the LoginActivity, just remember to change the Intent arguments. in the strings.xml file of your Android Studio Project.
:::

## It’s done!

At this stage, you can log in, register and log out of your app with Twitter using Parse Server core features through Back4App!

[title] SignIn with Apple
[path] Flutter/Parse SDK (REST)/User Authentication/Third party Authentication/

# Flutter Sign In with Apple on Parse

## Introduction

Parse Server supports 3rd party authentication.

In this guide, you will learn how to support Sign In with Apple to your Flutter app on Parse.

## Prerequisites

**To complete this tutorial, you will need:**

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- A Flutter app created and connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- Paid membership to the <a href="https://developer.apple.com/programs/" target="_blank">Apple Developer Program</a>.
- Set up the plugin sign\_in\_with\_apple on project.
- A device (not Simulator) running iOS.
:::

## Goal

Sign in with Apple in Flutter app on Parse Server

## 1 - Add the Sign In with Apple capability to your iOS base project

- Openios/Runner.xcworkspacein Xcode
- Check the pluginsign\_in\_with\_apple instructions for setting up Sign in with Apple in your iOS project
- SelectTeamfor project.
- Save e close Xcode

## 2 - Configure App ID in Developer Portal

Log into your [Apple Developer account](https://developer.apple.com/) and go to the Identifiers section.

Check if your created Bundle Identifier is there

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qeDkYoxsZTDmHhHOLAOy-_image.png)

Click the Bundle Identifier and scroll down. Check if the Sign In with Apple is selected

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2WcH3BZnNbaKdLOWiOd8a_image.png)

Click Edit and make sure the Enable as a primary App ID is selected

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NCcGv6xB-bj1pVxvXAp8v_image.png)

If everything is right, save and exit.

## 3 - Set up Parse Auth for Apple

Go to Back4App website, log in and then find your app. After that, click on Server Settings and search for the Apple Login block and select Settings.

The Apple Login section looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4Ub_ocO9ziz_qnUJ8xgB6_image.png" signedSrc size="50" width="255" height="314" position="center" caption}

Now, you just need to paste your Bundle ID in the field below and click on the button to save.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CxqEBlJmrs46J1cocmwas_image.png" signedSrc size="60" width="599" height="454" position="center" caption}

In case you face any trouble while integrating Apple Login, please contact our team via chat!

## 4 - Add the Sign In with Apple

Now that you have the project set up, we can get the user data and sign in to Parse.

According to the documentation, we must send a Map with user authentication data.

```swift
1         final credential = await SignInWithApple.getAppleIDCredential(
2           scopes: [
3             AppleIDAuthorizationScopes.email,
4             AppleIDAuthorizationScopes.fullName,
5           ],
6         );
7 
8         //https://docs.parseplatform.org/parse-server/guide/#apple-authdata
9         //According to the documentation, we must send a Map with user authentication data.
10        //Make sign in with Apple
11        final parseResponse = await ParseUser.loginWith('apple',
12            apple(credential.identityToken!, credential.userIdentifier!));
```

## 5 - Sign in with Apple from Flutter

Let’s now use our example for Sign in with Apple in Flutter App, with a simple interface.

Open your Flutter project, go to the main.dart file, clean up all the code, and replace it with:

```dart
1   import 'package:flutter/material.dart';
2   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
3   import 'package:sign_in_with_apple/sign_in_with_apple.dart';
4
5   void main() async {
6     WidgetsFlutterBinding.ensureInitialized();
7
8     final keyApplicationId = 'YOUR_APP_ID_HERE';
9     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10    final keyParseServerUrl = 'https://parseapi.back4app.com';
11
12    await Parse().initialize(keyApplicationId, keyParseServerUrl,
13        clientKey: keyClientKey, debug: true);
14
15    runApp(MyApp());
16  }
17
18  class MyApp extends StatelessWidget {
19    Future<bool> hasUserLogged() async {
20      ParseUser? currentUser = await ParseUser.currentUser() as ParseUser?;
21      if (currentUser == null) {
22        return false;
23      }
24      //Checks whether the user's session token is valid
25      final ParseResponse? parseResponse =
26          await ParseUser.getCurrentUserFromServer(currentUser.sessionToken!);
27  
28      if (parseResponse?.success == null || !parseResponse!.success) {
29        //Invalid session. Logout
30        await currentUser.logout();
31        return false;
32      } else {
33        return true;
34      }
35    }
36 
37    @override
38    Widget build(BuildContext context) {
39      return MaterialApp(
40        title: 'Flutter - Sign In with Apple',
41        theme: ThemeData(
42          primarySwatch: Colors.blue,
43          visualDensity: VisualDensity.adaptivePlatformDensity,
44        ),
45        home: FutureBuilder<bool>(
46            future: hasUserLogged(),
47            builder: (context, snapshot) {
48              switch (snapshot.connectionState) {
49                case ConnectionState.none:
50                case ConnectionState.waiting:
51                  return Scaffold(
52                    body: Center(
53                      child: Container(
54                          width: 100,
55                          height: 100,
56                          child: CircularProgressIndicator()),
57                    ),
58                  );
59                default:
60                  if (snapshot.hasData && snapshot.data!) {
61                    return UserPage();
62                  } else {
63                    return HomePage();
64                  }
65              }
66            }),
67      );
68    }
69  }
70  
71  class HomePage extends StatefulWidget {
72    @override
73    _HomePageState createState() => _HomePageState();
74  }
75
76  class _HomePageState extends State<HomePage> {
77    @override
78    Widget build(BuildContext context) {
79      return Scaffold(
80          appBar: AppBar(
81            title: const Text('Flutter - Sign In with Apple'),
82          ),
83          body: Center(
84            child: SingleChildScrollView(
85              padding: const EdgeInsets.all(8),
86              child: Column(
87                crossAxisAlignment: CrossAxisAlignment.stretch,
88                children: [
89                  Container(
90                    height: 200,
91                    child: Image.network(
92                        'http://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
93                  ),
94                  Center(
95                    child: const Text('Flutter on Back4App',
96                        style:
97                            TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
98                  ),
99                  SizedBox(
100                   height: 100,
101                 ),
102                 Container(
103                   height: 50,
104                   child: ElevatedButton(
105                     child: const Text('Sign In with Apple'),
106                     onPressed: () => doSignInApple(),
107                   ),
108                 ),
109                 SizedBox(
110                   height: 16,
111                 ),
112               ],
113             ),
114           ),
115         ));
116   }
117 
118   void doSignInApple() async {
119     late ParseResponse parseResponse;
120     try {
121       //Set Scope
122       final credential = await SignInWithApple.getAppleIDCredential(
123         scopes: [
124           AppleIDAuthorizationScopes.email,
125           AppleIDAuthorizationScopes.fullName,
126         ],
127       );
128
129       //https://docs.parseplatform.org/parse-server/guide/#apple-authdata
130       //According to the documentation, we must send a Map with user authentication data.
131       //Make sign in with Apple
132       parseResponse = await ParseUser.loginWith('apple',
133           apple(credential.identityToken!, credential.userIdentifier!));
134 
135       if (parseResponse.success) {
136         final ParseUser parseUser = await ParseUser.currentUser() as ParseUser;
137 
138         //Additional Information in User
139         if (credential.email != null) {
140           parseUser.emailAddress = credential.email;
141         }
142         if (credential.givenName != null && credential.familyName != null) {
143           parseUser.set<String>(
144               'name', '${credential.givenName} ${credential.familyName}');
145         }
146         parseResponse = await parseUser.save();
147         if (parseResponse.success) {
148           Message.showSuccess(
149               context: context,
150               message: 'User was successfully with Sign In Apple!',
151               onPressed: () async {
152                 Navigator.pushAndRemoveUntil(
153                   context,
154                   MaterialPageRoute(builder: (context) => UserPage()),
155                   (Route<dynamic> route) => false,
156                 );
157               });
158         } else {
159           Message.showError(
160               context: context, message: parseResponse.error!.message);
161         }
162       } else {
163         Message.showError(
164             context: context, message: parseResponse.error!.message);
165       }
166     } on Exception catch (e) {
167       print(e.toString());
168       Message.showError(context: context, message: e.toString());
169     }
170   }
171 }
172
173 class UserPage extends StatelessWidget {
174   Future<ParseUser?> getUser() async {
175     return await ParseUser.currentUser() as ParseUser?;
176   }
177
178   @override
179   Widget build(BuildContext context) {
180     void doUserLogout() async {
181       final currentUser = await ParseUser.currentUser() as ParseUser;
182       var response = await currentUser.logout();
183       if (response.success) {
184         Message.showSuccess(
185             context: context,
186             message: 'User was successfully logout!',
187             onPressed: () {
188               Navigator.pushAndRemoveUntil(
189                 context,
190                 MaterialPageRoute(builder: (context) => HomePage()),
191                 (Route<dynamic> route) => false,
192               );
193             });
194       } else {
195         Message.showError(context: context, message: response.error!.message);
196       }
197     }
198 
199     return Scaffold(
200         appBar: AppBar(
201           title: Text('Flutter - Sign In with Apple'),
202         ),
203         body: FutureBuilder<ParseUser?>(
204             future: getUser(),
205             builder: (context, snapshot) {
206               switch (snapshot.connectionState) {
207                 case ConnectionState.none:
208                 case ConnectionState.waiting:
209                   return Center(
210                     child: Container(
211                         width: 100,
212                         height: 100,
213                         child: CircularProgressIndicator()),
214                   );
215                 default:
216                   return Padding(
217                     padding: const EdgeInsets.all(8.0),
218                     child: Column(
219                       crossAxisAlignment: CrossAxisAlignment.stretch,
220                       mainAxisAlignment: MainAxisAlignment.center,
221                       children: [
222                         Center(
223                             child: Text(
224                                 'Hello, ${snapshot.data!.get<String>('name')}')),
225                         SizedBox(
226                           height: 16,
227                         ),
228                         Container(
229                           height: 50,
230                           child: ElevatedButton(
231                             child: const Text('Logout'),
232                             onPressed: () => doUserLogout(),
233                           ),
234                         ),
235                       ],
236                     ),
237                   );
238               }
239             }));
240   }
241 }
242 
243 class Message {
244   static void showSuccess(
245       {required BuildContext context,
246       required String message,
247       VoidCallback? onPressed}) {
248     showDialog(
249       context: context,
250       builder: (BuildContext context) {
251         return AlertDialog(
252           title: const Text("Success!"),
253           content: Text(message),
254           actions: <Widget>[
255             new ElevatedButton(
256               child: const Text("OK"),
257               onPressed: () {
258                 Navigator.of(context).pop();
259                 if (onPressed != null) {
260                   onPressed();
261                 }
262               },
263             ),
264           ],
265         );
266       },
267     );
268   }
269 
270   static void showError(
271       {required BuildContext context,
272       required String message,
273       VoidCallback? onPressed}) {
274     showDialog(
275       context: context,
276       builder: (BuildContext context) {
277         return AlertDialog(
278           title: const Text("Error!"),
279           content: Text(message),
280           actions: <Widget>[
281             new ElevatedButton(
282               child: const Text("OK"),
283               onPressed: () {
284                 Navigator.of(context).pop();
285                 if (onPressed != null) {
286                   onPressed();
287                 }
288               },
289             ),
290           ],
291         );
292       },
293     );
294   }
295 }
```

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Website](https://www.back4app.com/).

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = **App Id
- **keyClientKey = **Client Key

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Gprp9LZAobuixIswWSTxz_image.png" signedSrc size="50" width="313" height="628" position="center" caption}

## Conclusion

At this stage, you are able to use Sign in with Apple in Flutter on Back4app.

[title] Sign In With Google
[path] iOS/Parse Swift SDK/Users/

# Sign In with Google

## Introduction

Nowadays, it is common for applications to offer more than one type of sign-in method. Therefore, for users it is easier to sign in with an account they already have, such as a Google account or Apple ID. Furthermore, social networks (like Facebook) make available an SDK for developers to integrate a sign-in option using their social network credentials.

We start this guide by first integrating the sign-in feature provided by Google. After the user successfully signed in with their Google credential, we use the account information returned by Google to sign in a new user that will be associated with the corresponding Google account.

In <a href="https://github.com/templates-back4app/ios-sign-in-with-google" target="_blank">this repository</a>, we provide a simple Xcode template where you can test the different sign-in methods we are implementing. This project was already introduced in the [previous guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-log-in), you can revisit it for more details about the project.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at **Back4App**.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to **Back4App**.
:::

## Goal

To integrate a user sign-in feature using Google’s SDK and **ParseSwift**.

## 1 - Setting up Google’s SDK

Once we have the Xcode project <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/install-sdk" target="_blank">linked</a> to the **Back4App** application, we proceed to add Google’s SDK which will allow us to implement the sign-in flow. For this example, we use the **Swift Package Manager** (SPM) to add Google Sign-in dependencies. In the Xcode project, go to **File>Add packages…** and in the search bar, look for **https\://github.com/google/GoogleSignIn-iOS**.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/W3WNBnWdIwpab0RARKh7m_image.png)

Once the **GoogleSignIn-iOS** option appears in the results, click on the **Add Package** button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yUrjEkpjPQ8W_QVk59c0G_image.png)

Next, go to the <a href="https://console.cloud.google.com/home/dashboard" target="_blank">dashboard</a> of your Google developer account and <a href="https://console.cloud.google.com/projectcreate" target="_blank">create</a> a new project.

Once the project has been created, the next step is to create an OAuth client id. In the <a href="https://console.cloud.google.com/" target="_blank">Google cloud console</a> select your new project and go to the credentials page. This can be found from the left menu:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/r3-nFClVX_I7MG_c_P31O_image.png)

During this process, you may need to set up the OAuth consent screen that will appear in the sign-in with Google form and fill in the corresponding requirements accordingly.

To conclude this step, we have to add in the app’s URL schemes the URL generated during the creation of the OAuth client id. Go to the project configuration and select the main app target. Then go to the **info** section and add the corresponding URL scheme from your Google project.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gaOoUD3YXw93XJ9If5Nu7_image.png)

## 2 - Using Google Sign in with ParseSwift

With **GoogleSignIn** successfully integrated into your Xcode project, we proceed to implement the sign in with Google feature. In the <a href="https://github.com/templates-back4app/ios-sign-in-with-google" target="_blank">project example</a>, the **LogInController** is in charge of handling and displaying the different sign-in options. We then set up the **signInWithAppleButton** action in this controller:

```swift
1   // LogInController.swift file
2   import GoogleSignIn
3   ...
4
5   class LogInController: UIViewController {
6     ...
7
8     private let signInWithGoogleButton: UIButton = {
9       let button = UIButton(type: .system)
10      button.setImage(UIImage(named: "googleIcon"), for: .normal)
11      button.imageView?.contentMode = .scaleAspectFit
12      return button
13    }()
14
15    override func viewDidLoad() {
16      super.viewDidLoad()
17      // ...
18      // Layout configuration
19      // ...
20
21      signInWithGoogleButton.addTarget(self, action: #selector(handleSignInWithGoogle), for: .touchUpInside)
22    }
23  }
24
25  // MARK: - Sign in with Google section
26  extension LogInController {
27    @objc fileprivate func handleSignInWithGoogle() {
28      // TODO: Here we will implement the sign-in procedure
29    }
30  }
```

In order to show the sign-in form from Google, the GoogleSignIn SDK allows us to set up and present it via the signIn(with\:presenting\:callback) method from the GIDSignIn class. Additionally, we have to pass a GIDConfiguration object instantiated using the **client id** created in the previous step, then presenting view controller, and a callback closure. In the callback closure, Google returns the user credentials (embedded in a GIDGoogleUser object) or an error if the authentication failed.

```swift
1   // MARK: - Sign in with Google section
2   extension LogInController {
3     @objc fileprivate func handleSignInWithGoogle() {        
4       let signInConfig = GIDConfiguration(clientID: "MY_CLIENT_ID") // See https://developers.google.com/identity/sign-in/ios/sign-in for more details
5        
6       // Method provided by the GoogleSignIn framework. See https://developers.google.com/identity/sign-in/ios/sign-in for more details
7       GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { [weak self] googleUser, error in
8         if let error = error {
9           self?.showMessage(title: "Error", message: error.localizedDescription)
10          return
11        }
12            
13        // After Google returns a successful sign in, we get the users id and idToken
14        guard let googleUser = googleUser,
15              let userId = googleUser.userID,
16              let idToken = googleUser.authentication.idToken
17        else { fatalError("This should never happen!?") }
18            
19        // TODO: Sign in the user in your Back4App application.
20      }
21   }
22  }
```

We then take these credentials and use them to sign in to the user on the **Back4App** platform. The object representing the user is the following struct (see the [previous guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-log-in) for more details):&#x20;

```swift
1   import ParseSwift
2
3   struct User: ParseUser {
4     ...
5  
6     var username: String?
7     var email: String?
8     var emailVerified: Bool?
9     var password: String?
10  
11    var age: Int?
12  }
```

Therefore, the credential returned by Google contains an idToken and the user’s id that will be used to complete the sign-in process. More precisely, we instantiate a ParseGoogle\<User> object and call the login(id\:idToken\:completion) method.

```swift
1   // MARK: - Sign in with Google section
2   extension LogInController {
3     @objc fileprivate func handleSignInWithGoogle() {
4       GIDSignIn.sharedInstance.signOut() // This should be called when the user logs out from your app. For login testing purposes, we are calling it each time the user taps on the 'signInWithGoogleButton' button.
5        
6       let signInConfig = GIDConfiguration(clientID: "MY_CLIENT_ID") // See https://developers.google.com/identity/sign-in/ios/sign-in for more details
7        
8       // Method provided by the GoogleSignIn framework. See https://developers.google.com/identity/sign-in/ios/sign-in for more details
9       GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { [weak self] googleUser, error in
10        if let error = error {
11          self?.showMessage(title: "Error", message: error.localizedDescription)
12          return
13        }
14            
15        // After Google returns a successful sign in, we get the users id and idToken
16        guard let googleUser = googleUser,
17              let userId = googleUser.userID,
18              let idToken = googleUser.authentication.idToken
19        else { fatalError("This should never happen!?") }
20            
21        // With the user information returned by Google, you need to sign in the user on your Back4App application
22        User.google.login(id: userId, idToken: idToken) { result in
23          // Returns the User object asociated to the GIDGoogleUser object returned by Google
24          switch result {
25            case .success(let user):
26            // After the login succeeded, we send the user to the home screen
27            // Additionally, you can complete the user information with the data provided by Google
28            let homeController = HomeController()
29            homeController.user = user
30
31            self?.navigationController?.pushViewController(homeController, animated: true)
32            case .failure(let error):
33            // Handle the error if the login process failed
34            self?.showMessage(title: "Failed to sign in", message: error.message)
35          }
36        }
37      }
38    }
39  }
```

## 3 - Verifying user sign in and session creation

To make sure, that the Google sign-in worked, you can look at your **Back4App** application dashboard and see the new User, containing the Google authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CLeXCcDa0LxXyGQmBJZLY_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to the corresponding User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/RxwT1HBT-eeK-RD3EkBlC_image.png)

## 4 - Linking an existing User to a Google account

In case your iOS App requires you to associate a Google account with an existing user in your **Back4App** platform, the ParseGoogle\<User> object implements the method link(id\:completion:) where you pass the id of the Google account to be linked:

```swift
1   let googleUserId: String // The id of the Google account to link to
2
3   User.google.link(id: googleUserId) { [weak self] result in
4     switch result {
5     case .success(let user):
6       // Linking succeeded, user object now is linked to the corresponding Google account
7     case .failure(let error):
8       // Linking failed, handle the error
9     }
10  }
```

## 5 - Signing out

The sign-out process does not vary from the standard way of calling the User.signout() method (detailed in previous guides). However, when a user signs in with a Google account, for consistency, you should sign out the current Google user as well. We can accomplish this by calling the following method alongside User.signout().

> **1   GIDSignIn.**
>
> sharedInstance
>
> **.signOut**
>
> ()

In order to verify if the current user has a linked Google account, you can check it by looking at the User.current?.authData dictionary.

## 6 - Run the app

You can go to this <a href="https://github.com/templates-back4app/ios-sign-in-with-google" target="_blank">repository</a> and download the example project. Before running the project, make sure you set up the provisioning profiles with the ones associated with your developer account.

## Conclusion

At the end of this guide, you learned how to sign in or link existing **Back4App** users on iOS using sign in with Google. In the next guide, we will continue with a different sign-in method.

[title] Live Queries
[path] Android/Real Time/

# Real time application using Live Queries

## Introduction

Live queries are meant to be used in **real-time reactive applications**, where just using the traditional query paradigm would come with some problems, like increased response time and high network and server usage. Live queries should be used in cases where you need to continuous update a page with fresh data coming from the database, which often happens in, but is not limited to, online games, messaging clients and shared to do lists.

This section explains how to use Back4App’s Live Query in an Android environment through <a href="https://www.back4app.com/" target="_blank">Back4App</a>.

This tutorial uses a basic app created in Android Studio Arctic Fox 2020.3.1 Patch 1 with compileSdk 30 , minSdk 21 and targetSdk 30

:::hint{type="success"}
**At any time, you can access the complete Project via our GitHub repositories.**

- <a href="https://github.com/templates-back4app/android_crud_operations_kotlin" target="_blank">Kotlin Example Repository</a>
- <a href="https://github.com/templates-back4app/android_crud_operations_java" target="_blank">Java Example Repository</a>
:::

## Goal

Here is a preview of what we are gonna achive :

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/lEZABE-g4Zzsl_m0EIWm9_image.png" signedSrc size="50" width="346" height="750" position="center" caption}

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An app created on Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App.
- An android app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/android/parse-android-sdk" target="_blank">Install Parse SDK tutoria</a>l to create an Android Studio Project connected to Back4App.
- A device (or<a href="https://developer.android.com/studio/run/managing-avds.html" target="_blank"> virtual device</a>) running Android 4.1 (Jelly Bean) or newer.
:::

## 1 - Enable Live Query

Before you start coding, it’s necessary to have a class in your database to enable Live Query. To do that, simply find your app at <a href="https://www.back4app.com/" target="_blank">Back4App website</a>, and click on Dashboard > Create a class, as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qrg5XoslBM4XVGIav5Muh_image.png)

Now, to enable Live Query feature, log in to your account at <a href="https://www.back4app.com/" target="_blank">Back4App website</a>, find your app and click on Server Settings, then find the “Server URL and Live Query” block and click on SETTINGS.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-Dbz5b-ITMitwi2VOjI4Y_image.png)

Then, you will arrive at a page like the one below. At this page you will need to check the Activate your Back4App subdomain option, the Activate Live Query option and all the classes you want Live Query to be activated, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qtWsNKo65bANo55f_82EK_image.png)

:::hint{type="info"}
It’s necessary to activate **WebHosting** to use Live Queries, because your domain will work as the live server.

- **Note: **To know more about WebHosting look at <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Back4App WebHosting Tutorial</a>.      &#x20;
:::

## 2 - Set up the LiveQuery client

<a href="https://github.com/parse-community" target="_blank">Parse Server’s Official GitHub</a> have an implementation of the Live Query Client for [Android](https://github.com/parse-community/ParseLiveQuery-Android). It is necessary to implement the official Live Query client, which works nicely. To do so, add the following lines to your app app/build.gradle file, in the dependencies section and sync your project:

:::CodeblockTabs
app/build.gradle

```java
1    dependencies {
2        ...
3       // Don't forget to change the line below with the latest version of Parse SDK for Android
4       implementation "com.github.parse-community.Parse-SDK-Android:parse:1.26.0"
5       implementation 'com.github.parse-community:ParseLiveQuery-Android:1.2.2'
6       ...
7    }
```
:::

In this project, we will also create a class named Message, which will contains our messages.

## 3 - Subscribe to your query

To start using Live Queries, first create a LiveQueryClient that will manage the WebSocket connections for you. To do this, you will have to provide the Application ID, it’s JavaScript Key and also a server URL of Live Query which you did the setup in the first step.

:::CodeblockTabs
```java
1      Parse.initialize(new Parse.Configuration.Builder(this)
2                   .applicationId(getString(R.string.back4app_app_id))
3                   .clientKey(getString(R.string.back4app_client_key))
4                   .server(getString(R.string.back4app_server_url))
5                   .build());
```

```kotlin
1      Parse.initialize(Parse.Configuration.Builder(this)
2                   .applicationId(getString(R.string.back4app_app_id))
3                   .clientKey(getString(R.string.back4app_client_key))
4                   .server(getString(R.string.back4app_server_url))
5                   .build())
```
:::

The code for initializingLiveQueryClientis the following:

:::CodeblockTabs
```java
1     ParseLiveQueryClient parseLiveQueryClient = ParseLiveQueryClient.Factory.getClient();
```

```kotlin
1      val parseLiveQueryClient = ParseLiveQueryClient.Factory.getClient()
```
:::

&#x20;We have a RecyclerView adapter named MessageAdapter. messageAdapter.\* functions are trigerring when object added,deleted or updated. Here the our messageAdapter functions.

:::CodeblockTabs
```java
1      public void addItem(ParseObject t) {
2          this.list.add(t);
3          notifyItemInserted(list.size() - 1);
4      }
5
6      public void removeItem(ParseObject object) {
7          for (int i = 0; i < list.size(); i++) {
8              if (list.get(i).getObjectId().equals(object.getObjectId())){
9                  list.remove(i);
10                 notifyItemRemoved(i);
11                 notifyItemRangeChanged(i, list.size());
12                 return;
13             }
14         }
15     }
16     public void updateItem(ParseObject object) {
17         for (int i = 0; i < list.size(); i++) {
18             if (list.get(i).getObjectId().equals(object.getObjectId())){
19                 list.set(i,object);
20                 notifyDataSetChanged();
21                 return;
22             }
23         }
24     }
```

```kotlin
1      fun addItem(t: ParseObject?) {
2      list!!.add(t!!)
3      notifyDataSetChanged()
4      }
5  
6      fun removeItem(`object`: ParseObject) {
7          for (i in list!!.indices) {
8              if (list!![i].objectId == `object`.objectId) {
9                  list!!.removeAt(i)
10                 notifyItemRemoved(i)
11                 notifyItemRangeChanged(i, list!!.size)
12                 return
13             }
14         }
15     }
16
17     fun updateItem(`object`: ParseObject) {
18         for (i in list!!.indices) {
19             if (list!![i].objectId == `object`.objectId) {
20                 list!![i] = `object`
21                 notifyDataSetChanged()
22                 return
23             }
24         }
25     }
```
:::

Then, you should create a ParseQuery for what type of object you want to subscribe. A subscription is an event emitter, which will fire events when changes happen to an object that satisfies your query.
In this example, you will make a basic query and will subscribe all changes done to the Message objects.

:::hint{type="success"}
**See more about queries and subscriptions at **<a href="http://docs.parseplatform.org/android/guide/#queries" target="_blank">**Parse Official Queries Documentation**</a>**.**
:::

:::CodeblockTabs
```java
1        ParseQuery<ParseObject> parseQuery = new ParseQuery<>("Message");
2        subscriptionHandling = parseLiveQueryClient.subscribe(parseQuery);
3        subscriptionHandling.handleSubscribe(q -> {
4            subscriptionHandling.handleEvent(SubscriptionHandling.Event.CREATE, (query, object) -> {
5                MainActivity.this.runOnUiThread(() -> {
6                    messagesAdapter.addItem(object);
7                });
8            });
9            subscriptionHandling.handleEvent(SubscriptionHandling.Event.DELETE, (query, object) -> {
10                MainActivity.this.runOnUiThread(() -> {
11                    messagesAdapter.removeItem(object);
12                });
13            });
14            subscriptionHandling.handleEvent(SubscriptionHandling.Event.UPDATE, (query, object) -> {
15                MainActivity.this.runOnUiThread(() -> {
16                    messagesAdapter.updateItem(object);
17                });
18            });
19        });
```

```kotlin
1   val parseQuery = ParseQuery<ParseObject>("Message")
2           subscriptionHandling = parseLiveQueryClient!!.subscribe(parseQuery)
3           subscriptionHandling!!.handleSubscribe { subscriptionHandling!!.handleEvent(SubscriptionHandling.Event.CREATE
4               ) { _: ParseQuery<ParseObject?>?, `object`: ParseObject? ->
5                   runOnUiThread { messagesAdapter!!.addItem(`object`) }
6               }
7               subscriptionHandling!!.handleEvent(SubscriptionHandling.Event.DELETE
8               ) { _: ParseQuery<ParseObject?>?, `object`: ParseObject? ->
9                   runOnUiThread { messagesAdapter!!.removeItem(`object`!!) }
10              }
11              subscriptionHandling!!.handleEvent(SubscriptionHandling.Event.UPDATE
12              ) { _: ParseQuery<ParseObject?>?, `object`: ParseObject? ->
13                  runOnUiThread { messagesAdapter!!.updateItem(`object`!!) }
14              } 
15          }
```
:::

:::hint{type="info"}
**Note: **We are triggered all this events in the app. Also you can trigger this create,update and delete events from the Back4App (From table or javascript console)
:::

## It’s done!

At this point, you have the knowledge in how to use Live Queries to make real-time reactive applications in an Android environment and also how to setup Live Query using Back4App. Now you can start by implementing it in your own app.

You are now ready to explore [Parse Server core features](https://www.back4app.com/product/parse-server) and [Back4App add-ons](https://www.back4app.com/product/addons).

[title] Basic Operations
[path] React Native/Relay (GraphQL)/Data objects/

# React Native GraphQL CRUD tutorial

## Introduction

Managing data on Back4App using GraphQL is a powerful option for any type of application, speeding up queries and simplifying the most complex ones. Back4App uses common conventions for GraphQL configuration and provides great tools to help your environment setup for development.

In this guide, you will learn how to perform basic data operations through a CRUD example app, which will show you how to create, read, update and delete data from your Parse server database in React Native using GraphQL and Relay.

You will first create your component functions for each CRUD operation, using them later in a complete screen layout, resulting in a to-do list app.

:::hint{type="success"}
**At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.**

- <a href="https://github.com/templates-back4app/react-native-graphql-relay-js-users" target="_blank">JavaScript Example Repository</a>
:::

## Prerequisites

- **For this tutorial we will use the Parse Server in the 4.4 version. If you want to use other versions you can check the corresponding mutation code at **[**GraphQL Logout Guide **](https://www.back4app.com/docs/parse-graphql/graphql-logout-mutation)**example for your respective version.**
- **A React Native App created and connected to Back4App;**
- **Conclude the **<a href="https://www.back4app.com/docs/react-native/graphql/relay-setup" target="_blank">**Relay Environment setup tutorial**</a>**:**
- **Good understanding of Relay Store and Relay Connection Updater; you can read more on **<a href="https://app.archbee.com/docs/_roxIyUMXoBue9I7uv49e/h4VZ_HqkkIJx7yL2doaHJ" target="_blank">**Relay Modern Docs**</a>**:**
- **We will be using JavaScript as the default implementation.**

## Goal

To build a basic CRUD application in React Native using Parse, GraphQL, and Relay.

## 1 - Creating the Todo class

In this first step, we need to define the class that the application will be operating with. The Todo class needs only to have a title(string) field describing the task and a done(boolean) field indicating if the task is completed. You can use a generic mutation to create the class into your database if you don’t have it, following our guide in the [GraphQL Cookbook](https://www.back4app.com/docs/parse-graphql/graphql-mutation-create-object). You can also create the class using Back4App’s dashboard, through the database browser itself or from the JS, or GraphQL consoles.

After creating this new class, remember to download the schema.json file from the dashboard and save it in your application inside the data directory. This is necessary for the Relay Compiler to automatically generate the types from queries, mutations, etc.

At this point, your Back4App dashboard will have automatically generate CRUD Mutations for the class object, to see them you can go to the GraphQL Console, open the docs tab, and search for Todo:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2VZfpsqkBnaEhd3cJN0Rg_image.png" signedSrc size="70" width="431" height="763" position="center" caption}

## 2 - Querying and Rendering the Objects

Let’s now create a component that will be responsible for making the list query in the Back4App server, establishing a connection for the Objects, and automatically rendering and updating the list contents as they change.

Create a new file named TodoListQueryRenderer.js in your src directory with the following code:

```javascript
1	import React from 'react';
2	import { View, Text } from 'react-native';
3	import { graphql, QueryRenderer } from 'react-relay';
4	
5	// prerequisite: properly configured relay environment
6	import environment from '../../relay/environment';
7	
8	// This will be created in the next steps
9	import TodoList from './TodoList';
10	
11	const TodoListQueryRenderer = () => {
12	    return (
13	        // The QueryRenderer acts as a wrapper to any code, providing query results to
14	        // the child components and updating them as needed
15	        // Note that the query is using a fragment called TodoList_query,
16	        // which will be created in the next steps
17	        <QueryRenderer
18	            environment={environment}
19	            query={graphql`
20	            query TodoListQueryRendererQuery {
21	              ...TodoList_query
22	            }
23	          `}
24	            variables={null}
25	            render={({ error, props }) => {
26	                if (error) {
27	                    return (
28	                        <View>
29	                            <Text>{error.message}</Text>
30	                        </View>
31	                    );
32	                } else if (props) {
33	                    return <TodoList query={props} />;
34	                }
35	                return (
36	                    <View>
37	                        <Text>Loading...</Text>
38	                    </View>
39	                );
40	            }}
41	        />
42	    );
43	}
44	
45	export default TodoListQueryRenderer;
```

## 3 - Create the List component and Fragment of Object

Let’s now create our list component, which will render the data retrieved from the QueryRenderer. The component will for now contain only a simple scroll view containing a map function rendering each node. Go ahead and create a new file in your src directory called TodoList.js and add the following code:

```javascript
1	import React, {useState} from 'react';
2	import {
3	  View,
4	  SafeAreaView,
5	  Image,
6	  ScrollView,
7	  StatusBar,
8	  StyleSheet,
9	  TouchableOpacity,
10	} from 'react-native';
11	
12	import {
13	  List,
14	  Text as PaperText,
15	  Button as PaperButton,
16	  TextInput as PaperTextInput,
17	} from 'react-native-paper';
18	import {createFragmentContainer} from 'react-relay';
19	
20	const TodoList = props => {
21	  const [newTodoTitle, setNewTodoTitle] = useState('');
22	
23	  const {query} = props;
24	  const {todos} = query;
25	
26	  const renderTodos = () => {
27	    if (!todos) {
28	      return null;
29	    }
30	
31	    return todos.edges.map(({node: todo}) => (
32	      <List.Item
33	        key={todo.id}
34	        title={todo.title}
35	        titleStyle={todo.done ? Styles.todo_text_done : Styles.todo_text}
36	        style={Styles.todo_item}
37	        right={props => (
38	          <>
39	            {!todo.done && (
40	              <TouchableOpacity onPress={() => updateTodo(todo.id, true)}>
41	                <List.Icon {...props} icon="check" color={'#4CAF50'} />
42	              </TouchableOpacity>
43	            )}
44	
45	            <TouchableOpacity onPress={() => deleteTodo(todo.id)}>
46	              <List.Icon {...props} icon="close" color={'#ef5350'} />
47	            </TouchableOpacity>
48	          </>
49	        )}
50	      />
51	    ));
52	  };
53	
54	  return (
55	    <>
56	      <StatusBar backgroundColor="#208AEC" />
57	      <SafeAreaView style={Styles.container}>
58	        <View style={Styles.header}>
59	          <PaperText style={Styles.header_text_bold}>
60	            {'React Native on Back4App'}
61	          </PaperText>
62	          <PaperText style={Styles.header_text}>{'Product Creation'}</PaperText>
63	        </View>
64	        <View style={Styles.create_todo_container}>
65	        </View>
66	        <ScrollView style={Styles.todo_list}>{renderTodos()}</ScrollView>
67	      </SafeAreaView>
68	    </>
69	  );
70	};
71	
72	const TodoListFragmentContainer = createFragmentContainer(TodoList, {
73	  query: graphql`
74	    fragment TodoList_query on Query {
75	      todos(first: 50) @connection(key: "TodoList_todos", filters: []) {
76	        edges {
77	          node {
78	            id
79	            title
80	            done
81	          }
82	        }
83	      }
84	    }
85	  `,
86	});
```

The Query Renderer in Step 2 expects a fragment of data named TodoList\_query, so we added it to the list component as well at the end of the file. Remember that GraphQL fragments are structures responsible for calling the data the components need to render and specifying what needs to be returned.

## 4 - Creating the Objects

The first step to manage your data in your Back4App GraphQL database is to have some on it. We need to create a function to call to create a new Todo in our list component using a mutation responsible for it.

Let’s begin with the mutation, so create a new file named CreateTodoMutation.js in the src/mutations directory containing the following code:

```javascript
1	import { commitMutation, graphql } from "react-relay";
2	import { ROOT_ID, ConnectionHandler } from 'relay-runtime';
3	
4	const connectionCreateEdgeUpdater = (store, nodeId) => {
5	  const parentProxy = store.get(ROOT_ID);
6	  const todoConnection = ConnectionHandler.getConnection(parentProxy, 'TodoList_todos');
7	
8	  const newTodo = store.get(nodeId);
9	  const edge = ConnectionHandler.createEdge(store, todoConnection, newTodo, 'TodoEdge');
10	  
11	  // No cursor provided, append the edge at the end.
12	  ConnectionHandler.insertEdgeAfter(todoConnection, edge);
13	}
14	
15	const mutation = graphql`
16	  mutation CreateTodoMutation($input: CreateTodoInput!) {
17	    createTodo(input: $input) {
18	      todo {
19	        id
20	        title
21	        done
22	      }
23	    }
24	  }
25	`;
26	
27	function commit({ environment, input, onCompleted, onError }) {
28	  const variables = { input };
29	
30	  commitMutation(environment, {
31	    mutation,
32	    variables,
33	    onCompleted,
34	    onError,
35	    updater: (store) => {
36	      const todoId = store.getRootField('createTodo').getLinkedRecord('todo').getValue('id');
37	
38	      connectionCreateEdgeUpdater(store, todoId)
39	    }
40	  });
41	}
```

Relay Modern provides us a bunch of API endpoints to be used to update the data after mutation calls. In this function, we used some of its functions to get the new Todo created and update the already existing list on the frontend. With this approach, we avoid a new call to the backend saving time that would be spent on a new request, user internet, over fetching, and more.

Now add the following function to the TodoList component, which will call the CreateTodoMutation and will be referenced later by a button.

```javascript
1	const createTodo = () => {
2	    const input = {
3	        fields: {
4	            title: newTodoTitle,
5	            done: false,
6	        },
7	    };
8	
9	    CreateTodoMutation.commit({
10	        environment,
11	        input: input,
12	        onCompleted: () => {
13	            Alert.alert('Success!', 'Todo created!');
14	            setNewTodoTitle('');
15	        },
16	        onError: (errors) => {
17	            Alert.alert('Error!', errors);
18	        },
19	    });
20	}
```

## 5 - Updating the Objects

Updating an object is similar to creating it, with the addition that, when you are updating an object using Relay Modern, you just need to ask the fields that you want to update with the new state on the output of the mutation.

Create a new file named UpdateTodoMutation.js in the src/mutations directory containing the following code:

```javascript
1	import { commitMutation, graphql } from "react-relay";
2	
3	const mutation = graphql`
4	  mutation UpdateTodoMutation($input: UpdateTodoInput!) {
5	    updateTodo(input: $input) {
6	      todo {
7	        id
8	        title
9	        done
10	      }
11	    }
12	  }
13	`;
14	
15	function commit({ environment, input, onCompleted, onError }) {
16	  const variables = { input };
17	
18	  commitMutation(environment, {
19	    mutation,
20	    variables,
21	    onCompleted,
22	    onError,
23	  });
24	}
25	
26	export default {
27	  commit,
28	};
```

Just like in the previous step, add the following function to theTodoListcomponent, which will call theUpdateTodoMutationand will be referenced later by a button.

```javascript
1	const updateTodo = (todoId, done) => {
2	    const input = {
3	        id: todoId,
4	        fields: {
5	            done,
6	        }
7	    }
8	
9	    UpdateTodoMutation.commit({
10	        environment,
11	        input: input,
12	        onCompleted: () => {
13	            Alert.alert('Success!', 'Todo updated!');
14	        },
15	        onError: (errors) => {
16	            Alert.alert('Error!', errors);
17	        },
18	    });
19	}
```

## 6 - Deleting the Objects

When you are deleting an object, with the Relay Store APIs you can update the list and removing the old object from the frontend. We will call an updater callback, similar to the update from createTodo, but we will pass the connection and the id of the to-do to remove it from the list. In this way, we keep the frontend faithfully updated to our server.

Create a new file named DeleteTodoMutation.js in the src/mutations directory containing the following code:

```javascript
1	import { commitMutation, graphql } from "react-relay";
2	import { ROOT_ID, ConnectionHandler } from 'relay-runtime';
3	
4	const connectionDeleteEdgeUpdater = (store, nodeId) => {
5	  const parentProxy = store.get(ROOT_ID);
6	  const connection = ConnectionHandler.getConnection(parentProxy, 'TodoList_todos');
7	
8	  const newCount = connection.getValue('count');
9	  connection.setValue(newCount - 1, 'count');
10	
11	  ConnectionHandler.deleteNode(connection, nodeId);
12	}
13	
14	const mutation = graphql`
15	  mutation DeleteTodoMutation($input: DeleteTodoInput!) {
16	    deleteTodo(input: $input) {
17	      todo {
18	        id
19	      }
20	    }
21	  }
22	`;
23	
24	function commit({ environment, input, onCompleted, onError }) {
25	  const variables = { input };
26	
27	  commitMutation(environment, {
28	    mutation,
29	    variables,
30	    onCompleted,
31	    onError,
32	    updater: (store) => {
33	      connectionDeleteEdgeUpdater(store, input.id)
34	    }
35	  });
36	}
37	
38	export default {
39	  commit,
40	};
```

Just like in the previous step, add the following function to theTodoListcomponent, which will call theDeleteTodoMutationand will be referenced later by a button.

```javascript
1	const deleteTodo = (todoId) => {
2	    const input = {
3	        id: todoId,
4	    }
5	
6	    DeleteTodoMutation.commit({
7	        environment,
8	        input: input,
9	        onCompleted: () => {
10	            Alert.alert('Success!', 'Todo deleted!');
11	        },
12	        onError: (errors) => {
13	            Alert.alert('Error!', errors);
14	        },
15	    });
16	}
```

## 7 - Using CRUD in a React Native component

Let´s now complete our TodoList component code with the styled user interface elements, state variables, and calls to your CRUD functions.

```javascript
1	import React, {useState} from 'react';
2	import {
3	  Alert,
4	  View,
5	  SafeAreaView,
6	  Image,
7	  ScrollView,
8	  StatusBar,
9	  StyleSheet,
10	  TouchableOpacity,
11	} from 'react-native';
12	
13	import {
14	  List,
15	  Text as PaperText,
16	  Button as PaperButton,
17	  TextInput as PaperTextInput,
18	} from 'react-native-paper';
19	import {createFragmentContainer} from 'react-relay';
20	import CreateTodoMutation from './mutations/CreateTodoMutation';
21	import UpdateTodoMutation from './mutations/UpdateTodoMutation';
22	import DeleteTodoMutation from './mutations/DeleteTodoMutation';
23	
24	import environment from '../../relay/environment';
25	
26	const TodoList = props => {
27	  const [newTodoTitle, setNewTodoTitle] = useState('');
28	
29	  const {query} = props;
30	  const {todos} = query;
31	
32	  const createTodo = () => {
33	    const input = {
34	      fields: {
35	        title: newTodoTitle,
36	        done: false,
37	      },
38	    };
39	
40	    CreateTodoMutation.commit({
41	      environment,
42	      input: input,
43	      onCompleted: () => {
44	        Alert.alert('Success!', 'Todo created!');
45	        setNewTodoTitle('');
46	      },
47	      onError: errors => {
48	        Alert.alert('Error!', errors);
49	      },
50	    });
51	  };
52	
53	  const updateTodo = (todoId, done) => {
54	    const input = {
55	      id: todoId,
56	      fields: {
57	        done,
58	      },
59	    };
60	
61	    UpdateTodoMutation.commit({
62	      environment,
63	      input: input,
64	      onCompleted: () => {
65	        Alert.alert('Success!', 'Todo updated!');
66	      },
67	      onError: errors => {
68	        Alert.alert('Error!', errors);
69	      },
70	    });
71	  };
72	
73	  const deleteTodo = todoId => {
74	    const input = {
75	      id: todoId,
76	    };
77	
78	    DeleteTodoMutation.commit({
79	      environment,
80	      input: input,
81	      onCompleted: () => {
82	        Alert.alert('Success!', 'Todo deleted!');
83	      },
84	      onError: errors => {
85	        Alert.alert('Error!', errors);
86	      },
87	    });
88	  };
89	
90	  const renderTodos = () => {
91	    if (!todos) {
92	      return null;
93	    }
94	
95	    return todos.edges.map(({node: todo}) => (
96	      <List.Item
97	        key={todo.id}
98	        title={todo.title}
99	        titleStyle={todo.done ? Styles.todo_text_done : Styles.todo_text}
100	        style={Styles.todo_item}
101	        right={props => (
102	          <>
103	            {!todo.done && (
104	              <TouchableOpacity onPress={() => updateTodo(todo.id, true)}>
105	                <List.Icon {...props} icon="check" color={'#4CAF50'} />
106	              </TouchableOpacity>
107	            )}
108	
109	            <TouchableOpacity onPress={() => deleteTodo(todo.id)}>
110	              <List.Icon {...props} icon="close" color={'#ef5350'} />
111	            </TouchableOpacity>
112	          </>
113	        )}
114	      />
115	    ));
116	  };
117	
118	  return (
119	    <>
120	      <StatusBar backgroundColor="#208AEC" />
121	      <SafeAreaView style={Styles.container}>
122	        <View style={Styles.header}>
123	          <Image
124	            style={Styles.header_logo}
125	            source={ {
126	              uri:
127	                'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
128	            } }
129	          />
130	          <PaperText style={Styles.header_text_bold}>
131	            {'React Native on Back4App'}
132	          </PaperText>
133	          <PaperText style={Styles.header_text}>{'Product Creation'}</PaperText>
134	        </View>
135	        <View style={Styles.create_todo_container}>
136	          {/* Todo create text input */}
137	          <PaperTextInput
138	            value={newTodoTitle}
139	            onChangeText={text => setNewTodoTitle(text)}
140	            label="New Todo"
141	            mode="outlined"
142	            style={Styles.create_todo_input}
143	          />
144	          {/* Todo create button */}
145	          <PaperButton
146	            onPress={() => createTodo()}
147	            mode="contained"
148	            icon="plus"
149	            color={'#208AEC'}
150	            style={Styles.create_todo_button}>
151	            {'Add'}
152	          </PaperButton>
153	        </View>
154	        <ScrollView style={Styles.todo_list}>{renderTodos()}</ScrollView>
155	      </SafeAreaView>
156	    </>
157	  );
158	};
159	
160	const TodoListFragmentContainer = createFragmentContainer(TodoList, {
161	  query: graphql`
162	    fragment TodoList_query on Query {
163	      todos(first: 1000) @connection(key: "TodoList_todos", filters: []) {
164	        edges {
165	          node {
166	            id
167	            title
168	            done
169	          }
170	        }
171	      }
172	    }
173	  `,
174	});
175	
176	const Styles = StyleSheet.create({
177	  container: {
178	    flex: 1,
179	    backgroundColor: '#FFF',
180	  },
181	  wrapper: {
182	    width: '90%',
183	    alignSelf: 'center',
184	  },
185	  header: {
186	    alignItems: 'center',
187	    paddingTop: 10,
188	    paddingBottom: 20,
189	    backgroundColor: '#208AEC',
190	  },
191	  header_logo: {
192	    width: 170,
193	    height: 40,
194	    marginBottom: 10,
195	    resizeMode: 'contain',
196	  },
197	  header_text_bold: {
198	    color: '#fff',
199	    fontSize: 14,
200	    fontWeight: 'bold',
201	  },
202	  header_text: {
203	    marginTop: 3,
204	    color: '#fff',
205	    fontSize: 14,
206	  },
207	  flex_between: {
208	    flexDirection: 'row',
209	    alignItems: 'center',
210	    justifyContent: 'space-between',
211	  },
212	  create_todo_container: {
213	    flexDirection: 'row',
214	    paddingLeft: 10,
215	    paddingRight: 10,
216	  },
217	  create_todo_input: {
218	    flex: 1,
219	    height: 38,
220	    marginBottom: 16,
221	    backgroundColor: '#FFF',
222	    fontSize: 14,
223	  },
224	  create_todo_button: {
225	    marginTop: 6,
226	    marginLeft: 15,
227	    height: 40,
228	  },
229	  todo_list: {
230	    paddingLeft: 10,
231	    paddingRight: 10,
232	  },
233	  todo_item: {
234	    borderBottomWidth: 1,
235	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
236	  },
237	  todo_text: {
238	    fontSize: 15,
239	  },
240	  todo_text_done: {
241	    color: 'rgba(0, 0, 0, 0.3)',
242	    fontSize: 15,
243	    textDecorationLine: 'line-through',
244	  },
245	});
246	
247	export default TodoListFragmentContainer;
```

Before running your project, don´t forget to run yarn relay and update the Relay \_\_generated\_\_ types:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Nd4DwtvEmsFAoRVprGYvX_image.png" signedSrc size="80" width="513" height="222" position="center" caption}

If your component is properly set up, you should see something like this after building and running the app:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TeC_-iegf8yDxcwZNSjs9_image.png" signedSrc size="60" width="421" height="839" position="center" caption}

Go ahead and add some to-dos by typing its titles in the input box one at a time and pressing the Add button. Note that after every successful creation, the createTodo function triggers the updater callback into the Mutation, refreshing your task list automatically. You should now have a sizeable to-do list like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fZwXEt94yjPQHVwi55J9F_image.png" signedSrc size="60" width="423" height="841" position="center" caption}

You can now mark your tasks as done by clicking in the checkmark beside it, causing their done value to be updated to true and changing its icon status on the left. As said in the update function step, the Relay will update automatically the todo with the new value of the field done.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/J5kZwJeNNgD6TrkHqV9Zb_image.png" signedSrc size="60" width="425" height="843" position="center" caption}

The only remaining data operation is now the delete one, which can be done by pressing on the trash can icon at the far right of your to-do list object. After successfully deleting an object, you should get an alert message like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/mQDfk4qh08fZPSKiP_Be8_image.png" signedSrc size="60" width="423" height="842" position="center" caption}

## Conclusion

At the end of this guide, you learned how to perform basic data operations (CRUD) with GrapqhQL and Relay Modern on React Native, while also learning the Relay Connection APIs that help us updating our frontend.

[title] Slack Clone App
[path] ReactJS/Templates/

# React Slack Clone App

## Introduction

In this guide, we will keep exploring the Parse React hook in useful situations by creating a Slack Clone App. The App will have basic features such as signup/login, channels, and real-time chat. In addition, the React components highlight the usefulness of real-time notifications using Live Query, User management capabilities, and the flexibility to perform queries (relational).

At any moment you can quickly deploy a slack clone app on Vercel:

:::CtaButton{url="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Ftemplates-back4app%2Freact-js-slack-clone&env=REACT_APP_PARSE_APPLICATION_ID,REACT_APP_PARSE_LIVE_QUERY_URL,REACT_APP_PARSE_JAVASCRIPT_KEY&envDescription=Enter%20your%20Application%20ID%2C%20Javascript%20Key%20and%20Real%20Time%20URL&envLink=https%3A%2F%2Fparse-dashboard.back4app.com%2Fapps&project-name=slack-clone-javascript-template&repository-name=slack-clone-javascript-template" label="Deploy"}

:::

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

- An Back4App free <a href="https://www.back4app.com/signup" target="_blank">account</a>.
- Clone or download this project via our GitHub repositories so you can run it along with the guide. Make sure to follow the instructions on the README file to successfully set it up in your local environment:
- <a href="https://github.com/templates-back4app/react-js-slack-clone" target="_blank">JavaScript Example Repository
  </a>
- <a href="https://github.com/templates-back4app/react-ts-slack-clone" target="_blank">TypeScript Example Repository</a>
:::

## Goal

To build a Slack clone application on React using the @parse/react hook.

## 1 - Creating your Parse app from a template

This application comprises two database classes: Channel and Message, containing pointers to the Parse User class. Instead of creating the app and database classes from scratch, let’s clone an existing template at <a href="https://www.back4app.com/database/back4app/slackclone" target="_blank">Back4App Database Hub</a>. Click on the “Clone Database” button and proceed with logging in and creating your app. For more details on how to clone please check the <a href="https://www.back4app.com/docs/database-hub/clone" target="_blank">clone App guide</a>.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QpobPDGbgS_I7RgIwddlm_image.png)

Now your App has the complete backend structure necessary to create your Slack Clone on Back4App.

## 2 - Enabling Live Query

Now that you have created the App and classes, you need to enable the live query(real-time). Go to your Back4App dashboard and navigate to App Settings > Server Settings > Server URL and Live Query. After activating your Back4App subdomain, you can then activate Live Query by selecting the classes Message and Channel. After that, save the changes.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0giwuw26CsHMnPopYmHnd_image.png)

The URL above is your Live Query URL make sure to copy it in order to properly initialize the Parse hook.

Let’s now dive into the core React components: ChannelList, MessageList, and Home.

## 3 - The List components

The ChannelList and MessageList components use the @parse/react hook to retrieve the User data through Live Query. They have the same pattern structure as in the [React LiveChat guide](https://www.back4app.com/docs/react/real-time/react-chat-app). They are instantiated with initial parameters (retrieved via the props object) that dynamically compose their Live Query query. Take a look at their queries and how the classes are related to each other:

:::CodeblockTabs
JavaScript

```javascript
1	// Note that Parse.Object coming from props need to be changed into pointers
2	// to be able to be used by Parse, since React changes the object structure
3	// when passing down parameters to child components
4	
5	// channelList.js
6	
7	const ownerQuery = new Parse.Query("Channel");
8	ownerQuery.equalTo("owner", props.currentUser.toPointer());
9	const membersQuery = new Parse.Query("Channel");
10	membersQuery.containedIn("members", [props.currentUser.toPointer()]);
11	// Creates the OR query
12	const parseQuery = Parse.Query.or(ownerQuery, membersQuery);
13	// Set results ordering
14	parseQuery.ascending("name");
15	// Include all pointer fields
16	parseQuery.includeAll();
17	
18	// messageList.js
19	
20	const parseQuery = new Parse.Query("Message");
21	// Get messages that involve both nicknames
22	parseQuery.equalTo("channel", props.currentChannel.toPointer());
23	// Set results ordering
24	parseQuery.ascending("createdAt");
25	// Include nickname fields, to enable name getting on list
26	parseQuery.includeAll();
```

```typescript
1	// Note that Parse.Object coming from props need to be changed into pointers
2	// to be able to be used by Parse, since React changes the object structure
3	// when passing down parameters to child components
4	
5	// channelList.tsx
6	
7	// This query is a composite OR one, combining the results of both
8	const ownerQuery: Parse.Query = new Parse.Query("Channel");
9	ownerQuery.equalTo("owner", props.currentUser.toPointer());
10	const membersQuery: Parse.Query = new Parse.Query("Channel");
11	membersQuery.containedIn("members", [props.currentUser.toPointer()]);
12	// Creates the OR query
13	const parseQuery: Parse.Query = Parse.Query.or(ownerQuery, membersQuery);
14	// Set results ordering
15	parseQuery.ascending("name");
16	// Include all pointer fields
17	parseQuery.includeAll();
18	
19	// messageList.tsx
20	
21	const parseQuery: Parse.Query = new Parse.Query("Message");
22	// Get messages that involve both nicknames
23	parseQuery.equalTo("channel", props.currentChannel.toPointer());
24	// Set results ordering
25	parseQuery.ascending("createdAt");
26	// Include nickname fields, to enable name getting on list
27	parseQuery.includeAll();
```
:::

These queries will be running every time there is a change in the classes data, so if another user in the channel sends a message, you will see it appearing there in real-time.

## 4 - The Home component

The Home component acts as the main application screen, in which the list components are conditionally rendered and instantiated when needed. You can find below the component code. Take a look at the functions for creating channels and inviting users to them.

:::CodeblockTabs
Home.js

```javascript
1	import React, { useEffect, useState } from "react";
2	import "./App.css";
3	import { Modal } from "antd";
4	import { useHistory } from "react-router-dom";
5	import Parse from "parse";
6	import { ChannelList } from "./ChannelList";
7	import { MessageList } from "./MessageList";
8	import { MemberList } from "./MemberList";
9	
10	export const Home = () => {
11	  const history = useHistory();
12	
13	  // State variables holding input values and flags
14	  const [currentUser, setCurrentUser] = useState(null);
15	  const [isCreateChannelModalVisible, setIsCreateChannelModalVisible] =
16	    useState(false);
17	  const [createChannelInput, setCreateChannelInput] = useState("");
18	  const [currentChannel, setCurrentChannel] = useState(null);
19	
20	  // This effect hook runs at every render and checks if there is a
21	  // logged in user, redirecting to Login screen if needed
22	  useEffect(() => {
23	    const checkCurrentUser = async () => {
24	      try {
25	        const user = await Parse.User.currentAsync();
26	        if (user === null || user === undefined) {
27	          history.push("/");
28	        } else {
29	          if (currentUser === null) {
30	            setCurrentUser(user);
31	          }
32	        }
33	        return true;
34	      } catch (_error) {}
35	      return false;
36	    };
37	    checkCurrentUser();
38	  });
39	
40	  // Logout function
41	  const doLogout = async () => {
42	    // Logout
43	    try {
44	      await Parse.User.logOut();
45	      // Force useEffect execution to redirect back to Login
46	      setCurrentUser(null);
47	      return true;
48	    } catch (error) {
49	      alert(error);
50	      return false;
51	    }
52	  };
53	
54	  // Makes modal visible
55	  const showCreateChannelModal = () => {
56	    setIsCreateChannelModalVisible(true);
57	  };
58	
59	  // Clear input and hide modal on cancel
60	  const handleCreateChannelModalCancel = () => {
61	    setCreateChannelInput("");
62	    setIsCreateChannelModalVisible(false);
63	  };
64	
65	  // Creates a channel based on input from modal
66	  const doCreateChannel = async () => {
67	    const channelName = createChannelInput;
68	
69	    if (channelName === "") {
70	      alert("Please inform your new channel name!");
71	      return false;
72	    }
73	
74	    // Creates a new Parse.Object instance and set parameters
75	    const Channel = new Parse.Object("Channel");
76	    Channel.set("name", channelName);
77	    Channel.set("owner", currentUser);
78	    // Members is an array of Parse.User objects, so .add() should be used to
79	    // concatenate the value inside the array
80	    Channel.add("members", currentUser);
81	
82	    // Clears input value and hide modal
83	    setCreateChannelInput("");
84	    setIsCreateChannelModalVisible(false);
85	
86	    try {
87	      // Save object on Parse server
88	      const saveResult = await Channel.save();
89	      // Set the created channel as the active channel,
90	      // showing the message list for this channel
91	      setCurrentChannel(saveResult);
92	      alert(`Success on creating channel ${channelName}`);
93	      return true;
94	    } catch (error) {
95	      alert(error);
96	      return false;
97	    }
98	  };
99	
100	  // Changes the active channel and shows the message list for it
101	  // This is called using a callback in the ChannelList component
102	  const doSelectChannel = (channel) => {
103	    setCurrentChannel(null);
104	    setCurrentChannel(channel);
105	  };
106	
107	  // Settings current channel to null hides the message list component
108	  // This is called using a callback in the MessageList component
109	  const doClearCurrentChannel = () => {
110	    setCurrentChannel(null);
111	  };
112	
113	  return (
114	    <div className="grid">
115	      <div className="organizations">
116	        <div className="organization">
117	          <picture className="organization__picture">
118	            <img
119	              className="organization__img"
120	              src="https://scontent.fsqx1-1.fna.fbcdn.net/v/t1.6435-9/29136314_969639596535770_8356900498426560512_n.png?_nc_cat=103&ccb=1-5&_nc_sid=973b4a&_nc_ohc=D9actPSB8DUAX-zaA7F&_nc_ht=scontent.fsqx1-1.fna&oh=96679a09c5c4524f0a6c86110de697b6&oe=618525F9"
121	              alt=""
122	            />
123	          </picture>
124	          <p className="organization__title">Back4App</p>
125	        </div>
126	        <button className="button-inline" onClick={doLogout}>
127	          <svg
128	            className="button-inline__icon"
129	            xmlns="http://www.w3.org/2000/svg"
130	            width="24"
131	            height="24"
132	            viewBox="0 0 24 24"
133	            fill="none"
134	            stroke="currentColor"
135	            strokeWidth="2"
136	            strokeLinecap="round"
137	            strokeLinejoin="round"
138	          >
139	            <polyline points="9 10 4 15 9 20"></polyline>
140	            <path d="M20 4v7a4 4 0 0 1-4 4H4"></path>
141	          </svg>
142	          <span className="button-inline__label">Log out</span>
143	        </button>
144	      </div>
145	      <div className="channels">
146	        {/* Action buttons (new channel and logout) */}
147	        <div>
148	          <Modal
149	            title="Create new channel"
150	            visible={isCreateChannelModalVisible}
151	            onOk={doCreateChannel}
152	            onCancel={handleCreateChannelModalCancel}
153	            okText={"Create"}
154	          >
155	            <>
156	              <label>{"Channel Name"}</label>
157	              <input
158	                type={"text"}
159	                value={createChannelInput}
160	                placeholder={"New Channel Name"}
161	                onChange={(event) => setCreateChannelInput(event.target.value)}
162	              ></input>
163	            </>
164	          </Modal>
165	        </div>
166	        <div className="channels-header" onClick={showCreateChannelModal}>
167	          <p className="channels-header__label">Channels</p>
168	          <svg
169	            className="channels-header__icon"
170	            xmlns="http://www.w3.org/2000/svg"
171	            height="24px"
172	            viewBox="0 0 24 24"
173	            width="24px"
174	            fill="#000000"
175	          >
176	            <path d="M0 0h24v24H0z" fill="none" />
177	            <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
178	          </svg>
179	        </div>
180	        {/* Channel list component, instantiated only when the user is successfully fetched */}
181	        {currentUser !== null && (
182	          <ChannelList
183	            currentUser={currentUser}
184	            selectChannelCallback={doSelectChannel}
185	          />
186	        )}
187	      </div>
188	      <div className="messages">
189	        {/* Message list component, instantiated only when there is a selected channel */}
190	        {currentUser !== null && currentChannel !== null && (
191	          <MessageList
192	            currentUser={currentUser}
193	            currentChannel={currentChannel}
194	            closeChannelCallback={doClearCurrentChannel}
195	          />
196	        )}
197	      </div>
198	      <div className="info">
199	        {/* Member list component, instantiated only when there is a selected channel */}
200	        {currentUser !== null && currentChannel !== null && (
201	          <MemberList
202	            currentUser={currentUser}
203	            currentChannel={currentChannel}
204	            closeChannelCallback={doClearCurrentChannel}
205	          />
206	        )}
207	      </div>
208	    </div>
209	  );
210	};
```

Home.tsx

```typescript
1	import React, { useEffect, useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Modal } from 'antd';
4	import { useHistory } from 'react-router-dom';
5	import Parse from 'parse';
6	import { ChannelList } from './ChannelList';
7	import { MessageList } from './MessageList';
8	import { MemberList } from './MemberList';
9	
10	export const Home: FC<{}> = (): ReactElement => {
11	  const history = useHistory();
12	
13	  // State variables holding input values and flags
14	  const [currentUser, setCurrentUser] = useState<Parse.User | null>(null);
15	  const [isCreateChannelModalVisible, setIsCreateChannelModalVisible] = useState(false);
16	  const [createChannelInput, setCreateChannelInput] = useState('');
17	  const [currentChannel, setCurrentChannel] = useState<Parse.Object | null>(null);
18	
19	  // This effect hook runs at every render and checks if there is a
20	  // logged in user, redirecting to Login screen if needed
21	  useEffect(() => {
22	    const checkCurrentUser = async (): Promise<Boolean> => {
23	      try {
24	        const user: (Parse.User | null) = await Parse.User.currentAsync();
25	        if (user === null || user === undefined) {
26	          history.push('/');
27	        } else {
28	          if (currentUser === null) {
29	            setCurrentUser(user);
30	          }
31	        }
32	        return true;
33	      } catch (_error: any) {}
34	      return false;
35	    }
36	    checkCurrentUser();
37	  });
38	
39	  // Logout function
40	  const doLogout = async (): Promise<Boolean> => {
41	    // Logout
42	    try {
43	      await Parse.User.logOut();
44	      // Force useEffect execution to redirect back to Login
45	      setCurrentUser(null);
46	      return true;
47	    } catch (error: any) {
48	      alert(error);
49	      return false;
50	    }
51	  };
52	
53	  // Makes modal visible
54	  const showCreateChannelModal = (): void => {
55	    setIsCreateChannelModalVisible(true);
56	  }
57	
58	  // Clear input and hide modal on cancel
59	  const handleCreateChannelModalCancel = (): void => {
60	    setCreateChannelInput("");
61	    setIsCreateChannelModalVisible(false);
62	  }
63	
64	  // Creates a channel based on input from modal
65	  const doCreateChannel = async (): Promise<boolean> => {
66	    const channelName: string = createChannelInput;
67	    
68	    if (channelName === '') {
69	      alert("Please inform your new channel name!");
70	      return false;
71	    }
72	
73	    // Creates a new Parse.Object instance and set parameters
74	    const Channel: Parse.Object = new Parse.Object("Channel");
75	    Channel.set('name', channelName);
76	    Channel.set('owner', currentUser);
77	    // Members is an array of Parse.User objects, so .add() should be used to
78	    // concatenate the value inside the array
79	    Channel.add('members', currentUser);
80	
81	    // Clears input value and hide modal
82	    setCreateChannelInput("");
83	    setIsCreateChannelModalVisible(false);
84	
85	    try {
86	      // Save object on Parse server
87	      const saveResult: Parse.Object = await Channel.save();
88	      // Set the created channel as the active channel,
89	      // showing the message list for this channel
90	      setCurrentChannel(saveResult);
91	      alert(`Success on creating channel ${channelName}`);
92	      return true;
93	    } catch (error: any) {
94	      alert(error);
95	      return false;
96	    }
97	  }
98	
99	  // Changes the active channel and shows the message list for it
100	  // This is called using a callback in the ChannelList component
101	  const doSelectChannel = (channel: Parse.Object): void => {
102	    setCurrentChannel(null);
103	    setCurrentChannel(channel);
104	  }
105	
106	  // Settings current channel to null hides the message list component
107	  // This is called using a callback in the MessageList component
108	  const doClearCurrentChannel = (): void => {
109	    setCurrentChannel(null);
110	  }
111	
112	  return (
113	    <div className="grid">
114	      <div className="organizations">
115	        <div className="organization">
116	          <picture className="organization__picture">
117	            <img className="organization__img" src="https://scontent.fsqx1-1.fna.fbcdn.net/v/t1.6435-9/29136314_969639596535770_8356900498426560512_n.png?_nc_cat=103&ccb=1-5&_nc_sid=973b4a&_nc_ohc=D9actPSB8DUAX-zaA7F&_nc_ht=scontent.fsqx1-1.fna&oh=96679a09c5c4524f0a6c86110de697b6&oe=618525F9" alt="" />
118	          </picture>
119	          <p className="organization__title">Back4App</p>
120	        </div>
121	        <button className="button-inline" onClick={doLogout}>
122	          <svg className="button-inline__icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path></svg>
123	          <span className="button-inline__label">Log out</span>
124	        </button>
125	      </div>
126	      <div className="channels">
127	        {/* Action buttons (new channel and logout) */}
128	        <div>
129	          <Modal
130	            title="Create new channel"
131	            visible={isCreateChannelModalVisible}
132	            onOk={doCreateChannel}
133	            onCancel={handleCreateChannelModalCancel}
134	            okText={'Create'}
135	          >
136	            <>
137	              <label>{'Channel Name'}</label>
138	              <input
139	                type={"text"}
140	                value={createChannelInput}
141	                placeholder={"New Channel Name"}
142	                onChange={(event) => setCreateChannelInput(event.target.value)}
143	              ></input>
144	            </>
145	          </Modal>
146	        </div>
147	        <div className="channels-header" onClick={showCreateChannelModal}>
148	          <p className="channels-header__label">Channels</p>
149	          <svg className="channels-header__icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
150	        </div>
151	        {/* Channel list component, instantiated only when the user is successfully fetched */}
152	        {currentUser !== null && (
153	          <ChannelList
154	            currentUser={currentUser}
155	            selectChannelCallback={doSelectChannel}
156	          />
157	        )}
158	      </div>
159	      <div className="messages">
160	        {/* Message list component, instantiated only when there is a selected channel */}
161	        {currentUser !== null && currentChannel !== null && (
162	          <MessageList
163	            currentUser={currentUser}
164	            currentChannel={currentChannel}
165	            closeChannelCallback={doClearCurrentChannel}
166	          />
167	        )}
168	      </div>
169	      <div className="info">
170	        {/* Member list component, instantiated only when there is a selected channel */}
171	        {currentUser !== null && currentChannel !== null && (
172	          <MemberList
173	            currentUser={currentUser}
174	            currentChannel={currentChannel}
175	            closeChannelCallback={doClearCurrentChannel}
176	          />
177	        )}
178	      </div>
179	    </div>
180	  );
181	};
```
:::

This approach of dynamically instantiating the Live Query components allows us to reuse them whenever the user changes the active channel, creates a new one, sends a message, etc. Here is how the complete App will look.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TXsXf_EXKahapqgFN7SKk_image.png)

## 5 - Deploy on Vercel

At any time you can deploy the application on vercel by clicking on the link below:

:::CtaButton{url="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Ftemplates-back4app%2Freact-js-slack-clone&env=REACT_APP_PARSE_APPLICATION_ID,REACT_APP_PARSE_LIVE_QUERY_URL,REACT_APP_PARSE_JAVASCRIPT_KEY&envDescription=Enter%20your%20Application%20ID%2C%20Javascript%20Key%20and%20Real%20Time%20URL&envLink=https%3A%2F%2Fparse-dashboard.back4app.com%2Fapps&project-name=slack-clone-javascript-template&repository-name=slack-clone-javascript-templat" label="Deploy"}

:::

Make sure you have your Application ID, Client Key and LiveQuery URL. For the keys you can go to App Settings -> Security & Keys and then copy. For the Live Query URL you can go to Step 2 and copy it.

## Conclusion

At the end of this guide, you learned more about using the Parse React hook for live queries in Parse and how to use Back4App’s Database Hub.

[title] 1:N Relationship
[path] Flutter/Parse SDK (REST)/Data Objects/

# One to many Relationship on Flutter


Introduction
------------


Using Parse, you can store data objects establishing relations between them. To model this behavior, any ParseObject can be used as a value in other ParseObject. Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency. That can give you extra power when building and running complex queries. There are three main relation types:

-
  one-to-one, establishing direct relations between two objects and only them;
-
  one-to-many, where one object can be related to many other objects;
-
  many-to-many, which can create many complex relations between many objects.


In this guide we will detail how the one-to-many relation works using a pratical app example. There are two ways to create a one-to-many relation in Parse:

-
  The first is using the Pointers in Child Class, which is the fastest in creation and query time.
-
  The second is using Arrays of Pointers in Parent Class which can lead to slow query times depending on their size. Because of this performance issue, we will use only pointers examples.

You will implement a Flutter book registration App and will create and query related objects using the Parse Pointers.

:::hint{type="info"}
Relation as one-to-one is not common and we are not going to cover on our guides. As an example a relationship between the User class and another class that will contain sensitive user data for [security reasons](https://blog.back4app.com/parse-server-best-practices/) (*1.4. Don’t let users have access to sensitive data from others*).
:::

::embed[]{url="https://www.youtube.com/embed/b6fdFD0hlJo"}

## Prerequisites

:::hint{type="info"}
- [Flutter version 2.2.x or later](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)or <a href="https://code.visualstudio.com/" target="_blank">VS Code installed</a> (with <a href="https://docs.flutter.dev/get-started/editor" target="_blank">Plugins</a> Dart and Flutter)
- An app <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created</a> on Back4App:
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App Tutorial</a> to learn how to create a Parse App on Back4App.
- An Flutter app connected to Back4app.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk" target="_blank">Install Parse SDK on Flutter project</a> to create an Flutter Project connected to Back4App.
- A device (or virtual device) running Android or iOS.
:::

## Understanding the Book App


The main object class you’ll be using is the Book class, storing each book entry in the registration. Also, these are the other three object classes:


- Publisher: book publisher name, one-to-many relation with Book;
- Genre: book genre, one-to-many relation with Book. Note that for this example we will consider that a book can only have one genre;
- Author: book author, many-to-many relation with Book, since a book can have more than one author and an author can have more than one book as well;

A visual representation of these data model:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/A3OFhXHEJEAvPvi6DVVuq_image.png)

We will assume that each object class (Publisher, Genre) has only a string type name attribute and Book has title and year, apart from any additional relational attribute. In the previous guides we have already seen how to save and read ParseObject so in this guide, we will not cover how to save and read Genre and Publisher objects.

You will find the following screens on the Book App:

- **Registration and Listing of Genre**
- **Registration and List of Publishers**
- **Book Registration**
- **List of Publishers and Books**
- **Book details**

We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse using relations.

## 1 - Create Book App Template

Let’s first run the Book App project template. Open your Flutter project from the previous guide **Flutter plugin for Parse Server**. The [Book Flutter App](https://github.com/templates-back4app/flutter_associations) repository is also available to you clone and run the project. Copy the [main.dart](https://github.com/templates-back4app/flutter_associations/blob/master/lib/main.dart) file and replace your current code from previous guides.

**Note:**
When debug parameter in function Parse().initialize is true, allows displaying Parse API calls on the console. This configuration can assist in debugging the code. It is advisable to disable debug in the release version.

### Step 2 - Connect Template to Back4app Project

Find your Application Id and Client Key credentials navigating to your app Dashboard at [Back4App Dashboard->App Settings->Security & Keys](https://www.back4app.com/docs/parse-dashboard/app-settings). Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

- **keyApplicationId = App Id**
- **keyClientKey = Client Key**

Run the project, and the app will load as shown in the image.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yMFpRGpZJLsxJjtLPBDdk_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

Click on Add Genre to register and view the list of Genres that will be used in the registration of books.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AXMne48nSmI_omEvIIFLT_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

Click on Add Publisher to register and view the list of Publishers that will be used in the registration of books.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/s566XXvwuWEOBBdq6LHC5_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

Click on Add Book to register new Book using relations with Genre and Publisher.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jVUGzMZD8IIhrw57GNnRc_image.png" signedSrc size="30" width="328" height="637" position="center" caption}

Click on List Publisher/Book to view the list of Publishers and Books.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EGNDJ3ZrgC9_P-vPrSZ_a_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

## 3 - Save a Book object and its relations

This function will create a new Book in Back4app database with relations. Search for the function doSaveBook in file main.dart, and insert the code below inside the Future\<void> doSaveBook() function:

```dart
1       final book = ParseObject('Book')
2         ..set('title', controllerTitle.text.trim())
3         ..set('year', int.parse(controllerYear.text.trim()))
4         //the objectId will be converted to a Pointer on the save() method
5         ..set('genre', ParseObject('Genre')..objectId = genre.objectId)
6         //you can also convert to a Pointer object before the saving using the .toPointer() method
7         ..set(
8             'publisher',
9             (ParseObject('Publisher')..objectId = publisher.objectId)
10                .toPointer());
11
12      await book.save();
```

To build this function, follow these steps:

- 1\. Create a new instance of the Parse Book class with the command ParseObject('Book').
- 2\. Use the set function to set the fields for this object.
  - 2.1.title is a text attributes that receive value from the text controller.
  - 2.2.genre receives the value by defining a ParseObject with the objectId of the selected Genre. (*Parse will convert to pointer on save*)
  - 2.3.publisher receives the value by defining a ParseObject with the objectId of the selected Publisher. (*Note that we can specify for Parse that we want to save as a *pointer* using the *toPointer()*method*)
- 3\. Call the save function in ParseObject, which will effectively register the object to your database in the Back4app Dashboard.

Run the app and test the new function.

- Click on the Add Book button.
- Fill book information.

The app requires the selection of the Authors (s), but the code for them will be covered only in the next guide.

- Click on Save Book button



::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cssL-jdLG4ecfman8hIGp_image.png" signedSrc size="30" width="323" height="627" position="center" caption}

To confirm that the new object is save in the database with relations, you can access the Back4app Dashboard and access Book class.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SEpUCYQHevWFwhR33NxCp_image.png)

If you access your Book class using the Dashboard you can click on the object pointer value and you will be redirected to the referenced object. It may seem like a harmless feature, but this makes debugging and error tracing much quicker than searching for it manually.

## 4 - Query the Book List and its related objects

This function will query Books in Back4app database with using relations with Publisher. Through the Publisher, we will get the list of books.

Search for the function getBookList in the file main.dart, then replace the code below inside Future\<List\<ParseObject>> getBookList(String publisherId) function:

```dart
1       QueryBuilder<ParseObject> queryBook =
2           QueryBuilder<ParseObject>(ParseObject('Book'))
3             ..whereEqualTo('publisher',
4                 (ParseObject('Publisher')..objectId = publisherId).toPointer())
5             ..orderByAscending('title');
6       final ParseResponse apiResponse = await queryBook.query();
7   
8       if (apiResponse.success && apiResponse.results != null) {
9         return apiResponse.results;
10      } else {
11        return [];
12      }
```

To build this function, follow these steps:

1. Create an instance of ParseQuery object for Book class. Insert a condition in the query, to search Books where publisher field is equal pointer of Publisher ParseObject.
2. We sort the result in ascending name order
3. Do a Query’s search method using query() method.
4. If the operations succeed, objects in Book will be returned.

Run the app and test the new Query. First, click on the List Publisher/Book button.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/hUJS9Me_FZYq1DeuzH22Z_image.png" signedSrc size="30" width="327" height="634" position="center" caption}

## It’s done!

At this point, you learned how to create and query one-to-many relations in Parse on Flutter. In the next guide, we’ll show you how to make many-to-many relationships and how to perform queries returning data from related objects.

[title] Query Cookbook
[path] iOS/Parse Swift SDK/Data Objects/

# Query Cookbook for ParseSwift

## Introduction

In the [basic queries](https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/basic-queries) guide, we introduced some of the basic methods to construct queries and retrieve data from a **Back4App** Database.
The ParseSwift SDK offers practical ways to construct more complex queries for real-world applications.

In this guide, you will dive deep into the Query\<U> (generic) class and see all the methods you can use to build your queries. You will use a simple database class (named Profile) with some mocked data to perform queries using swift.

Before getting started, we should establish a couple of concepts about objects and data types. In this guide, we refer as objects to any data type (like structs) that can be stored in a database. In some situations, they are also referred as items or elements.
Properties are the members of a data type (mostly structs). However, in here, we usually call them fields. ParseSwift uses the term ‘key’, to indicate the properties’ name. Thus, any parameter labeled as ‘key’ in any method from the ParseSwift SDK indicates that its value has to match a field’s name.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An App <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">created on Back4App</a>.
- A basic iOS App to test queries
:::

## Goal

To explore the different methods theQuery\<U> class provide to execute queries.

## The Query\<U> class

For this guide, we will query information about a person’s contact. These data is stored under the class Profile on the **Back4App** Database. In order to retrieve profiles via an iOS App, we need to create the Profile struct that will contain such information

```swift
1   import ParseSwift
2   ...
3
4   struct Profile: ParseObject {
5       ...
6    
7       // Custom properties to organize the person's information
8       var name: String?
9       var birthday: Date?
10      var numberOfFriends: Int?
11      var favoriteFoods: [String]?
12      var luckyNumbers: [Int]?
13      var lastLoginLocation: ParseGeoPoint? // A data type to store coordinates for geolocation
14      var isActive: Bool?
15      var membership: Pointer<Membership>? // More details about Membership below
16    
17      ...
18  }
```

Additionally, we use the object Membership to show how queries between Profile and Membership interact with each other. The Membership struct is implemented in the following way

```swift
1   import ParseSwift
2   ...
3
4   struct Membership: ParseObject {
5       ...
6    
7       // Custom properties to organize the membership's information
8       var typeDescription: String?
9       var expirationDate: Date?
10
11      ...
12  }
```

Any query operation on a Back4App Database is performed by the generic class Query\<Profile> where the generic type Profile (conforming to the ParseSwift protocol) is the object we are trying to retrieve. After instantiating a Query\<Profile> object, we must call one of its find(\_) methods (see the [basic queries guide](https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/basic-queries)) to handle the result returned from the query.

You can read more about the Query\<U> class [here at the official documentation](https://github.com/parse-community/Parse-Swift).

## Saving Data Objects

With the following snippet we create and save some data to test miscellaneous queries

```swift
1   let adamSandler = Profile(
2     name: "Adam Sandler",
3     birthday: Date(string: "09/09/1996"),
4     numberOfFriends: 2,
5     favoriteFoods: ["Lobster", "Bread"],
6     luckyNumbers: [2, 7],
7     lastLoginLocation: try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
8     isActive: false,
9     membership: nil
10  )
11
12  let britneySpears = Profile(
13    name: "Britney Spears",
14    birthday: Date(string: "12/02/1981"),
15    numberOfFriends: 52,
16    favoriteFoods: ["Cake", "Bread"],
17    luckyNumbers: [22, 7],
18    lastLoginLocation: try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
19    isActive: true,
20    membership: nil
21  )
22  
23  let carsonKressley = Profile(
24    name: "Carson Kressley",
25    birthday: Date(string: "03/27/1975"),
26    numberOfFriends: 12,
27    favoriteFoods: ["Fish", "Cookies"],
28    luckyNumbers: [8, 7],
29    lastLoginLocation: try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
30    isActive: true,
31    membership: nil
32  )
33
34  // We create and save a Membership object
35  let premiumMembership = Membership(typeDescription: "Premium", expirationDate: Date(string: "10/10/2030"))
36  let savedPremiumMembership = try! premiumMembership.save() // Careful with the forced unwrap
37 
38  let danAykroyd = Profile(
39    name: "Dan Aykroyd",
40    birthday: Date(string: "07/01/1952"),
41    numberOfFriends: 66,
42    favoriteFoods: ["Jam", "Peanut Butter"],
43    luckyNumbers: [22, 77],
44    lastLoginLocation: try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
45    isActive: true,
46    membership: try? .init(savedPremiumMembership)
47  )
48
49  let eddyMurphy = Profile(
50    name: "Eddie Murphy",
51    birthday: Date(string: "04/03/1961"),
52    numberOfFriends: 49,
53    favoriteFoods: ["Lettuce", "Pepper"],
54    luckyNumbers: [6, 5],
55   lastLoginLocation: try? ParseGeoPoint(latitude: -27.104919974838154, longitude: -52.61428045237739),
56    isActive: false,
57    membership: nil
58  )
59
60  let fergie = Profile(
61    name: "Fergie",
62    birthday: Date(string: "03/27/1975"),
63    numberOfFriends: 55,
64    favoriteFoods: ["Lobster", "Shrimp"],
65    luckyNumbers: [12, 7],
66    lastLoginLocation: try? ParseGeoPoint(latitude: -27.104919974838154, longitude: -52.61428045237739),
67    isActive: false,
68    membership: nil
69  )
70 
71  do {
72    _ = try? adamSandler.save()
73    _ = try? britneySpears.save()        
74    _ = try? carsonKressley.save()
75    _ = try? danAykroyd.save()
76    _ = try? eddyMurphy.save()
77  } catch {
78    // Handle the errors here
79  }
```

Once the above code is executed, we should have a small database to work with.

:::hint{type="success"}
See more about how to save data with ParseSwift at <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/data-objects/swift-crud-database-operations" target="_blank">iOS Data Objects</a>.
:::

## Query retrievers

These methods are responsible for running the query and retrieving its results, being always present in your query implementation.

:::CodeblockTabs
Find

```swift
//This is the basic method for retrieving your query results
1   let query = Profile.query() // Instantiate a Query<Profile> class with no additional constraints
2
3   let profiles = try? query.find() // Executes the query synchronously returns and array of Profiles. It throws an error if something happened
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

First

```swift
//Retrieves the first Profile instance that meets the query criteria.
1   let query = Profile.query()
2
3   let profile = try? query.first() // Executes the query synchronously
4
5   query.first { result in // Executes the query asynchronously
6     // Handle the result (of type Result<Profile, ParseError>)
7   }
```

Count

```swift
//Retrieves the number of elemets found by the query
1   let query = Profile.query() // Instantiate a Query<Profile> class with no additional constraints
2
3   let profiles = try? query.count() // Executes the query synchronously
4
5   query.count { result in // Executes the query asynchronously
6     // Handle the result (of type Result<Int, ParseError>)
7   }
```

Distinct

```swift
//Runs the query and returns a list of unique values from the results and the specified field, name in this case.
1   let query = Profile.query() // Instantiate a Query<Profile> class with no additional constraints
2
3   let profiles = try? query.distinct("name") // Executes the query synchronously
4
5   query.distinct("name") { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Find all

```swift
//Retrieves a complete list of Profile’s that satisfy this query.
1   let query = Profile.query()
2
3   let profiles = try? query.findAll() // Executes the query synchronously
4        
5   query.findAll { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Using the objectId

```swift
//When you need to retrieve an object with a specific objectId
1   let profile = try? Profile(objectId: "HuYfN4YXFD").fetch() // Retrieves synchronously the Profile with the given objectId. It throws an error if something happened
2
3   Profile(objectId: "HuYfN4YXFD").fetch { // Retrieves asynchronously the Profile with the given objectId
4     // Handle the result (of type Result<Profile, ParseError>)
5   }
```
:::

## Queries with constraints on string type fields

We now start applying specific constraints to queries. Firstly, we will impose constraints that select objects only when a String type field satisfies a given condition. This and any other type of constraint imposed on a Query\<Profile> object is done via the QueryConstraint object. Below and in the next sections we detail how to construct such constraints.

:::CodeblockTabs
Equal

```swift
//Used when we need to select all the objects which have a field equal to a given String. We use the == operator to construct the corresponding constraint. On the left-hand side goes the field’s name and on the right hand side goes the String to compare against
1   let constraint: QueryConstraint = "name" == "Fergie" // Selects all Profiles with name equals to Fergie
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

Regular expression

```swift
//Used when one of the fields contains a given String
1   let constraint: QueryConstraint = containsString(key: "name", substring: "ie")
2   // containsString(key:substring:) is a method provided by the ParseSwift SDK
3   let query = Profile.query(constraint)
4
5   let profiles = try? query.find() // Executes the query synchronously
6
7   query.find { result in // Executes the query asynchronously
8     // Handle the result (of type Result<[Profile], ParseError>)
9   }
```

Contains substring

```swift
//Retrieves the number of elemets found by the query
1   let query = Profile.query() // Instantiate a Query<Profile> class with no additional constraints
2
3   let profiles = try? query.count() // Executes the query synchronously
4
5   query.count { result in // Executes the query asynchronously
6     // Handle the result (of type Result<Int, ParseError>)
7   }
```

Matches text

```swift
//Similar to == but it has additional options like case sensite, language and diacritic sensitive
1   // Careful with the force unwrap!
2   let constraint: QueryConstraint = try! matchesText(key: "name", text: "fergie", options: [.caseSensitive: false]) 
3   // matchesText(key:text:,options:) is a method provided by the ParseSwift SDK
4
5   let query = Profile.query(constraint)
6
7   let profiles = try? query.find() // Executes the query synchronously
8
9   query.find { result in // Executes the query asynchronously
10    // Handle the result (of type Result<[Profile], ParseError>)
11  }
```
:::

## Queries with constraints on comparable-like fields

It is very common to try to select objects where a certain number type field has to be equal, distinct, larger or smaller than a given value. This is accomplished by using the following comparable operations

:::CodeblockTabs
Equal

```swift
//Use this constraint when you need to select objects where a certain number type field is equal to a given value.
1   let constraint: QueryConstraint = "numberOfFriends" == 2 // Selects all Profiles with number of friends = 2
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

Not equal

```swift
//The opposite constraint of equals to and it also works with Date type fields.
1   let constraint: QueryConstraint = "numberOfFriends" != 2 // Selects all Profiles where the number of friends is different from 2
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

Less than

```swift
//To construct this constraint we use the < operator. On the left-hand side goes the field’s name and on the right-hand side goes the value to compare against
1   let constraint: QueryConstraint = "numberOfFriends" < 49 // Selects all Profiles with a maximum of 48 friends
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

Less than or equal

```swift
//To construct this constraint we use the <= operator. On the left-hand side goes the field’s name (also referred as key) and on the right-hand side goes the value to compare against
1   let constraint: QueryConstraint = "numberOfFriends" <= 49 // Selects all Profiles with a maximum of 49 friends
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

Greater than

```swift
//Once again, for this constraint we use another operator to construct it, the > operator.
1   let constraint: QueryConstraint = "birthday" > Date(string: "08/19/1980") // Selects all Profiles who born after 08/19/1980
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

Greater than or equal

```swift
//Last but not least, the greater than or equal constraint are constructed using the >= operator.
1   let constraint: QueryConstraint = "numberOfFriends" >= 20 // Selects all Profiles with at least 20 number of friends
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```
:::

These constraints also work on Date** **type fields. For instance, we can have

```swift
1   let constraint: QueryConstraint = "birthday" > Date(string: "08/19/1980") // Selects all Profiles who were born after 08/19/1980
2   let query = Profile.query(constraint)
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

For Bool type fields, we have the **Equal** and **Not equal** options. For instance

```swift
1   let constraint: QueryConstraint = "isActive" == true
2   let query = Profile.query(constraint) // Selects all active Profiles
3
4   let profiles = try? query.find() // Executes the query synchronously
5
6   query.find { result in // Executes the query asynchronously
7     // Handle the result (of type Result<[Profile], ParseError>)
8   }
```

## Queries with constraints involving geolocation

For fields containing location data (i.e., of type ParseGeoPoint) we have the following query options.

:::CodeblockTabs
Near

```swift
//In order to select objects where one of their ParseGeoPoint type fields is near to another given geopoint, we use
1   guard let geopoint = try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319) else {
2     return
3   }
4
5   let query = Profile.query(near(key: "lastLoginLocation", geoPoint: geopoint))
6   // near(key:geoPoint:) is a method provided by the ParseSwift SDK
7
8   let profiles = try? query.find() // Executes the query synchronously
9
10  query.find { result in // Executes the query asynchronously
11    // Handle the result (of type Result<[Profile], ParseError>)
12  }
```

Contains

```swift
//Given an object with a ParsePolygon type field, in order to select them in a query depending on what geopoints the field contains, we can use the following constraint
1   struct MyObject: ParseSwift {
2     ...
3  
4     var aPolygon: ParsePolygon?
5
6     ...
7   }
8
9   guard let geopoint = try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319) else {
10    return
11  }
12        
13  let query = MyObject.query(polygonContains(key: "aPolygon", point: geopoint))
14  // polygonContains(key:point:) is a method provided by the ParseSwift SDK
15
16  let myObjects = try? query.find() // Executes the query synchronously
17
18  query.find { result in // Executes the query asynchronously
19    // Handle the result (of type Result<[MyObject], ParseError>)
20  }
```

Within geo box

```swift
//Used to select objects where a ParseGeoPoint type field is inside of a given geopoint box. This box is described by its northeastPoint and southwestPoint geopoints.
1   guard let southwestPoint = try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319),
2         let northeastPoint = try? ParseGeoPoint(latitude: 37.28412167489413, longitude: -121.91268034622319) else {
3     return
4   }
5        
6   let query = Profile.query(withinGeoBox(key: "lastLoginLocation", fromSouthWest: southwestPoint, toNortheast: northeastPoint))
7   // withinGeoBox(key:fromSouthWest:toNortheast:) is a method provided by the ParseSwift SDK
8
9   let profiles = try? query.find() // Executes the query synchronously
10
11  query.find { result in // Executes the query asynchronously
12    // Handle the result (of type Result<[Profile], ParseError>)
13  }
```

Within km/mph

```swift
//Used to select objects according to whether or not a ParseGeoPoint type field value is near a given geopoint. Additionally, we should provide the maximum distance for how far the field value can be from the geopoint.
1   guard let geopoint = try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319) else {
2     return
3   }
4        
5   let query1 = Profile.query(withinKilometers(key: "lastLoginLocation", geoPoint: geopoint, distance: 100))
6   // withinKilometers(key:geoPoint:distance:) is a method provided by the ParseSwift SDK
7
8   let query2 = Profile.query(withinMiles(key: "lastLoginLocation", geoPoint: geopoint, distance: 100))
9   // withinMiles(key:geoPoint:distance:) is a method provided by the ParseSwift SDK
10
11  ... // Execute the corresponding query
```

Within Polygon

```swift
//Used to select objects with a ParseGeoPoint type field in a given polygon. The vertices of the polygon are represented by ParseGeoPoint objects.
1   // Create the vertices of the polygon (4 in this case)
2   guard let geopoint1 = try? ParseGeoPoint(latitude: 37.48412167489413, longitude: -122.11268034622319),
3         let geopoint2 = try? ParseGeoPoint(latitude: 37.48412167489413, longitude: -121.91268034622319),
4         let geopoint3 = try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -121.91268034622319),
5         let geopoint4 = try? ParseGeoPoint(latitude: 37.38412167489413, longitude: -122.01268034622319) else {
6     return
7   }
8        
9   let constraint: QueryConstraint = withinPolygon(
10    key: "lastLoginLocation",
11    points: [geopoint1, geopoint2, geopoint3, geopoint4]
12  )
13  // withinPolygon(key:points:) is a method provided by the ParseSwift SDK
14        
15  let query = Profile.query(constraint)
16
17  let profiles = try? query.find() // Executes the query synchronously
18
19  query.find { result in // Executes the query asynchronously
20    // Handle the result (of type Result<[Profile], ParseError>)
21  }
```

Within radians

```swift
//Similar to ‘within kilometers’ or ‘within miles’ but the distance is expressed in radians.
1   guard let geopoint = try? ParseGeoPoint(latitude: 37.48412167489413, longitude: -122.11268034622319) else {
2     return
3   }
4
5   let query = Profile.query(withinRadians(key: "lastLoginLocation", geoPoint: geopoint, distance: 1.5))
6   // withinRadians(key:geoPoint:distance:) is a method provided by the ParseSwift SDK
7
8   let profiles = try? query.find() // Executes the query synchronously
9
10  query.find { result in // Executes the query asynchronously
11    // Handle the result (of type Result<[Profile], ParseError>)
12  }
```
:::

## Queries with constraints involving array type fields

When the objects we want to retrieve need to satisfy a given condition on any of their array type fields, ParseSwift SDK provides the following alternatives to accomplish that

:::CodeblockTabs
Contained in

```swift
//Add a constraint to the query that requires a particular field to be contained in the provided array.
1   let constraint: QueryConstraint = containedIn(key: "luckyNumbers", array: [2, 7])
2   // containedBy(key:array:) is a method provided by the ParseSwift SDK
3
4   let query = Profile.query(constraint)
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```

Contained by

```swift
//Add a constraint to the query that requires a particular field to be contained by the provided array. It retrieves objects where all the elements of their array type field match.
1   let constraint: QueryConstraint = containedBy(key: "luckyNumbers", array: [2, 7])
2   // containedBy(key:array:) is a method provided by the ParseSwift SDK
3
4   let query = Profile.query(constraint)
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```

Contains all

```swift
//Add a constraint to the query that requires a particular array type field to contain every element of the provided array.
1   let constraint: QueryConstraint = containsAll(key: "luckyNumbers", array: [2, 7])
2   // containsAll(key:array:) is a method provided by the ParseSwift SDK
3
4   let query = Profile.query(constraint)
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```
:::

## Advanced queries

Until now we introduced the main conditions we can apply on a given field to select objects from a Back4App Database using ParseSwift SDK. In this section we continue with the composition of queries and advanced queries.

:::CodeblockTabs
Exists

```swift
//It is used to select objects depending on the value of a given field not being nil.
1   let constraint: QueryConstraint = exists(key: "membership")
2   // exists(key:) is a method provided by the ParseSwift SDK
3
4   let query = Profile.query(constraint) // Will retrieve all Profiles which have a membership
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```

Does not exists

```swift
//It is used to select objects depending on the value of a given field being nil.
1   let constraint: QueryConstraint = doesNotExist(key: "membership")
2   // doesNotExist(key:) is a method provided by the ParseSwift SDK
3
4   let query = Profile.query(constraint) // Will retrieve all Profiles which do not have a membership
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```

Matches key in query

```swift
//When we need to retrieve objects using the result from a different query as a condition, we make use of the method doesNotMatchKeyInQuery(key:queryKey:query:) to construct the corresponding condition. To illustrate this in more detail, we first introduce a new object Friend which has a property numberOfContacts of type Int
1   struct Friend: ParseSwift {
2     ...
3     var numberOfContacts: Int? 
4     ...
5   }
```

Does not match key in query

```swift
//This case is the opposite of Matches key in query. It is useful when we want to select all the Profile objects where their numberOfFriends field does not match with the numberOfContacts field in all Friend objects. The method we use to compose this query is doesNotMatchKeyInQuery(key:queryKey:query:)
1   let friendQuery = Friend.query() // Selects all Friends objects
2
3   let constraint: QueryConstraint = doesNotMatchKeyInQuery(
4     key: "numberOfFriends",
5     queryKey: "numberOfContacts",
6     query: friendQuery
7   )
8   // doesNotMatchKeyInQuery(key:queryKey:query:) is a method provided by the ParseSwift SDK
9
10  let query = Profile.query(constraint)
11
12  let profiles = try? query.find() // Executes the query synchronously
13
14  query.find { result in // Executes the query asynchronously
15    // Handle the result (of type Result<[Profile], ParseError>)
16  }
```
:::

## Query ordering

Sorting the results from a query is key before starting to display or manipulate those results. The ParseSwift SDK provides the following options to accomplish that.

:::CodeblockTabs
Ascending

```swift
//Sort the results in ascending order, overwrites previous orderings. Multiple fields can be used to solve ordering ties. By calling the order(_:) method on a Query<Profile> object, we obtain a new query which implements the given order option. The argument for the order(_:) method is an Array of Query<Profile>.Order items. These enumerations contain the name of the field to be used for the sort algorithm. For ascending order we use Query<Profile>.Order.ascending("nameOfTheField").
1   let query = Profile.query().order([.ascending("numberOfFriends")])
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Descending

```swift
//Similar to the ascending order, the only diference is that instead of using Query<Profile>.Order.ascending("nameOfTheField") we should use Query<Profile>.Order.descending("nameOfTheField").
1   let query = Profile.query().order([.descending("numberOfFriends")])
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Text Score

```swift
//This kind of sorting procedure relies on a score metric. In order to use this option, the Profile object must conform to the ParseQueryScorable protocol. Once the protocol is implemented, we call the sortByTextScore() method on the Query<Profile> object to obtain a new one that implements the required sorting method.
1   extension Profile: ParseQueryScorable {
2     var score: Double? {
3       // Add the corresponding implementation
4     }  
5   }
6
7   let query = Profile.query().sortByTextScore()
8 
9   let profiles = try? query.find() // Executes the query synchronously
10
11  query.find { result in // Executes the query asynchronously
12    // Handle the result (of type Result<[Profile], ParseError>)
13  }
```
:::

## Field selecting

Depending on what data is required during a query, this can take more or less time. In some scenarios it may be enough to retrieve certain fields from an object and ignore the unnecessary fields. Furthermore, by selecting only the fields we need from a query, we avoid over-fetching and improve performance on the fetching process.

:::CodeblockTabs
Exclude

```swift
//Calling the exclude(_:) method on a query returns a new query that will exclude the fields passed (in variadic format) as the argument.
1   let query = Profile.query().exclude("name", "numberOfFriends")
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Include

```swift
//It is used to retrieve the fields whose data type conforms to the ParseObject protocol (i.e., objects stored in the same Database under its corresponding class name). Calling the include(_:) method on a query returns a new query that will include the fields passed (in variadic format) as the argument. For instance, the membership field in Profile will not be fetched unless we include it manually.
1   let query = Profile.query().include("membership")
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Include all

```swift
//When we need to retrieve the fields that conforms to the ParseObject we call the includeAll() method on the query.
1   let query = Profile.query().includeAll()
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Select

```swift
//It returns only the specified fields in the returned objects. Calling the select(_:) method on a query returns a new query which will select only the fields passed (in variadic format) as argument.
1   let query = Profile.query().select("name", "birthday")
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```
:::

## Pagination

In large Databases pagination, is a fundamental feature for querying and handling a large amount of results. the ParseSwift SDK provides the following methods to manage those situations.

:::CodeblockTabs
Limit

```swift
//Determines the maximum number of results a query is able to return, the default value is 100. We call the limit(_:) method on the query in question to change this value.
1   let query = Profile.query().limit(2)
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

Skip

```swift
//Together with the previous option, the skip option allows us to paginate the results. We call the skip(_:) method on the query in question to change this value.
1   let query = Profile.query().skip(2)
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```

With count

```swift
//When we need to know in advance the number of results retrieved from a query, we use the withCount(completion:) method instead of the usual find(...). In this way, the result is composed by the objects found together with an integer indicating the length of the array.
1   let query = Profile.query()
2
3   query.withCount { result in // Executes the query asynchronously
4     // Handle the result (of type Result<([Profile], Int), ParseError>)
5   }
```
:::

## Compound queries

These methods will create compound queries, which can combine more than one Query\<Profile> instance to achieve more complex results.

:::CodeblockTabs
And

```swift
//Compose a compound query that is the AND of the passed queries. This is accomplished by first instantiating the queries to be used for the AND operation. The and(queries:) method provided by the ParseSwift SDK allows us to perform the AND operation and embed the result in a QueryConstraint object. This constraint is then used to instantiate the final query to be used for retrieveing the results.
1   let query1 = Profile.query("numberOfFriends" > 10)
2   let query2 = Profile.query("numberOfFriends" < 50)
3
4   let query = Profile.query(and(queries: query1, query2))
5   // and(queries:) is a method provided by the ParseSwift SDK. It takes an array of queries and applies the AND op. on them.
6
7   let profiles = try? query.find() // Executes the query synchronously
8   
9   query.find { result in // Executes the query asynchronously
10    // Handle the result (of type Result<[Profile], ParseError>)
11  }
```

Nor

```swift
//In this case, we apply a NOR operation on a set of queries. We use the nor(queries:) method provided by the ParseSwift SDK to perform such operation. We handle the result in a similar way as the AND operation.
1   let query1 = Profile.query("numberOfFriends" > 10)
2   let query2 = Profile.query("numberOfFriends" < 50)
3        
4   let query = Profile.query(nor(queries: query1, query2))
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```

Or

```swift
//Similar to previous cases, we apply an OR operation on a set of queries. We use the or(queries:) method provided by the ParseSwift SDK to perform such operation. We handle the result in a similar way as the AND (or NOR) operation.
1   let query1 = Profile.query("numberOfFriends" > 10)
2   let query2 = Profile.query("numberOfFriends" < 50)
3        
4   let query = Profile.query(or(queries: query1, query2))
5
6   let profiles = try? query.find() // Executes the query synchronously
7
8   query.find { result in // Executes the query asynchronously
9     // Handle the result (of type Result<[Profile], ParseError>)
10  }
```
:::

## Database related

These methods are related to the database preferences and operations.

:::CodeblockTabs
aggregate

```swift
//Executes an aggregate query, retrieving objects over a set of input values. To use this feature, instead of executing the query with find(...), we call the aggregate(_:completion:). The first argument of this function is a dictionary containing the information about the pipeline. For more details, refer to MongoDB documentation on aggregate.
1   let query = Profile.query()
2
3   query.aggregate([["pipeline": 5]]) { result in
4     // Handle the result (of type Result<[Profile], ParseError>)
5   }
```

explain

```swift
//Investigates the query execution plan, related to MongoDB explain operation. This option requires a new object (conforming to the Decodable protocol) to handle the results.
1   struct AnyCodable: Decodable {
2  // Implement the corresponding properties
3}
4
5let query = Profile.query()
6
7let results = try? query.findExplain() // Executes the option synchronously
8
9// Instantiate the completion block explicitely in order for the method findExplain(completion:) to infer the type of the returning results
10  let completion: (Result<[AnyCodable], ParseError>) -> Void = { result in
11    // Handle the result
12  }
13
14  // Executes the option asynchronously
15  query.findExplain(completion: completion)
```

readPreference

```swift
//When using a MongoDB replica set, use this method to choose from which replica the objects will be retrieved. The possible values are PRIMARY (default), PRIMARY_PREFERRED, SECONDARY, SECONDARY_PREFERRED, or NEAREST.
1   let query = Profile.query().readPreference("NEAREST")
2
3   let profiles = try? query.find() // Executes the query synchronously
4
5   query.find { result in // Executes the query asynchronously
6     // Handle the result (of type Result<[Profile], ParseError>)
7   }
```
:::

## Conclusion

Using the different methods, objects and operators provided by the ParseSwift SDK, we were able to understand how to construct and retrieve objects from a **Back4App** Database. With this cookbook you should be able to perform queries with very flexible constraints and manage the results according to your use case. For more details about any of the above topics, you can refer to the [ParseSwift repository](https://github.com/parse-community/Parse-Swift).

[title] Untitled
[path] Android/


[title] Untitled
[path] /


[title] User Log In
[path] iOS/Parse Swift SDK/Users/

# User log in and log out

## Introduction

In the [user registration guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-registration) we learned how to integrate a signup option into an iOS app using the **Back4App** platform and the ParseSwift SDK. Once a user successfully signs up in your app, logging in and logging out actions are key features within the app’s flow.

The ParseSwift SDK will allow us to integrate these features seamlessly into any iOS app.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at **Back4App**.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to **Back4App**.
:::

## Goal

To implement a user login and logout feature using the **ParseSwift SDK** and the **Back4App** platform.

## 1 - Setting up the login and logout features

Before starting to implement any login functionality, we have to create the object that will represent the user. For simplicity, we will reuse the same User struct (which conforms to the ParseUser protocol) we introduced in the [user registration guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-registration):

```swift
import Foundation
import ParseSwift

struct User: ParseUser {
  ...
    
  var username: String?
  var email: String?
  var emailVerified: Bool?
  var password: String?
    
  var age: Int?
}
```

We recommend following the user registration guide and registering at least one user to use it as an example for this guide.

Similar to the signup process, logging in requires a form where the user enters their **username** and **password**. Then, we perform a login request using the corresponding methods provided by the ParseSwift SDK. In its turn, **Back4App** processes the request and returns a response containing the login information. When an error occurs, the response returns information to identify and handle this error.

The logout process is straightforward. The ParseSwift SDK allows us to implement it in a single line of code.

## 2 - Setting up the app

Once you [connected](https://www.back4app.com/docs/ios/parse-swift-sdk/install-sdk) your Xcode project to your **Back4App** application, the next step is to set up the app’s user interface.

For the login process, we will implement a simple controller containing the corresponding input fields and a login button:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/C2xjVDNgWjfrg49Nlicjz_image.png" signedSrc size="40" width="1170" height="2532" position="center" caption}

The class in charge of this form is called LogInController and it is a subclass of UIViewController. The key components to integrate into this controller are two UITextField’s and one UIButton. The following snippet shows the implementation of the LogInController class:

```swift
1   import UIKit
2   import ParseSwift
3
4   class LogInController: UIViewController {
5     private let usernameTextField: UITextField = {
6       let textField = UITextField()
7       textField.borderStyle = .roundedRect
8       textField.placeholder = "Username *"
9       textField.autocapitalizationType = .none
10      textField.textAlignment = .center
11      return textField
12    }()
13    
14    private let passwordTextField: UITextField = {
15      let textField = UITextField()
16      textField.borderStyle = .roundedRect
17      textField.isSecureTextEntry = true
18      textField.placeholder = "Password *"
19      textField.textAlignment = .center
20      return textField
21    }()
22    
23    private let logInButton: UIButton = {
24      let button = UIButton(type: .roundedRect)
25      button.setTitle("Log in", for: .normal)
26      return button
27    }()
28    
29    override func viewDidLoad() {
30    super.viewDidLoad()
31        
32      navigationItem.title = "Back4App Log In"
33          
34      // Lays out the login form
35      let stackView = UIStackView(arrangedSubviews: [usernameTextField, passwordTextField, logInButton])
36      stackView.translatesAutoresizingMaskIntoConstraints = false
37      stackView.spacing = 8
38      stackView.axis = .vertical
39      stackView.distribution = .fillEqually
40        
41      let stackViewHeight = CGFloat(stackView.arrangedSubviews.count) * (44 + stackView.spacing) - stackView.spacing
42        
43      view.addSubview(stackView)
44      stackView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
45      stackView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
46      stackView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.7).isActive = true
47      stackView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true
48        
49      // Adds the method that will be called when the user taps the login button
50      logInButton.addTarget(self, action: #selector(handleLogIn), for: .touchUpInside)
51        
52      // If the user is already logged in, we redirect them to the HomeController
53      guard let user = User.current else { return }
54      let homeController = HomeController()
55      homeController.user = user
56        
57      navigationController?.pushViewController(homeController, animated: true)
58    }
59      
60    /// Called when the user taps on the logInButton button
61    @objc private func handleLogIn() {
62      guard let username = usernameTextField.text, !username.isEmpty,
63            let password = passwordTextField.text, !password.isEmpty else {
64        // Shows an alert with the appropriate title and message.
65        return showMessage(title: "Error", message: "Invalid credentials.")
66      }
67        
68      logIn(with: username, password: password)
69    }
70    
71    /// Logs in the user and presents the app's home screen (HomeController)
72    /// - Parameters:
73    ///   - username: User's username
74    ///   - password: User's password
75    private func logIn(with username: String, password: String) {
76      // TODO: Here we will implement the login process
77    }
78  }
```

Additionally, the helper function showMessage(title\:message:) is implemented in an extension of UIViewController:

```swift
1   extension UIViewController {
2
3       /// Presents an alert with a title, a message and a back button.
4       /// - Parameters:
5       ///   - title: Title for the alert
6       ///   - message: Shor message for the alert
7       func showMessage(title: String, message: String) {
8           let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
9           
10          alertController.addAction(UIAlertAction(title: "Back", style: .cancel))
11        
12          present(alertController, animated: true)
13      }
14  }
```

For the logout process, we insert a button in the home controller, i.e., HomeController. This view controller will only contain the logout button and a label showing the user’s username:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/M3zOsTaYq0bCTkdE2zDRK_image.png" signedSrc size="40" width="1170" height="2532" position="center" caption}

The implementation of this view controller is straightforward:

```swift
1   import UIKit
2   import ParseSwift
3
4   class HomeController: UIViewController {
5    
6     /// When set, it updates the usernameLabel's text with the user's username.
7     var user: User? {
8       didSet {
9         usernameLabel.text = "Hello \(user?.username ?? "N/A")!"
10      }
11    }
12    
13    private let usernameLabel: UILabel = {
14      let label = UILabel()
15      label.textAlignment = .center
16      label.font = .boldSystemFont(ofSize: 18)
17      label.translatesAutoresizingMaskIntoConstraints = false
18      return label
19    }()
20    
21    private let logOutButton: UIButton = {
22      let button = UIButton(type: .roundedRect)
23      button.setTitle("Log out", for: .normal)
24      button.translatesAutoresizingMaskIntoConstraints = false
25      return button
26    }()
27    
28    override func viewDidLoad() {
29      super.viewDidLoad()
30        
31      // Sets up the layout (usernameLabel and logOutButton)
32      view.backgroundColor = .systemBackground
33      navigationItem.hidesBackButton = true
34      navigationItem.title = "Back4App"
35      view.addSubview(usernameLabel)
36      view.addSubview(logOutButton)
37        
38      usernameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8).isActive = true
39      usernameLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
40        
41      logOutButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 8).isActive = true
42      logOutButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
43        
44      // Adds the method that will be called when the user taps the logout button
45      logOutButton.addTarget(self, action: #selector(handleLogOut), for: .touchUpInside)
46    }
47    
48    /// Called when the user taps the logout button.
49    @objc private func handleLogOut() {
50      // TODO: Here we will implement the logout process
51    }
52  }
```

## 3 - Login request

We now proceed to implement the **logIn(with\:password)** method in the **LogInController** class. The **ParseUser** protocol gives the **User** object the static method **login(username\:password)**. This method prepares and sends the login request to your **Back4App** application. Depending on the use case, one can use one of the many implementations of the **login(...)** method. We now complete the **logIn(with\:password)** method in **LogInController**:

```swift
1   class HomeController: UIViewController {
2    ...
3
4     /// Logs in the user and presents the app's home screen (HomeController)
5     /// - Parameters:
6     ///   - username: User's username
7     ///   - password: User's password
8     private func logIn(with username: String, password: String) {
9       // WARNING: Use only one of the following implementations, the synchronous or asynchronous option
10
11      // Logs in the user synchronously, it throws a ParseError error if something happened. 
12      // This should be executed in a background thread!
13      do {
14        let loggedInUser = try User.login(username: username, password: password)
15
16        // After the login success we send the user to the home screen
17        let homeController = HomeController()
18        homeController.user = loggedInUser
19
20        navigationController?.pushViewController(homeController, animated: true)
21      } catch let error as ParseError {
22        showMessage(title: "Error", message: "Failed to log in: \(error.message)")
23      } catch {
24        showMessage(title: "Error", message: "Failed to log in: \(error.localizedDescription)")
25      }
26    
27      // Logs in the user asynchronously
28      User.login(username: username, password: password) { [weak self] result in // Handle the result (of type Result<User, ParseError>)
29        switch result {
30        case .success(let loggedInUser):
31          self?.usernameTextField.text = nil
32          self?.passwordTextField.text = nil
33
34          // After the login success we send the user to the home screen
35          let homeController = HomeController()
36          homeController.user = loggedInUser
37  
38          self?.navigationController?.pushViewController(homeController, animated: true)
39        case .failure(let error):
40          self?.showMessage(title: "Error", message: "Failed to log in: \(error.message)")
41        }
42      }
43    }
44  }
```

## 4 - Logout request

The logout request is as simple as the login request. Once again, the **ParseUser** protocol provides the **User** with the static method **logout(...)**. By calling this method the current user (accessed via **User.current**) logs out from your **Back4App** application. We will call this method when the user taps the logout button located on the home screen, i.e., in the **handleLogOut()** method in the **HomeController** class, we add the following:

```swift
1   class HomeController: UIViewController {
2     ...
3
4     /// Called when the user taps the logout button.
5     @objc private func handleLogOut() {
6       // WARNING: Use only one of the following implementations, the synchronous or asynchronous option
7
8       // Logs out the user synchronously, it throws a ParseError error if something happened.
9       // This should be executed in a background thread!
10      do {
11        try User.logout()
12
13        // After the logout succeeded we dismiss the home screen
14        navigationController?.popViewController(animated: true)
15      } catch let error as ParseError {
16        showMessage(title: "Error", message: "Failed to log out: \(error.message)")
17      } catch {
18        showMessage(title: "Error", message: "Failed to log out: \(error.localizedDescription)")
19      }
20    
21      // Logs out the user asynchronously
22      User.logout { [weak self] result in // Handle the result (of type Result<Void, ParseError>)
23        switch result {
24        case .success:
25          // After the logout succeeded we dismiss the home screen
26          self?.navigationController?.popViewController(animated: true)
27        case .failure(let error):
28          self?.showMessage(title: "Error", message: "Failed to log out: \(error.message)")
29        }
30      }
31    }
32  }
```

## 5 - Run the app!

In this [repository](https://github.com/templates-back4app/ios-user-log-in-and-log-out), you will find an Xcode project containing the login and logout processes we described above. Before running the app, make sure you connected the Xcode project to your Back4App application.

## Conclusion

The **Back4App** and the ParseSwift SDK allow us to integrate login and logout features in iOS apps in a quick way. After connecting your **Back4App** application with your Xcode project, the login (or logout) process only requires calling a single method.

[title] Untitled
[path] Flutter/


[title] Templates
[path] ReactJS/


[title] User LogIn
[path] ReactJS/Users/

# User LogIn and LogOut for React

## Introduction

After implementing a component that handles user registration in Parse in the last guide, you will now learn how to log in and log out users using the same Parse.User class.

The Parse.User.logIn method stores in your local storage a valid user session, so future calls to methods like current will successfully retrieve your User data. On the other hand, logOut will clear this session from the disk and log out of any linked services in your Parse server.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

-
  A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
- Complete the previous guide so you can have a better understanding of <a href="https://www.back4app.com/docs/react/working-with-users/sign-up-page-react" target="_blank">the Parse.User class</a>.
- If you want to test/use the screen layout provided by this guide, you should set up the <a href="https://ant.design/docs/react/introduce" target="_blank">Ant Design library.</a>
:::

## Goal

To build a User LogIn and LogOut feature using Parse for a React App.

## 1 - Understanding the logIn and logOut methods

Parse User management uses the Parse.User object type, which extends the default ParseObject type while containing unique helper methods, such as current and getUsername, that will help you retrieve user data throughout your app. You can read more about the Parse.User object [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html).

In this guide, you will learn how to use the logIn and logOut methods that will handle the user login process, saving the user data locally.

## 2 - Creating a login component

Let’s now build the UserLogIn component in your App. Create a new file in your src directory called UserLogIn.js (UserLogIn.tsx if you are using TypeScript) and add the input elements using state hooks to manage their data. Note that there is also a function responsible for updating a state variable with the current logged in user, that you will use later:

:::CodeblockTabs
UserLogIn.js

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider, Input } from 'antd';
5	
6	export const UserLogin = () => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	  const [currentUser, setCurrentUser] = useState(null);
11	
12	  // Function that will return current user and also update current username
13	  const getCurrentUser = async function () {
14	    const currentUser = await Parse.User.current();
15	    // Update state variable holding current user
16	    setCurrentUser(currentUser);
17	    return currentUser;
18	  };
19	
20	  return (
21	    <div>
22	      <div className="header">
23	        <img
24	          className="header_logo"
25	          alt="Back4App Logo"
26	          src={
27	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
28	          }
29	        />
30	        <p className="header_text_bold">{'React on Back4App'}</p>
31	        <p className="header_text">{'User Login'}</p>
32	      </div>
33	      <div className="container">
34	        <h2 className="heading">{'User Login'}</h2>
35	        <Divider />
36	        <div className="form_wrapper">
37	          <Input
38	            value={username}
39	            onChange={(event) => setUsername(event.target.value)}
40	            placeholder="Username"
41	            size="large"
42	            className="form_input"
43	          />
44	          <Input
45	            value={password}
46	            onChange={(event) => setPassword(event.target.value)}
47	            placeholder="Password"
48	            size="large"
49	            type="password"
50	            className="form_input"
51	          />
52	        </div>
53	        <div className="form_buttons">
54	          <Button
55	            onClick={() => doUserLogIn()}
56	            type="primary"
57	            className="form_button"
58	            color={'#208AEC'}
59	            size="large"
60	            block
61	          >
62	            Log In
63	          </Button>
64	        </div>
65	        <Divider />
66	        <div className="login-social">
67	          <div className="login-social-item login-social-item--facebook">
68	            <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
69	          </div>
70	          <div className="login-social-item">
71	            <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
72	          </div>
73	          <div className="login-social-item">
74	            <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
75	          </div>
76	        </div>
77	        <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
78	      </div>
79	    </div>
80	  );
81	};
```

UserLogIn.tsx

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	const Parse = require('parse/dist/parse.min.js');
5	
6	export const UserLogin: FC<{}> = (): ReactElement => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	  const [currentUser, setCurrentUser] = useState<Parse.Object | null>(null);
11	
12	  // Function that will return current user and also update current username
13	  const getCurrentUser = async function (): Promise<Parse.User | null> {
14	    const currentUser: (Parse.User | null) = await Parse.User.current();
15	    // Update state variable holding current user
16	    setCurrentUser(currentUser);
17	    return currentUser;
18	  }
19	
20	  return (
21	    <div>
22	      <div className="header">
23	        <img
24	          className="header_logo"
25	          alt="Back4App Logo"
26	          src={
27	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
28	          }
29	        />
30	        <p className="header_text_bold">{'React on Back4App'}</p>
31	        <p className="header_text">{'User Login'}</p>
32	      </div>
33	      <div className="container">
34	        <h2 className="heading">{'User Login'}</h2>
35	        <Divider />
36	        <div className="form_wrapper">
37	          <Input
38	            value={username}
39	            onChange={(event) => setUsername(event.target.value)}
40	            placeholder="Username"
41	            size="large"
42	            className="form_input"
43	          />
44	          <Input
45	            value={password}
46	            onChange={(event) => setPassword(event.target.value)}
47	            placeholder="Password"
48	            size="large"
49	            type="password"
50	            className="form_input"
51	          />
52	        </div>
53	        <div className="form_buttons">
54	          <Button
55	            onClick={() => doUserLogIn()}
56	            type="primary"
57	            className="form_button"
58	            color={'#208AEC'}
59	            size="large"
60	            block
61	          >
62	            Log In
63	          </Button>
64	        </div>
65	        <Divider />
66	        <div className="login-social">
67	          <div className="login-social-item login-social-item--facebook">
68	            <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
69	          </div>
70	          <div className="login-social-item">
71	            <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
72	          </div>
73	          <div className="login-social-item">
74	            <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
75	          </div>
76	        </div>
77	        <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
78	      </div>
79	    </div>
80	  );
81	};
```
:::

You can now implement the function that will call the Parse.User.logIn method, using state variables:

:::CodeblockTabs
JavaScript

```javascript
1	const doUserLogIn = async function () {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue = username;
4	  const passwordValue = password;
5	  try {
6	    const loggedInUser = await Parse.User.logIn(usernameValue, passwordValue);
7	    // logIn returns the corresponding ParseUser object
8	    alert(
9	      `Success! User ${loggedInUser.get(
10	        'username'
11	      )} has successfully signed in!`
12	    );
13	    // To verify that this is in fact the current user, `current` can be used
14	    const currentUser = await Parse.User.current();
15	    console.log(loggedInUser === currentUser);
16	    // Clear input fields
17	    setUsername('');
18	    setPassword('');
19	    // Update state variable holding current user
20	    getCurrentUser();
21	    return true;
22	  } catch (error) {
23	    // Error can be caused by wrong parameters or lack of Internet connection
24	    alert(`Error! ${error.message}`);
25	    return false;
26	  }
27	};
```

```typescript
1	const doUserLogIn = async function (): Promise<boolean> {
2	  // Note that these values come from state variables that we've declared before
3	  const usernameValue: string = username;
4	  const passwordValue: string = password;
5	  try {
6	    const loggedInUser: Parse.User = await Parse.User.logIn(usernameValue, passwordValue);
7	    // logIn returns the corresponding ParseUser object
8	    alert(
9	      `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
10	    );
11	    // To verify that this is in fact the current user, `current` can be used
12	    const currentUser: Parse.User = await Parse.User.current();
13	    console.log(loggedInUser === currentUser);
14	    // Clear input fields
15	    setUsername('');
16	    setPassword('');
17	    // Update state variable holding current user
18	    getCurrentUser();
19	    return true;
20	  } catch (error: any) {
21	    // Error can be caused by wrong parameters or lack of Internet connection
22	    alert(`Error! ${error.message}`);
23	    return false;
24	  }
25	};
```
:::

Insert this function inside the UserLogIn component, just before the return call, to be called and tested. Remember to update the form’s login button onClick action to () => doUserLogIn(). If you want to fully render this screen layout, add these classes to your App.css file:

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 900px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.flex {
77	  display: flex;
78	}
79	
80	.flex_between {
81	  display: flex;
82	  justify-content: space-between;
83	}
84	
85	.flex_child {
86	  flex: 0 0 45%;
87	}
88	
89	.heading_button {
90	  margin-left: 12px;
91	}
92	
93	.list_item {
94	  padding-bottom: 15px;
95	  margin-bottom: 15px;
96	  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
97	  text-align: left;
98	}
99	
100	.list_item_title {
101	  color: rgba(0, 0, 0, 0.87);
102	  font-size: 17px;
103	}
104	
105	.list_item_description {
106	  color: rgba(0, 0, 0, 0.5);
107	  font-size: 15px;
108	}
109	
110	.form_input {
111	  margin-bottom: 20px;
112	}
113	
114	.login-social {
115	  display: flex;
116	  justify-content: center;
117	  margin-bottom: 30px;
118	}
119	
120	.login-social-item {
121	  width: 54px;
122	  height: 54px;
123	  border-radius: 54px;
124	  padding: 12px;
125	  margin: 0 12px;
126	  border: 1px solid #e6e6e6;
127	  box-shadow: 0 2px 4px #d6d6d6;
128	}
129	
130	.login-social-item--facebook {
131	  padding: 4px;
132	  background-color: #3C5B9B;
133	}
134	
135	.login-social-item__image {
136	  width: 100%;
137	}
138	
139	.form__hint {
140	  color: rgba(0, 0, 0, 0.5);
141	  font-size: 16px;
142	  text-align: center;
143	}
```
:::

Go ahead and run your application, importing this login component into your main application file. You should see a screen just like this and also a success message after logging in with the right credentials:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SsOX_fEH0uUbp0_3VRB-l_image.png)

## 3 - Checking logged-in user

After your user successfully logs in to the application, he should be able to know about that and also be able to log out of the application. First, let’s hide the login form and render a welcome message containing the username. Note the usage of the conditional operator in conjunction with the state variable holding the current username.

:::CodeblockTabs
UserLogIn.js

```javascript
1	// Your imports ...
2	export const UserLogin = () => {
3	  // State variables and functions ...
4	
5	  return (
6	    <div>
7	      <div className="header">
8	        <img
9	          className="header_logo"
10	          alt="Back4App Logo"
11	          src={
12	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
13	          }
14	        />
15	        <p className="header_text_bold">{'React on Back4App'}</p>
16	        <p className="header_text">{'User Login'}</p>
17	      </div>
18	      {currentUser === null && (
19	        <div className="container">
20	          <h2 className="heading">{'User Login'}</h2>
21	          <Divider />
22	          <div className="form_wrapper">
23	            <Input
24	              value={username}
25	              onChange={(event) => setUsername(event.target.value)}
26	              placeholder="Username"
27	              size="large"
28	              className="form_input"
29	            />
30	            <Input
31	              value={password}
32	              onChange={(event) => setPassword(event.target.value)}
33	              placeholder="Password"
34	              size="large"
35	              type="password"
36	              className="form_input"
37	            />
38	          </div>
39	          <div className="form_buttons">
40	            <Button
41	              onClick={() => doUserLogIn()}
42	              type="primary"
43	              className="form_button"
44	              color={'#208AEC'}
45	              size="large"
46	              block
47	            >
48	              Log In
49	            </Button>
50	          </div>
51	          <Divider />
52	          <div className="login-social">
53	            <div className="login-social-item login-social-item--facebook">
54	              <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
55	            </div>
56	            <div className="login-social-item">
57	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
58	            </div>
59	            <div className="login-social-item">
60	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
61	            </div>
62	          </div>
63	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
64	        </div>
65	      )}
66	      {currentUser !== null &&
67	        (<div className="container">
68	          <h2 className="heading">{'User Screen'}</h2>
69	          <Divider />
70	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
71	          <div className="form_buttons">
72	            <Button
73	              onClick={() => {}}
74	              type="primary"
75	              className="form_button"
76	              color={'#208AEC'}
77	              size="large"
78	            >
79	              Log Out
80	            </Button>
81	          </div>
82	        </div>)
83	      }
84	    </div>
85	  );
86	};
```

UserLogIn.tsx

```typescript
1	// Your imports ...
2	export const UserLogin: FC<{}> = (): ReactElement => {
3	  // State variables and functions ...
4	  
5	  return (
6	    <div>
7	      <div className="header">
8	        <img
9	          className="header_logo"
10	          alt="Back4App Logo"
11	          src={
12	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
13	          }
14	        />
15	        <p className="header_text_bold">{'React on Back4App'}</p>
16	        <p className="header_text">{'User Login'}</p>
17	      </div>
18	      {currentUser === null && (
19	        <div className="container">
20	          <h2 className="heading">{'User Login'}</h2>
21	          <Divider />
22	          <div className="form_wrapper">
23	            <Input
24	              value={username}
25	              onChange={(event) => setUsername(event.target.value)}
26	              placeholder="Username"
27	              size="large"
28	              className="form_input"
29	            />
30	            <Input
31	              value={password}
32	              onChange={(event) => setPassword(event.target.value)}
33	              placeholder="Password"
34	              size="large"
35	              type="password"
36	              className="form_input"
37	            />
38	          </div>
39	          <div className="form_buttons">
40	            <Button
41	              onClick={() => doUserLogIn()}
42	              type="primary"
43	              className="form_button"
44	              color={'#208AEC'}
45	              size="large"
46	              block
47	            >
48	              Log In
49	            </Button>
50	          </div>
51	          <Divider />
52	          <div className="login-social">
53	            <div className="login-social-item login-social-item--facebook">
54	              <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
55	            </div>
56	            <div className="login-social-item">
57	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
58	            </div>
59	            <div className="login-social-item">
60	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
61	            </div>
62	          </div>
63	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
64	        </div>
65	      )}
66	      {currentUser !== null &&
67	        (<div className="container">
68	          <h2 className="heading">{'User Screen'}</h2>
69	          <Divider />
70	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
71	          <div className="form_buttons">
72	            <Button
73	              onClick={() => {}}
74	              type="primary"
75	              className="form_button"
76	              color={'#208AEC'}
77	              size="large"
78	            >
79	              Log Out
80	            </Button>
81	          </div>
82	        </div>)
83	      }
84	    </div>
85	  );
86	};
```
:::

Go ahead and run your app again. Notice that your app will now show the user screen and welcome message just like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/iphqrWbJfIa34MW3ei4aL_image.png" signedSrc size="70" width="702" height="503" position="center" caption}

## 4 - Creating a log out function

The logOut method is simpler than the login since the Parse.User.logOut method takes no arguments and clears the currentUser data stored locally automatically. Create this function in the UserLogin component and call it in the onClick attribute of the logout button:

:::CodeblockTabs
UserLogOut.js

```javascript
1	const doUserLogOut = async function () {
2	  try {
3	    await Parse.User.logOut();
4	    // To verify that current user is now empty, currentAsync can be used
5	    const currentUser = await Parse.User.current();
6	    if (currentUser === null) {
7	      alert('Success! No user is logged in anymore!');
8	    }
9	    // Update state variable holding current user
10	    getCurrentUser();
11	    return true;
12	  } catch (error) {
13	    alert(`Error! ${error.message}`);
14	    return false;
15	  }
16	};
```

UserLogOut.tsx

```typescript
1	const doUserLogOut = async function (): Promise<boolean> {
2	  try {
3	    await Parse.User.logOut();
4	    // To verify that current user is now empty, currentAsync can be used
5	    const currentUser: Parse.User = await Parse.User.current();
6	    if (currentUser === null) {
7	      alert('Success! No user is logged in anymore!');
8	    }
9	    // Update state variable holding current user
10	    getCurrentUser();
11	    return true;
12	  } catch (error: any) {
13	    alert(`Error! ${error.message}`);
14	    return false;
15	  }
16	};
```
:::

If you perform a successful log out, you will see a message like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/JmAG22pgR2Pr-TQj0SuB__image.png" signedSrc size="80" width="732" height="494" position="center" caption}

Here is the full component code containing all the functions shown before:

:::CodeblockTabs
UserLogIn.js

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider, Input } from 'antd';
5	
6	export const UserLogin = () => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	  const [currentUser, setCurrentUser] = useState(null);
11	
12	  const doUserLogIn = async function () {
13	    // Note that these values come from state variables that we've declared before
14	    const usernameValue = username;
15	    const passwordValue = password;
16	    try {
17	      const loggedInUser = await Parse.User.logIn(usernameValue, passwordValue);
18	      // logIn returns the corresponding ParseUser object
19	      alert(
20	        `Success! User ${loggedInUser.get(
21	          'username'
22	        )} has successfully signed in!`
23	      );
24	      // To verify that this is in fact the current user, `current` can be used
25	      const currentUser = await Parse.User.current();
26	      console.log(loggedInUser === currentUser);
27	      // Clear input fields
28	      setUsername('');
29	      setPassword('');
30	      // Update state variable holding current user
31	      getCurrentUser();
32	      return true;
33	    } catch (error) {
34	      // Error can be caused by wrong parameters or lack of Internet connection
35	      alert(`Error! ${error.message}`);
36	      return false;
37	    }
38	  };
39	
40	  const doUserLogOut = async function () {
41	    try {
42	      await Parse.User.logOut();
43	      // To verify that current user is now empty, currentAsync can be used
44	      const currentUser = await Parse.User.current();
45	      if (currentUser === null) {
46	        alert('Success! No user is logged in anymore!');
47	      }
48	      // Update state variable holding current user
49	      getCurrentUser();
50	      return true;
51	    } catch (error) {
52	      alert(`Error! ${error.message}`);
53	      return false;
54	    }
55	  };
56	
57	  // Function that will return current user and also update current username
58	  const getCurrentUser = async function () {
59	    const currentUser = await Parse.User.current();
60	    // Update state variable holding current user
61	    setCurrentUser(currentUser);
62	    return currentUser;
63	  };
64	
65	  return (
66	    <div>
67	      <div className="header">
68	        <img
69	          className="header_logo"
70	          alt="Back4App Logo"
71	          src={
72	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
73	          }
74	        />
75	        <p className="header_text_bold">{'React on Back4App'}</p>
76	        <p className="header_text">{'User Login'}</p>
77	      </div>
78	      {currentUser === null && (
79	        <div className="container">
80	          <h2 className="heading">{'User Login'}</h2>
81	          <Divider />
82	          <div className="form_wrapper">
83	            <Input
84	              value={username}
85	              onChange={(event) => setUsername(event.target.value)}
86	              placeholder="Username"
87	              size="large"
88	              className="form_input"
89	            />
90	            <Input
91	              value={password}
92	              onChange={(event) => setPassword(event.target.value)}
93	              placeholder="Password"
94	              size="large"
95	              type="password"
96	              className="form_input"
97	            />
98	          </div>
99	          <div className="form_buttons">
100	            <Button
101	              onClick={() => doUserLogIn()}
102	              type="primary"
103	              className="form_button"
104	              color={'#208AEC'}
105	              size="large"
106	              block
107	            >
108	              Log In
109	            </Button>
110	          </div>
111	          <Divider />
112	          <div className="login-social">
113	            <div className="login-social-item login-social-item--facebook">
114	              <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
115	            </div>
116	            <div className="login-social-item">
117	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
118	            </div>
119	            <div className="login-social-item">
120	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
121	            </div>
122	          </div>
123	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
124	        </div>
125	      )}
126	      {currentUser !== null && (
127	        <div className="container">
128	          <h2 className="heading">{'User Screen'}</h2>
129	          <Divider />
130	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
131	          <div className="form_buttons">
132	            <Button
133	              onClick={() => doUserLogOut()}
134	              type="primary"
135	              className="form_button"
136	              color={'#208AEC'}
137	              size="large"
138	            >
139	              Log Out
140	            </Button>
141	          </div>
142	        </div>
143	      )}
144	    </div>
145	  );
146	};
```

UserLogIn.tsx

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider, Input } from 'antd';
4	const Parse = require('parse/dist/parse.min.js');
5	
6	export const UserLogin: FC<{}> = (): ReactElement => {
7	  // State variables
8	  const [username, setUsername] = useState('');
9	  const [password, setPassword] = useState('');
10	  const [currentUser, setCurrentUser] = useState<Parse.Object | null>(null);
11	
12	  const doUserLogIn = async function (): Promise<boolean> {
13	    // Note that these values come from state variables that we've declared before
14	    const usernameValue: string = username;
15	    const passwordValue: string = password;
16	    try {
17	      const loggedInUser: Parse.User = await Parse.User.logIn(usernameValue, passwordValue);
18	      // logIn returns the corresponding ParseUser object
19	      alert(
20	        `Success! User ${loggedInUser.get('username')} has successfully signed in!`,
21	      );
22	      // To verify that this is in fact the current user, `current` can be used
23	      const currentUser: Parse.User = await Parse.User.current();
24	      console.log(loggedInUser === currentUser);
25	      // Clear input fields
26	      setUsername('');
27	      setPassword('');
28	      // Update state variable holding current user
29	      getCurrentUser();
30	      return true;
31	    } catch (error: any) {
32	      // Error can be caused by wrong parameters or lack of Internet connection
33	      alert(`Error! ${error.message}`);
34	      return false;
35	    }
36	  };
37	
38	  const doUserLogOut = async function (): Promise<boolean> {
39	    try {
40	      await Parse.User.logOut();
41	      // To verify that current user is now empty, currentAsync can be used
42	      const currentUser: Parse.User = await Parse.User.current();
43	      if (currentUser === null) {
44	        alert('Success! No user is logged in anymore!');
45	      }
46	      // Update state variable holding current user
47	      getCurrentUser();
48	      return true;
49	    } catch (error: any) {
50	      alert(`Error! ${error.message}`);
51	      return false;
52	    }
53	  };
54	
55	  // Function that will return current user and also update current username
56	  const getCurrentUser = async function (): Promise<Parse.User | null> {
57	    const currentUser: (Parse.User | null) = await Parse.User.current();
58	    // Update state variable holding current user
59	    setCurrentUser(currentUser);
60	    return currentUser;
61	  }
62	
63	  return (
64	    <div>
65	      <div className="header">
66	        <img
67	          className="header_logo"
68	          alt="Back4App Logo"
69	          src={
70	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
71	          }
72	        />
73	        <p className="header_text_bold">{'React on Back4App'}</p>
74	        <p className="header_text">{'User Login'}</p>
75	      </div>
76	      {currentUser === null && (
77	        <div className="container">
78	          <h2 className="heading">{'User Login'}</h2>
79	          <Divider />
80	          <div className="form_wrapper">
81	            <Input
82	              value={username}
83	              onChange={(event) => setUsername(event.target.value)}
84	              placeholder="Username"
85	              size="large"
86	              className="form_input"
87	            />
88	            <Input
89	              value={password}
90	              onChange={(event) => setPassword(event.target.value)}
91	              placeholder="Password"
92	              size="large"
93	              type="password"
94	              className="form_input"
95	            />
96	          </div>
97	          <div className="form_buttons">
98	            <Button
99	              onClick={() => doUserLogIn()}
100	              type="primary"
101	              className="form_button"
102	              color={'#208AEC'}
103	              size="large"
104	              block
105	            >
106	              Log In
107	            </Button>
108	          </div>
109	          <Divider />
110	          <div className="login-social">
111	            <div className="login-social-item login-social-item--facebook">
112	              <img className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
113	            </div>
114	            <div className="login-social-item">
115	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN8AAADiCAMAAAD5w+JtAAABWVBMVEX////qQzU0qFNChfT7vAUufPPk7f08gvR0o/Zzofb7uQD7uADpNCP/vQDqPzAspk7pLhrpOysYokLpOyzpNST97OvwgnskpEnsXFH8wgAVoUHpLRj+9/b0paD4xsOIx5fd7+H74uDvenLrU0f61tT1r6vuc2vtZVvxioP5zsvpNjb93p4nefOFrPeqxPnK5dBUs2zt9u++0vtuvYFelfWq1rT2u7j86ejyl5HrSz7/9+X80nT94637xDX8yU//+/D93Jb+785SjvWdu/j+9NzC1fvv9P7a5v1FrmDl8+nD4spru34zqkLoHwD0qKTwhnPwdjf1ly/5sSL82IbuZjjyiDL3pyfsWDjwezb1mi7vazn8zWH+68L7wjDq04OkszlurkrfuiG7tjKGsERSq1DSuSqatEQ4o31Kk9pJnrQ/qmRIjuVJmcRHo5uYzqVKmMtIoqJBqHlesJcm1X3QAAALTUlEQVR4nO2c2X/a2BWAFRniBAttyIiwmNUsM46BEGycZDY7cTC47iztdKbJdJ1u09ad9v9/qCQESKDlbrpX4pfvyS9Y+nzOvefcBXPcB0hRLh+en9cNzg/LrN+FHIeto+Nuo9PMlQzkBeZPYqHRrZz1zpOrWm4ddwuiLAuakhPFB5uIuZyiCbLSbFSODlm/KySGWv6iZIhta3l4KkIp1670khLJVqWjyVoOQM2BIak1J7F3LB81NBkkap6RNALZPo5vrpbP2oKQQ3NbOWpyIZ6KvQa23FKx1DmLWaK2JgoZOVtRkPMt1k5rjguyQk5ugSI3z1h7WRxOZA1xQglGFLQK8zQ975IP3RpN6DKda+r5EsFR54VSYmd4mJcjtrMMhS6TLC1PShFmpstQntA3vBMo2ZloIuW5tHch0LMzkQsU6+FhW46kIgQhynlaSXpMslUBR8kd0bA77FBOzTVyI/oQHtOoCX4oSsQhLLdldnYmpXyUei2RYlHwRmnWI9OrlKhPm9uIpahqYZvZxOJGjiRHzx8wz80lSpN8z30kxCA3l4haj7DeXYm1k5vSMVG9CeOysM0vSAo2YjKzrBFIzjEdjbXOJkT1CrGZOJeQ1Cs3d1yPYT/tjdDYbb0dH3sEo8d14qdHMnqN+BUGktGb7HZZP45dU0Y0er2YtdSEo3e+28nJXcRovWdBVq8Rt8pAVq8St7mFrF6L9Nwi5hRNEwTBvH4mCBrs9R/CeuUH5AafmNPkktbJT+7OjnqtVr3e6h2d3XU7YkkGur8VgR65wacIcjN/3PI8NijXzyYFsNtOhPXOiAw+UZHFbtjVwHKr0iyF3b8grHdIYvApcqECuJN+fhd8f4awHtfBXvKJgjKBOiaoTxTf/VWiTRlHIDtFuYBwRHBU8L5rQjp6Zcy+TJQ7iEfl9bbH2SLp6HFtrOwUS6h2JvX25gkV6ehxPazsFAqYBwOtgit9iOthtdWKRmDT/ExZz6Xk9e4wRh+h4/9yfplC5PXK6BsuOXJn/z0lF40e10VuzIQ2wbsbZfOoOAI99M6F6HEVZx71R6DH5RFrgygSvx3Wi0DvHLE2RHEeHgW/RAsf8RYjIl5kvvwIRa/L+sUBeZl58hW8oDxh/d6AfJZJpZ58fQGrV2H93qB8Y/gZ/BYqhImJHsct9FJQOZqYscdxr2w/mBxV2qzfGpxPUmt+BRZCscn6pcF5feDwe/JrIEEtGWXd4mUm5RT8FkBPjtFX2EJx6RmCB78JC6GQmMpg8OogtUFYjuY6rN8Zhk839QzB7wMFkzT4uBdb4QvLUTke364E5FXGw8/gOz/BZGWnV3oG56iQpOy0Wmsfwa8vvAy1JM2d/ulp4bEoFB+wfmM43gXoeTXcMpVvcpEjKHweDbdYYP3CcHzhVR1cuBeFMulvH0TM58HxS200M0kLn2tp5Cf47TpHhYSNPv/q4GK5KBQvWL8wJOHDz5WjJA7BqPIxWPyMHLUEZeb/9AKSd4B+i4Y7l5wtJRtAO8vwu48StWo38Vwb+Qp+n7DWjOPew/ilUt/gPe0hJdZPDK/uTg7e4/k9P0nT4OTt6okvofwyeHrc4/09GqRPV08E6F4cfJoMv/2nywd+BqWX+TgZfnuXKz+o6eXgdUL89q/tBwJ2Z8v4YepR80svJ5j3UNPLJ4nxe2Y/ELT7XIQPs/pRzM8r+4FQ5SGDWf0o+j2yHxi4t7QJ9vRCz285gfpt7Xr74epR89tL2w+E0UulkuN3co3ghz19Uoyf3WLDlL/MuwT5LQogVPuCXx4o+r1B8Ps8QX6LAg+1esfurin67Z/uuN+igXkN5ffqg98Hvw9+BP12fX7Zdb+dre/LBe7O9menCH5J6q9tP7rbS9T7z51d39rrB7jt+QPss1va6z/w01vLLzH7S6v1O9z+IHYDQ8/P3n+BOv5Lzv7u3on9wMC7g1skZn9+b99+4I6er6z2d3f1fOzx0g9GLznnm+sD3B+gBBNyPr3cXuIgC2BS7hes2hfa90Oon99C3u/J/i4hfsvzd7gVfPb3PKZfeh8ZKMH1IyHsUn/g1T6W39XjR8hA2K3KAwdxwpn9I8/zUhXLD4c0hN/V+mOgE4yRmyYqK703EH6r6xMcaIeWzf7Z0uP1MSO/K4gBuJ4+Ae+XW7m5YMrI7xJcb3X6bgGwhM/+aaWHO8Og8hBm+D13fjJ0AK5y00IaMPE7RZxewgdg9ocfeSdsAvgcZvg9c300OH7O3GQXwGuI8K02X2yCWuxs9q/8JuqMvh9Mejq7F5OAPYps6sctPQP6fjCz53rxt8C/QmT/4mXHoAbCFPfN4effotkti4fgkLIf1Lrj5Hrj096XQM122gdpTlfv7QlMel5uftxzk8nRsmxDeYp5BBM+d/Wz8EhQ39xkkKFvoSZPZ/Nps/X/Ndwti1eG0iyCMLV9vbXrZGMABuamnaH05tBnUOHbrA4W7mOWrZbFU5BamwZj55me7nswoblJeQg+hdyT8vwl60XSZjvti0RnJQhV2n3S09Gj+bQsnoI05phryOh5pie3nGG82umADB1F7wc3dzq+WbWB9f/5AloWb8HId9OewmWn85t/bswmGyI3bdSIBeGWRXvOjetNXmZCWhYGgs9g+k6T1fdytnkBmZs2UY5ByKlzz392MRlJKH68HtlaAl7Pd3YxuVGR/Iw6GE2hh05Oj5WtiypaAHlJiqJVu4KPnk/vsmRYRPPj+SL5ZvsRgp5vcbCp6qiC6pxsjj68RDkITYf81iGyHy/pJFf0p2kkvZDwcdwYXZBXR6RCeP0YZejthYw+iym6nxFCMqNw9jek5AQIH8f1EWvEAp3HT9LaQNX5vyMFEOTXIxb5JeqghmV3My+aL3D7D3jB4Nq3BGOKsZDUAXox7I9U+897+789yBx1n/n5M8bK0IUh2jgcT9V18ug//RNO8CSg83Qxx8tQ01BXqzVIOSN0uuvB0u2/YHLUZ1vCgyFuAK23U6dV8DydVXl1+696+2+IOz3+677tp5EQNBXV0ewm9Gn98byoe6fM7X+BCwVIbViBOYc6FHV1Ohr3fSSHtXF1IKk+ctbnJcBCATq52BDSsx11VR2MquPZrN+v1Wr9fn82vq/Op2rRUAv7S97+DNSpbRxIh9FHXkj4SUqGpuFpYfwULrYSBGlmoLLT5J7MECSBFN7MQGanCX6RIEZ4odgHnztX8PERDCsUJ2/CdbZA3YyJhMBmJr19XAsC8TkGB0n/j1+OIgy+BdiNKFFuf/bJUZTBt6AaL0HvZga4rfZghLlWIotnoQBb9Pkxj5WguerdCCHi3LJiEKMqwZvNjHvVmwZeFPkxjZegUSgcOer8FsCOCDqbGeTK4CJmKbpuZravmSEKxmuS4W9/so7kSenFbhY1FltGM7N/iVzXt4hXHeStVS+x6JnEq5Phze1RknrGejdOzXYUR+KzgF0g6kRxZ+MmPgveCA6LTebxGISSHtW9zHEcBqEe0WUNkxr7HFWjvdE3YpujUuSXopnOo/o0/DgDlyG7aaZI56vNYzYh1Hla99mHI4/DuoiRor5o6qI/pdxx69MaRT3OTFKKhjqD76QPq0VKSSoVq7S/jWdxQ2UYSuSufUFTG0UdQ6k4qrGyMzFiGOE4NGIXfUEPM7zXI6qHulplbmcxHpAfiJLK37P2WlOrSiQVJV2dM/iKfSBb96uQ0YvTMbMpM4jZHHsomheC7musRfyZjXT0MEp6cTCOx5QSQO1+gOBoBI6vzmKZltsMa+PRFOT2lWVmqBWnVYCbePFi2B9XB7x5G0vy9LSubBndwaA6ZvMPgYgwrM3G1dFgKqnForrC+Jm3rtzVEpKRIAyHNzc1i5sdsmLK/wHcjdWqyATPYAAAAABJRU5ErkJggg=='} alt=""/>
116	            </div>
117	            <div className="login-social-item">
118	              <img className="login-social-item__image" src={'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAV1BMVEWZmZn///+SkpKVlZWRkZH19fWgoKDg4OCampqjo6P8/Py+vr7l5eX29vb5+fnIyMirq6vQ0NDp6enW1ta3t7fU1NS7u7uwsLDKysrv7+/b29vi4uKtra2OjCyUAAAJGUlEQVR4nO2d6ZrqIAyGKdSldrSrVh3v/zpPq6PjjCULxhLnOd9/aV8LAUISTKJSq8OxaEtzFGjKCLQhrboojXXOGLcVaE0dYZ33dOYitxdoUBnhNrvhDYSFQJOaCFd7c4f39whXhfvJ99cIG/ObryesBBpWQjjL7APfn7Kl1RifMfavzIebchzQ2FqgdQWE9cgI/CKcCTQfn/CYevh6SbQfnXAPAJYSD4hNWACALpd4QmTC3GNjLsNQwpRGJiwgQJNKGJq4hBXQRXvNRR4Sk/ATBpQZhjEJD2AX7Yfhh8hjIhLOYUDjZB4TjzD3rWRkO2k8wgU8CHtLKrEoTeIRrhA+YzKhJ8UibJE+amwj9KRIhB9YH5VZdQ+KRIjZURkHxllxCLfIVNhrJfWsOITYIJQbhZEIK5RQZkl6VhRCtIuKOGiuD5Nriix0FLpW8GkxCFFDKmdmkiiEH9gnTD8lHxeBcDfRkvtLEQixLiq1IL0+TrY5gj6RTurWss+bnhDZF0pOFGdNT7gEAVMRD+K9JiecwQ5EsQX3TZMTHiHCFwBOT1gAw/AVgNMTdoCRER+DgyYnzPyE0lb0oskJfZ3UZZvXPHBqwpXHQZPKLtXuNDXhbJTQGdHF9g9pIHSv+4CJBkKXtiLnhD5NTbj5Rejs7vDaJ05uS9Mfny+rXmRBvzU54Rebc9acqhd/vssDJ3jGD+3mvcq2aGpJZwyg2NEmr9d/QprWh89j0xwXn/XsactxWGyLtpzP+53yMju1eXU8PNXm04SHbV4uba/BeFibumWZN6EWpK5Kc27p29wOrbp5uw02Sk8Rbo6tsw+xy/1bWddV3J3CoTpZ612Xu9S0x6Bv+QThsXPeNxqm8mVOX2weijnQ1rVF1wYsX0MJZ4VBzwD7V9pRXmm2n6foadT1byu4zsYwwtkO/cevr2QKZAQdS2Jb1xZ3PMYQwg2V7/JKabb1DqBDjneFR8acMyADCCvWf355p24x0tCqKYm986E9hsuKTVjP2X/6oN7u/ApTC/l8381lZFPNJczxMBGP+nlt11x3gr3tDPt8N6XUfBoe4Tp76rWGDUV2qooOnxpwWaLrikX48fx7mYFTopVBpJ7KIURCeqdXSolJYRCGD8GXiTIY6YRduOV7nSyOSCbsxEaPqHBEKqFSQAIikXCnsYtehJkbGiGc+RFZ6diKkEnY6LOi93Kgz5xCeNANiETgEAhXcPSEAlmoMgGBUK0ZvclBySc4YaPZypwFTxgoIRz/okBuDrtJUMIyNgEiu0MAMEIwwEeB8O0FQrhSbmUcvkVECKEIJg0i+PphQt1mxs0pfgyYEM2/iioSIEw4HvyiRXPaITJIqPsTEp37EKHqUWipB4oQoWZDSo/UhAgVf0JGTgZA2Cj+hIycDIBQ8Yo0ZZzq+wkV7+xZtfj8hIrtDCv/0k+I59DFUsoqmOElxOpyRFTHAfQT7tV2UvJcjxCqtaTcFFof4UZtJ+XMFBChXu/FiQfoJcSqx0QTO3XIR6h3rmAC+gjXWjspPw3aQ4gl60YTP4nWQ6h3NuQC+gi1+i8c5uEmE2o1NI6fhOkh1LpzCqjZOk6odtm9ZAN6CBdaCXnbCoAQLwIURyElzMcJte7vAwyNh1DrZBFSNGOcUOvmMA2oezJOqHU6tAEZfeOEWiNoAiaLcUK8MGUkhZRxGyVcKzU0coRqj9X+E/4nvOkvLbzfjDAk0+69CMVmfL2EAfnO72VpxFbeagnFdk9q1zRiO2C161ITUJx23P7GBvEqYMp/r/1hSOnPcUKgbFxcWTgDiE4IlP6LqwBTM06o9nw0oATvOKHeoyf+DUnjhHqD9vh3JowToiW344ndTccJFQd4sxffnh2X2l7KP8j3EKqdEPl7RN8pd2wOv7gXPb9dpILh3pzgIVR7RGrY6xoPodo98CDLqmrm817FpoDEi7/0EZ5iY0BihUH7CLWec1/E8Qz7CDWbGl5olI9wrXfdNohxSYTXT67WkXERXAuDRIjddxNbZJ+Ul/ConNBR4729hKrn/EHUWfEds4K+hFZTwAj1eqOuoiH6CXXPiGdZSkf1E2ovGDHIlfhOCjg3Vr00/ZKbo/MiQLh9g49IyKEBCBU73O6FXf8BRTcodkfdCykyBBGqdtbcCywUBRGqDcl4kFv6RyMYg/Mm3XSQLX1hGiDh23TTQWk5Xv/9jevTPGjcGw5HimmNaB+V50QDJtR7jjgiTwo0TPgOa9ObPCeLSDyjdl/GnZynNh1CWL+PrfHVv8RiUtXv9K/ynpxihFqz2B7kLbWAEb6NrfFmJqKR0/rdNWdZ74U2KKHiYkr38juI8eh3tTFu97L+MqY4oeLooTv535+QwfAGeygoPoNAqLdUzU0WeH1KFor6WR+MzqAQbrV/RDA2mpRJFJsAERzqRiJU/hFTML6Glg2mNfP5LCRakUaoura+g0OkiBl9is0pFuZGJFR8mIh8QvJdQWq9bmi4KZVQ7xYDe3NyZq3ScifApoJLuNFJiCcn0LOjK43GhhCHycj/VriLcoS6UQxChb5TSjoiJ4dfnQOclJjAIVR3tRUpVJhVh2Ghq5+mpFu6eZUmVE2KxOBEHqGqfuoE7ih5lCJ7Sk165tZDUZOlQE4rYVd80TLvL6n5XWxCJTUz0pfdPJ4knxqGIuEe4HDCZB9/Ce5K+uuGVF6Kf6kl5rl4ljC6taEPwlDCTdyJP2VVHQgijJtuYnn56mGESR0PkbLrFSBMPmLNGY5bNiKUMNbdAo54J6AAYaTNIu1WRxnCpJ6ej3YvpxhhMltyp35nbWrKrmtP2TJNLfvnAYBPESZJyXhHZ+fdfnEXfbb+qDrDobTcS3QECJOcNhiddV0zmqFU7zMqJJYc49GThMnRoe/n7DKHfEazKksJkCE1Ewc9S5isS3DacNbscM/7oVgiX9KWAeXYz3qaMEka4305Z1tqCbnPFmB0vhBnggQIk1U++nLOlg1nel415Tikc0VA7dmrJAj7rpq7X33VpVnFvzFltu3sL8reSOWhHfQsGcJex1P/Lu7yl1vTBeB9qd6fjO05B1k7z1nXOY5IjLDvZfU2b09lVzQB1X5/alYvmmpfHT+C/6dv/QMH/ovCU90cLAAAAABJRU5ErkJggg=='} alt=""/>
119	            </div>
120	          </div>
121	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
122	        </div>
123	      )}
124	      {currentUser !== null &&
125	        (<div className="container">
126	          <h2 className="heading">{'User Screen'}</h2>
127	          <Divider />
128	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
129	          <div className="form_buttons">
130	            <Button
131	              onClick={() => doUserLogOut()}
132	              type="primary"
133	              className="form_button"
134	              color={'#208AEC'}
135	              size="large"
136	            >
137	              Log Out
138	            </Button>
139	          </div>
140	        </div>)
141	      }
142	    </div>
143	  );
144	};
```
:::

## Conclusion

At the end of this guide, you learned how to log in and log out Parse users on React. In the next guide, we will show you how to perform useful user queries.

[title] Using GeoPoints
[path] Android/Data objects/

# Using Parse GeoPoints on your Android app

## Introduction

Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a **ParseGeoPoint** to a **ParseUser**, you will be able to easily find out which user is closest to another, show the locations of the users of your app and also store the user’s location information, among other possibilities.

You can also associate a **ParseGeoPoint** to any **ParseObject**. For example, if your app is associated with a store with physical affiliates, you will be able to create an activity to show the location of those stores or to show the user which store is closest to him. Another example of this association usage: if your app is a game in which you created ParseObjects to represent characters, adding ParseGeoPoints to these characters would allow them to be shown along the player’s path.

This tutorial explains how to use some features of ParseGeoPoint through Back4App.

After following this tutorial, you will be able to do this:



::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vq11izFSiACJsleR19j_A_image.png" signedSrc size="50" width="414" height="827" position="center" caption}

:::hint{type="success"}
At any time, you can access the complete Android Project built with this tutorial at our <a href="https://github.com/back4app/android-geopoints-tutorial" target="_blank">GitHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, we need:**

- <a href="https://developer.android.com/studio/index.html" target="_blank">Android Studio</a>
- An User Registration - Login App created on Back4App.
  - **Note: **Follow the User Registration - login tutorial to learn how to create an User Registration - Login App on Back4App. .
- A real device running Android 4.0 (Ice Cream Sandwich) or newer.
  - **Note:** It is very likely that the app built with this tutorial won't run as expected in a virtual device and may even crush because it might not retrieve the current location of the virtual device. So we strongly recommend that you use a real device to run it.
:::

## 1 - Set up Google API Key

To show the location you stored in a ParseGeoPoint, you will need to display a Map. To do that, it’s interesting to use a Google Maps Activity. In order to create a Google Maps Activity in Android Studio, do the following:

1. Go to File > New > Google > Google Maps Activity. Then, automatically, it will create a java file, a layoutfile and a values file corresponding to the Google Maps Activity that you have created.
2. Go to the created values file (you can do this by accessing app > res > values > google\_maps\_api.xml), as shown in the image below. This file will give you some instructions on how to get a Google Maps API Key. Basically, you should open the link shown in the image.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pQrS9WvLGBK-Xl2iOpMKp_image.png)

&#x20;    3\. After opening it, you should login in your Google Account, select the Create a project option and click on Continue. While creating the project, Google will enable your API.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qw5OD2Yy4C5hDUQCkSx40_image.png)

&#x20;    4\. After your API is enabled, you will be able to get an API key, to do so click on

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/c1tRU2IbSSgv0CV2pn5lv_image.png)

&#x20;    5\. Then, your key will be created and you can copy it and paste it in the values file that lead you to this page, in the place where its written YOUR KEY HERE.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vKHMI6Y2UQRegSW7S1z1B_image.png)

&#x20;    6\. It’s important to have the uses-permission below in your AndroidManifest.xml file. If you created the Google Maps Activity following the instructions above, then one of these permissions should already be in your manifest, anyway, you’ll need both of them for your app to work properly, so check if they are in your AndroidManifest.xml file, otherwise, insert them on it.

```xml
1   <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

&#x20;    7\. At the beginning of your MapsActivity, import the following:

```java
1   // Android Dependencies
2   import android.Manifest;
3   import android.app.ProgressDialog;
4   import android.content.Context;
5   import android.content.DialogInterface;
6   import android.content.Intent;
7   import android.content.pm.PackageManager;
8   import android.location.Location;
9   import android.location.LocationManager;
10  import android.support.annotation.NonNull;
11  import android.support.v4.app.ActivityCompat;
12  import android.support.v4.app.FragmentActivity;
13  import android.os.Bundle;
14  import android.util.Log;
15  import android.view.View;
16  import android.widget.Button;
17  // Google Maps Dependencies
18  import com.google.android.gms.maps.CameraUpdateFactory;
19  import com.google.android.gms.maps.GoogleMap;
20  import com.google.android.gms.maps.OnMapReadyCallback;
21  import com.google.android.gms.maps.SupportMapFragment;
22  import com.google.android.gms.maps.model.BitmapDescriptorFactory;
23  import com.google.android.gms.maps.model.LatLng;
24  import com.google.android.gms.maps.model.MarkerOptions;
25  // Parse Dependencies
26  import com.parse.FindCallback;
27  import com.parse.ParseException;
28  import com.parse.ParseGeoPoint;
29  import com.parse.ParseQuery;
30  import com.parse.ParseUser;
31  // Java dependencies
32  import java.util.List;
```

## 2 - Set up Back4App Dashboard to save user’s location

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App website </a>login, find your app and open its Dashboard.
2. Go to Core > Browser > User, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EM9imOEAfCvIhzCYgKKEg_image.png)

&#x20;    3\. Now, create a new column to save the user’s location. To do so, click on the Edit button in the top right, as shown below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8ov3mPji-5pyg0Bn-8B0j_image.png)

&#x20;    4\. Then, click on Add a column.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5l5kpmezZk2xlablyDI8-_image.png)

&#x20;    5\. In the field What type of data do you want to store?, choose the GeoPoint option.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N7bLZpTRSeZTIf_yvB0Bs_image.png)

&#x20;    6\. In the field What should we call it?, type “**Location**”.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yXvBMPYZGCduNr_181WsT_image.png)

&#x20;    7\. So, click on Add column, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/hJohTYLWB1BqOyMERptvr_image.png)

&#x20;    8\. Now, your app is able to store location data of users and your User Class in your Back4pp Dashboard should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eNUxXQn3_1AzdMj7dcQlN_image.png)

## 3 - Save users current location in your Back4pp Dashboard

1. Open your MapsActivity and inside the public class MapsActivity define an int called REQUEST\_LOCATION with value 1 and a locationManager, as in the following code.

```java
1  private static final int REQUEST_LOCATION = 1;
2  LocationManager locationManager;
```

&#x20;    2\. In the onCreate method create the locationManager, as in the following code.

```java
1 locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
```

&#x20;    3\. To save user’s location in your Back4pp Dashboard, simply implement the following method and call it in your onMapReady method.

```java
1    private void saveCurrentUserLocation() {
2     // requesting permission to get user's location
3     if(ActivityCompat.checkSelfPermission(UsersActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(UsersActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
4         ActivityCompat.requestPermissions(UsersActivity.this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
5     }
6     else {
7         // getting last know user's location
8         Location location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
9
10        // checking if the location is null
11        if(location != null){
12            // if it isn't, save it to Back4App Dashboard
13            ParseGeoPoint currentUserLocation = new ParseGeoPoint(location.getLatitude(), location.getLongitude());
14
15            ParseUser currentUser = ParseUser.getCurrentUser();
16
17            if (currentUser != null) {
18                currentUser.put("Location", currentUserLocation);
19                currentUser.saveInBackground();
20            } else {
21                // do something like coming back to the login activity
22            }
23        }
24        else {
25            // if it is null, do something like displaying error and coming back to the menu activity
26        } 
27    }
28   }
```

&#x20;    4\. It’s really important to implement the following method in order to make saveCurrentUserLocation method works.

```java
1    @Override
2    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
3     super.onRequestPermissionsResult(requestCode, permissions, grantResults);
4
5     switch (requestCode){
6         case REQUEST_LOCATION:
7             saveCurrentUserLocation();
8             break;
9     }
10   } 
```

## 4 - Retrieve user’s location

To retrieve user’s location, you’ll need to find who is the current user, save its location and then return the location saved in your Back4App Dashboard.
In order to do that, implement the following method in your MapsActivity and call it when needed.

```java
1    /* saving the current user location, using the saveCurrentUserLocation method of Step 3, to avoid
2    null pointer exception and also to return the user's most current location */
3    saveCurrentUserLocation();
4    private ParseGeoPoint getCurrentUserLocation(){
5
6    // finding currentUser
7    ParseUser currentUser = ParseUser.getCurrentUser();
8
9    if (currentUser == null) {
10        // if it's not possible to find the user, do something like returning to login activity
11    }
12    // otherwise, return the current user location
13    return currentUser.getParseGeoPoint("Location");
14
15  }
```

## 5 - Showing current user’s location in map

To display user’s location in the map, you’ll need to retrieve user’s location and then create a marker in the map using the information retrieved.
In order to do that, implement the following method in your MapsActivity and call it in the onMapReady method.

```java
1   private void showCurrentUserInMap(final GoogleMap googleMap){
2
3       // calling retrieve user's location method of Step 4
4       ParseGeoPoint currentUserLocation = getCurrentUserLocation();
5
6       // creating a marker in the map showing the current user location
7       LatLng currentUser = new LatLng(currentUserLocation.getLatitude(), currentUserLocation.getLongitude());
8       googleMap.addMarker(new MarkerOptions().position(currentUser).title(ParseUser.getCurrentUser().getUsername()).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)));
9
10      // zoom the map to the currentUserLocation
11      googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentUser, 5));
12  }
```

## 6 - Finding the closest user to the current one

1. Now that you have a bunch of users with spatial coordinates associated, it would be good to find out which user is closest to another. This can be done by using a ParseQuery. First, you’ll need to create a ParseUser query and restrict it with whereNear, informing in which column of the User class on Back4App you are doing the query (in this case the “Location” column) and also inform the reference ParseGeoPoint for the query from which will be found the closest user (in this case the ParseGeoPoint associated to the current user location). The following code do that:

```java
1   ParseQuery<ParseUser> query = ParseUser.getQuery();
2   query.whereNear("Location", getCurrentUserLocation());
```

&#x20;    2\. You can limit the number of results of this query adding to it the restriction setLimit. By default, results are limited to 100. As the whereNear restriction will make the query retrieve an array of users ordered by distance (nearest to farthest) from currentUserLocation, setting the limit of close users to find to the number 2, the list of results of the query will only have two users: the current and the closest user from him. The following code set the limit of results to 2:

```java
1  query.setLimit(2);

```

&#x20;    3\. Now that you restricted your query, let’s retrieve its results. In order to do that, you will call a findInBackground method and pass as argument a List of users, in which it’ll be stored the results of the query and also a ParseException to handle errors. To avoid errors, don’t forget to clear cached results after the query run. The following code do that:

```java
1   query.findInBackground(new FindCallback<ParseUser>() {
2      @Override  public void done(List<ParseUser> nearUsers, ParseException e) {
3          if (e == null) {
4            // do something with the list of results of your query
5          } else {
6              // handle the error
7          }
8      }
9     });
10    ParseQuery.clearAllCachedResults();
11  }
```

&#x20;    4\. If no error occurs, you’ll have in the nearUsers list the two closest users to the current user (the actual current user and the closest user from him), now you’ll only have to find who isn’t the current user. To do that, inside the if (e == null) block use the following code:

```java
1   // avoiding null pointer
2   ParseUser closestUser = ParseUser.getCurrentUser();
3   // set the closestUser to the one that isn't the current user
4   for(int i = 0; i < nearUsers.size(); i++) {
5   if(!nearUsers.get(i).getObjectId().equals(ParseUser.getCurrentUser().getObjectId())) {
6       closestUser = nearUsers.get(i);
7   }
8  }
```

&#x20;    5\. Now that you know who is the closest user to the current one you can mesure the distance between them using the distanceInKilometersTo method. In order to do that, use the following code:

```java
1  // finding and displaying the distance between the current user and the closest user to him
2  double distance = getCurrentUserLocation().distanceInKilometersTo(closestUser.getParseGeoPoint("Location"));
3  alertDisplayer("We found the closest user from you!", "It's " + closestUser.getUsername() + ". \nYou are " + Math.round (distance * 100.0) / 100.0  + " km from this user.");

```

:::hint{type="info"}
The alertDisplayer method used above, is the following:
:::

```java
1   private void alertDisplayer(String title,String message){
2      android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(UsersActivity.this)
3              .setTitle(title)
4              .setMessage(message)
5              .setPositiveButton("OK", new DialogInterface.OnClickListener() {
6                  @Override
7                  public void onClick(DialogInterface dialog, int which) {
8                      dialog.cancel();
9                  }
10             });
11     android.app.AlertDialog ok = builder.create();
12     ok.show();
13  }
```

&#x20;    6\. You can also show both users in the map, using the following code:

```java
1  // showing current user in map, using the method implemented in Step 5
2  showCurrentUserInMap(mMap);
3  // creating a marker in the map showing the closest user to the current user location, using method implemented in Step 4
4  LatLng closestUserLocation = new LatLng(closestUser.getParseGeoPoint("Location").getLatitude(), closestUser.getParseGeoPoint("Location").getLongitude());
5  googleMap.addMarker(new MarkerOptions().position(closestUserLocation).title(closestUser.getUsername()).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
6  // zoom the map to the currentUserLocation
7  googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(closestUserLocation, 3));
```

The complete method that executes all 6 steps above is the following:

```java
1   private void showClosestUser(final GoogleMap googleMap){
2       ParseQuery<ParseUser> query = ParseUser.getQuery();
3       query.whereNear("Location", getCurrentUserLocation());
4       // setting the limit of near users to find to 2, you'll have in the nearUsers list only two users: the current user and the closest user from the current
5       query.setLimit(2);
6       query.findInBackground(new FindCallback<ParseUser>() {
7           @Override  public void done(List<ParseUser> nearUsers, ParseException e) {
8               if (e == null) {
9                   // avoiding null pointer
10                  ParseUser closestUser = ParseUser.getCurrentUser();
11                  // set the closestUser to the one that isn't the current user
12                  for(int i = 0; i < nearUsers.size(); i++) {
13                      if(!nearUsers.get(i).getObjectId().equals(ParseUser.getCurrentUser().getObjectId())) {
14                          closestUser = nearUsers.get(i);
15                      }
16                  }
17                  // finding and displaying the distance between the current user and the closest user to him, using method implemented in Step 4
18                  double distance = getCurrentUserLocation().distanceInKilometersTo(closestUser.getParseGeoPoint("Location"));
19                  alertDisplayer("We found the closest user from you!", "It's " + closestUser.getUsername() + ". \n You are " + Math.round (distance * 100.0) / 100.0  + " km from this user.");
20                  // showing current user in map, using the method implemented in Step 5
21                  showCurrentUserInMap(mMap);
22                  // creating a marker in the map showing the closest user to the current user location
23                  LatLng closestUserLocation = new LatLng(closestUser.getParseGeoPoint("Location").getLatitude(), closestUser.getParseGeoPoint("Location").getLongitude());
24                  googleMap.addMarker(new MarkerOptions().position(closestUserLocation).title(closestUser.getUsername()).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
25                  // zoom the map to the currentUserLocation
26                  googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(closestUserLocation, 3));
27              } else {
28                  Log.d("store", "Error: " + e.getMessage());
29              }
30          }
31      });
32      ParseQuery.clearAllCachedResults();
33  }
```

## 7 - Set up Back4App to associate ParseGeoPoint to ParseObjects

Supose that the example app you are builiding is associated with a group of stores with physical affiliates. It would be interesting to show all the physical affiliates of this store in the map. In order to do so create a Stores class on Back4pp Dashboard, save the existing stores as ParseObjects, their locations and after that the app will be able to query the physical affiliates and display their possitions on the map. The following steps will help you with that.

&#x20;    1 . Go to <a href="https://www.back4app.com/" target="_blank">Back4App website</a>, login, find your app and open its Dashboard.
&#x20;    2\. Go to Core > Browser > Create a class, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/MhVphU5R_RReJsrBy3228_image.png)

&#x20;    3\. In the field What type of class do you need?, choose the Custom option.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/GrPKqZY1CNezwx16-9KgY_image.png)

&#x20;    4\. In the field What should we call it?, type “Stores” and then click on Create classbutton.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZWRMkTxTiXy99xR1Gt7NJ_image.png)

&#x20;    5\. Then a new class called “Stores” will be shown and you should insert 2 columns on it: a collumn with data type String called Name, as well as another column with data type GeoPoint called Location. If you don’t remember how to create a column, go back to Step 2, there it’s explained how to create a column at Back4App Dashboard. Your class should end up like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/M6bFQfcB0jl9b0aBBRFT3_image.png)

&#x20;    6\. Now, fill this class with information. To do so, click on Add a row button in the menu on the top right or in the middle of the page, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0bql9F58RsmBcS1il3vRg_image.png)

&#x20;    7\. Then fill the row with the informations required: the name of the store and its latitude and longitude. After inserting some data, your Stores class should look like the image below. If you want to insert more data, you can click on the Add a row button on the top, or in the + button bellow the last row of data.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dUBM-pQN-2Jye_YyQJ6uA_image.png)

Now you are ready to use the location information of the stores in your app.

## 8 - Display all the stores location on Map

&#x20;    1\. You can show all the stores you added to your class on the MapsActivity by using a ParseQuery. First, you’ll need to create a ParseObject query, informing the name of the Back4pp Dashboard class you want to query (in this case the “Stores” class), and restrict it with whereExists, informing the column of the Store class on Back4App you are doing the query (in this case the “Location” column). The following code do that:

1.

```java
1  ParseQuery<ParseObject> query = ParseQuery.getQuery("Stores");
2  query.whereExists("Location");
```

&#x20;    2\. Now that you restricted your query, let’s retrieve its results. In order to do that, you will call a findInBackground method and pass as argument a List of stores, in which it’ll be stored the results of the query and also a ParseException to handle errors. The whereExists restriction will make only the ParseObjects of the class that have a ParseGeoPoint associated to them indicating their location be stored in the list of stores. To avoid errors, don’t forget to clear cached results after the query run. The following code do that:

```java
1   query.findInBackground(new FindCallback<ParseObject>() {
2      @Override  public void done(List<ParseObject> stores, ParseException e) {
3          if (e == null) {
4            // do something with the list of results of your query
5          } else {
6              // handle the error
7          }
8      }
9     });
10    ParseQuery.clearAllCachedResults();
11  }
```

&#x20;    3\. If no error occurs, you’ll have in the stores list all the stores with location associated, now you’ll only have to make a loop to display them on the map. To do so, inside the if (e == null) block use the following code.

```java
1   for(int i = 0; i < stores.size(); i++) {
2   LatLng storeLocation = new LatLng(stores.get(i).getParseGeoPoint("Location").getLatitude(), stores.get(i).getParseGeoPoint("Location").getLongitude());
3   googleMap.addMarker(new MarkerOptions().position(storeLocation).title(stores.get(i).getString("Name")).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));
4   }
```

The complete method that executes all 3 steps above is below. Call it on the onMapReady method for it to work.

```java
1   private void showStoresInMap(final GoogleMap googleMap){
2
3       ParseQuery<ParseObject> query = ParseQuery.getQuery("Stores");
4       query.whereExists("Location");
5       query.findInBackground(new FindCallback<ParseObject>() {
6           @Override  public void done(List<ParseObject> stores, ParseException e) {
7               if (e == null) {
8                   for(int i = 0; i < stores.size(); i++) {
9                       LatLng storeLocation = new LatLng(stores.get(i).getParseGeoPoint("Location").getLatitude(), stores.get(i).getParseGeoPoint("Location").getLongitude());
10                      googleMap.addMarker(new MarkerOptions().position(storeLocation).title(stores.get(i).getString("Name")).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));
11                  }
12              } else {
13                  // handle the error
14                  Log.d("store", "Error: " + e.getMessage());
15              }
16          }
17      });
18      ParseQuery.clearAllCachedResults();
19  }
```

## 9 - Show closest user to a store

&#x20;    1\. Now that you have a bunch of stores with spatial coordinates associated, it would be good to find out which store is closest to a user. This can be done by using a ParseQuery. First, you’ll need to create a ParseObject query, informing the name of the Back4pp Dashboard class you want to query (in this case the “Stores” class), and restrict it with whereNear, informing the column of the Store class on Back4App you are doing the query (in this case the “Location” column). The following code do that:

```java
1   ParseQuery<ParseObject> query = ParseQuery.getQuery("Stores");
2   query.whereNear("Location", getCurrentUserLocation());
```

&#x20;    2\. You can limit the number of results of this query adding to it the restriction setLimit. By default, results are limited to 100. As the whereNear restriction will make the query retrieve an array of users ordered by distance (nearest to farthest) from currentUserLocation, setting the limit of close users to find to the number 1, the list of results of the query will only have one store: the closest to the current user. The following code set the limit of results to 1:

```java
1   query.setLimit(1);
```

&#x20;    3\. Now that you restricted your query, let’s retrieve its results. In order to do that, you will call a findInBackground method and pass as argument a List of store obejcts, in which it’ll be stored the results of the query and also a ParseException to handle errors. To avoid errors, don’t forget to clear cached results after the query found any result. The following code do that:

```java
1   query.findInBackground(new FindCallback<ParseObject>() {
2      @Override  public void done(List<ParseObject> nearStores, ParseException e) {
3          if (e == null) {
4            // do something with the list of results of your query
5          } else {
6              // handle the error
7          }
8      }
9     });
10    ParseQuery.clearAllCachedResults();
11  }
```

&#x20;    4\. If no error occurs, you’ll have in the nearStores list the closest store to the current user, now you’ll only have to display it on the MapsActivity. To do that, inside the if (e == null) block use the following code:

```java
1   ParseObject closestStore = nearStores.get(0);
2   // showing current user location, using the method implemented in Step 5
3   showCurrentUserInMap(mMap);
4   // finding and displaying the distance between the current user and the closest store to him, using method implemented in Step 4
5   double distance = getCurrentUserLocation().distanceInKilometersTo(closestStore.getParseGeoPoint("Location"));
6   alertDisplayer("We found the closest store from you!", "It's " + closestStore.getString("Name") + ". \n You are " + Math.round (distance * 100.0) / 100.0  + " km from this store.");
7   // creating a marker in the map showing the closest store to the current user
8   LatLng closestStoreLocation = new LatLng(closestStore.getParseGeoPoint("Location").getLatitude(), closestStore.getParseGeoPoint("Location").getLongitude());
9   googleMap.addMarker(new MarkerOptions().position(closestStoreLocation).title(closestStore.getString("Name")).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
10  // zoom the map to the closestStoreLocation
11  googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(closestStoreLocation, 3));
```

:::hint{type="info"}
The alertDisplayer method used above, is the same of the Step 6 (Finding the closest user to the current one).

:::

The complete method that executes all 4 steps above is the following:

```java
1   private void showClosestStore(final GoogleMap googleMap){
2       ParseQuery<ParseObject> query = ParseQuery.getQuery("Stores");
3       query.whereNear("Location", getCurrentUserLocation());
4       // setting the limit of near stores to 1, you'll have in the nearStores list only one object: the closest store from the current user
5       query.setLimit(1);
6       query.findInBackground(new FindCallback<ParseObject>() {
7           @Override  public void done(List<ParseObject> nearStores, ParseException e) {
8               if (e == null) {
9                   ParseObject closestStore = nearStores.get(0);
10                  // showing current user location, using the method implemented in Step 5
11                  showCurrentUserInMap(mMap);
12                  // finding and displaying the distance between the current user and the closest store to him, using method implemented in Step 4
13                  double distance = getCurrentUserLocation().distanceInKilometersTo(closestStore.getParseGeoPoint("Location"));
14                  alertDisplayer("We found the closest store from you!", "It's " + closestStore.getString("Name") + ". \nYou are " + Math.round (distance * 100.0) / 100.0  + " km from this store.");
15                  // creating a marker in the map showing the closest store to the current user
16                  LatLng closestStoreLocation = new LatLng(closestStore.getParseGeoPoint("Location").getLatitude(), closestStore.getParseGeoPoint("Location").getLongitude());
17                  googleMap.addMarker(new MarkerOptions().position(closestStoreLocation).title(closestStore.getString("Name")).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
18                  // zoom the map to the closestStoreLocation
19                  googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(closestStoreLocation, 3));
20              } else {
21                  Log.d("store", "Error: " + e.getMessage());
22              }
23          }
24      });
25
26      ParseQuery.clearAllCachedResults();
27
28  }
```

## 10 - Test your app

&#x20;    1\. Login at [Back4App Website](https://www.back4app.com/)

&#x20;    2\. Find your app and click on Dashboard > Core > Browser > User and create some users with location associated to them to allow the method showClosestUser to work.&#x20;

&#x20;    3\. Run your app in a real device and sign up an account. Try every feature!

&#x20;    4\. Go back to Back4pp Dashboard and verify if your location is stored in your user row.

## It’s done!

At this stage, you can use some features of ParseGeoPoint through Back4App!

[title] Basic Queries
[path] ReactJS/Data objects/

# Query in React using Parse

## Introduction

In this guide, you will perform basic queries in Parse and implement a React component using these queries. You will learn how to set up and query realistic data using Back4App and React.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:

- A React App created and <a href="https://www.back4app.com/docs/react/quickstart" target="_blank">connected to Back4App</a>.
-
  If you want to run this guide’s example project, you should set up the <a href="https://ant.design/" target="_blank">Ant Design library</a>.
:::

## Goal

Query data stored on Back4App from a React application.

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

```javascript
1   // This will create your query
2   let parseQuery = new Parse.Query("Profile");
3   // The query will resolve only after calling this method
4   let queryResult = await parseQuery.find();
```

You can read more about the Parse.Query class [here at the official documentation](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).&#x20;

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

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kae3_kBlMlgvZe8w96EOF_image.png)

Go ahead and create the user Profile class with the following example content:

```javascript
1   // Add Profile objects and create table
2   // Adam Sandler
3   let Profile = new Parse.Object('Profile');
4   Profile.set('name', 'Adam Sandler');
5   Profile.set('birthDay', new Date('09/09/1966'));
6   Profile.set('friendCount', 2);
7   Profile.set('favoriteFoods', ['Lobster', 'Bread']);
8   await Profile.save();
9
10  // Adam Levine
11  Profile = new Parse.Object('Profile');
12  Profile.set('name', 'Adam Levine');
13  Profile.set('birthDay', new Date('03/18/1979'));
14  Profile.set('friendCount', 52);
15  Profile.set('favoriteFoods', ['Cake', 'Bread']);
16  await Profile.save();
17
18  // Carson Kressley
19  Profile = new Parse.Object('Profile');
20  Profile.set('name', 'Carson Kressley');
21  Profile.set('birthDay', new Date('11/11/1969'));
22  Profile.set('friendCount', 12);
23  Profile.set('favoriteFoods', ['Fish', 'Cookies']);
24  await Profile.save();
25
26  // Dan Aykroyd
27  Profile = new Parse.Object('Profile');
28  Profile.set('name', 'Dan Aykroyd');
29  Profile.set('birthDay', new Date('07/01/1952'));
30  Profile.set('friendCount', 66);
31  Profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
32  await Profile.save();
33
34  // Eddie Murphy
35  Profile = new Parse.Object('Profile');
36  Profile.set('name', 'Eddie Murphy');
37  Profile.set('birthDay', new Date('04/03/1961'));
38  Profile.set('friendCount', 49);
39  Profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
40  await Profile.save();
41
42  // Fergie
43  Profile = new Parse.Object('Profile');
44  Profile.set('name', 'Fergie');
45  Profile.set('birthDay', new Date('03/27/1975'));
46  Profile.set('friendCount', 55);
47  Profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
48  await Profile.save();
49
50  console.log('Success!');
```

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

```javascript
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   // `contains` is a basic query method that checks if string field
5   // contains a specific substring
6   parseQuery.contains('name', 'Adam');
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(result.get('name'));
16  };
```

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.

```javascript
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   // `greaterThan` is a basic query method that does what it
5   // says on the tin
6   parseQuery.greaterThan('friendCount', 20);
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}`);
16  };
```

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.

```javascript
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   // `descending` and `ascending` can and should be chained
5   // with other query methods to improve your queries
6   parseQuery.descending('birthDay');
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(`name: ${result.get('name')}, birthday: ${result.get('birthDay')}`);
16  };
```

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:

```javascript
1   // Create your query
2   let parseQuery = new Parse.Query('Profile');
3
4   parseQuery.contains('name', 'Adam');
5   parseQuery.greaterThan('friendCount', 20);
6   parseQuery.descending('birthDay');
7
8   // The query will resolve only after calling this method, retrieving
9   // an array of `Parse.Objects`
10  let queryResults = await parseQuery.find();
11
12  // Let's show the results
13  for (let result of queryResults) {
14    // You access `Parse.Objects` attributes by using `.get`
15    console.log(`name: ${result.get('name')}, friend count: ${result.get('friendCount')}, birthday: ${result.get('birthDay')}`);
16  };
```

## 4 - Query from a React component

Let’s now use our example queries inside a component in React, 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.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider } from 'antd';
5	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
6	
7	export const QueryBasic = () => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState();
10	
11	  const doQueryByName = async function () {
12	    // Create our Parse.Query instance so methods can be chained
13	    // Reading parse objects is done by using Parse.Query
14	    const parseQuery = new Parse.Query('Profile');
15	
16	    // `contains` is a basic query method that checks if string field
17	    // contains a specific substring
18	    parseQuery.contains('name', 'Adam');
19	
20	    try {
21	      let profiles = await parseQuery.find();
22	      setQueryResults(profiles);
23	      return true;
24	    } catch (error) {
25	      // Error can be caused by lack of Internet connection
26	      alert(`Error! ${error.message}`);
27	      return false;
28	    }
29	  };
30	
31	  const doQueryByFriendCount = async function () {
32	    // Create our Parse.Query instance so methods can be chained
33	    // Reading parse objects is done by using Parse.Query
34	    const parseQuery = new Parse.Query('Profile');
35	
36	    // `greaterThan` is a basic query method that does what it
37	    // says on the tin
38	    parseQuery.greaterThan('friendCount', 20);
39	
40	    try {
41	      let profiles = await parseQuery.find();
42	      setQueryResults(profiles);
43	      return true;
44	    } catch (error) {
45	      // Error can be caused by lack of Internet connection
46	      alert(`Error! ${error.message}`);
47	      return false;
48	    }
49	  };
50	
51	  const doQueryByOrdering = async function () {
52	    // Create our Parse.Query instance so methods can be chained
53	    // Reading parse objects is done by using Parse.Query
54	    const parseQuery = new Parse.Query('Profile');
55	
56	    // `descending` and `ascending` can and should be chained
57	    // with other query methods to improve your queries
58	    parseQuery.descending('birthDay');
59	
60	    try {
61	      let profiles = await parseQuery.find();
62	      setQueryResults(profiles);
63	      return true;
64	    } catch (error) {
65	      // Error can be caused by lack of Internet connection
66	      alert(`Error! ${error.message}`);
67	      return false;
68	    }
69	  };
70	
71	  const doQueryByAll = async function () {
72	    // Create our Parse.Query instance so methods can be chained
73	    // Reading parse objects is done by using Parse.Query
74	    const parseQuery = new Parse.Query('Profile');
75	
76	    parseQuery.contains('name', 'Adam');
77	    parseQuery.greaterThan('friendCount', 20);
78	    parseQuery.descending('birthDay');
79	
80	    try {
81	      let profiles = await parseQuery.find();
82	      setQueryResults(profiles);
83	      return true;
84	    } catch (error) {
85	      // Error can be caused by lack of Internet connection
86	      alert(`Error! ${error.message}`);
87	      return false;
88	    }
89	  };
90	
91	  const clearQueryResults = async function () {
92	    setQueryResults(undefined);
93	    return true;
94	  };
95	
96	  return (
97	    <div>
98	      <div className="header">
99	        <img
100	          className="header_logo"
101	          alt="Back4App Logo"
102	          src={
103	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
104	          }
105	        />
106	        <p className="header_text_bold">{'React on Back4App'}</p>
107	        <p className="header_text">{'Basic Queries'}</p>
108	      </div>
109	      <div className="container">
110	        <div className="flex_between">
111	          <h2 className="heading">{'Query List'}</h2>
112	          <div className="flex">
113	            <Button
114	              onClick={() => doQueryByName()}
115	              type="primary"
116	              className="heading_button"
117	              color={'#208AEC'}
118	              icon={<SearchOutlined />}
119	            >
120	              BY NAME
121	            </Button>
122	            <Button
123	              onClick={() => doQueryByFriendCount()}
124	              type="primary"
125	              className="heading_button"
126	              color={'#208AEC'}
127	              icon={<SearchOutlined />}
128	            >
129	              BY FRIEND COUNT
130	            </Button>
131	            <Button
132	              onClick={() => doQueryByOrdering()}
133	              type="primary"
134	              className="heading_button"
135	              color={'#208AEC'}
136	              icon={<SearchOutlined />}
137	            >
138	              BY ORDERING
139	            </Button>
140	            <Button
141	              onClick={() => doQueryByAll()}
142	              type="primary"
143	              className="heading_button"
144	              color={'#208AEC'}
145	              icon={<SearchOutlined />}
146	            >
147	              BY ALL
148	            </Button>
149	            <Button
150	              onClick={() => clearQueryResults()}
151	              type="primary"
152	              className="heading_button"
153	              color={'#208AEC'}
154	              icon={<CloseOutlined />}
155	            >
156	              CLEAR
157	            </Button>
158	          </div>
159	        </div>
160	        <Divider />
161	        <div className="flex_between">
162	          <div className="flex_child">
163	            {/* Query list */}
164	            {queryResults !== undefined &&
165	              queryResults.map((profile, index) => (
166	                <div className="list_item" key={`${index}`}>
167	                  <p className="list_item_title">{`${profile.get('name')}`}</p>
168	                  <p className="list_item_description">{`Friend count: ${profile.get(
169	                    'friendCount'
170	                  )}, Birthday: ${profile.get('birthDay')}`}</p>
171	                </div>
172	              ))}
173	            {queryResults !== undefined && queryResults.length <= 0 ? (
174	              <p>{'No results here!'}</p>
175	            ) : null}
176	          </div>
177	        </div>
178	      </div>
179	    </div>
180	  );
181	};
```

TypeScript

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider } from 'antd';
4	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
5	const Parse = require('parse/dist/parse.min.js');
6	
7	export const QueryBasic: FC<{}> = (): ReactElement => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState<Parse.Object[]>();
10	
11	  const doQueryByName = async function (): Promise<boolean> {
12	    // Create our Parse.Query instance so methods can be chained
13	    // Reading parse objects is done by using Parse.Query
14	    const parseQuery: Parse.Query = new Parse.Query('Profile');
15	
16	    // `contains` is a basic query method that checks if string field
17	    // contains a specific substring
18	    parseQuery.contains('name', 'Adam');
19	
20	    try {
21	      let profiles: Parse.Object[] = await parseQuery.find();
22	      setQueryResults(profiles);
23	      return true;
24	    } catch (error: any) {
25	      // Error can be caused by lack of Internet connection
26	      alert(`Error! ${error.message}`);
27	      return false;
28	    }
29	  };
30	
31	  const doQueryByFriendCount = async function (): Promise<boolean> {
32	    // Create our Parse.Query instance so methods can be chained
33	    // Reading parse objects is done by using Parse.Query
34	    const parseQuery: Parse.Query = new Parse.Query('Profile');
35	
36	    // `greaterThan` is a basic query method that does what it
37	    // says on the tin
38	    parseQuery.greaterThan('friendCount', 20);
39	
40	    try {
41	      let profiles: Parse.Object[] = await parseQuery.find();
42	      setQueryResults(profiles);
43	      return true;
44	    } catch (error: any) {
45	      // Error can be caused by lack of Internet connection
46	      alert(`Error! ${error.message}`);
47	      return false;
48	    }
49	  };
50	
51	  const doQueryByOrdering = async function (): Promise<boolean> {
52	    // Create our Parse.Query instance so methods can be chained
53	    // Reading parse objects is done by using Parse.Query
54	    const parseQuery: Parse.Query = new Parse.Query('Profile');
55	
56	    // `descending` and `ascending` can and should be chained
57	    // with other query methods to improve your queries
58	    parseQuery.descending('birthDay');
59	
60	    try {
61	      let profiles: Parse.Object[] = await parseQuery.find();
62	      setQueryResults(profiles);
63	      return true;
64	    } catch (error: any) {
65	      // Error can be caused by lack of Internet connection
66	      alert(`Error! ${error.message}`);
67	      return false;
68	    }
69	  };
70	
71	  const doQueryByAll = async function (): Promise<boolean> {
72	    // Create our Parse.Query instance so methods can be chained
73	    // Reading parse objects is done by using Parse.Query
74	    const parseQuery: Parse.Query = new Parse.Query('Profile');
75	
76	    parseQuery.contains('name', 'Adam');
77	    parseQuery.greaterThan('friendCount', 20);
78	    parseQuery.descending('birthDay');
79	
80	    try {
81	      let profiles: Parse.Object[] = await parseQuery.find();
82	      setQueryResults(profiles);
83	      return true;
84	    } catch (error: any) {
85	      // Error can be caused by lack of Internet connection
86	      alert(`Error! ${error.message}`);
87	      return false;
88	    }
89	  };
90	
91	  const clearQueryResults = async function (): Promise<boolean> {
92	    setQueryResults(undefined);
93	    return true;
94	  };
95	
96	  return (
97	    <div>
98	      <div className="header">
99	        <img
100	          className="header_logo"
101	          alt="Back4App Logo"
102	          src={
103	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
104	          }
105	        />
106	        <p className="header_text_bold">{'React on Back4App'}</p>
107	        <p className="header_text">{'Basic Queries'}</p>
108	      </div>
109	      <div className="container">
110	        <div className="flex_between">
111	          <h2 className="heading">{'Query List'}</h2>
112	          <div className="flex">
113	            <Button
114	              onClick={() => doQueryByName()}
115	              type="primary"
116	              className="heading_button"
117	              color={'#208AEC'}
118	              icon={<SearchOutlined />}
119	            >
120	              BY NAME
121	            </Button>
122	            <Button
123	              onClick={() => doQueryByFriendCount()}
124	              type="primary"
125	              className="heading_button"
126	              color={'#208AEC'}
127	              icon={<SearchOutlined />}
128	            >
129	              BY FRIEND COUNT
130	            </Button>
131	            <Button
132	              onClick={() => doQueryByOrdering()}
133	              type="primary"
134	              className="heading_button"
135	              color={'#208AEC'}
136	              icon={<SearchOutlined />}
137	            >
138	              BY ORDERING
139	            </Button>
140	            <Button
141	              onClick={() => doQueryByAll()}
142	              type="primary"
143	              className="heading_button"
144	              color={'#208AEC'}
145	              icon={<SearchOutlined />}
146	            >
147	              BY ALL
148	            </Button>
149	            <Button
150	              onClick={() => clearQueryResults()}
151	              type="primary"
152	              className="heading_button"
153	              color={'#208AEC'}
154	              icon={<CloseOutlined />}
155	            >
156	              CLEAR
157	            </Button>
158	          </div>
159	        </div>
160	        <Divider />
161	        <div className="flex_between">
162	          <div className="flex_child">
163	            {/* Query list */}
164	            {queryResults !== undefined &&
165	              queryResults.map((profile: Parse.Object, index: number) => (
166	                <div className="list_item" key={`${index}`}>
167	                  <p className="list_item_title">{`${profile.get('name')}`}</p>
168	                  <p className="list_item_description">{`Friend count: ${profile.get(
169	                    'friendCount'
170	                  )}, Birthday: ${profile.get('birthDay')}`}</p>
171	                </div>
172	              ))}
173	            {queryResults !== undefined &&
174	            queryResults.length <= 0 ? (
175	              <p>{'No results here!'}</p>
176	            ) : null}
177	          </div>
178	        </div>
179	      </div>
180	    </div>
181	  );
182	};
```
:::

Also append these classes to your App.css file to properly render all the interface elements:

```css
1   html {
2     box-sizing: border-box;
3     outline: none;
4     overflow: auto;
5   }
6
7   *,
8   *:before,
9   *:after {
10    margin: 0;
11    padding: 0;
12    box-sizing: inherit;
13  }
14
15  h1,
16  h2,
17  h3,
18  h4,
19  h5,
20  h6 {
21    margin: 0;
22    font-weight: bold;
23  }
24
25  p {
26   margin: 0;
27  }
28
29  body {
30    margin: 0;
31    background-color: #fff;
32  }
33
34  .container {
35    width: 100%;
36    max-width: 900px;
37    margin: auto;
38    padding: 20px 0;
39    text-align: left;
40  }
41
42  .header {
43    align-items: center;
44    padding: 25px 0;
45    background-color: #208AEC;
46  }
47
48  .header_logo {
49    height: 55px;
50    margin-bottom: 20px;
51    object-fit: contain;
52  }
53
54  .header_text_bold {
55    margin-bottom: 3px;
56    color: rgba(255, 255, 255, 0.9);
57    font-size: 16px;
58    font-weight: bold;
59  }
60
61  .header_text {
62    color: rgba(255, 255, 255, 0.9);
63    font-size: 15px;
64  }
65
66  .heading {
67    font-size: 22px;
68  }
69
70  .flex {
71    display: flex;
72  }
73
74  .flex_between {
75    display: flex;
76    justify-content: space-between;
77  }
78
79  .flex_child {
80    flex: 0 0 45%;
81  }
82
83  .heading_button {
84    margin-left: 12px;
85  }
86
87  .list_item {
88    padding-bottom: 15px;
89    margin-bottom: 15px;
90    border-bottom: 1px solid rgba(0,0,0,0.06);
91    text-align: left;
92  }
93
94  .list_item_title {
95    color: rgba(0,0,0,0.87);
96    font-size: 17px;
97  }
98
99  .list_item_description {
100   color: rgba(0,0,0,0.5);
101   font-size: 15px;
102 }
```

This is how the component should look like after rendering and querying by all the query functions:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3yLOHR3ISkYFFvYif--Fe_image.png)

## 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 App. In the next guide, you will explore the Parse.Query full potential using all the methods available on this class.

[title] Untitled
[path] JS Framework/


[title] Install Parse SDK (ObjC)
[path] iOS/

## Install Parse SDK on your iOS Objective-C Project

## Introduction

In this section you will learn how to install Parse iOS SDK into your Xcode project.

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

In this tutorial we will use a basic app created in Objective-C with Xcode 9.1 and **iOS 11**.

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app created at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create an app at Back4App.
- Xcode.
- Basic iOS app.
  - **Note:**If you don’t have a basic app created you can open Xcode and hit **File-> New-> Project -> iOS**. Then select **App**. After you create your basic app you are ready to follow this guide.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cNLdHv0CpMyIVzG3ZJa_v_image.png)



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BVaGv4xYWoLZlA-aWy8Q6_image.png)
:::

:::hint{type="danger"}
**Note: **Parse iOS SDK works with iOS 7.0 or higher.
:::

## 1 - Install SDK

:::hint{type="info"}
Follow this step if you haven’t yet installed Parse iOS SDK.
:::

Xcode can use <a href="https://cocoapods.org/" target="_blank">CocoaPods</a> as dependency manager for Swift and Objective-C Cocoa projects.

You can refer to <a href="https://guides.cocoapods.org/using/getting-started.html" target="_blank">CocoaPods Getting Started Guide</a> for additional details.

To install CocoaPods, open your terminal, copy the following code snippet and paste it in to your terminal and hit return:

> $ sudo gem install cocoapods

CocoaPods should install automatically after you enter your password. If there’s a problem you may need to upgrade your local version of Ruby.

Next open up the Xcode Project folder and open a terminal window in that folder.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/leelNtzDVafWX_AkS5rwE_image.png)

Now you are going to create a Podfile. Copy the following code snippet and paste it in to your terminal and hit return:

> $ pod init

If your folder now shows your Podfile you did it correctly.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/V-VPF_KBpi5r6BnxTbH5B_image.png)

:::hint{type="danger"}
**Be careful, **If you don’t see the podfile make sure your terminal is actually inside the project folder..
:::

Next open your Podfile with Xcode or any text editor and under each target add “pod ‘Parse’”.

> pod 'Parse'

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/mhHx0L5FFRUqJvJL0ihxO_image.png)

Now you are going to add Parse to your project. Make sure your terminal is opened to your project folder. Copy the following code snippet and paste it in to your terminal and hit return:

> $ pod install

CocoaPods will rebuild the project as a workspace and your project will now look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gRP51g1lUdtePoOQcaaXh_image.png)

If you have already opened your Xcode project close it. From now on you’ll open the workspace file instead of the project file. Double click on the workspace file to open it.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WFWQGDFa8aopZ8nbeipCx_image.png)

Congratulations! You have now installed the Parse iOS SDK

## 2 - Connect your Parse App

1. Open your project’s AppDelegate.swift file to set up the app’s credentials.
2. Parse iOS SDK uses these settings to connect to the Back4App servers.
3. At the top of the file you should see a function called ‘didFinishLaunchingWithOptions’.
4. Paste the following code snippet inside this function, and make sure it is above the line that says ‘return true’.

> \[Parse initializeWithConfiguration:[ParseClientConfiguration configurationWithBlock:^(id<ParseMutableClientConfiguration> configuration) {
>         configuration.applicationId = @"PASTE_YOUR_APPLICATION_ID_HERE";
>         configuration.clientKey = @"PASTE_YOUR_CLIENT_ID_HERE";
>         configuration.server = @"https://parseapi.back4app.com/";
>     }]];

At the top of your AppDelegate.m file make sure to include Parse as a module by including the follwing code snippet right below ‘#import “AppDelegate.h”’.

> \#import <Parse/Parse.h>

Your AppDelegate.m file should now look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_mQQyeFPAgq7YoKyRJJP1_image.png)

:::hint{type="danger"}
**Be careful**, If Xcode tells you there is **No Such Module ‘Parse’** there’s an easy solution. In Xcode open ‘Target > Build Settings > Search Paths > Framework Search Paths’ and then add two values: ‘$(PROJECT\_DIR)’ and ‘$(inherited)’. Xcode will now be able to find your Parse module.
:::

1. Go to your App Dashboard at Back4App website.
2. Navigate to app’s settings: Click on Features>Core Settings block>Server
3. Return to your AppDelegate.m file and paste your applicationId and clientKey.

:::hint{type="success"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 3 - Test your connection

Open your AppDelgate.m file. Inside the function called ‘didFinishLaunchingWithOptions’ add a snippet of code below the code that configures parse.

>  \[self saveInstallationObject];

Then add a snippet of code to AppDelegate.m file just below ‘didFinishLaunchingWithOptions’to create a new parse installation object upon your app loading.

> -(void)saveInstallationObject{
>     PFInstallation *currentInstallation = [PFInstallation currentInstallation];
>     \[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>         if (!error) {
>             NSLog(@"You have successfully connected your app to Back4App!");
>         }else{
>             NSLog(@"installation save failed %@",error.debugDescription);
>         }
>     }];
> }
> }

Your finished AppDelegate.m file should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jW2_eqRvoii12BtgxOrrx_image.png)



1. Build your app in a device or simulator (Command+R).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tJ-6lAOYOkBloVSVg-2DX_image.png)

&#x20;    2\. Wait until the main screen appears.

&#x20;    3\. Login at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.

&#x20;    4\. Find your app and click on Dashboard.

&#x20;    5\. Click on Core.

&#x20;    6\. Go to Browser

If everything works properly, you should find a class named Installation as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/716sk5uPu9oUIfgV8wtKg_image.png)

## Next Steps

At this point, you have learned how to get started with iOS apps. You are now ready to explore [Parse Server core features](https://www.back4app.com/product/parse-server) and [Back4App add-ons](https://www.back4app.com/product/addons).

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Untitled
[path] Cloud Code Functions/


[title] Install Parse SDK (Swift)
[path] iOS/

# Install Parse SDK on your iOS Swift Project

## Introduction

In this section you will learn how to install Parse iOS SDK into your Xcode project.

In this tutorial we will use a basic app created in Swift with Xcode 12 and **iOS 14**.

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app created at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create an app at Back4App.
- Xcode.
- Basic iOS app.
  - **Note:**If you don’t have a basic app created you can open Xcode and hit **File-> New-> Project -> iOS**. Then select **App**. After you create your basic app you are ready to follow this guide.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cNLdHv0CpMyIVzG3ZJa_v_image.png)



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BVaGv4xYWoLZlA-aWy8Q6_image.png)
:::

## 1 - Install SDK

:::hint{type="info"}
Follow this step if you haven’t yet installed Parse iOS SDK.
:::

Xcode can use <a href="https://cocoapods.org/" target="_blank">CocoaPods</a> as dependency manager for Swift and Objective-C Cocoa projects.

You can refer to <a href="https://guides.cocoapods.org/using/getting-started.html" target="_blank">CocoaPods Getting Started Guide</a> for additional details.

To install CocoaPods, open your terminal, copy the following code snippet and paste it in to your terminal and hit return:

> $ sudo gem install cocoapods

CocoaPods should install automatically after you enter your password. If there’s a problem you may need to upgrade your local version of Ruby.

Next open up the Xcode Project folder and open a terminal window in that folder.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/77plfWhTus5KeDKC56q_n_image.png)

Now you are going to create a Podfile. Copy the following code snippet and paste it in to your terminal and hit return:

> $ pod init

If your folder now shows your Podfile you did it correctly.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LhXcmFLfeelgpS4LlZOpe_image.png)

:::hint{type="danger"}
**Be careful, **If you don’t see the podfile make sure your terminal is actually inside the project folder..
:::

Next open your Podfile with Xcode or any text editor and under each target add “pod ‘Parse’”.

> pod 'Parse'

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZvFhP0GtHEe5aNEda5sYY_image.png)

Now you are going to add Parse to your project. Make sure your terminal is opened to your project folder. Copy the following code snippet and paste it in to your terminal and hit return:

> $ pod install

CocoaPods will rebuild the project as a workspace and your project will now look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LGfkZP-mFwbeqG-NVXgOv_image.png)

If you have already opened your Xcode project close it. From now on you’ll open the workspace file instead of the project file. Double click on the workspace file to open it.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OH112Wi7wz45Ltv18RHh9_image.png)

Congratulations! You have now installed the Parse iOS SDK

## 2 - Connect your Parse App

1. Open your project’s AppDelegate.swift file to set up the app’s credentials.
2. Parse iOS SDK uses these settings to connect to the Back4App servers.
3. At the top of the file you should see a function called ‘didFinishLaunchingWithOptions’.
4. Paste the following code snippet inside this function, and make sure it is above the line that says ‘return true’.

:::CodeblockTabs
AppDelegate.swift

```swift
1   let configuration = ParseClientConfiguration {
2       $0.applicationId = "PASTE_YOUR_APPLICATION_ID_HERE"
3       $0.clientKey = "PASTE_YOUR_CLIENT_ID_HERE"
4       $0.server = "https://parseapi.back4app.com"
5   }
6   Parse.initialize(with: configuration)
```
:::

At the top of your AppDelegate.swift file make sure to include Parse as a module by including the follwing code snippet right below ‘import UIKit’.

:::CodeblockTabs
AppDelegate.swift

```swift
1    import Parse
```
:::

Your AppDelegate.swift file should now look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/I0m97xMzS-Npo_iDv3Lx7_image.png)

:::hint{type="danger"}
**Be careful**, If Xcode tells you there is **No Such Module ‘Parse’** there’s an easy solution. In Xcode open ‘Target > Build Settings > Search Paths > Framework Search Paths’ and then add two values: ‘$(PROJECT\_DIR)’ and ‘$(inherited)’. Xcode will now be able to find your Parse module.
:::

1. Go to your App Dashboard at Back4App website.
2. Navigate to app’s settings: Click on Features>Core Settings block>Server
3. Return to your AppDelegate.m file and paste your applicationId and clientKey.

:::hint{type="success"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 3 - Test your connection

Open your ViewController.swift file.

At the top of the file make sure to include Parse as a module by including the follwing code snippet right below ‘import UIKit’.

:::CodeblockTabs
ViewController.swift

```swift
1    import Parse
```
:::

Inside the function called ‘viewDidLoad’ add a snippet of code below the code that configures parse.

:::CodeblockTabs
ViewController.swift

```swift
1    testParseConnection()
```
:::

Then add a function below viewDidLoad() method.

:::CodeblockTabs
ViewController.swift

```swift
1    func testParseConnection(){
2           let myObj = PFObject(className:"FirstClass")
3           myObj["message"] = "Hey ! First message from Swift. Parse is now connected"
4           myObj.saveInBackground { (success, error) in
5               if(success){
6                   print("You are connected!")
7               }else{
8                   print("An error has occurred!")
9               }
10          }
11      }
12  }
```
:::

Your finished ViewController.swift file should look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xtSTRXfOrnG8GUo4is4ug_image.png)

1. Build your app in a device or simulator (Command+R).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/RrFlieDzH5i-2YuFu4PiC_image.png)

&#x20;    2\. Wait until the main screen appears.

&#x20;    3\. Login at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.

&#x20;    4\. Find your app and click on Dashboard.

&#x20;    5\. Click on Core.

&#x20;    6\. Go to Browser

If everything works properly, you should find a class named FirstClass as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/JXPuKH1LOmpW_8c3lCzHD_image.png)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/E-NDj5p0YUF_sFYNure7K_image.png)

## Next Steps

At this point, you have learned how to get started with iOS apps. You are now ready to explore [Parse Server core features](https://www.back4app.com/product/parse-server) and [Back4App add-ons](https://www.back4app.com/product/addons).

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Manual SDK integration
[path] iOS/

# Manual Parse Integration

## Introduction

In this section you will learn how to manually install Parse iOS SDK into your Xcode project.

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

In this tutorial we will use a basic app created in Objective-C with Xcode 9.1 and **iOS 11**.

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app created at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create an app at Back4App.
- Xcode.
- Basic iOS app.
  - **Note:**If you don’t have a basic app created you can open Xcode and hit **File-> New-> Project -> iOS**. Then select **App**. After you create your basic app you are ready to follow this guide.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cNLdHv0CpMyIVzG3ZJa_v_image.png)



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BVaGv4xYWoLZlA-aWy8Q6_image.png)
:::

:::hint{type="danger"}
**Note: **Parse iOS SDK works with iOS 7.0 or higher.
:::

## 1 - Download the SDK

Download [Parse’s latest version](https://github.com/parse-community/Parse-SDK-iOS-OSX/releases/latest) from Github.
Extract the file and keep the Parse.framework folder that you will be using soon.

## 2 - Download Bolts

Parse depends on [Bolts Framework](https://github.com/BoltsFramework) in order to work.
Download the latest version of Bolts, build it and keep the Bolts.framework folder.

If you have trouble building Bolts, you can download it pre-built from our website:

- [Download Bolts 1.8 here](https://www.back4app.com/docs/assets/downloads/Bolts_1.8.framework.zip)
- [Download Bolts 1.9 (Objective C) here](https://www.back4app.com/docs/assets/downloads/OBJC_Bolts_1.9.framework.zip)
- [Download Bolts 1.9 (Swift) here](https://www.back4app.com/docs/assets/downloads/Swift_Bolts_1.9.framework.zip)

Or if you prefer, we have the full projects running in Swift for IOS 13, Bolts 1.9 and Parse 1.17.3 here:

- [Swift IOS 13, Parse 1.17.3, bolts 1.9 WITH Cocoapods](https://www.back4app.com/docs/assets/downloads/Parse1.17.3_Bolts1.9_iOS13_WithPods.zip)
- [Swift IOS 13, Parse 1.17.3, bolts 1.9 WITHOUT Cocoapods](https://www.back4app.com/docs/assets/downloads/Parse1.17.3_Bolts1.9_iOS13_WithoutPods.zip)

## 3 - Add the Libraries to your Project

Parse relies in a number of libraries in order to work. Please add the following libraries to your project:

- **Parse.framework**
- **Bolts.framework**
- **libz.tbd**
- **libsqlite3.tbd**
- **Foundation.framework**
- **CFNetwork.framework**
- **SystemConfiguration.framework**
- **StoreKit.framework**
- **Security.framework**
- **QuartzCore.framework**
- **MobileCoreServices.framework**
- **CoreLocation.framework**
- **CoreGraphics.framework**
- **AudioToolbox.framework**

At the end, your project target must look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/g-y3ZZQbT1i2510p_gAhI_image.png" signedSrc size="50" width="504" height="612" position="center" caption}

## Next Steps

At this point, you have learned how to get started manually with iOS apps.

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] via Dashboard (ObjC)
[path] iOS/Send Push Notifications/

# Send iOS push notifications using Back4App - Objective-C

## Introduction

This section explains how you can send push notifications using Cloud Code through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OaAA5-WSsHA5Jb-pWGklu_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- A paid Apple Developer Account.
:::

## 1 - Create Your Push Certificates In The Apple Developer Center

:::hint{type="danger"}
Pay attention to the steps below because you need to get them right in the exact order. If pushes are not being received there isn’t much we can do to debug besides going over the steps again
:::



1. Go to the target and go to Capabilities - click on push notifications - then turn push notifications on. This creates your app id and sets your entitlements.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_GwawOnu-WdgjcvEr63aI_image.png)

&#x20;    2\. Go to the [Apple Developer Center](https://developer.apple.com/) and login to your account:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9wE9u-m0vkfpsZvoqf9Re_image.png)

&#x20;    3\. Click on Certificates, Identifiers & Profiles.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dxN0FXd_tUBjoaD6EZTo4_image.png)

&#x20;    4\. In the certificates section hit the plus sign. Choose to create a apple push notification certificate for sandboxes.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6R8uln7MpDHUgS5ddTayA_image.png)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ucHJ4WkNhrlWTJc0vRUXv_image.png)

&#x20;    5\. Pick your app id that matches the app id used in your current Xcode project.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2jveJXHlp-MV22CPDcnG8_image.png)

&#x20;    6\. Now you will be asked for a Certificate Signing Request or CSR. You will generate your CSR from your Mac computer.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bkk99AOgUS2fkD3tAzZrf_image.png)

&#x20;    7\. On your Mac computer open your Keychain Access.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/EPe-aVTybJoNJeQPiQp_1_image.png)

&#x20;    8\. Next, request a certificate from a certificate authority.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cf7mcKSSBIXK1anAdXYg0_image.png)

&#x20;    9\. Pick your user email, then make sure you save your certificate to disk - save it on a folder on your desktop called PushCerts.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/L6JwTQsXS8FMZMhQzrWCJ_image.png)

&#x20;     10\. Go back to the Apple Developer center. Upload your CSR and hit continue.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QF92X7rvMfbo4lZbCJyhN_image.png)

&#x20;    11\. Download your Development APN certificate into the same folder called PushCerts. Call it apn\_dev.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/01-12Kn8cR5VFkOnVZGkm_image.png)

&#x20;    12\. Let’s start the process over. This time we will create production push certificates. You need both for testing and release. Select Apple Push Notification Service SSL (Sanbox & Production).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kVXMtRExD6Z7QY4qKi6rp_image.png)

&#x20;    13\. Upload your CSR your previously created and hit continue.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Z6lPgccNEzTCUuShtfann_image.png)

&#x20;    14\.  Download your Production APN certificate into the same folder called PushCerts. Call it apn\_prod.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/n9vcomDVcWKhd12aGWRrY_image.png)

&#x20;    15\. At this point you should have 3 files in your PushCerts folder. Double click on your apn\_prod and your apn\_dev files to add them to your keychain.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2JXMV-9MfjrskIYdkouHr_image.png)

&#x20;      16\. Open the keychain and find the files in the keychain. Click on each on and hit export. You will want to export them as a .p12 file into your PushCerts Folder. Name the development one cert Dev\_PushCertificates.p12 and name the production cert as Prod\_PushCertificate.p12.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BBab-cCZ28y62-gd5bDFd_image.png)

&#x20;       17\. It will ask you add a password to your exported file. Just leave it blank. You will have to enter your master key to sign the certificate though, and thats fine.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HMEwrho_kZGim2f-5PW4m_image.png)

&#x20;      18\. Now that you have added your .p12 files your folder should look like this. If you have all these files in your folder you can go on to Step 2. If you don’t have any of these files go back to the beginning and figure out where you missed a step.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/i-YqeZKI8XofI4zCmKG2K_image.png)

## 2 - Adding Your .P12 certificates to Back4App

1. You’re almost done. Aren’t you excited? Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on iOS Push notification.

![](https://www.back4app.com/docs/assets/images/png/ios-push18.png)

&#x20;    2\. Upload the dev cert and the prod cert and hit send for each.

![](https://www.back4app.com/docs/assets/images/png/ios-push19.png)

&#x20;     3\. After you’ve uploaded both certificates your screen should look like this.

![](https://www.back4app.com/docs/assets/images/png/ios-push20.png)

## 3 - Setting up Your Xcode Project to receive Push Notifications

1. Open your project’s AppDelegate.M file to create a push installation object. Add the UserNotifications framework at the top of the file.

<a href="https://github.com/mpc20001/ios-objc-push-back4app/blob/master/AddingParseSDKObjc/AppDelegate.m#L11" target="_blank">AppDelegate.m</a>

> \#import <UserNotifications/UserNotifications.h>

&#x20;     2\. Add the code below inside of the didFinishLaunchingWithOptions function, and make sure it is before ‘return true’ statement.

<a href="https://github.com/mpc20001/ios-objc-push-back4app/blob/master/AddingParseSDKObjc/AppDelegate.m#L27" target="_blank">AppDelegate.m</a>

>  \[self registerForRemoteNotifications];

&#x20;     3\. Add the following code snippets to your AppDelegate.m file below the didFinishLaunchingWithOptions function. This code will issue a request for push notifications permissions when the app first launches. Make sure to say yes to this request or your app will not be able to receive pushes. It will also handle the resulting token when the request is approved and save it as an installation object on Back4App.

<a href="https://github.com/mpc20001/ios-objc-push-back4app/blob/master/AddingParseSDKObjc/AppDelegate.m#L31-L55" target="_blank">AppDelegate.m</a>

> \- (void)registerForRemoteNotifications {
>  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
>  \[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge |     UNAuthorizationOptionCarPlay) completionHandler:^(BOOL granted, NSError * _Nullable error){
>      if(!error){
>          dispatch_async(dispatch_get_main_queue(), ^{
>              \[[UIApplication sharedApplication] registerForRemoteNotifications];
>          });
>      }else{
>          NSLog(@"%@",error.description);
>      }
>  }];
> }
> \- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
>  // Store the deviceToken in the current Installation and save it to Parse
>  PFInstallation *currentInstallation = [PFInstallation currentInstallation];
>  \[currentInstallation setDeviceTokenFromData:deviceToken];;
>  \[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>      if (!error) {
>          NSLog(@"installation saved!!!");
>      }else{
>          NSLog(@"installation save failed %@",error.debugDescription);
>      }
>  }];
> }

&#x20;     4\. Test it by running your app. You should see this in your simulator.

::Image[]{src="https://www.back4app.com/docs/assets/images/png/ios-push21.png" signedSrc="https://www.back4app.com/docs/assets/images/png/ios-push21.png" size="60" width="562" height="1000" position="center" caption}

&#x20;    5\. From here on out you must use a physical device, an iphone or ipad. Push notifications do not work with the Xcode simulator. If you do not have a physical device you cannot go any further in the tutorial. Once you have your physical device attached to your Mac computer and Xcode, try running the app on your device through Xcode. When you see the push permissions request hit approve.

## 4 - Test your app

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website </a>log in, find your app and click on Dashboard.
2. First check that your device’s installation record is visible in Installation table.

![](https://www.back4app.com/docs/assets/images/png/ios-push22.png)

&#x20;    3\. Then Click on Push > Send New Push and create an audience for your push notification.

![](https://www.back4app.com/docs/assets/images/png/push-dashboard.png)

&#x20;   4\. Write your message and look at the preview by clicking at iOS option.

&#x20;   5\. If you have already reviewed the push notification and you want to send, click on Send push.

![](https://www.back4app.com/docs/assets/images/png/send-push.png)

:::hint{type="info"}
You may explore the other options for Push Notification at Parse Dashboard.&#x20;

There, it’s also possible to look at Past Pushes you sent and the Audiences you created for them.
:::

## It’s done!

At this stage, you can send push notifications using Parse Dashboard through Back4App!

[title] Twilio
[path] Cloud Code Functions/Integrations/

# Using cloud functions and Twilio API to send Text Message

## Introduction

This guide explains how you can use the Twilio REST API to send SMS. After completing this step-by-step tutorial, you can use your cloud code function to send SMS to your device.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:




- An app created at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Back4App Command Line Configured with the project.
- Follow the <a href="https://www.back4app.com/docs/local-development/parse-cli" target="_blank">Setting up Cloud Code tutorial</a> to learn how to set up cloud code for a project.
- Account created in <a href="https://login.twilio.com/u/signup?state=hKFo2SBSTHoyUXZ1NnUzTjZaMHk2Q3EtaXV5LUtnUlhaMzdtN6Fur3VuaXZlcnNhbC1sb2dpbqN0aWTZIE9JTzFKSU9LaVMzTV9fYVo0VDM0a2pnSEpZM09SYUo4o2NpZNkgTW05M1lTTDVSclpmNzdobUlKZFI3QktZYjZPOXV1cks" target="_blank">Twilio</a>.
:::

## Let’s get started!

Below are some steps you need to follow when writing a function to send SMS to a User and phone number.

To learn how to create or access an Account in Twilio, check the links given below:

<a href="https://www.twilio.com/try-twilio" target="_blank">Create a new account</a> - <a href="https://www.twilio.com/login" target="_blank">Log in to your account</a>

## 1 - Activate your Phone Number

After logging in or creating a new account, you will be redirected to your Project. There, on the left, you need to click on the #Phone Numbers. Next, tap on the last link ‘Getting Started’, and then click on the button ‘Get your first Twilio phone number’. Same as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zWncYRIV74NXGLPTimCVF_image.png)

After that you will receive your first Phone Number for your Twilio Account. If you can’t find your phone number, go to #Phone Numbers and Manage Numbers.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/z11IgD_ew9-O-CQxq67t3_image.png)

## 2 - Get Account SID and Auth Token

To find your Account SID and Auth Token, log in to your Account, go to your Dashboard and click on Settings. All the important information about your Project will be available in that section; as shown in the image below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fBwxRTxumzqViO6-afJb6_image.png" signedSrc size="70" width="450" height="425" position="center" caption}

Now, you can Copy your SID and Authentication Token for the Cloud Code.

## 3 - Install Module from Twilio

After configuring the environment for the Command Line Interface in your computer, create a file called package.json, and inside this file, you need to install the Twilio module, like:

```json
1   {
2     "dependencies": {
3       "twilio": "*"
4     }
5   }
```

## 4 - Implement Cloud Code

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.define("SendSMS", async(request) => {
2
3	   // Requiring the values to send
4	   let
5		   getMessage = request.params.message,
6		   getPhoneTo = '+Target test Phone number',
7		   getPhoneFrom = "+Your first Phone number",
8		   accountSid = 'AccountSID',
9		   authToken  = 'AuthToken';
10
11	  //require the Twilio module and create a REST client
12	  let client = require('twilio')(accountSid, authToken);
13
14	  return await client.messages
15		  .create({
16		  body: getMessage, // Any number Twilio can deliver to
17		  from: getPhoneFrom, // A number you bought from Twilio and can use for outbound communication
18		  to: getPhoneTo // body of the SMS message
19	  });
20  });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define("SendSMS",function(request,response){
2
3	   // Requiring the values to send
4	   var
5		   getMessage = request.params.message,
6		   getPhoneTo = '+Target test Phone number',
7		   getPhoneFrom = "+Your first Phone number",
8		   accountSid = 'AccountSID',
9		   authToken  = 'AuthToken';
10
11
12	  //require the Twilio module and create a REST client
13	  var client = require('twilio')(accountSid, authToken);
14
15	  client.messages
16	  .create({
17		  body: getMessage, // Any number Twilio can deliver to
18		  from: getPhoneFrom, // A number you bought from Twilio and can use for outbound communication
19		  to: getPhoneTo // body of the SMS message
20	  })
21	  .then(function(results) {
22		  response.success(results.sid);
23	  })
24	  .catch(function(error) {
25		  response.error(error);
26	  })
27  });
```
:::

## 5- Test the function “sendSMS”

You can also test the function in client SDKs, but for now, we will use the REST API command to send it:

```curl
curl -X POST \
  	-H "X-Parse-Application-Id: APP_ID" \
  	-H "X-Parse-REST-API-Key: REST_KEY" \
  	-H "Content-Type: application/json" \
  	-d '{ "message": "Now, I can send SMS from Cloud Code using Twilio", "phone": "+Target test Phone number" }' \
https://parseapi.back4app.com/functions/SendSMS
```

And the result will be something like as this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fUVk4Kb6xIhjLpQHTvmgV_image.png" signedSrc size="60" width="500" height="889" position="center" caption}

## 6 - It’s done!

With the guide described above, you’ll be able to use Twilio with a Cloud Code Function in Back4App and send SMS to your customers!

In case you need any help or a function/link doesn’t work, please contact our team via chat!

[title] Start from template
[path] JS Framework/Ionic/

# Start your Ionic project from an App Template

## Introduction

In this section you learn how to get started with an Ionic template and get ready to use Back4App in 4 steps.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://code.visualstudio.com/download" target="_blank">Visual Studio Code </a>(or any web IDE you like).
- <a href="https://ionicframework.com/getting-started/" target="_blank">Ionic Framework </a>
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
:::

## 1 - Get the template

1. Download the template at our
   <a href="https://github.com/back4app/ionic-quickstart-example" target="_blank">GitHub repository</a>, and unzip files in your project folder. You can do that using the following command line:

> $ curl -LOk https://github.com/back4app/ionic-quickstart-example/archive/master.zip && unzip master.zip

&#x20;    2\. Navigate to the extracted folder and install the depedencies using the following command line:

> $ cd ionic-quickstart-example-master && npm install

## 2 - Open the project template

1. Open Visual Studio Code.
2. Click on Open folder.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2wqiTLb6yOllAInN6hwtU_image.png)

&#x20;   3\. Navigate to the project folder and click on OK.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7gK4FOarlAUNlzxZjsWcK_image.png)

&#x20;    4\. Wait for Visual Studio to open.

## 3 - Set up app’s credentials

Update your strings values to set up the app’s credentials. Parse Javascript SDK uses these settings to connect to the Back4App servers. In order to do that, follow these steps:

1. Open your home typescript file:.../src/pages/home/home.ts

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aNZ35hroSIRh5FjtO8cgu_image.png)

&#x20;     2\. Go to your App Dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.

&#x20;     3\. Navigate to app’s settings: Click on Server Settings > Core Settingsblock > SETTINGS.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cZKaNzJ7Ph1a5HSqsqQGb_image.png)

&#x20;       4\. Return to your home.ts file and paste your App Id and JavaScript Key.

:::hint{type="info"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App guide</a>.
:::

## 4 - Testing your connection

1. Run your app on your browser.

> $ ionic serve

&#x20;    1\. Wait until a new tab opens on your browser.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HkOhB4Y0daIJcEi_InwSV_image.png)

:::hint{type="success"}
**I**n order to see the page in a phone frame, press F12.
:::

&#x20;    2\. Login at [Back4App Website](https://www.back4app.com/).

&#x20;    3\. Find your app and click on Dashboard.

&#x20;    4\. Click on Core.

&#x20;    5\. Go to Browser.

If everything works properly, you should find a class named Installation as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CKxmI6Y-djmpQBGVYjqMJ_image.png)

### It’s done!

At this point, you have learned how to get started with Ionic apps.

[title] Sign-up/Sign-in - ObjC
[path] iOS/

## Login and User registration tutorial using XCode and Back4App

### Introduction

This section explains how you can create an app with a simple user registration using <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-oR4uTmXHkc1iW15BS3Kq_image.png" signedSrc size="30" width="974" height="1602" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-objc-sdk" target="_blank">Install Parse SDK (ObjC) Tutorial</a> to create an Xcode Project connected to Back4App.
- A paid Apple Developer Account.
:::

## 1 - Set up

1. Add another view controller called LoggedInViewController. In the main storyboard drag a view controller on to the canvas and set the class to LoggedInViewController and set the Storyboard ID to LoggedInViewController
2. In both ViewController.m and LoggedInViewController.m make sure to include the Parse module by including it at the top of the file.

> \#import <Parse/Parse.h>

## 2 - Create your Sign Up and Login UI

Logging in creates a Session object, which points to the User logged in. If login is successful, *ParseUser.CurrentUser()* returns a User object, and a Session object is created in the Dashboard. Otherwise, if the target username does not exist, or the password is wrong, it returns null.

The method used to perform the login action is *ParseUser.logInWithUsername()*, which requires as many arguments as the strings of username and password, and may call a callback function.

:::hint{type="info"}
**Note: **After signing up, login is performed automatically.
:::



1. Drag four UITextFields onto the ViewController in the main storyboard. Center the textfield and put two at the top and two at the bottom of the view controller. Drag two more UIButtons on to the view and place them below the textfields. Set the top button text to say Sign In. Set the bottom bottom to say Sign Up. Set the text fields to say username and password. It should look like this.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cgQDT44MUapwapprPY2KA_image.png" signedSrc size="30" width="974" height="1602" position="flex-start" caption}

1. 2\. Next we are going to connect your UITextFields in your storyboard to properties in your view controller. Add the following properties to the top of ViewController.m. Next go to your storyboard and right click on each UITextField and click on the reference outlet then drag a line back to the ViewController icon and set it to the appropriate field. signInUsernameField connects to the sign In Username Field, etc… Finally we will add a UIActivityIndicatorView for later.

> @interface ViewController (){
>  IBOutlet UITextField * signInUserNameTextField;
>  IBOutlet UITextField * signInPasswordTextField;
>  IBOutlet UITextField * signUpUserNameTextField;
>  IBOutlet UITextField * signUpPasswordTextField;
>  UIActivityIndicatorView *activityIndicatorView;
> }

&#x20;    2\. Then in the viewDidLoad method set the UIActivityIndicatorView to be attached to the middle of the screen.

> \- (void)viewDidLoad {
>  \[super viewDidLoad];
>  CGRect frame = CGRectMake (120.0, 185.0, 80, 80);
>  activityIndicatorView = [[UIActivityIndicatorView alloc] initWithFrame:frame];
>  activityIndicatorView.color = [UIColor blueColor];
>  \[self.view addSubview:activityIndicatorView];
> }

&#x20;    3\. Then in the viewDidAppear method check to see if you are already logged in. If you are logged in you will redirect the user to the LoggedInViewController.

> \- (void)viewDidAppear:(BOOL)animated {
>  if ([PFUser currentUser]) {
>      \[self goToMainPage];
>  }
> }

&#x20;    4\. Next lets add the goToMainPage method. It will redirec the user to the LoggedInViewController. Make sure the that the LoggedInViewController in the storyboard has its class and Storyboard ID set to LoggedInViewController.

>  \- (void)goToMainPage {
>  NSString * storyboardName = @"Main";
>  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
>  LoggedInViewController * vc = [storyboard instantiateViewControllerWithIdentifier:@"LoggedInViewController"];
>  \[self presentViewController:vc animated:YES completion:nil];
> }

&#x20;     5\. Now lets set up the IBAction that will connect to the SignUp button on the ViewController in the main storyboard.

> \- (IBAction)signUp:(id)sender {
>  PFUser *user = [PFUser user];
>  user.username = signUpUserNameTextField.text;
>  user.password = signUpPasswordTextField.text;
>  \[activityIndicatorView startAnimating];
>  \[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>      \[self->activityIndicatorView stopAnimating];
>      if (!error) {
>          \[self goToMainPage];
>      } else {
>          \[self displayMessageToUser:error.localizedDescription];
>      }
>  }];
> }

&#x20;     6\. We need to add the displayErrorMessage function to show any error messages from the server. We will use this method anytime we communicate with our parse app.

> \- (void)displayMessageToUser:(NSString*)message {
>  UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Message"
>                                                                 message:message
>                                                          preferredStyle:UIAlertControllerStyleAlert];
>  UIPopoverPresentationController *popPresenter = [alert popoverPresentationController];
>  popPresenter.sourceView = self.view;
>  UIAlertAction *Okbutton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
>
>  }];
>  \[alert addAction:Okbutton];
>  popPresenter.sourceRect = self.view.frame;
>  alert.modalPresentationStyle = UIModalPresentationPopover;
>  \[self presentViewController:alert animated:YES completion:nil];
> }

&#x20;      7\. Now that we can handle network activity and network errors lets set up the IBAction that will connect to the SignIn button on the ViewController in the main storyboard.

> \- (IBAction)signUp:(id)sender {
>  PFUser *user = [PFUser user];
>  user.username = signUpUserNameTextField.text;
>  user.password = signUpPasswordTextField.text;
>  \[activityIndicatorView startAnimating];
>  \[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>      \[self->activityIndicatorView stopAnimating];
>      if (!error) {
>          \[self goToMainPage];
>      } else {
>          \[self displayMessageToUser:error.localizedDescription];
>      }
>  }];
> }

## 3 - Log Out

Logging out deletes the active Session object for the logged User. The method used to perform log out is *ParseUser.logOutInBackgroundWithBlock()*.

1. Drag a UIButton on to LoggedInViewController in the main storyboard. Set the button title to ‘Logout’. It should look like this.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/S0ZDJ3xgypXJ7bzVazxRm_image.png" signedSrc size="30" width="974" height="1602" position="flex-start" caption}

1. Lets add the displayErrorMessage function again to show any error messages from the server. We will use this method anytime we communicate with our parse app.

> \- (void)displayMessageToUser:(NSString*)message {
>  UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Message"
>                                                                 message:message
>                                                          preferredStyle:UIAlertControllerStyleAlert];
>  UIPopoverPresentationController *popPresenter = [alert popoverPresentationController];
>  popPresenter.sourceView = self.view;
>  UIAlertAction *Okbutton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
>
>  }];
>  \[alert addAction:Okbutton];
>  popPresenter.sourceRect = self.view.frame;
>  alert.modalPresentationStyle = UIModalPresentationPopover;
>  \[self presentViewController:alert animated:YES completion:nil];
> }

&#x20;    2\. Lets add the goToStartPage function to take us back to the login/signup screen after we logout.

> \- (void)goToStartPage {
>  NSString * storyboardName = @"Main";
>  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
>  ViewController * vc = [storyboard instantiateViewControllerWithIdentifier:@"ViewController"];
>  \[self presentViewController:vc animated:YES completion:nil];
> }

&#x20;   3\. Finally lets add the IBAction to execute the logout call and take us back to ViewController.m signup/login page. This method logs out the PFUser and takes you back to the signup page. Connect this IBAction to the logout button on LoggedInViewController.

>  \- (IBAction)logout:(id)sender {
>  \[activityIndicatorView startAnimating];
>  \[PFUser logOutInBackgroundWithBlock:^(NSError * _Nullable error) {
>      \[self->activityIndicatorView stopAnimating];
>      if(error == nil){
>          \[self goToStartPage];
>      }else{
>          \[self displayMessageToUser:error.debugDescription];
>      }
>  }];
> }

## 4 - Test your app

1. Run your app and create a couple of users, also try logging in again after registering them.
2. Login at [Back4App Website](https://www.back4app.com/)
3. Find your app and click on Dashboard>Core>Browser>User.
4. Try logging in and out with the same user and signing back in.

At this point, you should see your users as displayed below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tCMf5EqkqlpQbFhoyL5XT_image.png)

:::hint{type="info"}
**Note**: Using the codes displayed above, every time you log in with a user, a Session is opened in your Dashboard, but when the user logs out that particular Session ends. Also, whenever an unsuccessful login or sign up attempt occurs, the Session opened in Parse Server Dashboard is deleted.
:::

## It’s done!

At this stage, you can log in, register or log out of your app using Parse Server core features through Back4App!

[title] Install SDK
[path] JS Framework/Ionic/

# Start your Ionic project using a pre built template

## Introduction

In this section you learn how to install Parse JavaScript SDK into your Ionic project.

:::hint{type="success"}
See more about Parse SDK at <a href="https://parseplatform.org/Parse-SDK-JS/api/4.3.1/" target="_blank">Parse JavaScript SDK API Reference</a> and <a href="https://docs.parseplatform.org/js/guide/" target="_blank">Parse open source documentation for JavaScript SDK</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial </a>to learn how to create a Parse App at Back4App.
- An Ionic project started.
  - Follow the <a href="https://ionicframework.com/docs/intro/cli" target="_blank">Getting Started </a>tutorial if you don’t have it set up.
:::

## 1 - Install SDK

Since Ionic packages are managed by npm, all you have to do is to run the following command at your project folder level:

> $ npm install parse

## 2 - Connect your Parse App

Import Parse in home.ts or in the page you want to make use of:

:::CodeblockTabs
home.ts

```typescript
1   import Parse from 'parse';
```
:::

UseParse.serverURL atrribute to set up the url for Back4app parse server.

:::CodeblockTabs
home.ts

```typescript
1   Parse.serverURL = 'https://parseapi.back4app.com/';
```
:::

UseParse.initialize method to set up the authentication token, connecting your page with Back4App servers.

:::CodeblockTabs
home.ts

```typescript
1   Parse.initialize("YOUR-APP-ID", "YOUR-JS-KEY");
```
:::

**Find your Application ID and your Client Key**

1. Go to your App Dashboard at Back4App website.
2. Navigate to app’s settings: Click on Server Settings > Core Settingsblock > Settings.
3. Return to your Parse.Initialize function and paste your applicationId and javaScriptKey.

## 3 - Test your conection

**Create a test code**

Test your initial setup with the following code which creates an Installation object.

First, go ahead and create a variable to show the result on your app Home page.

:::CodeblockTabs
home.ts

```typescript
1   result: string;
```
:::

Next, display that variable on your Home view.

:::CodeblockTabs
home.html

```html
1   <ion-content padding>
2     <h4>{{result}}</h4>
3   </ion-content>
```
:::

Finally, add the code that instanciates an Installation object and saves it.

Add this piece of code after setting up the communication.

:::CodeblockTabs
home.ts

```typescript
1     let install = new Parse.Installation();
2
3     install.save(null, {
4       success: (install) => {
5         // Execute any logic that should take place after the object is saved.
6         this.result = 'New object created with objectId: ' + install.id;
7       },
8       error: (install, error) => {
9         // Execute any logic that should take place if the save fails.
10        // error is a Parse.Error with an error code and message.
11        this.result = ('Failed to create new object, with error code:' + error.message.toString());
12     }
13   });
```
:::

1. Run your app on your browser.

> $ ionic serve

&#x20;    2\. Wait until a new tab opens on your browser.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bbUt7hk5KCSjQm23LtqNH_image.png)

:::hint{type="success"}
**I**n order to see the page in a phone frame, press F12.
:::

3\. Login at [Back4App Website](https://www.back4app.com/)

4\. Find your app and click on Dashboard.

5\. Click on Core.

6\. Go to Browser.



If everything works properly, you should find a class named Installation as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/D-xCV3dRfrg3uRgGTcMru_image.png)

## It’s done!

At this point, you have learned how to get started with Ionic apps.

[title] Parse Migration
[path] App Migration/

# How can I migrate my Parse Server App to Back4App?

## Introduction

In this guide, you will learn how to migrate an existing Parse App to Back4App. Before moving your App, you’ll see how to test your Database, files, and cloud functions running on Back4App servers. Once validated, you will be ready to finish the migration and start using Back4App as your Parse provider. If you prefer to migrate using the CLI migration tool, please check [this guide](https://www.back4app.com/docs/app-migration/parse-cli-migration).

## Goal

Migrate a Parse App to Back4App servers.

## 1 - Create a New Back4App App

The first step is to create a Back4App App where you will migrate your current App. Check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Connect Back4App to your current Database

Go to the database section of your current hosting account’s control panel and copy your [connection string](https://docs.mongodb.com/manual/reference/connection-string/#standard-connection-string-format). Now you’ll use it to connect the Back4App Parse Server with your existing Database.

Open your Back4App Dashboard, go to the Server Settings > Core Settings > Settings, and then scroll to the bottom of the page to spot shown in the image:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_xNP4IArhoUyHEnby2U0-_image.png" signedSrc size="70" width="450" height="290" position="center" caption}

Next, click on the button Edit details, go to the Connection String, and click on the Change database URI link. By doing so, you will be able to update the connection string with the new copied value.

## 3 - Upload your Cloud Code

If your App has Cloud Code files, you will need to deploy them on Back4App to ensure that all the files are working correctly.

You can deploy your Cloud Code files in batch using the Back4App CLI ([Command Line Interface](https://www.back4app.com/docs/command-line-tool/parse-server-setup)) tool or manually using the Dashboard under Cloud Code Functions.

After uploading them, you can check the logs available at Server Settings > Logs > Settings > Server System Log.

## 4 - Migrate your Parse Files

If your App has files (images, documents, videos), you’ll need to migrate them to the Back4App (we are using Amazon AWS S3 as file storage). The process of migrating your files is manual. You should compact all the objects into a zip file or within your S3 bucket and contact us (at community\@back4app.com) to help you deploy them on Back4App servers.

:::hint{type="warning"}
**Note**: If you have files saved directly on the database please, let us know. The process is different in this case.
:::

## 5 - Updating your frontend

Now, you need to test if all your App features are working when connected to Back4App servers. Go to your frontend code and update your App keys and server URL of your App. Test all your app features to make sure that the migration has worked. You can now release a new App version to your users.

In this scenario, you will have two versions of your App: a new one with Back4App and the old one that uses your previous Parse hosting.

- The non-updated users will still be pointing to your “old” API;
- The updated users will be pointing to Back4app API;

You have to choose the best time to finish the migration and move your Database definitely to Back4App.

## 6 - To finish the process

It’s time to migrate your Database. We recommend proceeding with this step after making sure most of your users are using your Back4App App version and successfully concluding all the previous migration steps.

Now you can migrate the Database by yourself using the Back4App migration CLI or open a ticket on our [support channel](https://www.back4app.com/support), so we can do it for you.

If you decide to open a ticket, please provide your previous Parse hosting connection string and your Back4App App ID.

## It’s done!

With the guide described above, you’ll be able to migrate your Parse App to the Back4App and work with our various full-fledged, excellent features!

In case you need any help or a link doesn’t work, please [contact our team via chat](https://www.back4app.com/support)!

[title] Upgrade to Parse 3.1
[path] Advanced Guides/

# More power to your App with Parse Server 3.1

## Introduction

The Parse Community recently released version [3.1 of the Parse Server](https://docs.parseplatform.org/parse-server/guide/). This update has cleaned up Cloud Code syntax: it is far more amenable to leveraging the es6 async and await constructs.

Additionally, some idiosyncrasies associated with using Parse were dropped, for example, Cloud functions simply return a Promise rather than using the error or success messages on the Response object.

You can upgrade your apps on Back4App easily on your Dashboard. This guide will demonstrate how to upgrade your code to leverage the new features of 3.1.

:::hint{type="info"}
To follow this guide you are welcome to take a look at the <a href="https://github.com/back4app/parse-server-break-changes" target="_blank">example project</a> provided.
:::

This is a guest tutorial written by [John Considine](https://github.com/considine), lead developer at [K-Optional](https://koptional.com/).

## Goals

- To update your Back4App Parse Server to 3.1 and migrate your Cloud Code accordingly.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you need:

° An existing Back4App application that’s using Parse Server 2.x
&#x20;        °    Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## Summary of changes

The most notable changes are the following:

** 1. Cloud Code runs with Parse-SDK 2.x.**&#x20;

Previously, Cloud Code ran with Parse-SDK 1.x. With Parse Server 3.1, it runs Parse-SDK 2.x.

:::hint{type="info"}
Look at <a href="https://github.com/parse-community/Parse-SDK-JS/releases" target="_blank">Parse SDK releases</a> to understand better what this entails. This major version bump mostly involves bug fixes. It also adds <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.Query.html#containedBy" target="_blank">containedBy</a> and <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.Query.html#includeAll" target="_blank">includeAll</a> query methods, as well as abilities to fetch an object with includes.
:::

&#x20;**2**. **Aggregate update**

Since Parse 2.7.1, you can use the <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.Query.html#aggregate" target="_blank">aggregate</a> method on a query. This allows you to leverage the underlying database a little bit more. Now, the syntax for the <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.Query.html#aggregate" target="_blank">aggregate method</a> on Parse.Query has been updated.

You can execute a query using two stages: the match and the group stage.

> 1    // matches users whose name are Foo, and groups by their objectId
> 2    const pipeline = [{ group: { objectId: {} } }, { match: { name: 'Foo' } }];
> 3
> 4    var query = new Parse.Query("Person");
> 5    query.aggregate(pipeline)

Previously, you didn’t need the pipeline key in the pipeline object. Due to the underlying API, you must now explicitly include the pipeline key. The value should be an array of one or two stages, featuring group and match.

:::hint{type="info"}
Look at <a href="https://docs.parseplatform.org/js/guide/#aggregate" target="_blank">Parse Official Documentation</a> for more specific examples.
:::

**3**. **Under-the-hood optimizations**

Some under-the-hood optimizations have been made. For example, a <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.LiveQuery.html" target="_blank">Parse LiveQuery</a> will fetch Class Level Permissions (CLPs) along with data to prevent double database access.

**4. Parse Reset Email**

When requesting a password reset email, the server will return success even if that email is not saved. Additionally, password reset tokens expire when a user’s email is reset.

**5.** **Cloud triggers update**

With this release, you can share data between the <a href="https://docs.parseplatform.org/cloudcode/guide/#beforesave-triggers" target="_blank">beforeSave</a> and <a href="https://docs.parseplatform.org/cloudcode/guide/#aftersave-triggers" target="_blank">afterSave</a> triggers on the same object. For example:

```javascript
1    Parse.Cloud.beforeSave('Comment', async request => {
2      request.context = {
3        foo: 'bar'
4      };
5    });
6
7    Parse.Cloud.afterSave('Comment', async request => {
8      console.log(request.context.foo); //Bar
9    });
```

:::hint{type="info"}
You can see more about changes on Parse Server in the official Parse 3.1 Changelog by clicking<a href="https://github.com/parse-community/parse-server/blob/alpha/CHANGELOG.md" target="_blank"> here</a>.
:::

**6. LiveQuery Improvement**

The Parse LiveQuery client allows you to subscribe to queries, and receive updates from the server as they come in. Traditional Queries are executed once by the client, so this is very helpful for cases like messaging, etc.

With Back4App you can also [take advantage of this technology](https://www.back4app.com/docs/platform/parse-server-live-query-example).

With the release of 3.x, the Parse community has improved the system for LiveQuery [ACLs](https://docs.parseplatform.org/js/guide/#security-for-other-objects).

You can pass a session token now to the subscribe method of a live query, and the Parse Server will manage only returning results that this user has access to. For example, a user may have read/write access to certain ‘Messages’, but not all.

```javascript
1   let query = new Parse.Query('Message');
2   // you can get session token with
3   // Parse.User.current().getSessionToken() when logged in
4   let subscription = client.subscribe(query, sessionToken); 
```

The above code will automatically subscribe to all messages that the user has access to, relieving you from the responsibility of querying specific messages.

**The main part of the changes pertains to how Cloud Code is handled. For this, see the migration guide below.**

## 1 - Aligning technical fundamentals

We’ll start with an example cloud project using the 2.x release. That way we can navigate through the appropriate changes of syntax. You can see <a href="https://github.com/back4app/parse-server-break-changes" target="_blank">the repository</a> for this example project.

**If you’re familiar with async and await, you can skip this section.**

The evolution of asynchronous code in Javascript looks something like this:

- <a href="https://developer.mozilla.org/en-US/docs/Glossary/Callback_function" target="_blank">Callback Functions</a>
- <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank">Promises</a>
- <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function" target="_blank">Async / Await</a>

Any modern Javascript application will likely use all three. Callback functions involve passing a function as an argument to another function. This second function can execute the first at some point.

```javascript
1   // Callbacks:
2   // executes callback function after waiting 100 milliseconds
3   setTimeout(function() {
4     alert('My callback function');
5   }, 100);
```

Callbacks are essential but can be unwieldy when it comes to chaining many of them. Specifically, nesting several layers can be difficult to read, and error handling proves difficult. Hence in ES2015 the Promise was introduced.

```javascript
1   // promises
2   // executes several promises in a row with no significant nesting
3   const myPromise = new Promise(function(resolve, reject) {
4     setTimeout(function() {
5        if (Math.random() < 0.2) reject(new Error('Random failure!'));
6        resolve('Finished');
7     }, 100);
8   });
9
10  // Executes this promise 4 times and catches errors involved
11  myPromise
12    .then(() => myPromise)
13    .then(() => myPromise)
14    .then(() => myPromise)
15    .then(() => myPromise)
16    .catch(e => console.log(e));
```

Promises improve the readability of asynchronous programming. They also make pipelines more explicit. But even bigger strides were made in <a href="https://www.ecma-international.org/ecma-262/8.0/#sec-async-function-definitions" target="_blank">ES2017</a> with the async / await constructs. Your code can now wait for the results of a Promise without relying on the then / catch blocks (which can also become tough to read).&#x20;

```javascript
1   // Using the definition of myPromise from the above code:
2   async function foo() {
3     try {
4       let result = await myPromise;
5       result = await myPromise;
6       result = await myPromise;
7     } catch (e) {
8       console.log(e);
9     }
10  }
```

Perhaps for a very simple example, this may not seem more elegant than plain promises. But awaiting the results of an asynchronous function is often precisely what we want to do. Hence, it truly optimizes the readability of our code.

**Support for async/await**

As aforementioned, async/await was included in the <a href="https://www.ecma-international.org/ecma-262/8.0" target="_blank">ECMAScript 2017 specification</a> (es8). For server code, versioning is hardly an issue since you can update to <a href="https://node.green/#ES2017" target="_blank">the version of Node.js that supports these features</a>. Rest assured, Back4App’s environment supports recent stable versions. For browser code, transpilers like <a href="https://babeljs.io/" target="_blank">Babel</a> will produce an es2016 compatible with code that uses async / await and works in modern browsers.

## 2 - Thinking about your code differently

The main change with Cloud Code involves what the developer does versus what the library does. Previously, you would explicitly manage the response. Since most Cloud Code will execute asynchronously - making database queries and writes - it makes more sense to return a Promise, reducing the boilerplate code.

The intuition behind Cloud functions is that there is minimal setup and configuration involved with writing server-side code. This release embodies that idea; keep this in mind as you’re refactoring and creating new functions.

To show how Cloud Code functions work in Parse Server 3.1, we rewrote a functional Cloud Code sample from a version of Parse Server before migration. You can find this code by clicking <a href="https://github.com/back4app/parse-server-break-changes/tree/step_1" target="_blank">here</a>. The same Cloud Code function is written in Parse 3.1 as shown below.

```javascript
1   // Cloud Code BEFORE migration
2   // Full code found in link above
3   const POST = 'Post';
4   Parse.Cloud.define('posts:get', function(request, response) {
5     // needs a post ID
6     return new Parse.Query(POST)
7       .get(request.params.id, { useMasterKey: true })
8       .then(post => {
9         response.success(post);
10      })
11      .catch(e => {
12        response.error({ message: e.message });
13      });
14  });
```

## 3 - Adding all async markers

Any function that uses await must be declared with the async modifier. This simple refactoring will attach async to all Cloud Functions. It will also replace them with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions" target="_blank">arrow functions</a> as they are more succinct in this case (and what the updated official Parse guide uses).

```javascript
1   // Snippet of step 2 code refactoring. See full code
2   //  here in the link at the top of this step
3   const POST = 'Post';
4   const COMMENT = 'Comment';
5
6   Parse.Cloud.define('posts:get', async (request) => {
7     // Needs a post ID
8     return new Parse.Query(POST)
9       .get(request.params.id, { useMasterKey: true });
10  }    
```

**Your code will look like **<a href="https://github.com/back4app/parse-server-break-changes/tree/step_2" target="_blank">**this**</a>** after this refactoring**

Nothing crazy so far. In the next step, we’ll get our money’s worth for this change.

## 4 - Removing references to response, employ await

**Your code will look like **<a href="https://github.com/back4app/parse-server-break-changes/tree/step_3" target="_blank">**this**</a>** after this refactoring**

This step is a little bit trickier. We need to:

- Remove all references to the ‘response’ variable, returning a promise instead
- In the case of multiple query/save functions, await the response

Check out the comment create method to see how this is done

```javascript
1   // Snippet of step 3 code refactoring. See full code
2   //  here in the link at the top of this step
3   Parse.Cloud.define('comment:create', async request => {
4     // Post should have text and should have a user and a post id
5     if (!request.user) {
6       throw new Error('unauthenticated!');
7     }
8
9     if (!request.params.text) {
10      throw new Error('A comment needs text!');
11    }
12    if (!request.params.post_id) {
13      throw new Error('A comment needs a post!');
14    }
15
16    //   Get the post
17
18    const post = await new Parse.Query(POST).get(request.params.post_id, {
19      useMasterKey: true
20    });
21    return new Parse.Object(COMMENT, {
22      text: request.params.text,
23      user: request.user,
24      post: post
25    }).save(null, { useMasterKey: true });
26  });
```

:::hint{type="info"}
Note that:


- Now a JavaScript Error is thrown instead of calling response.error. Parse will handle transforming this into a response for us.
- Deleting a ‘Post’ or ‘Comment’ involves grabbing the object first, then destroying it. By using ‘await’, the destroy method can access the saved object outside of a block.&#x20;
:::

That completes all necessary refactoring for migration. That’s to say if you do up until this Step, congrats! Your code will run on Back4App Parse 3.1!

## 5 - Advanced Tricks (optional)

The following changes are optional but can shorten your code significantly. I find they reduce boilerplate.

You probably noticed a lot of manual checks for parameters or the authenticated user. These ensure that strange behavior doesn’t occur. For example, we’ve decided our ‘Post’ object needs text, so if no ‘text’ parameter is passed, the object will get saved without it. One way to prevent this is to check that text was passed.

But manual checks can be time-consuming and inelegant. In this section, we take advantage of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" target="_blank">object destructuring</a> to implicitly complete these checks.

You should see the documentation above if you don’t know what destructuring is. But in short, it allows you to turn an object’s property into a variable with very concise syntax.

```javascript
1   // This
2   var obj = {
3     hi: true,
4     bye: false
5   };
6   var hi = obj.hi;
7   var bye = obj.bye;
8
9   // Is equivalent to this:
10  var obj = {
11    hi: true,
12    bye: false
13  };
14  var { hi, bye } = obj;
15
16  console.log(hi);
17  // true
18  console.log(bye);
19  // false
```

Destructuring is less verbose than manual assignment. It also allows you to declare parameter variables on the fly which is nice:

```javascript
1   Parse.Cloud.define('posts:get', async ({ params: { id } }) => {
2     // id is declared
3   });
```

When combined with an es2015 notation for <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015" target="_blank">object initialization</a> we can optimize our parameter checks.

We can do this:

```javascript
1   // Iterates through object's keys. Makes sure, for each key, the value is set
2   const AssertParams = parameter_obj => {
3     for (var key of Object.keys(parameter_obj)) {
4       if (typeof parameter_obj[key] === 'undefined')
5         throw new Error(`Missing parameter ${key}`);
6     }
7   };
8
9   var obj = {
10    hi: true,
11    bye: false
12  };
13  var { hi, undef, bye } = obj; // undef will be undefined
14  var check_1 = { hi, bye };
15  var check_2 = { hi, undef };
16
17  // check = { hi : true, no : undefined }
18  AssertParams(check_1); // passes
19  AssertParams(check_2); // throws error
```

So for our Parse code, we can do this:

```javascript
1   // Snippet of advanced code refactoring. See full code
2   //  here in the link at the top of this step
3   Parse.Cloud.define('posts:delete', async ({ user, params: { id } }) => {
4     // Makes sure user is authenticated, and id parameter is passed
5     AssertParams({ user, id });
6     const post = await new Parse.Query(POST).get(id, {
7       useMasterKey: true
8     });
9     return post.destroy({ useMasterKey: true });
10  });
```

If this is daunting, don’t fret. The <a href="https://github.com/back4app/parse-server-break-changes/tree/step_advanced" target="_blank">full code</a> example might help.

In short, ‘AssertParams’ is a utility function for throwing an error if a value is undefined. We can check for parameters in one motion by combining object destructuring and es2015 object initialization.

This removes eight or nine manual checks which start to become unsightly after a while.

## 6 - Upgrading on Back4App

Once you have migrated your code, you must do two more things to have it running on Back4App.

1. Upgrade your Back4App server version
2. Upload your new cloud code.

I’ve briefly mentioned the first step before. All you need to do is log into Back4App and go to your app’s dashboard. From there, just select “Server Settings” on the left, followed by the “Settings” button on the “Manage Parse Server” card. You may then select the Parse Server 3.1.1 radio button and hit “Save”.

Last but not least, you can upload your code via the [Back4App CLI](https://blog.back4app.com/2017/01/20/cli-parse-server/), or your dashboard using the manual upload. See [this guide](https://www.back4app.com/docs/android/parse-cloud-code) if you need more information on this step.

**Notes on launching a new version**

If your code is complex, it might be a good idea to run some tests before doing these two steps. Back4App has some guides on how to do this <a href="https://www.back4app.com/docs/advanced-guides/parse-cloud-code-testing" target="_blank">here</a> and <a href="https://www.back4app.com/docs/cloud-code-functions/unit-tests" target="_blank">here</a>.

Finally, it’s important to note that these changes are **breaking**. Using the code we wrote on a 2.x Parse Server will fail, and using 2.x code on a 3.1 server will also fail. Therefore, you must make sure to upgrade your Back4App parse version right when you upload your upgraded Cloud Code. If you have many users and are concerned about the code upload and version upgrade being slightly out of sync, you can do something like this.

```javascript
1   const serverVersion =
2     Parse.CoreManager.get('VERSION') === 'js1.11.1' ? 'OLD' : 'NEW';
3
4   if (serverVersion === 'NEW') {
5     Parse.Cloud.define('posts:get', async ({ params: { id } }) => {
6       AssertParams({ id });
7       // Needs a post ID
8       return new Parse.Query(POST).get(id, {
9         useMasterKey: true
10      });
11    });
12  } else if (serverVersion === 'OLD') {
13    // Old definition here
14  }
```

This code dynamically figures out the version and defines Cloud Functions based on that. After uploading this, you would change to 3.1, and then you could re-upload the code with the old part removed. Including the check at first ensures there isn’t a point where your code will crash. Since you can upgrade and upload within a couple of seconds, it’s usually not necessary.

## Conclusion

At this guide, we demonstrated simply how to migrate your Cloud Code to the Parse 3.1 release. Remember to bump your Back4App version to 3.1 after you make these changes.

Also importantly, this guide demonstrated an improved syntax that Parse 3.1 leverages. Our refactorings cut the codebase from \~160 lines to \~90 and made it much more readable. For actual applications, this will pay dividends.

[title] Advanced Guides
[path] /


[title] Connect to a Database
[path] Database Hub/

# Connect to a Database

## Introduction

Connecting a Database using the <a href="https://www.back4app.com/database" target="_blank">Database Hub</a> allows you to have access to the source database structure and data from inside your App in Back4app.

This ensures any new updates for schemas and/or data are immediately delivered to your App so everything is up to date at any given moment.

This is also useful when big (a.k.a. in huge) databases are available, allowing you to use its data without having to fully copy the database to your account.

## Prerequisites

:::hint{type="info"}
**In order to use or share a database, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## 1 - Find a Database to connect

Go to the <a href="https://www.back4app.com/database/search" target="_blank">Database Hub list of databases</a> and search for any topic that interests you.

For this tutorial, will be used the <a href="https://www.back4app.com/database/back4app/list-of-all-continents-countries-cities" target="_blank">Database of World Continents, Countries and Cities</a>:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HHatr99-x3cYyR-l3VHbH_image.png)

## **2 - Connect**

Click the Connect button on the top right corner of the screen. The counter on the right side shows how many Apps are using that database at the moment:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FeitNM0xrlCPP_T-UiAwc_image.png)

The Connect dialog will appear. Then choose to which App, from one of your already existing apps, you want to connect. The chosen App in the drop down will have access to the Database that you are connecting to.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/30APUspPPLazNk8t3dqpw_image.png)

Then, success message will appear:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/a_UNZXmNa3fBo6cOtWXsE_image.png)

## 3 - Use the data

Go to the <a href="https://parse-dashboard.back4app.com/apps" target="_blank">Dashboard</a> of the App you connected. You will notice the classes from the connected Database appear in the Database Browser in the following formats:

DatabaseName\_ClassName if it is a custom class
DatabaseName\_\_ClassName if it is a Parse class

The difference is that Parse classes have two \_ between the DatabaseName and ClassName:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AOVn_U6e13TnyjC41SVkG_image.png" signedSrc size="60" width="431" height="433" position="center" caption}

From now on you can query the data of the connected classes on your code, the API Console, the GraphQL console and even make relations and pointers among your App’s classes and the connected ones.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fRFxAtAqx73t1bO9Chqe5_image.png" signedSrc size="60" width="498" height="370" position="center" caption}


[title] Integrated Tests
[path] Advanced Guides/

# How to integrate tests into your Parse Cloud Code functions

## Introduction

***This is a guide written by ***[***John Considine***](https://github.com/considine)***, our guest writer and lead developer at ***[***K-Optional***](https://koptional.com/)***. The tutorial covers how to setup automated tests for your Back4App Cloud Code.***

We will talk briefly about moving some of your client Parse code to the cloud, and then about how integrate your project with a testing ecosystem. You can also check out the [example project](https://github.com/back4app/template-cloud-code-unit-test) directly for a working version.

## Goals

We hope to combine the robust, scalable aspects of automated tests with the developer-friendly Parse environment. By leveraging Cloud Code, perhaps an underappreciated feature of Parse, developers can continue to rapidly iterate their code AND be confident that the software will run as expected.

[Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development) is an immense field; rather than talk philosophically about testing, we will run through an implementation and talk about some strategies (stubbing for instance).

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Back4App Command Line Configured with the project
- Follow the <a href="https://www.back4app.com/docs" target="_blank">Setting up Cloud Code tutorial</a> to learn how to set up cloud code for a project
- npm installed on your command line
:::

**Note: This library will use the **[**JavaScript Promise**](https://www.promisejs.org/)**, which shouldn’t be too complicated**

## Let’s create a basic social media backend

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/RVQdoVboWH_oPulNo6y6e_image.png)

Ok! Imagine a social media application that includes a profile model to go along with the user model. For some apps, you may place profile information in the user model, although in many cases this is not efficient; you often will need to separate the concerns of authorization/authentication with user content, and thus maintain two different models.

In this tutorial, we will be implementing a feature that manages the creation of users and profiles in this way, placing minimal strain on the client. Let’s get started!

### **1. Defining Our Functions**

**This assumes you have a Back4App project created, and the command line tool installed (see prerequisites).**

**For examples of frontend code, this guide will refer to the Parse JavaScript SDK syntax for simplicity**

When someone signs up for this application, a profile should be created and coupled to the user object.

**The signup function**

On many Parse applications, you will create the user with the following syntax

```javascript
1   var user = new Parse.User();
2   user.set("username", "my name");
3   user.set("password", "my pass");
4   user.set("email", "email@example.com");
```

In our case, we would like to also initialize a Profile, and point it at the User object.

:::CodeblockTabs
Parse Server 3.X

```javascript
1   try {
2     await user.signUp(null, {useMasterKey : true});
3     let Profile = Parse.Object.extend("Profile");
4     let profile = new Profile({
5       firstname : params.firstname,
6       lastname : params.lastname,
7       user : user
8     })
9     return profile.save(null, {useMasterKey : true});
10   } catch (err){
11     return (err.message);
12   }
```

Parse Server 2.X

```javascript
1   user.signUp(null, {
2     success : function (newUser) {
3       var Profile = Parse.Object.extend("Profile");
4       var profile = new Profile();
5       profile.set("firstname", "John");
6       profile.set("lastname", "Smith");
7       profile.set("user", newUser);
8       profile.save();
9     },
10    error : function (err) {
11      // handle error
12    }
13  })
```
:::

You could shorten that syntax to be something like this:

:::CodeblockTabs
Parse Server 3.X

```javascript
1   let user = new Parse.User({
2     username : params.username,
3     password : params.password,
4     email : params.email
5   });
6
7   try {
8     await user.signUp(null, {useMasterKey : true});
9     let Profile = Parse.Object.extend("Profile");
10    let profile = new Profile({
11      firstname : params.firstname,
12      lastname : params.lastname,
13      user : user
14    })
15    return profile.save(null, {useMasterKey : true});
16  } catch (err){
17    return (err.message);
18  }
```

Parse Server 2.X

```javascript
1   var user = new Parse.User({
2     username : params.username,
3     password : params.password,
4     email : params.email
5   });
6   user.signUp(null)
7   .then((newUser) => {
8     var Profile = Parse.Object.extend("Profile");
9     var profile = new Profile({
10      "firstname" : "John",
11      "lastname" : "Smith",
12      "user" : newUser
13   });
14    return profile.save();
15  })
```
:::

Unfortunately, this still involves making two separate requests to the Parse Server which is inefficient for a frontend to do; it is wise to avoid multiple-step client-server communication flows when possible.

Also, regarding security, the above code is putting the creation process in a client's hands which is never smart. We would like to prevent our data integrity from relying on a client properly completing all steps of a flow. They could, for example, send a custom request that creates a user with no profile, corrupting the app’s persistent data.

Why not do this all in one step using cloud code? It can prevent bloating of frontend code, and ensure that the client is not doing unnecessary/insecure work!

**Here’s what we want to do instead from the client for sign up**

```javascript
1   Parse.Cloud.run('signupUser',
2   {
3     username: 'myname',
4     password : "mypass",
5     email : "email@example.com",
6     firstname : "John",
7     lastname : "Smith" }
8   ).then(function(newuser) {
9
10  });
```

*Parse also defines for *[*beforeSave*](http://docs.parseplatform.org/cloudcode/guide/#beforesave-triggers)* triggers, allowing the creation of the profile when the user signs up. However by using a function we may intuitively pass firstname and lastname attributes that the profile will use*&#x20;

**Cloud code signup function**

Let's get started! Move to your project directory that is synced with Back4App (see prereqs if you don’t know what this means). We will assume the following structure:

> 1   ./cloud
> 2   ./cloud/main.js

In our case, upon initialization, we chose ‘cloud’ as our Directory Name. Your directory can be called whatever you want.

:::CodeblockTabs
Parse Server 3.X main.js

```javascript
1   Parse.Cloud.define("signuserUp", async(request) => {
2     // Make sure the necessary parameters are passed first
3     let params = request.params;
4     if (!params.username || !params.email || !params.password || !params.firstname || !params.lastname)
5       throw new Error("Missing parameters: need username, email, password, firstname, & lastname");
6
7     // Execute the signup flow
8     let user = new Parse.User({
9       username : params.username,
10      password : params.password,
11      email : params.email
12    });
13
14    try {
15      await user.signUp(null, {useMasterKey : true});
16      let Profile = Parse.Object.extend("Profile");
17      let profile = new Profile({
18        firstname : params.firstname,
19        lastname : params.lastname,
20        user : user
21      })
22      return profile.save(null, {useMasterKey : true});
23    } catch (err){
24      return (err.message);
25    }
26  });
```

Parse Server 2.X main.js

```javascript
1   Parse.Cloud.define("signuserUp", function(request, response) {
2     // Make sure the necessary parameters are passed first
3     var params = request.params;
4     if (!params.username || !params.email || !params.password || !params.firstname || !params.lastname)
5       return response.error("Missing parameters: need username, email, password, firstname, & lastname");
6
7     // Execute the signup flow
8     var user = new Parse.User({
9       username : params.username,
10      password : params.password,
11      email : params.email
12    });
13    user.signUp(null, {useMasterKey : true})
14    .then((newUser) => {
15      var Profile = Parse.Object.extend("Profile");
16      var profile = new Profile({
17        firstname : params.firstname,
18        lastname : params.lastname,
19        user : newUser
20      })
21      return profile.save(null, {useMasterKey : true});
22    })
23    .then((prof) => response.success(prof))
24    .catch((e) => {
25      response.error(e.message);
26    })
27  });
```
:::

*You may notice the ‘useMasterKey’ option being passed; this allows the cloud code to supersede any Roles or ACLs that may be in place. Since the client doesn’t touch this code, there is no risk of them hijacking our server. However, please be careful with this flag!*

In case it’s not obvious why this might be preferable to placing this functionality in the client code, here are some advantages:

- Offloads computation to the server rather than the device
- Explicitly defines the functionality of a process
- Easier to create fail-safe functions
- Gives the client an intuitive interface
- This prevents the possibility that a client will ‘half-do’ a process.

### **2. Refactoring our directory structure**

Great, we’ve created two cloud functions. We could obviously test these functions by running them and checking the Parse Dashboard, but that is not scalable or efficient. We instead want to create automated tests specifically for the methods that can be run continuously. So we will separate our code a little bit.

We will move the functions we created in main.js to a new file called cloud-functions.js (in the same directory). Then we will *import* these functions into main, and bind them to the Cloud Function definitions. The idea is to *decouple* the functions from the cloud interface so we may test them without inefficiently sending HTTP requests. This will make a lot of sense as we create the test suite.

**create the functions file**

> \# remember,  the 'cloud' directory was determined
> \# when we initialized the directory with our Back4App project
> \# check the Prerequisites if this does not make sense
> touch ./cloud/cloud-functions.js

You may be aware that you can use ‘require’ in Node.js to pull in functions, objects, and variables from other files.

We will thus define functions corresponding with the Parse Cloud Function we created in Step 1.

One possibly confusing point is that the functions we are defining will be *returning functions*, which then can be hooked into the Parse Cloud definition. It may seem strange to use a function to return a function, but this will give us the power to swap out Parse Servers later on when we are writing our tests.

You may have noticed that you can use the Parse object in your Cloud Code, without ever having to define or import it. This is due to the server that runs this code adding Parse automatically. However, if we want to run tests on the functions locally, we are not afforded an instance. As a matter of fact, we would like to supply our own instance that corresponds to a test Parse server, where there is no harm in data being created or deleted.

So each function will accept
‘Parse’ as a parameter, and return the cloud functions.

:::CodeblockTabs
Parse Server 3.X cloud-functions.js

```javascript
1   // cloud-functions.js
2   module.exports.SignupUser = function(Parse) {
3     return async(request) => {
4       // Copied from main.js:
5       // Make sure the necessary parameters are passed first
6       let params = request.params;
7       if (!params.username || !params.email || !params.password || !params.firstname || !params.lastname)
8         throw new Error("Missing parameters: need username, email, password, firstname, & lastname");
9
10      // Execute the signup flow
11      let user = new Parse.User({
12        username : params.username,
13        password : params.password,
14        email : params.email
15      });
16
17      try {
18        await user.signUp(null, {useMasterKey : true});
19        let Profile = Parse.Object.extend("Profile");
20        let profile = new Profile({
21          firstname : params.firstname,
22          lastname : params.lastname,
23          user : user
24        })
25        return profile.save(null, {useMasterKey : true});
26      } catch (err){
27        return (err.message);
28      }
29    }
30  }
```

Parse Server 2.X cloud-functions.js

```javascript
1   // cloud-functions.js
2   module.exports.SignupUser = function(Parse) {
3     return function (request, response) {
4       // Copied from main.js:
5       // Make sure the necessary parameters are passed first
6       var params = request.params;
7       if (!params.username || !params.email || !params.password || !params.firstname || !params.lastname)
8           return response.error("Missing parameters: need username, email, password, firstname, & lastname");
9       // Execute the signup flow
10      var user = new Parse.User({
11        username : params.username,
12        password : params.password,
13        email : params.email
14      });
15      user.signUp(null, {useMasterKey : true})
16      .then((newUser) => {
17        var Profile = Parse.Object.extend("Profile");
18        var profile = new Profile({
19          firstname : params.firstname,
20          lastname : params.lastname,
21          user : newUser
22        })
23        return profile.save(null, {useMasterKey : true});
24      })
25      .then((prof) => response.success(prof))
26      .catch((e) => {
27        response.error(e.message);
28      })
29    }
30  }
```
:::

In main.js, remove everything from before. Import the Cloud Function, and bind the function to the Cloud Function definition like this:

```javascript
1   // main.js
2   var cloudFunctions = require("./cloud-functions");
3   // Note that we are injecting the Parse instance, which is automatically supplied in the
4   // context of Parse Cloud Code, but not on local tests
5   Parse.Cloud.define("signuserUp", cloudFunctions.SignupUser(Parse));
```

Great! We have not changed the functionality at all since step 1, but we have decoupled the function from the Cloud Code.
In the next step we will create a unit test!

### **3. Create the test suite**

For our test suite we will be using [Jasmine](https://jasmine.github.io/), the popular testing framework. However, our code so far is completely agnostic of our tests, so you may use whatever framework or platform that you prefer.

Let’s install Jasmine and Jasmine-node (an integration of Jasmine and our Node.js environment)

> $ npm install jasmine jasmine-node --save-dev

Now let’s install two libraries our test suite will use. It will use the Parse SDK to connect to a fake Parse Server, and the events library for stubbing out the request object

> $ npm install parse events --save-dev

Now, using the Jasmine utility, let’s initialize our test directory.

> $ ./node_modules/jasmine/bin/jasmine.js init

If you prefer, you may install jasmine globally with $ npm install -g jasmine, then you can initialize with this $ jasmine init

*This guide will assume you do not install Jasmine globally, though it is recommended. If you do, you may replace all instances of ‘/node\_modules/jasmine/bin/jasmine.js’ with simply ‘jasmine’*

This should create a directory called spec, which itself includes a support folder containing configuration information for Jasmine.

By default, Jasmine knows to look for files that end in the “.spec.js” extension, so we will name our tests accordingly.

Create the file for our first unit test:

> $ # in the ./spec directory
> $ touch signup-user.spec.js'

Add a utilities directory with two files that will help with our tests:

> $ # in the ./spec directory
> $ touch utils/purge-parse-table.js
> $ touch utils/response-stub.js

Finally, create a constants file in the same directory. The utility for this file will be explained later

> $ # in the ./spec directory
> $ touch constants.js

Here’s what your directory should now look like:

> ├── cloud
> │   ├── cloud-functions.js
> │   ├── main.js
> ├── node_modules
> ├── spec
> │   ├── support
> │   │   ├── jasmine.json
> │   ├── utils
> │   │   ├── purge-parse-table.js
> │   │   ├── response-stub.js
> │   ├── signup-user.spec.js
> │   ├── constants.js

### **4. Swapping in a test Parse Server**

**Testing around Parse**

Since our methods involve a Parse Server, we want to be able to test that interaction. There are two ways to do this:

A. We can “stub” out the Parse SDK object, by defining an object that implements the same interface. Then simply pass that object as the parameter to our cloud methods. That might look something like this.

```javascript
1   var ParseStub = {
2     // make sure all used methods  and properties are defined
3     User : function () {
4       // Constructor function
5         this.set = function (key, val) {
6           // logic here to implement the parse object set
7         }
8     }
9   }
10  signupUser(ParseStub); // returns cloud function that we can test
```

B. Another approach is to set up a real Parse Server that will serve only for test data. This will involve the slow HTTP layer that Parse uses, but also allow us to test the data in the database. In our tests we’d need to import the Parse SDK, and configure it with a test server.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xONtQ4ACrrwyhQdmbfF-X_image.png)

*The two places that can be stubbed when testing cloud code: A.) Stub a Parse SDK that won’t make HTTP requests, or B.) Swap in a test database implementation*

Neither of these approaches is the “right” answer. It depends on what you’re trying to test. Stubbing out the interface for the Parse SDK (even just the parts we are using) is a lot of work. Additionally, we are going to test the persistence of data after saving in this example, so we will use the second approach.

Lets:

- Create a test Parse Server on Back4App
- Grab the Application ID, and Master Key and save them into our constants file
- Initialize the Parse SDK in our spec file, so our test uses the test server

You are welcome to run a local [Parse Server](https://github.com/parse-community/parse-server) for your tests. We will simply create another Back4App application in our dashboard.

If you need a refresher on how to provision another Back4App server, head on over to the [Create New App tutorial](https://www.back4app.com/docs/get-started/new-parse-app). Call your application whatever you want, though it might be wise to use something like TestBackend. Then just grab the Application ID and Master Key from Dashboard > App Settings > Security & Keys.

Now save these tokens in our constants file like this:

```javascript
1   // ./spec/constants.js
2   // Paste in your app id and master key where the strings are
3   module.exports = {
4     APPLICATION_ID : "PASTE YOUR APPLICATION KEY HERE",
5     MASTER_KEY : "PASTE YOUR MASTER KEY HERE"
6   }
```

**DO NOT put the Application ID and Master Key from your Production App!!! We will be deleting data, and doing so will risk you losing data**

### **5. Testing Utilities**

Cloud functions are passed as parameters in the Express Request and Response objects.

The server automatically creates these parameters when they are run on the cloud, so for our test environments we must create doubles.

This case is easier. When a cloud function is called, data is passed; in our case, the profile and user information are passed. Every argument that is provided is accessible from the request.params property.

So if we call a cloud function like

```javascript
1   // client code, calling Parse function
2   Parse.Cloud.run('fakefunction',
3   {
4     data1: 'I am data1',
5     data2: {
6       prop : "Nested property"
7     }
8   }
9   );
```

Then the request.params property will contain the passed data:

```javascript
1   // Server code, running the Parse function
2     console.log(request.params);
3   // {
4   //   data1: 'I am data1',
5   //   data2: {
6   //     prop : "Nested property"
7   //   }
8   // }
```

Simple enough, for our tests, when calling our cloud function the first argument should be of the form

```javascript
1   {
2     params : {
3       username: 'testuser',
4       firstname: "John",
5       // the rest of the arguments
6     }
7   }
```

Thus we don’t need to create a special mock object in this case.

The response object allows the cloud code to send an HTTP response to the client representing either a success or a failure. We would like to know what is called when invoking the cloud function. Below is a [mock object](https://msdn.microsoft.com/en-us/library/ff650441.aspx) that will allow our test to determine whether the invocation was successful or not. If this is confusing, don’t worry, just place it in your ./spec/utils/response-stub.js file.

```javascript
1   // ./spec/utils/response-stub.js
2   const EventEmitter = require('events');
3   /**
4    * Wrapper around response stub. Simplifies testing cloud functions that
5    * employ a response parameter
6    */
7   function ResponseStub () {
8     this.responseListener = new EventEmitter();
9     this.responseStub = {};
10    /**
11     * Success method that cloud functions expect
12     */
13    this.responseStub.success = (resp) => {
14      this.responseListener.emit("success", resp);
15    }
16    /**
17     * Error method that cloud functions expect
18     */
19    this.responseStub.error = (resp) => {
20      this.responseListener.emit("error", resp);
21    }
22    /**
23     * Listens for errors and successes from stub and returns Promise that resolves or rejects accordingly
24     */
25    this.resolver = new Promise((resolve, reject) => {
26      this.responseListener.on("success", (resp) => resolve(resp));
27      this.responseListener.on("error", (err) => reject(err));
28    });
29  }
30
31  /**
32   * reeturns stub to feed to cloud function
33   */
34  ResponseStub.prototype.getStub = function () {
35    return this.responseStub;
36  }
37
38  /**
39   * returns promise that will indicate the success or failure
40   */
41  ResponseStub.prototype.onComplete = function () {
42    return this.resolver;
43  }
44
45  module.exports = ResponseStub;
```

In short, this javascript constructor function will provide a way for our test to pass in a response object which indicates by Promise resolution / rejection whether the Cloud Function would have returned a success or an error.

**Cleaning up the database**

We obviously don’t want our test Parse database to hold onto what is accumulated during a test. Let's define a utility for clearing database tables, that can be called prior to (or after) test cases.

Add the following to ‘spec/utils/purge-parse-table.js’:

```javascript
1   // spec/utils/purge-parse-table.js
2   /**
3    * Removes all rows from the Parse Database
4    * @param  {string} tablename the name of the parse table to be purged
5    * @return {Promise}  promise to destroy each item  in the table
6    */
7   module.exports = function (Parse) {
8     return (tablename) => {
9       var tableQuery;
10      if (tablename === "User")
11        tableQuery = new Parse.Query(Parse.User);
12      else tableQuery = new Parse.Query(tablename);
13      return tableQuery.find({useMasterKey : true}).then((items) => {
14        var destroyQueue = [];
15        for (var i=0; i<items.length; i++) {
16          destroyQueue.push(items[i].destroy({useMasterKey : true}));
17        }
18        return Promise.all(destroyQueue).catch((e) => {console.log("Error destroying: " + e.message)});
19      });
20    }
21  }
```

**After defining this function, it is a good time to remind you to make sure your spec/utils/constants.js is configured to your TEST Parse Application, NOT your Production Parse Application. This will delete data, so please confirm that this is the empty database you created above**

This function accepts our configured Parse SDK, and returns another function. The returned function accepts a tablename, and removes all data from the corresponding Parse table.

*Again, the idea of returning a function may seem strange, but it allows the test spec to configure the Parse endpoint, and then reference a function that will clear THAT Parse endpoint’s table*

Awesome! Now let’s write our test!

### **6. Test that the Cloud Function will send an error if the proper parameters are not passed**

The Cloud Function relies on certain parameters being included and *should* fail if, for example, the ‘firstname’ was not sent. Let’s be sure.

We will be editing our test file (finally!) spec/signup-user.spec.js.

Here’s what needs to happen before the test definitions:

- Import the Parse NodeJS SDK
- Import our constants, and configure the Parse SDK to point at our test server
- Import our Cloud Function
- Import our “Purge Table” utility
- Import the Response Mock Object we created

The following will do:

```javascript
1   // Hook into your testing server
2   var Parse = require('parse/node');
3   var constants = require("./constants");
4   // head over to your Parse dash board for your test server, and grab your keys. Swap out the strings with the place holders below
5   Parse.initialize(constants.APPLICATION_KEY, null, constants.MASTER_KEY);
6   // if you are running a localhost Parse server, set the serverUrl accordingly
7   Parse.serverURL = 'https://parseapi.back4app.com'
8   var signupUser = require("../cloud/cloud-functions").SignupUser(Parse);
9   var purgeTable = require("./utils/purge-parse-table")(Parse);
10  var ResponseStub = require("./utils/response-stub");
```

Now let's add the test cases. The [Jasmine introduction](https://jasmine.github.io/2.1/introduction) may help to understand the structure better, but it looks like this (taken from the intro):

> describe("A suite", () => {
>   it("contains spec with an expectation", () => {
>     expect(true).toBe(true);
>   });
> });

So describe blocks encapsulate test suites, and the ‘it’ blocks represent cases and expectations.

By passing a parameter to the ‘it’ blocks, you may run tests asynchronously. The test won’t complete until the parameter is invoked like this:

> it("will not finish until done is called", (done) => {
>   setTimeout(() => {
>     done();
>   }, 100); // 100 ms
>   expect(x).toBe(y);
> })

This is helpful since one of our tests will use HTTP, thus should be run asynchronously in this manner as using HTTP is a non-blocking procedure in NodeJS.

Additionally, Jasmine allows for special blocks within suites that can run at different points in the testing lifecycle. We want to delete all tables before each test, so we will execute the purging code in the beforeEach block.

Enough talking, let's add some code! Place the code below into your spec/signup-user.spec.js, below the imports we already added:

```javascript
1   //spec/signup-user.spec.js
2   // imports above
3   describe("SignupUser", ()=> {
4     beforeEach((done) => {
5       /// purge the user and profile tables, and then proceed
6       Promise.all([purgeTable("User"), purgeTable("Profile")])
7       .catch((e) => fail(e))
8       .then(() => done());
9     });
10    it ("should reject a request to signup that does not contain all the parameters", (done) => {
11      var responseStub = new ResponseStub();
12      responseStub.onComplete()
13      .then(() => fail("Should have failed due to invalid parameters"))
14      .catch((e) => {})
15      .then(() => done());
16
17      signupUser({ params : {}}, responseStub.getStub());
18
19    });
20  });
```

Awesome, our first test is under our belts. In the beforeEach block, we purge the User and Profile tables. Then the first test case is triggered. It verifies that passing invalid parameters to the signupUser function causes the function to send an error.

It uses the response stub to make sure the function ultimately rejected. Because ‘signupUser’ will fail, the initial ‘then’ block on the stub should not be invoked. If it is, then our test fails!

Go ahead and run the test using the following:

> $ ./node_modules/jasmine/bin/jasmine.js spec/signup-user.spec.js

You should see the following output:

> Randomized with seed 24618
> Started
> ..
>
>
> 1 specs, 0 failures
> Finished in 1.376 seconds
> Randomized with seed 24618 (jasmine --random=true --seed=24618)

### **7. A Test On Data Persistence**

Hope you have one more test in you! We are going to verify that when our Cloud Function runs properly, our database will be as expected: a Profile will exist, with a reference to a User object, both with the expected attributes.

Add the following block to our existing ‘describe’ suite block:

```javascript
1   //spec/signup-user.spec.js
2   // inside describe
3   it ("should signup a User, and also create a Profile that contains a reference to the user", (done) => {
4     var responseStub = new ResponseStub();
5     var stub = responseStub.getStub();
6     signupUser({
7         params : {
8           firstname : "John",
9           lastname : "Smith",
10          email : "jsmith@example.com",
11          username : "jsmith1",
12          password : "SecretCatchphrase1"
13        },
14      },
15      stub
16    );
17    responseStub.onComplete()
18    .then((resp) => {
19      var profileQ =  new Parse.Query("Profile");
20      profileQ.equalTo("lastname", "Smith");
21      return profileQ.find({useMasterKey : true});
22    })
23    // Check to make sure the profile we retrieve is valid
24    .then((profiles) => {
25      if (profiles.length === 0) throw new Error("No profile's found");
26      expect(profiles[0].get('firstname')).toBe("John");
27      // get the corresponding user
28      return profiles[0].get("user").fetch({useMasterKey : true})
29    })
30    // Check to make sure the user is what we expect
31    .then((user) => {
32      expect(user.getUsername()).toBe("jsmith1");
33    })
34    .catch((e) => {
35      console.log(e)
36      fail(e);
37    })
38    .then(() => done());
39  });
```

Ok this is a lot, so let's step through what occurs.

We instantiate a response mock object, as in the first test case. Then we run signupUser with a request double containing **valid** parameters, as well as the response mock (lines 6-16).

Next, this code listens for the mock object’s onComplete method, which will return a Promise. The Promise will reject if a response.error was called, and resolve if a response.success was called. Any rejections will cause the chain of Promises to skip to the catch block. Therefore, the fail method is placed in the catch block, as the test should fail if the Promise rejects.

The response of the Promise **should** resolve to the profile object. Once it resolves, we will query for a profile of the same last name as we created (lines 19-21). Then the test confirms that the ‘firstname’ of the profile is the same one that we passed (lines 25-26).

The next block fetches the user object associated with the profile. Parse object pointers fetch separately, hence the need for another Promise block.

Finally, the code confirms that the corresponding user has the username that was passed to the signupUser function. Then the test finishes.

Go ahead and run the suite one more time:
Go ahead and run the test using the following:

> $ ./node_modules/jasmine/bin/jasmine.js spec/signup-user.spec.js

You should see the following output:

> Randomized with seed 24618
> Started
> ..
>
>
> 2 specs, 0 failures
> Finished in 2.876 seconds
> Randomized with seed 24618 (jasmine --random=true --seed=24618)

Awesome! we wrote some cloud code, and integrated a testing framework.

## Conclusion

If you got lost at all, or merely want the code for this example head over to the [GitHub Repo](https://github.com/back4app/template-cloud-code-unit-test). Follow the instructions to download and run.

If something is not clear, or doesn’t work, please reach out to me via my Gmail, jackconsidine3.

I hope you enjoyed this tutorial, and gained some insight!

[title] Querying with NSPredicate
[path] iOS/

# Querying with NSPredicate

## Introduction

In this section you will learn how to use NSPredicate to define your queries in Objective-C.

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

In this tutorial we will use a basic app created in Objective-C with Xcode 9.1 and **iOS 11**.

:::hint{type="info"}
**To complete this tutorial, you need:**

- An app created at Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create an app at Back4App.
- Xcode.
- Basic iOS app.
  - **Note:**If you don’t have a basic app created you can open Xcode and hit **File-> New-> Project -> iOS**. Then select **App**. After you create your basic app you are ready to follow this guide.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cNLdHv0CpMyIVzG3ZJa_v_image.png)



![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/BVaGv4xYWoLZlA-aWy8Q6_image.png)
:::

:::hint{type="danger"}
**Note: **Parse iOS SDK works with iOS 7.0 or higher.
:::

## 1 - Get the template

Download the template at
<a href="https://github.com/back4app/ios-objective-c-quickstart-example/archive/master.zip" target="_blank">Back4App’s GitHub repository</a>, and unzip files in your project folder.

You can do that using the following command line:

>   $ curl -LOk https://github.com/back4app/ios-objective-c-quickstart-example/archive/master.zip && unzip master.zip

## 2 - Open the project template

1. Open Xcode.
2. Click on File->Open.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/o-vrZ2axir-Tgc_SQbLGU_image.png)

3\. Navigate to the project folder and double click on QuickStartObjcExampleApp.xcworkspace.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N7i40E8hUhNvNYZxNtBSI_image.png)

&#x20;   4\. Wait for Xcode to open the project.

## 3 - Understandig the Difference

Usually for Objective-C you have two options for building queries: using the ‘PFQuery’ or the ‘NSPredicate’. Both work similarly but depending on how many constraints you want to use, it might make more sense using one instead of the other.

For instance, a simple query using PFQuery would be:

> 1   [query whereKey:@"playerName" notEqualTo:@"Michael Yabuti"];
> 2   [query whereKey:@"playerAge" greaterThan:@18];

But a more complex query could become

> 1   [query whereKey:@"playerName" notEqualTo:@"Michael Yabuti"];
> 2   [query whereKey:@"playerAge" greaterThan:@18];
> 3   [query whereKey:@"playerHeight" greaterThan:@180];
> 4   [query whereKey:@"playerWeight" greaterThan:@80];
> 5   [query whereKey:@"playerFavoriteColour" notEqualTo:@"blue"];
> 6   [query whereKey:@"playerIsLeftHanded" equalTo:@true];
> 7   [query whereKey:@"playerShoeSize" notEqualTo:@42];
> 8   [query whereKey:@"playerLivingState" equalTo:@"Arizona"];
> 9   [query whereKey:@"playerLivingCity" notEqualTo:@"Springfield"];
> 10  [query whereKey:@"playerMothersName" equalTo:@"Jane"];

So, depending on each case, you can choose to use ‘NSPredicate’ instead.
A simple query using ‘NSPredicate’ would be:

> 1   NSPredicate *predicate = [NSPredicate predicateWithFormat:
> @"playerName != 'Michael Yabuti' AND playerAge > 18"];
> 2   PFQuery *query = [PFQuery queryWithClassName:@"GameScore" predicate:predicate];

While a more complex query could become:

> 1   NSPredicate *predicate = [NSPredicate predicateWithFormat:   @"playerName != 'Michael Yabuti' AND playerAge > 18 AND playerHeight > 180 AND playerWeight > 80 AND playerFavoriteColour != 'blue' AND playerIsLeftHanded = true AND playerShoeSize != 42 AND playerLivingState = 'Arizona' AND playerLivingCity != 'Springfield' AND playerMothersName = 'Jane'"];
> 2   PFQuery *query = [PFQuery queryWithClassName:@"GameScore" predicate:predicate];

## 4 - Executing your Query

You can, then, execute your query:

> 1   [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
> 2     if (!error) {
> 3       // Request succeeded
> 4     }
> 5   }];

## Next Steps

At this point, you have learned how to get started with iOS apps.

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Release Notes
[path] /

# Release Notes (Dec24) - Simplified Onboarding Flow, Improved Security and Stability, Admin App.

We’ve been hard at work refining every aspect of our platform, from onboarding to backend administration. Here are some of the key improvements we’ve made:

- **Streamlined Onboarding Flow:** We’ve simplified the initial setup process to help you get started faster and make your first interactions with the platform more intuitive.
- **Strengthened Platform Control and Security:**
  - Implemented tighter controls on the number of times requests can be called, ensuring more predictable and stable application performance.
  - Introduced the ability to block deployments based on their creation time, adding another layer of security and operational clarity.
- **More Comprehensive Documentation:** Our documentation now reflects the latest platform updates, making it easier to find the information you need and stay aligned with best practices.
- **Backend Dashboard Enhancements:**
  - Added a dedicated admin screen to facilitate refined access management, giving you more control over who does what within your environments.
  - Enhanced the user interface for file-type fields, offering clearer visuals and interactions.
  - Published adjustments to the menu for better, more intuitive navigation.
  - Addressed usability concerns in the JavaScript console, making debugging and troubleshooting simpler.
- **Bug Fixing and Platform Stability:**
  - Resolved issues with handling invalid pointers and arrays, ensuring smoother data management.
  - Resolved issues with handling invalid pointers and arrays, ensuring smoother data management.
  - Blocked the use of reserved class names to prevent conflicts and errors.
  - Resolved  dashboard bugs to deal with large data volumes to ensure greater overall stability.
  - Fixed import/export data functionality so you can transfer resources seamlessly.



These improvements are just part of our ongoing commitment to deliver a smooth, secure, and efficient backend development experience. Your feedback continues to guide us as we refine and evolve the platform.

# Release Notes (Sep 24) - Parse Server 6 and new Onboarding AI Advisor

## Parse Server 6 on Back4app Backend&#x20;

### **Security Improvements**

**File upload restrictions**: Back4App users benefit from enhanced security as potential phishing attacks through HTML file uploads are now blocked by default. Users who rely on HTML uploads will need to customize settings, but most users will see this as a protective measure against vulnerabilities.

**Custom Rate limiting**: With this new feature, Back4App users can now limit the number of requests to their apps by adjusting the rateLimit parameter, adding an extra layer of protection against DDoS attacks. This helps ensure app availability and protects against malicious traffic.

### Fixes and Improvements

**Fixes for LiveQuery, Cloud Code triggers, nested object encoding, and client IP issues**: Back4App users will experience more stable and predictable app behavior, particularly for real-time applications using LiveQuery and Cloud Code. These fixes resolve issues with date formatting and object handling, improving data integrity and accuracy.

### Breaking Changes

**Nested objects stored properly**: Back4App users who store complex data structures with nested objects will now see them properly serialized and saved. This improves data consistency but may require review of previous implementations to ensure no unexpected issues arise with data storage.

**Removal of Parse.Cloud.httpRequest**: Back4App users who relied on the httpRequest convenience method will need to switch to alternative third-party libraries for making HTTP requests. This change could require code adjustments for some users, but more flexibility is available through popular libraries like Axios or Fetch.

### New Features

**Cloud code using Parse SDK JS 4.0**: With this update, Back4App users gain access to more powerful and feature-rich SDK capabilities, improving how they handle data and perform operations within cloud code. This update is crucial for users wanting more robust cloud functions.

**New Custom Parse Options**: Users can now leverage more customizable options for configuring their Parse Server setups (e.g. allowExpiredAuthDataToken, defaultLimit, rateLimit). This opens new possibilities for app-specific optimizations, offering greater control and customization.

**Node 18 support**: Back4App Backend now supports Node.js 18, enabling users to install and use more recent versions of Node packages in their Cloud Code. This allows for improved performance, access to the latest features, and better security for server-side code execution.


[title] Untitled
[path] iOS/


[title] Install Parse SDK
[path] JavaScript/

# Install Parse SDK to your JavaScript Project

## Introduction

In this section, you learn how to install Parse JavaScript SDK into your HTML project.

:::hint{type="success"}
See more about Parse SDK at <a href="https://parseplatform.org/Parse-SDK-JS/api/4.3.1/" target="_blank">Parse JavaScript SDK API Reference</a> and <a href="https://docs.parseplatform.org/js/guide/" target="_blank">Parse open source documentation for JavaScript SDK</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">new Parse App tutorial</a> to learn how to create a Parse App at Back4App.
:::

:::hint{type="danger"}
Parse JavaScript SDK supports Firefox 23+, Chrome 17+, Safari 5+, and IE 10.

**IE 9 is supported only for apps that are hosted with HTTPS.**
:::

## 1 - Install SDK

To enable the Parse JavaScript SDK in your web app, create a new index.html file or use your main HTML file and add the following script inside its \<head> tag:

:::CodeblockTabs
index.html

```html
<!-- This is the minified production version of Parse JS -->
<script type="text/javascript" src="https://unpkg.com/parse/dist/parse.min.js"></script>
```
:::

## 2 - Connect your Parse App

### **Initialize your Parse app**

Before using any Parse functionality, you need to call the Parse.Initialize method to set up the authentication token and connect your page with Back4App servers.

In your index.html file, create a \<script> tag at the bottom of it and add the following code:

:::CodeblockTabs
index.html

```html
<script>
  // Initialize Parse
  Parse.initialize("YOUR_PARSE_APP_ID", "YOUR_PARSE_JS_KEY"); //PASTE HERE YOUR Back4App APPLICATION ID AND YOUR JavaScript KEY
  Parse.serverURL = "https://parseapi.back4app.com/";
</script>
```
:::

Note that creating and importing a separate JavaScript file is strongly advised instead of using inline JS code in your HTML file, but for simplicity, we will use the latter here.

### **Find your Application ID and your Client Key**

1. Go to your App Dashboard at the Back4App website.
2. Navigate to app settings: Click on Server Settings > Core Settings block> Settings.
3. Return to your Parse.Initialize function and paste your applicationId and javaScriptKey there.

## 3 - Testing your connection

### **Create a test code**

Test your initial setup with the following code which creates a new object of the User class. Add it inside the \<script> tag, right after the Parse.initialize method:

:::CodeblockTabs
index.html

```html
// Create a new User
async function createParseUser() {
  // Creates a new Parse "User" object, which is created by default in your Parse app
  let user = new Parse.User();
  // Set the input values to the new "User" object
  user.set("username", document.getElementById("username").value);
  user.set("email", document.getElementById("email").value);
  user.set("password", document.getElementById("password").value);
  try {
    // Call the save method, which returns the saved object if successful
    user = await user.save();
    if (user !== null) {
      // Notify the success by getting the attributes from the "User" object, by using the get method (the id attribute needs to be accessed directly, though)
      alert(
        `New object created with success! ObjectId: ${
          user.id
        }, ${user.get("username")}`
      );
    }
  } catch (error) {
    alert(`Error: ${error.message}`);
  }

 // Add on click listener to call the create parse user function
 document.getElementById("createButton").addEventListener("click", async function () {
   createParseUser();
});
```
:::

You also need to create the input fields to pass data to your JavaScript function, so add these plain elements to your \<body> section of your HTML file:

:::CodeblockTabs
index.html

```html
<h1>JS SDK</h1>   
<input id="username" type="text" placeholder="Username"/>
<input id="email" type="email" placeholder="Email"/>
<input id="password" type="password" placeholder="Password" />
<button id="createButton">Create User</button>
```
:::

Go ahead and test this example app following these steps:

1. Open your HTML file in your web browser.
2. Fill the input fields with data and click;
3. An alert box will show the id of the new User object that was created.
4. Login at Back4App.
5. Find your app and click on Dashboard.
6. Click on Core.
7. Go to Browser.
8. Click on User class.
9. Check your new object there.

After these steps you should see something like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wj7qnwGleQ7IQYyQwDV1y_image.png)

Here is the full app code:

:::CodeblockTabs
index.html

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- This is the minified production version of parse js -->
    <script
      type="text/javascript"
      src="https://unpkg.com/parse/dist/parse.min.js"
    ></script>
  </head>
  <body>
    <h1>JS SDK</h1>
    <input id="username" type="text" placeholder="Username" />
    <input id="email" type="email" placeholder="Email" />
    <input id="password" type="password" placeholder="Password" />
    <button id="createButton">Create User</button>
  </body>
  <script>
    // Initialize Parse
    Parse.initialize("YOUR_PARSE_APP_ID", "YOUR_PARSE_JS_KEY"); //PASTE HERE YOUR Back4App APPLICATION ID AND YOUR JavaScript KEY
    Parse.serverURL = "https://parseapi.back4app.com/";

    // Create a new User
    async function createParseUser() {
      // Creates a new Parse "User" object, which is created by default in your Parse app
      let user = new Parse.User();
      // Set the input values to the new "User" object
      user.set("username", document.getElementById("username").value);
      user.set("email", document.getElementById("email").value);
      user.set("password", document.getElementById("password").value);
      try {
        // Call the save method, which returns the saved object if successful
        user = await user.save();
        if (user !== null) {
          // Notify the success by getting the attributes from the "User" object, by using the get method (the id attribute needs to be accessed directly, though)
          alert(
            `New object created with success! ObjectId: ${
              user.id
            }, ${user.get("username")}`
          );
        }
      } catch (error) {
        alert(`Error: ${error.message}`);
      }
    }

    // Add on click listener to call the create parse user function
    document.getElementById("createButton").addEventListener("click", async function () {
      createParseUser();
    });
  </script>
</html>
```
:::

## It’s done!

At this point, you have learned how to get started with JavaScript web apps.

:::hint{type="info"}
Learn more by reading the <a href="https://docs.parseplatform.org/js/guide/" target="_blank">Parse open source documentation for JavaScript SDK</a>.
:::


[title] Untitled
[path] /


[title] Clone a database
[path] Database Hub/

# Clone a Database

## Introduction

Cloning a Database using the <a href="https://www.back4app.com/database" target="_blank">Database Hub</a> allows you to have a full copy of schemas and data cloned, delivered to your Back4app account.

Updates in the original datasource will NOT be delivered to your database, and updates you do will NOT be reflected to the source database.

This is useful when you want a copy of the database to manage the way you prefer.

## Prerequisites

:::hint{type="info"}
**In order to use or share a database, you will need:**

- An account created at Back4App.
:::

## 1 - Find a Database to clone

You can go to the <a href="https://www.back4app.com/database/search" target="_blank">Database Hub list of databases</a> and search for any topic that interests you.

For this tutorial, will be used the <a href="https://www.back4app.com/database/back4app/car-make-model-dataset" target="_blank">Database of Car Models, Manufacturer, Category and Year</a>:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Dwj88OqA2u79yf4iRNHmK_image.png)

## 2 - Clone

Click the Clone button on the top right corner of the screen. The counter on the right side shows how many Apps have cloned that database at the moment:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tku75frvZHaMJna4JjwFY_image.png)

The Clone dialog will appear. Then choose a name for your cloned App.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/vMZiSvM5kofuCoC0Wt_-q_image.png)

Then, a success message will appear:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wIphh1afIlteRIVJ4Sp-2_image.png)

## ** 3 - Use the data**

Once cloned, go to the <a href="https://parse-dashboard.back4app.com/apps" target="_blank">Dashboard</a> for your newly created App.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dpVjdQPyDBgCsRtA4eXNs_image.png" signedSrc size="50" width="587" height="709" position="center" caption}

From now on you can query the data of the cloned App.

[title] Start from Swift Template
[path] iOS/

# Start your iOS project from an App Template - Swift

## Introduction

In this section you learn how to get started with an Xcode template and get ready to use Back4App in 3 easy steps.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
:::

## 1 - Get the template

Download the template at
<a href="https://github.com/templates-back4app/ios-template-todo-list/archive/refs/heads/main.zip" target="_blank">Back4App’s GitHub repository</a>, and unzip files in your project folder.

You can do that using the following command line:

>   $ curl -LOk https://github.com/templates-back4app/ios-template-todo-list/archive/refs/heads/main.zip && unzip swiftProjectBranch.zip

## 2 - Open the project template

1. Open Xcode.
2. Click on File->Open.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/A5mL0Y6kXcp7SwjDxvB5O_image.png)

&#x20;   3\. Navigate to the project folder and double click on Todo\_List\_Back4app.xcworkspace.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2-72Dv2ut08ip11-amMpf_image.png)

&#x20;    4\. Wait for Xcode to open the project.

## 3 - Setup app’s credentials

Update your App Delegate’s Parse Client Configuration values to set up the app’s credentials. Parse iOS SDK uses these settings to connect to the Back4App servers.

1. Open your App Delegate file: .../Todo\_List\_Back4app/AppDelegate.swift

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SMDZnK9wIC023MSdnJu8b_image.png)

&#x20;    2\. Go to your App Dashboard at Back4App website.&#x20;

&#x20;    3\. Navigate to app's settings: Click on Features > Core Settingsblock > Server.

&#x20;    4\.  Return to your AppDelegate.swift file and paste your applicationId and clientKey.

:::hint{type="success"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 4 - Test your connection

1. Build your app in a device or simulator (+R).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YjQGAPgH3G1EnV6GlRsV9_image.png)

&#x20;    2\. Wait until the Hello World! screen appears.

&#x20;    3\. Login at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.

&#x20;    4\. Find your app and click on Dashboard.

&#x20;    5\. Click on Core.

&#x20;    6\. Go to Browser.

If everything works properly, you should find a class named Installation as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Od2UtqcYrHV0g4J2Xcnzp_image.png)

## Next Steps

At this point, you have learned how to get started with iOS apps.

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Untitled
[path] /


[title] Angular
[path] JS Framework/

# Start your Angular project using a pre built template

## Introduction

In this section you will learn how to get install Parse and started with an **Angular 8** App in 5 easy steps.

At any time, you can test the app built with this tutorial by clicking <a href="https://angulardocs.back4app.io/" target="_blank">here</a>.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- An app created on Back4App.
  - **Note:  **Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse App on Back4App**.**
- Node Package Manager installed in your system.
  - Look at the <a href="https://docs.npmjs.com/getting-started" target="_blank">get npm guide</a> for more info.
- Basic knowledge in <a href="https://angular.io/" target="_blank">Angular</a>.
:::

## 1 - Install the Angular CLI

You’re first going to need to install the Angular CLI (Command Line Interface) tool. The CLI helps you to start new Angular project as well as assist you during development. In your terminal, please type the following line:

> npm install -g @angular/cli

## 2 - Get the template

Download the template at our GitHub repository. You can do that using the following command line:

> curl -LOk https://github.com/templates-back4app/angular-integration/archive/master.zip && unzip master.zip

Navigate to the folder of your project and install all dependencies by running the following command:

> cd angular-integration-master/ && npm i

## 3 - Update the app’s credentials

Update the strings values for App ID and JavaScript Key to set up the app’s credentials. Parse JavaScript SDK uses these settings to connect your app to Back4App servers.

1. Go to your app Dashboard at [Back4App Website ](https://www.back4app.com/)and click on *Server Settings*.
2. Find the *Core Settings *block and click on *Settings. *Need Help? Take a look at <a href="https://www.back4app.com/docs/platform/app-settings" target="_blank">these steps</a> to find your keys
3. Copy your *App Id* and *Javascript Key* and return to your project's folder.
4. Go to src > environments > environment.ts and paste your keys.

## 4 - Test your connection locally

1. Start the project by running ng serve.
2. Navigate to http\://localhost:4200/.
3. Wait until the login screen appears.
4. Create an example user by clicking on the register button.

## 5 - Upload your code to the Back4App server

To deploy your app with Back4App, you first need to proper build your app. Use the following command to compile and build your app to a dist directory:

> $ ng build

Then, you need to upload the created dist directory to the public folder of your Cloud Code. In order to do that, choose one of the options to deploy:

### **5.1 - Deploy via CLI**

To upload through Back4App Command Line Interface, you take a look at <a href="https://www.back4app.com/docs/command-line-tool/how-to-use" target="_blank">these steps</a>.

### **5.2 - Deploy via Dashboard**

In order to upload the code via Back4App visual interface, go to your App’s Dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and click on *Cloud Code Functions*.

Click on +ADD button and select all the files of the dist directory. Move them to public and then click SAVE, as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7anZ_trqrQ4ZylbCDfFUd_image.png)

Finally, to deploy your app, see the <a href="https://www.back4app.com/docs/platform/parse-web-hosting" target="_blank">Back4App Web Hosting Tutorial</a>.

## It’s done!

At this point, you have learned how to get started with Angular apps.

[title] GDPR
[path] Security & Privacy/

# How to turn a GDPR Compliant App

## Introduction

This section explains how you can turn your app GDPR Compliant. After completing this guide, step-by-step, you will be safe and able to store European Data in your application without breaking any rules in your backend.

## Prerequisites

:::hint{type="info"}
**To get started with this tutorial, you will need:**

- An account created in <a href="https://www.back4app.com/" target="_blank">Back4App</a>.
- An app that you want turn GDPR Compliant. Don’t you have any apps yet? <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create a New App</a>
:::

# What is GDPR?

The GDPR - General Data Protection Regulation is an extensive new European law that mandates how companies can collect, store, delete, modify, and otherwise process the personal data of EU citizens. The GDPR was adopted on 27 April 2016 and becomes enforceable from 25 May 2018, after a two-year transition period. The GDPR will substitute the EU Data Protection Directive, also known as Directive 95/46/EC, and is intended to standardize data protection laws throughout the European Union (EU) by applying a single data protection law that is binding throughout each member state. It applies to any company that processes the personal data of EU citizens, irrespective of whether it has any physical presence in the EU, or even whether it has any EU customers. Companies are also required to pass these obligations down to all of their vendors who may also handle the personal data of EU citizens anywhere in the world.

## What are the penalties for non-compliance?

Organizations can be fined up to 4% of annual global turnover for breaching GDPR or €20 Million. This is the maximum fine that can be imposed for the most serious infringements e.g.not having sufficient customer consent to process data or violating the core of Privacy by Design concepts. There is a tiered approach to fines e.g. a company can be fined 2% for not having their records in order (article 28), not notifying the supervising authority and data subject about a breach, or not conducting impact assessment. It is important to note that these rules apply to both controllers and processors – meaning ‘clouds’ will not be exempt from GDPR enforcement.

## Back4App as data controller

Back4App will act as a data controller when it determines the purposes and means of the processing of personal data. Some examples are: (I) When we store data regarding account registration, administration, services access. (II) When were store data regarding support activities.

**How can you be GDPR compliant?**

1 - Complete the Addendum by signing and providing the Customer’s full legal entity name, address and signatory information;

<a href="https://www.back4app.com/data-processing-addendum.pdf" target="_blank">Download DPA (PDF)</a>

2 - Submit the completed and signed Addendum to Back4App via email to **gdpr\@back4app.com**.

In this email, you must also mention the app(s) that you want to turn GDPR Compliant.

Take a look at the <a href="https://www.back4app.com/product/parse-gdpr" target="_blank">Back4App GDPR Page</a> for extra information.

[title] Sign In with Facebook
[path] iOS/Parse Swift SDK/Users/

# Sign In with Facebook

## Introduction

In the [previous guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/sign-in-with-google), we learned how a user signs into a **Back4App** application using a Google account. As a follow-up, we may now add a sign-in alternative that uses a Facebook account instead. To accomplish this, we first go to Facebook’s developer page and set up the requirements to integrate such functionality in an Xcode project. Facebook provides an SDK to developers to integrate a **sign in with Facebook** option among different apps.

In <a href="https://github.com/templates-back4app/ios-sign-in-with-facebook" target="_blank">this repository</a>, we provide a simple Xcode template where you can test the different sign-in methods we are implementing. This example was already introduced in the [log-in guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-log-in), you can revisit it for more details about the project.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- A recent version of Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at **Back4App**.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to **Back4App**.
:::

## Goal

To integrate a user sign-in feature using Facebook’s SDK and **ParseSwift**.

## 1 - Setting up Facebook’s SDK

- Once we have the Xcode project <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/install-sdk" target="_blank">linked</a> to the **Back4App** application, we proceed to add Facebook’s SDK which will allow us to implement the sign-in flow. For this example, we use the **Swift Package Manager** (SPM) to add Facebook Login dependencies. In the Xcode project, go to **File>Add packages…** and in the search bar, look for [https://github.com/facebook/facebook-ios-sdk](https://github.com/facebook/facebook-ios-sdk).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CW8U0sO5dl1GGzKS0qXJ6_image.png)

Once the **facebook-ios-sdk** appears in the results, click on the **Add Package** button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WlzFWey-5h3lSwYiPkPaT_image.png)

- Next, go to the <a href="https://developers.facebook.com/apps/" target="_blank">apps</a> section of your Facebook developer account and create a new app. Enter the type of app your app belongs to and go to the next page. Enter the remaining information and click on **Create app**. While you are on your <a href="https://developers.facebook.com/apps/" target="_blank">apps</a> page, locate your newly created app, copy its **App ID** and enter the dashboard by clicking on its name. In the top right, it is located the **Client ID** associated with your Facebook developer account. These two IDs are necessary for the following configuration.
- The next configuration Facebook needs to set up the sign-in capability is to enter a couple of key-value data in your Xcode Info.plist file. More precisely, go to your **Xcode** project navigator, locate the Info.plist and open it as **source code**. Add the following values:

```html
1   <key>CFBundleURLTypes</key>
2   <array>
3	        <dict>
4		            <key>CFBundleTypeRole</key>
5		            <string>Editor</string>
6		            <key>CFBundleURLSchemes</key>
7	             	<array>
8   			            <string>fbAPP_ID</string>
9   		        </array>
10  	    </dict>
11  </array>
12  <key>FacebookAppID</key>
13  <string>APP_ID</string>
14  <key>FacebookClientToken</key>
15  <string>CLIENT_TOKEN</string>
16  <key>LSApplicationQueriesSchemes</key>
17  <array>
18  	<string>fbapi</string>
19  	<string>fb-messenger-share-api</string>
20  </array>
```

Replace the **APP\_ID** string with the **App ID** associated with the newly created app on Facebook. The **CLIENT\_TOKEN** string has to be replaced with the client token located in **Dashboard>Settings>Advanced>Security>Client token**.

- Now we have to add the **Keychain Sharing** capability to the Xcode project. To do this, select your project from the project navigator and go to the targets section. Select a target and go to the **Signing & Capabilities** tab, then click on the **+ Capability** button and add the **Keychain Sharing** capability:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/erkdHx55y3vdC8dvPZDJK_image.png)

- In the **AppDelegate**, we add the following line in the application(\_:didFinishLaunchingWithOptions:) delegate method (Make sure you import the **FacebookCore** framework first)

```swift
1   import FacebookCore
2
3   @main
4   class AppDelegate: UIResponder, UIApplicationDelegate {
5     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
6       ...     
7
8       ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
9       return true
10    }
11  
12    ...
13  }
```

- Lastly, in the **SceneDelegate**, we add the following code to handle incoming URL contexts

```swift
1   import FacebookCore
2
3   class SceneDelegate: UIResponder, UIWindowSceneDelegate {
4     ...
5
6     func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
7       guard let url = URLContexts.first?.url else {
8         return
9       }
10        
11      ApplicationDelegate.shared.application(
12        UIApplication.shared,
13        open: url,
14        sourceApplication: nil,
15        annotation: [UIApplication.OpenURLOptionsKey.annotation]
16      )
17    }
18  }
```

## 2 - Using Facebook Login with ParseSwift

With FacebookLogin successfully integrated into your Xcode project, we proceed to implement the sign in with Facebook feature. In the <a href="https://github.com/templates-back4app/ios-sign-in-with-facebook" target="_blank">project example</a>, the LogInController is in charge of handling and displaying the different sign-in options. We then set up the signInWithFacebookButton action:

```swift
1   // LogInController.swift file
2   import FacebookLogin
3   ...
4
5   class LogInController: UIViewController {
6     ...
7
8     private let signInWithFacebookButton: UIButton = {
9       let button = UIButton(type: .system)
10      button.setImage(UIImage(named: "facebookIcon"), for: .normal)
11      button.imageView?.contentMode = .scaleAspectFit
12      return button
13    }()
14
15    override func viewDidLoad() {
16      super.viewDidLoad()
17      // ...
18      // Layout configuration
19      // ...
20
21      signInWithFacebookButton.addTarget(self, action: #selector(handleSignInWithFacebook), for: .touchUpInside)
22    }
23  }
24
25  // MARK: - Sign in with Facebook section
26  extension LogInController {
27    @objc fileprivate func handleSignInWithFacebook() {
28      // TODO: Here we will implement the sign-in procedure
29    }
30  }
```

In order to show the sign-in form from Facebook, the FacebookLogin SDK allows us to set up and present it via the **signIn(with\:presenting\:callback)** method from the **LoginManager** class. We have to pass an array containing **String** values associated with the data we want to gather from Facebook. The common values are **public\_profile** and **email**.

On the other hand, the second parameter is a callback closure where Facebook returns the user credential (embedded in a **LoginManagerLoginResult** object) or an error if the authentication fails.

```swift
1   // MARK: - Sign in with Facebook section
2   extension LogInController {
3     @objc fileprivate func handleSignInWithFacebook() {
4       let loginManager = LoginManager()
5               
6       // Method provided by the Facebook SDK. See https://developers.facebook.com/docs/facebook-login/ios/ for more details
7       loginManager.logIn(permissions: ["public_profile", "email"], from: self) { [weak self] result, error in
8         if let error = error {
9           self?.showMessage(title: "Error", message: error.localizedDescription)
10          return
11        } else if let result = result, result.isCancelled {
12          self?.showMessage(title: "Alert", message: "Sign in cancelled")
13          return
14        }
15      
16        // Once facebook successfully signs in the user, we retrieve the information related to the sign-in process via the result.token object, an AccessToken class type
17        guard let accessToken = result?.token else { fatalError("This dhould never hapen.") }
18      
19        // TODO: Sign in the user on your Back4App application with the accessToken.
20      }
21    }
22  }
```

We then take this credential and use them for the user to sign in to the **Back4App** platform. The object representing the user is the following struct (see the [Login guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-log-in) for more details):

```swift
1   import ParseSwift
2
3   struct User: ParseUser {
4     ...
5  
6     var username: String?
7     var email: String?
8     var emailVerified: Bool?
9     var password: String?
10  
11    var age: Int?
12  }
```

Therefore, the credential returned by Facebook contains an accessToken and the user’s id that will be used to complete the sign-in process. More precisely, we instantiate a ParseFacebook\<User> object and call the login(userId\:authenticationToken\:completion:) method:

```swift
1   // MARK: - Sign in with Facebook section
2   extension LogInController {
3     @objc fileprivate func handleSignInWithFacebook() {
4       let loginManager = LoginManager()
5            
6       // Method provided by the Facebook SDK. See https://developers.facebook.com/docs/facebook-login/ios/ for more details
7       loginManager.logIn(permissions: ["public_profile", "email"], from: self) { [weak self] result, error in
8         if let error = error {
9           self?.showMessage(title: "Error", message: error.localizedDescription)
10          return
11        } else if let result = result, result.isCancelled {
12          self?.showMessage(title: "Alert", message: "Sign in cancelled")
13          return
14        }
15            
16        // Once facebook successfully signed in the user, we retrieve the information related to the sign in process via the result.token object, an AccessToken class type
17        guard let accessToken = result?.token else { fatalError("This dhould never hapen.") }
18        
19        // With the accessToken returned by Facebook, you need to sign in the user on your Back4App application
20        User.facebook.login(userId: accessToken.userID, accessToken: accessToken.tokenString) { [weak self] result in
21          // Returns the User object asociated to the facebook user returned by Facebook
22          switch result {
23          case .success(let user):
24            // After the login succeeded, we send the user to the home screen
25            // Additionally, you can complete the user information with the data provided by Facebook
26            let homeController = HomeController()
27            homeController.user = user
28
29            self?.navigationController?.pushViewController(homeController, animated: true)
30          case .failure(let error):
31            // Handle the error if the login process failed
32            self?.showMessage(title: "Failed to sign in", message: error.message)
33          }
34        }
35      }
36    }
37  }
```

## 3 - Verifying user sign in and session creation

To make sure that the Google sign-in worked, you can look at your **Back4App** application dashboard and see the new User containing the Facebook authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6wIuranhRTUYw7yZEIDul_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to the corresponding User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ARFLcRazu_0Nlgxtu6ZJC_image.png)

## 4 - Linking an existing User to a Facebook account

In case your iOS App requires you to associate a Facebook account to an existing user in your **Back4App** platform, the **ParseFacebook\<User>** object implements the method **link(id\:accessToken\:completion:)** where you pass the **userId** of the Facebook account and the **accessToken** associated with the session

```swift
1   let facebookUserId: String // The userID of the Facebook account to link to
2   let accessToken: String = AccessToken.current!.tokenString // The access token of the currently signed in Facebook user
3
4   User.facebook.link(userId: facebookUserId, accessToken: accessToken) { result in
5      switch result {
6     case .success(let user):
7        // Linking succeeded, the user is now linked to the corresponding Facebook account
8     case .failure(let error):
9       // Linking failed, handle the error
10    }
11  }
```

## 5 - Signing out

The sign-out process does not vary from the standard way of calling the User.signout() method (detailed in previous guides). However, when a user signs in with a Facebook account, for consistency, you have to sign out the current Facebook user as well. We can accomplish this by calling the following method alongside User.signout().

> **1   LoginManager**
>
> ()
>
> **.logOut**
>
> ()

In order to verify if the current user has a linked Facebook account, you can check it by looking at the **User.current?.authData** dictionary.

## 6 - Run the app

You can go to this <a href="https://github.com/templates-back4app/ios-sign-in-with-facebook" target="_blank">repository</a> and download the example project. Before running the project, make sure you set up the provisioning profiles with the ones associated with your developer account.

## Conclusion

At the end of this guide, you learned how to sign in or link existing **Back4App** users on iOS using sign in with Facebook. In the next guide, we will continue with a different sign-in method.

[title] Untitled
[path] /


[title] Untitled
[path] /


[title] via Dashboard (Swift)
[path] iOS/Send Push Notifications/

# Send iOS push notifications from your Parse Server - Swift

## Introduction

This section explains how you can send push notifications using Parse Dashboard through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wPGXE8KmF7QKYlOAywcYk_image.png" signedSrc size="40" width="640" height="1136" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- A paid Apple Developer Account.
:::

## 1 - Create Your Push Certificates In The Apple Developer Center

:::hint{type="danger"}
Pay attention to the steps below because you need to get them right in the exact order. If pushes are not being received there isn’t much we can do to debug besides going over the steps again
:::



1. Go to the target and go to Capabilities - click on push notifications - then turn push notifications on. This creates your app id and sets your entitlements.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7wNJ8j4vdRag0IUNvZnCZ_image.png)

&#x20;    2\. Go to the [Apple Developer Center](https://developer.apple.com/) and login to your account:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Y-8oeYqh_a_EiIRQoj8e0_image.png)

&#x20;    3\. Click on Certificates, Identifiers & Profiles.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PvQpClhDtBHjxgfnC5ZEr_image.png)

&#x20;    4\. In the certificates section hit the plus sign. Choose to create a apple push notification certificate for sandboxes.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8oa7EHB0m3X1BIK8HGkzg_image.png)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rRNpoyltOVGnw2vGUXFrt_image.png)

&#x20;    5\. Pick your app id that matches the app id used in your current Xcode project.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/r8i8N1O28UncUyAiHkmSt_image.png)

&#x20;    6\. Now you will be asked for a Certificate Signing Request or CSR. You will generate your CSR from your Mac computer.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N9gV9mIAVqHAiy-azI46c_image.png)

&#x20;    7\. On your Mac computer open your Keychain Access.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/2JpLjFss7mnJP3OeXT7hx_image.png)

&#x20;    8\. Next, request a certificate from a certificate authority.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_m4lIxXE4-Y2AdAYZpmOT_image.png)

&#x20;    9\. Pick your user email, then make sure you save your certificate to disk - save it on a folder on your desktop called PushCerts.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/B87-lzvBzNx8a06WqkkJ8_image.png)

&#x20;     10\. Go back to the Apple Developer center. Upload your CSR and hit continue.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XubdNcydnGGPG8XbXi0C3_image.png)

&#x20;    11\. Download your Development APN certificate into the same folder called PushCerts. Call it apn\_dev.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/JCEaJWTiF1FcP264UvJ9d_image.png)

&#x20;    12\. Let’s start the process over. This time we will create production push certificates. You need both for testing and release. Select Apple Push Notification Service SSL (Sanbox & Production).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/qbQ6xY8_ZnHdWTbBupnyx_image.png)

&#x20;    13\. Upload your CSR your previously created and hit continue.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/r1LNpX7MKo2rn63oNb3lp_image.png)

&#x20;    14\.  Download your Production APN certificate into the same folder called PushCerts. Call it apn\_prod.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/m0g-xi-mZdHuGvveGAWCE_image.png)

&#x20;    15\. At this point you should have 3 files in your PushCerts folder. Double click on your apn\_prod and your apn\_dev files to add them to your keychain.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Q4IThevZAyapvDUyX_rwC_image.png)

&#x20;      16\. Open the keychain and find the files in the keychain. Click on each on and hit export. You will want to export them as a .p12 file into your PushCerts Folder. Name the development one cert Dev\_PushCertificates.p12 and name the production cert as Prod\_PushCertificate.p12.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/APa9QBL0sC6_j5MvPPAZ4_image.png)

&#x20;       17\. It will ask you add a password to your exported file. Just leave it blank. You will have to enter your master key to sign the certificate though, and thats fine.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/E3eRjL8lfXLaH8OhJV_b4_image.png)

&#x20;      18\. Now that you have added your .p12 files your folder should look like this. If you have all these files in your folder you can go on to Step 2. If you don’t have any of these files go back to the beginning and figure out where you missed a step.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pk2CgaobB5-NZ_5QXCRGD_image.png)

## 2 - Adding Your .P12 certificates to Back4App

1. You’re almost done. Aren’t you excited? Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>, log in, find your app and click on iOS Push notification.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SLmWrZTJEgVVUozfnm_C4_image.png)

&#x20;    2\. Upload the dev cert and the prod cert and hit send for each.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ohzrd_mYOlDFd2audt9_2_image.png)

&#x20;     3\. After you’ve uploaded both certificates your screen should look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xf8nNjmA1KOa6IYiqUf_m_image.png)

## 3 - Setting up Your Xcode Project to receive Push Notifications

1. Open your project’s AppDelegate.swift file to create a push installation object. Add the UserNotifications framework at the top of the file.

<a href="https://github.com/mpc20001/ios-swift-push-back4app/blob/master/AddingParseSDK/AppDelegate.swift#L10-L11" target="_blank">AppDelegate.swift</a>

> \#import UserNotifications

&#x20;     2\. Add the code below inside of the didFinishLaunchingWithOptions function, and make sure it is before ‘return true’ statement.

<a href="https://github.com/mpc20001/ios-swift-push-back4app/blob/master/AddingParseSDK/AppDelegate.swift#L25-L30" target="_blank">AppDelegate.swift</a>

>  
>
> **UNUserNotificationCenter.current**
>
> ()
>
> **.requestAuthorization**
>
> (options: [
>
> **.**
>
> alert, 
>
> **.**
>
> sound, 
>
> **.**
>
> badge, 
>
> **.**
>
> carPlay ]) {
>          (granted, error) 
>
> **in**
>
>
>          
>
> **print**
>
> ("Permission granted: \\(granted)")
>          
>
> **guard**
>
>  granted 
>
> **else**
>
>  \{ 
>
> **return**
>
>  }
>          
>
> **self.getNotificationSettings**
>
> ()
>      }

&#x20;     3\. Add the following code snippets to your AppDelegate.swift file below the didFinishLaunchingWithOptions function. This code will issue a request for push notifications permissions when the app first launches. Make sure to say yes to this request or your app will not be able to receive pushes. It will also handle the resulting token when the request is approved and save it as an installation object on Back4App.

:::CodeblockTabs
AppDelegate.swift

```swift
1   func getNotificationSettings() {
2        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
3            print("Notification settings: \(settings)")
4            guard settings.authorizationStatus == .authorized else { return }
5            UIApplication.shared.registerForRemoteNotifications()
6        }
7    }
8
9    func application(_ application: UIApplication,
10                    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
11       createInstallationOnParse(deviceTokenData: deviceToken)
12   }
13
14   func application(_ application: UIApplication,
15                    didFailToRegisterForRemoteNotificationsWithError error: Error) {
16       print("Failed to register: \(error)")
17   }
18
19   func createInstallationOnParse(deviceTokenData:Data){
20       if let installation = PFInstallation.current(){
21           installation.setDeviceTokenFrom(deviceTokenData)
22           installation.saveInBackground {
23               (success: Bool, error: Error?) in
24               if (success) {
25                   print("You have successfully saved your push installation to Back4App!")
26               } else {
27                   if let myError = error{
28                       print("Error saving parse installation \(myError.localizedDescription)")
29                   }else{
30                       print("Uknown error")
31                   }
32               }
33           }
34       }
35   }
```
:::

&#x20;     4\. Test it by running your app. You should see this in your simulator.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7hGkqv8WqWuac5Kyz8GSw_image.png" signedSrc size="40" width="562" height="1000" position="flex-start" caption}

&#x20;    5\. From here on out you must use a physical device, an iphone or ipad. Push notifications do not work with the Xcode simulator. If you do not have a physical device you cannot go any further in the tutorial. Once you have your physical device attached to your Mac computer and Xcode, try running the app on your device through Xcode. When you see the push permissions request hit approve.

## 4 - Test your app

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website </a>log in, find your app and click on Dashboard.
2. First check that your device’s installation record is visible in Installation table.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/g9nVfZ4aQO7raMj7Za6Iw_image.png)

&#x20;    3\. Then Click on Push > Send New Push and create an audience for your push notification.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3EYkrPwt2dREgfM_244DV_image.png)

&#x20;   4\. Write your message and look at the preview by clicking at iOS option.

&#x20;   5\. If you have already reviewed the push notification and you want to send, click on Send push.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fQ4huje7fM94tU8WYLPtq_image.png)

:::hint{type="info"}
You may explore the other options for Push Notification at Parse Dashboard.&#x20;

There, it’s also possible to look at Past Pushes you sent and the Audiences you created for them.
:::

## It’s done!

At this stage, you can send push notifications using Parse Dashboard through Back4App!

[title] Sign-up/Sign-in - Swift
[path] iOS/

# Login and User registration tutorial using Swift

## Introduction

This section explains how you can create an app with a simple user registration using <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- A paid Apple Developer Account.
:::

## Let’s get started!

Following the next steps you will be able to build an App that will Sign-in, Sign-up and Logout at Back4App Database.

## 1 - Set up and Create your Sign Up and Login UI

1. Go to Xcode, access the main folder from the Project and then open the ViewController.swift file to edit it.
2. In ViewController.swift make sure to include the Parse module by including it at the top of the file.

```swift
1   import Parse
```

&#x20;    3\. Go to Main.storyboard, drag four UITextFields onto the ViewController in the main storyboard. Center the textfield and put two at the top and two at the bottom of the view controller. Drag two more UIButtons on to the view and place them below the textfields. Drag one more Loader Indicators on each Button.
Set the top button text to say Sign In. Set the bottom bottom to say Sign Up. Set the text fields to say username and password.

&#x20;    4\. Next we are going to connect your UITextFields in your storyboard to properties in your view controller. Add the following properties to the top of ViewController.swift. Next go to your storyboard and right click on each UITextField and click on the reference outlet then drag a line back to the ViewController icon and set it to the appropriate field. signInUsernameField connects to the sign In Username Field, etc…

It should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uVLBHeYZQQZcbGLyiyb38_image.png)

```swift
1   import UIKit
2   import Parse
3
4   class ViewController: UIViewController {
5
6       @IBOutlet weak var txtUsernameSignin: UITextField!
7       @IBOutlet weak var txtPasswordSignin: UITextField!
8       @IBOutlet weak var indicatorLogin: UIActivityIndicatorView!
9    
10      @IBOutlet weak var txtUsernameSignup: UITextField!
11      @IBOutlet weak var txtPasswordSignup: UITextField!
12      @IBOutlet weak var indicatorSignup: UIActivityIndicatorView!
13    
14      override func viewDidLoad() {
15          super.viewDidLoad()
16          // Do any additional setup after loading the view.
17      }
18    
19      @IBAction func signin(_ sender: Any) {
20          //todo
21      }
22    
23      @IBAction func signup(_ sender: Any) {
24          //todo
25      }
26  
27  }
```

## 2 - Create a Sign-in function

Add the following code inside the sign-in function:

```swift
1          @IBAction func signin(_ sender: Any) {
2           PFUser.logInWithUsername(inBackground: self.txtUsernameSignin.text!, password: self.txtPasswordSignin.text!) {
3             (user: PFUser?, error: Error?) -> Void in
4             if user != nil {
5               self.displayAlert(withTitle: "Login Successful", message: "")
6             } else {
7               self.displayAlert(withTitle: "Error", message: error!.localizedDescription)
8             }
9           }
10      }
```

## 3 - Create a Sign-up function

Add the following code inside the sign-up function:

```swift
1       @IBAction func signup(_ sender: Any) {
2           let user = PFUser()
3           user.username = self.txtUsernameSignup.text
4           user.password = self.txtPasswordSignup.text
5        
6           self.indicatorSignup.startAnimating()
7           user.signUpInBackground {(succeeded: Bool, error: Error?) -> Void in
8               self.indicatorSignup.stopAnimating()
9               if let error = error {
10                  self.displayAlert(withTitle: "Error", message: error.localizedDescription)
11              } else {
12                  self.displayAlert(withTitle: "Success", message: "Account has been successfully created")
13              }
14          }
15      }
```

## 4 - Log Out

When logging, it creates a Session object, which points to the User logged in. If login is successful, ParseUser.CurrentUser() returns a User object, and a Session object is created in the Dashboard. Otherwise, if the target username does not exist, or the password is wrong, it returns null.

To do Logout follow the steps below:

1. Go to Main.storyboard, drag one UIButton called “Logout”, and add a action between this UIbutton and the ViewController.swift.
2. Add the following code in this function:

```swift
1    @IBAction func logout(_ sender: Any) {
2        PFUser.logOut()
3    }
```

## 5 - Application Code

```swift
1   import UIKit
2   import Parse
3
4   class ViewController: UIViewController {
5    
6       @IBOutlet weak var txtUsernameSignin: UITextField!
7       @IBOutlet weak var txtPasswordSignin: UITextField!
8       @IBOutlet weak var indicatorSignin: UIActivityIndicatorView!
9    
10      @IBOutlet weak var txtUsernameSignup: UITextField!
11      @IBOutlet weak var txtPasswordSignup: UITextField!
12      @IBOutlet weak var indicatorSignup: UIActivityIndicatorView!
13    
14      @IBOutlet weak var btnLogout: UIButton!
15    
16      override func viewDidLoad() {
17          super.viewDidLoad()
18      }
19
20      @IBAction func signin(_ sender: Any) {
21          PFUser.logInWithUsername(inBackground: self.txtUsernameSignin.text!, password: self.txtPasswordSignin.text!) {
22            (user: PFUser?, error: Error?) -> Void in
23            if user != nil {
24              self.displayAlert(withTitle: "Login Successful", message: "")
25            } else {
26              self.displayAlert(withTitle: "Error", message: error!.localizedDescription)
27            }
28          }
29      }
30   
31      @IBAction func signup(_ sender: Any) {
32          let user = PFUser()
33          user.username = self.txtUsernameSignup.text
34          user.password = self.txtPasswordSignup.text
35          
36          self.indicatorSignup.startAnimating()
37          user.signUpInBackground {(succeeded: Bool, error: Error?) -> Void in
38              self.indicatorSignup.stopAnimating()
39              if let error = error {
40                  self.displayAlert(withTitle: "Error", message: error.localizedDescription)
41              } else {
42                  self.displayAlert(withTitle: "Success", message: "Account has been successfully created")
43              }
44          }
45      }
46    
47      @IBAction func logout(_ sender: Any) {
48          PFUser.logOut()
49      }
50      
51      func displayAlert(withTitle title: String, message: String) {
52          let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
53          let okAction = UIAlertAction(title: "Ok", style: .default)
54          alert.addAction(okAction)
55          self.present(alert, animated: true)
56      }
57    
58  }
```

The application UI will be similar to this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Hb0Hy0l7KmiL8nM7sUG2M_image.png" signedSrc size="40" width="640" height="1136" position="center" caption}

## 6 - Test your app

1. Run your app and create a couple of users, also try logging in again after registering them.
2. Login at [Back4App Website](https://www.back4app.com/)
3. Find your app and click on Dashboard>Core>Browser>User.
4. Try logging in and out with the same user and signing back in.

At this point, you should see your users as displayed below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/bd-AZQhcoldg7wXvBK28B_image.png)

:::hint{type="info"}
**Note**: Using the codes displayed above, every time you log in with a user, a Session is opened in your Dashboard, but when the user logs out that particular Session ends. Also, whenever an unsuccessful login or sign up attempt occurs, the Session opened in Parse Server Dashboard is deleted.
:::

## It’s done!

At this stage, you can log in, register or log out of your app using Parse Server core features through Back4App!

[title] Twitter login
[path] iOS/

# Add Twitter login to your iOS App using Swift

## Introduction

This section explains how you can create an app with user registration using Twitter Login and <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-objc-sdk" target="_blank">Install Parse SDK (ObjC) Tutorial</a> to create an Xcode Project connected to Back4App.
:::

## 1 - Twitter Set up

To start using Twitter functions, you need to:

1. Go to <a href="https://twitter.com/login?redirect_after_login=https%3A%2F%2Fdeveloper.twitter.com%2Fapps" target="_blank">Twitter Application Management Website</a>, sign in with a Twitter account and click on Create New App.
2. Add your application’s Twitter consumer key on your Parse application’s settings page.
3. When asked to specify a “Callback URL” for your Twitter app, please insert a valid URL likehttp\://twitter-oauth.callback. This value will not be used by your iOS or Android application, but is necessary in order to enable authentication through Twitter.
4. Add the  and Social.framework libraries to your Xcode project.
5. Add the following where you initialize the Parse SDK, such as  inapplication\:didFinishLaunchingWithOptions:

```swift
1   PFTwitterUtils.initializeWithConsumerKey("YOUR CONSUMER KEY",  consumerSecret:"YOUR CONSUMER SECRET")
```

## 2 - Login and SignUP

PFTwitterUtils provides a way to allow your PFUsers to log in or sign up through Twitter. This is accomplished using the logInWithBlock or logInWithTarget messages:

```swift
1   PFTwitterUtils.logInWithBlock {
2     (user: PFUser?, error: NSError?) -> Void in
3     if let user = user {
4       if user.isNew {
5         print("User signed up and logged in with Twitter!")
6       } else {
7         print("User logged in with Twitter!")
8       }
9     } else {
10      print("Uh oh. The user cancelled the Twitter login.")
11    }
12  }
```

When this code is run, the following happens:

1. The user is shown the Twitter login dialog.
2. The user authenticates via Twitter, and your app receives a callback.
3. Our SDK receives the Twitter data and saves it to a PFUser. If it’s a new user based on the Twitter handle, then that user is created.
4. Yourblock is called with the user.

## 3 - Twitter Linking

If you want to associate an existing PFUser with a Twitter account, you can link it like so:

```swift
1   if !PFTwitterUtils.isLinkedWithUser(user) {
2     PFTwitterUtils.linkUser(user, {
3       (succeeded: Bool?, error: NSError?) -> Void in
4       if PFTwitterUtils.isLinkedWithUser(user) {
5         print("Woohoo, user logged in with Twitter!")
6       }
7     })
8   }
```

The steps that happen when linking are very similar to log in. The difference is that on successful login, the existing PFUser is updated with the Twitter information. Future logins via Twitter will now log the user into their existing account.

If you want to unlink Twitter from a user, simply do this:

```swift
1   PFTwitterUtils.unlinkUserInBackground(user, {
2     (succeeded: Bool?, error: NSError?) -> Void in
3     if error == nil && succeeded {
4       print("The user is no longer associated with their Twitter account.")
5     }
6   })
```

## 6 - Twitter API calls

Our SDK provides a straightforward way to sign your API HTTP requests to the [Twitter REST API](https://dev.twitter.com/rest/public) when your app has a Twitter-linked PFUser. To make a request through our API, you can use the PF\_Twitter singleton provided by PFTwitterUtils:

```swift
1   let verify = NSURL(string: "https://api.twitter.com/1.1/account/verify_credentials.json")
2   var request = NSMutableURLRequest(URL: verify!)
3   PFTwitterUtils.twitter()!.signRequest(request)
4   let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
5     // Check for error
6     // Data will contain the response data
7   }
8   task.resume()
```


[title] Publish a database
[path] Database Hub/

# Publish your Database on the HUB

## Introduction

Have you already worked on a dataset that took you some time and you believe that can help other developers to save their time?

Now you can share your database (including files and cloud functions/triggers) with other developers using the Database HUB.

By sharing your datasets with other developers you can:

- Help other people develop their apps using ready-to-use datasets;
- Share your data as a service in an easy-to-use format (APIs - REST & GraphQL, SDKs to more than 13 technologies);
- Receive feedback and keep improving your datasets;

## Prerequisites

:::hint{type="info"}
**In order to use or share a database, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

At the moment, this feature is unavailable from the Dashboard. If you wish to publish a database, please contact us on support channel!

[title] Instagram Clone (SwiftUI)
[path] iOS/

# Download an Instagram Clone App Template (SwiftUI)

## Introduction

In this tutorial you learn how to get started with an Xcode template and get an Instagram clone App in a few quick steps.

## Tutorial

:::hint{type="info"}
**You can find a series of step by step blog posts about this template here:**

- [Part 1 - An Instagram clone using SwiftUI and GraphQL](https://blog.back4app.com/2019/08/27/instagram-clone/)
- [Part 2 - An Instagram clone using SwiftUI and GraphQL – Login](https://blog.back4app.com/2019/09/03/swift-instagram-clone/)
- [Part 3 - An Instagram clone using SwiftUI and GraphQL – ProfileView](https://blog.back4app.com/2019/09/16/instagram-clone-profile/)
- [Part 4 - Instagram Clone App using SwiftUI and GraphQL – HomeView](https://blog.back4app.com/2019/09/26/instagram-clone-homeview/)
:::

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode11 or above.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- <a href="https://cocoapods.org/" target="_blank">Cocoapods</a> installed in your Mac.
:::

## 1 - Get the template

Download the template at
<a href="https://github.com/back4app/Back4Gram/archive/master.zip" target="_blank">Back4App’s GitHub repository</a>, and unzip files in your project folder.

You can do that using the following command line:

>   $ curl -LOk https://github.com/back4app/Back4Gram/archive/master.zip && unzip master.zip

## 2 - Install the Cocoapods dependencies

1. Open your Terminal.
2. Navigate to the project’s folder
3. Run this command:

   pod install
4. Wait for the process to complete.

## 3 - Open the project template

1. Open Xcode.
2. Click on File->Open.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Sc0UIbbA3OcXGzcXgS3Lf_image.png)

&#x20;     3\. Navigate to the project folder and double click on Back4Gram.xcworkspace
&#x20;     4\. Wait for Xcode to open the project.

## 4 - Setup app’s credentials

Update your App Delegate’s Parse Client Configuration values to set up the app’s credentials. Parse iOS SDK uses these settings to connect to the Back4App servers.

1. Open your App Delegate file: .../Back4Gram/AppDelegate.swift
2. Go to your App Dashboard at Back4App website.
3. Navigate to app’s settings: Click on Features > Core Settings block > Server.
4. Return to your AppDelegate.swift file and paste your applicationId and clientKey.

:::hint{type="success"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 5 - Test your connection

1. Build your app in a device or simulator (Command+R)
2. Wait until the Back4Gram screen appears.

## Next Steps

At this point, you have your Back4Gram copy working with your Back4app App.

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Database Operations
[path] JavaScript/

# Performing Serverless Database Operations

## Introduction

This section explains how to implement the **CRUD (Create, Read, Update and Delete) Operations**
in a JavaScript environment through Back4app.
It also provides code snippets and an online environment to execute and test your code with no local setup.

:::hint{type="success"}
See more about Parse SDK at <a href="https://parseplatform.org/Parse-SDK-JS/api/4.3.1/" target="_blank">Parse JavaScript SDK API Reference</a> and <a href="https://docs.parseplatform.org/js/guide/" target="_blank">Parse open source documentation for JavaScript SDK</a>.
:::

## Prerequisites

:::hint{type="info"}
There are no additional requisites other than having the basic knowledge of JavaScript.
**Optional:** To complete this tutorial using your own app, you will need:

- An app created and configured for JavaScript at Back4app.
  - **Note: Follow the **<a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">**JavaScript Install Parse SDK tutorial**</a>** to learn how you can do that.**
:::

## 1 - Set up the environment

This guide uses the <a href="https://jsbin.com/" target="_blank">JSbin</a> platform as a code editor.
It’s very easy to use, all you need to do is open its main page and click on the HTML, JavaScript and Console buttons:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/F1dr16wL8lXj-IFiHtm5i_image.png)

The first step to start coding is to include the Parse API and to add your App’s keys.

For this tutorial, a public Back4app app has been created so that you can check your changes on the database without having to create your own app.

:::hint{type="info"}
**Optional: **To check the Parse database for this example, you need to create your own app and access the Parse Dashboard option.
:::

To include Parse API in your app, add the following line of code inside the HTML’s head tag:

```html
<script type="text/javascript" src="https://unpkg.com/parse/dist/parse.min.js"></script>
```

Then add your your credentials at the beginning of the JavaScript file. The default keys are the ones related to our public app.

```javascript
//Paste your Application Key and JavaScript Key, respectively
Parse.initialize("Your-Application-Id", "Your-Javascript-Key");
Parse.serverURL = "https://parseapi.back4app.com/";
```

In this tutorial, we will build the **CRUD Operations **based on a Pet class that has name and age fields, in which name is a string and the age is a number. Because of that, the code should start by creating a subclass of the Pet class so that it can be used later in our functions, as shown below:

```javascript
var Pet = Parse.Object.extend("Pet");
```

All of the basic operations will require the user to say what is the desired Pet’s name. That way, create a global variable “textName”. It’s also a good idea to create a “textAge” one, which will be used in create and update methods.



```javascript
var textName = "myName";
var textAge = 10;
```

## 2 - Create

The create function will create a new Pet with the name and age that you provided in the “textName” and “textAge” variables.

To build that function, just follow these steps:

1. Make a new instance of the Parse’s Pet class with the command
2. Use the set function to set the parameters for this object.
3. Call the save function, which will effectively register the pet to your database in theParse Dashboard.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/bozuguh/edit?html,js,console" target="_blank">Back4app JavaScript Create Function</a> to see the code that has already been implemented.
:::

The code for the create function is written below:

:::CodeblockTabs
create.js

```javascript
create();

function create() {
    mypet = new Pet();
    mypet.set("name", textName);
    mypet.set("agePet", textAge);

    mypet.save().then(function(pet){
         console.log('Pet created successful with name: ' + pet.get("name") + ' and age: ' + pet.get("agePet"));
    }).catch(function(error){
         console.log('Error: ' + error.message);
    });
}
```
:::

:::hint{type="info"}
To test it, paste this code snippet in the JavaScript file in the <a href="https://jsbin.com/?html,js,output" target="_blank">JSbin</a>, click on
the Run button in the console part and wait for the output.
It should print that the pet was created successfully.
To confirm that the new object is in the database, you can access the Parse Dashboard or you can code the read function.
:::

## 3 - Read

The read function is responsible for querying the database and returning the object that matches your search criteria. It can also be used to check the existence of an object.
Here’s the step-by-step guide for building your own read function:

1. Make an instance of the Parse’s Query class.
2. Add constraints to your query to restraint the search. More constraints options can be found in <a href="http://docs.parseplatform.org/js/guide/#query-constraints" target="_blank">Parse Query Documentation.</a>
3. Do a Query’s search method. This tutorial will use query.first to get only the first element that matches your criteria.
4. If the operations succeed, a pet object will be returned. If no object is found, the return object will have an value of **undefined**.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/roziroy/edit?html,js,output" target="_blank">Back4app JavaScript Read Function</a> to see the code that has already been implemented.
:::

The code for the read function is the following:

:::CodeblockTabs
read.js

```javascript
read();

function read() {
    query = new Parse.Query(Pet);
    query.equalTo("name", textName);
    query.first().then(function(pet){
        if(pet){
           console.log('Pet found successful with name: ' + pet.get("name") + ' and age: ' + pet.get("agePet"));
        } else {
           console.log("Nothing found, please try again");
        }
    }).catch(function(error){
        console.log("Error: " + error.code + " " + error.message);       
    });
}
```
:::

:::hint{type="info"}
To test the read function, paste the snippet to your JSBin JavaScript file. When the code runs, it wil print the age of the pet found (if found) or else will print that no pet was found.
:::

:::hint{type="danger"}
If while testing the printed age does not correspond to the age of your object, it means that there are
&#x20;more objects with the same name, but your query only returns one of them. So, to really test the read function, create an object with another name, one that no one has created yet, then run the function, which will correctly print the age of the object.
:::

## 4 - Update

For the update function, a pet is passed as parameter and the function changes it’s age to the one you provided in the “textAge” variable. To find the pet which will be passed, we use a modified version of our read function.

Below are the steps to make your own update function:

1. Write a modified read function called readThenUpdate, which calls the update function when it finds a pet successfully.
2. In the update function, use the set function to modify the parameters of your pet.
3. Call the save function for this pet to push the changes to the database.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/jidinim/edit?html,js,output" target="_blank">Back4app JavaScript Update Function</a> to see the code that has already been implemented.
:::

Here’s the code for the readThenUpdate function and update function:

:::CodeblockTabs
update.js

```javascript
readThenUpdate();

function readThenUpdate() {
    query = new Parse.Query(Pet);
    query.equalTo("name", textName);
    query.first().then(function (pet) {
      if (pet) {
        console.log('Pet found with name: ' + pet.get("name") + ' and age: ' + pet.get("agePet"));
        update(pet);
      } else {
        console.log("Nothing found, please try again");
      }
    }).catch(function (error) {
      console.log("Error: " + error.code + " " + error.message);
    });
}

function update(foundPet) {
    textName = "myNameUpdated";
    textAge = 20;
    console.log(textAge);
    foundPet.set('name', textName);
    foundPet.set('agePet', textAge);

    foundPet.save().then(function (pet) {
      console.log('Pet updated! Name: ' + pet.get("name") + ' and new age: ' + pet.get("agePet"));
    }).catch(function(error) {
      console.log('Error: ' + error.message);
    });
}
```
:::

:::hint{type="info"}
To confirm if the update function is working, paste the code above to the JavaScript file in the JSBin page.
Use an unusual name for your object to not conflict with other users, then follow these steps:

1\. Create an object with your desired name.
2\. Check that the object is created with your read function.
3\. Call your readThenUpdate function made in this topic with an age different than the original one.
4\. Check if the age of the Pet has changed by calling your read function again.
:::

## 5 - Delete

The delete function erases a pet received by the read function. It is an irreversible action, which means that you should be careful while using it, especially because your read function might return more objects than you actually want to delete. Because of that, it’s recommended to delete only one object at a time. The steps for writing your own delete function can be found below:

1. In the end of the success of your “read” function (readThenDelete in this example), make a call for the delete function.
2. In the deletePet function, call the destroy method on the received object “foundPet”.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/vubiqoq/edit?html,js,output" target="_blank">Back4app JavaScript Delete Function</a> to see the code that has already been implemented.
:::

Here’s the code for the readThenDelete function and deletePet function:

:::CodeblockTabs
delete.js

```javascript
readThenDelete();

function readThenDelete() {
    query = new Parse.Query(Pet);
    query.equalTo("name", textName);
    query.first().then(function (pet) {
        if (pet) {
            console.log('Pet found with name: ' + pet.get("name") + ' and age: ' + pet.get("agePet"));
            deletePet(pet);
        } else {
            console.log("Nothing found, please try again");
            return null;
        }
    }).catch(function (error) {
        console.log("Error: " + error.code + " " + error.message);
        return null;
    });
}

function deletePet(foundPet) {
    foundPet.destroy().then(function(response) {
      console.log('Pet '+ foundPet.get("name") + ' erased successfully');
    }).catch(function(response, error) {
      console.log('Error: '+ error.message);
    });
}
```
:::

:::hint{type="info"}
To test it, it’s recommended to create an object with an unusual name just like the other functions to
&#x20;not conflict with objects from other users.
Just paste the snippet to the JSBin and run the code with the name of your object and the object that will be deleted.
Then, you can call your read function to confirm that there are no objects with that name.
:::

:::hint{type="danger"}
If the read returns an object, which it shouldn’t, it probably means that you have more objects with the
&#x20;same name and it returned one of them as the delete function just deletes one object.
You can check your object by accessing your Parse Dashboard.
:::

## It’s done!

At this point, you have learned how to do the basic CRUD operations with JavaScript.

[title] Twitter login
[path] JavaScript/

# Twitter Login

## Introduction

This section explains how you can integrate Twitter Login into your Javascript project. After completing this step-by-step guide, you will be ready to upload your code to Cloud Code.

:::hint{type="danger"}
This project will use the newly released version 3.1 Parse Server. On your project dashboard, go to Server Settings > Manage Parse Server(settings) and select 3.1.1. For more information on migrating to Parse Server 3.1.x, see <a href="https://www.back4app.com/docs/advanced-guides/parse-server-3" target="_blank">this guide</a>.
See this guide if you do not understand the syntax of the cloud code for this project.
:::

## Prerequisites

:::hint{type="info"}
**To begin with this tutorial, you will need:**

- An app created at Back4App.
  - See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
  - See <a href="https://www.back4app.com/docs/platform/activating-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a> to learn how to create an subdomain in Back4App.
- A <a href="https://twitter.com/" target="_blank">Twitter account</a> and you must apply for <a href="https://twitter.com/i/flow/login?input_flow_data=%7B%22requested_variant%22%3A%22eyJyZWRpcmVjdF9hZnRlcl9sb2dpbiI6Imh0dHBzOi8vZGV2ZWxvcGVyLnR3aXR0ZXIuY29tL2VuL3BvcnRhbC9wZXRpdGlvbi9lc3NlbnRpYWwvYmFzaWMtaW5mbyJ9%22%7D" target="_blank">Developer access</a>.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Generate Access Tokens

In order for you to get the Twitter Login working in your project, you must have the Consumer Key and Consumer Secret.

To generate your access tokens keys, you must have a Twitter app. Basically, you have two options:

- If you have existing Twitter apps and you would like to edit them, please access the <a href="https://twitter.com/i/flow/login?input_flow_data=%7B%22requested_variant%22%3A%22eyJyZWRpcmVjdF9hZnRlcl9sb2dpbiI6Imh0dHBzOi8vZGV2ZWxvcGVyLnR3aXR0ZXIuY29tL2VuL2FwcHMifQ%3D%3D%22%7D" target="_blank">Twitter app dashboard</a>.
- If you want to create a new app, you can access the same dashboard, but you must have an approved developer account.

:::hint{type="warning"}
**Hint: **While you are requesting access for a developer account, you will certainly be asked to answer some questions, but don’t forget to agree with the terms of the contract.
:::

### **2.1 - Configure the app details**

In this topic, we’re going to configure the Twitter app credentials.

Note that it’s necessary to activate **WebHosting** to this application and to read more about WebHosting look at <a href="https://www.back4app.com/docs/platform/parse-web-hosting" target="_blank">Back4App WebHosting Tutorial</a>.

After that go to <a href="https://developer.twitter.com/en/apps" target="_blank">Twitter Dashboard</a> and choose to Create an app or you can edit an existing app going to Details > Edit > Edit details.

Fill up the:

- *App name*
- *Application description*
- *Tell us how this app will be used*

For the **Website URL** you can leave it as your [Back4App Subdomain](https://www.back4app.com/docs/platform/parse-web-hosting) and the **Callback URLs** will be the [subdomain](https://www.back4app.com/docs/platform/parse-web-hosting) + /twitter-callback. It will looks something like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xULOFfw_M7P5-mgdnbnLO_image.png" signedSrc size="60" width="516" height="358" position="center" caption}

## 3 - Twitter Set up

To start using the Twitter Login for JavaScript, you need to follow these steps:

1. To link Back4app with your Twitter App and log in to your Back4App account;
2. Click on Server Settings of your App:
3. Then, click on SETTINGS of the **Twitter Login** block. It should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cKRrWzxdF3MDIKFsJGXMf_image.png" signedSrc size="40" width="260" height="322" position="center" caption}

## 4 - Get the template

Download the template at our GitHub repository. You can do that using the following command line:

> curl -LOk https://github.com/templates-back4app/twitter-login-js/archive/master.zip && unzip master.zip

## 5 - Replace keys

After downloading the project above, please open the ./cloud/app.js file and replace the following variables with the keys generated in step 2:

> **const**
>
>  back4appWebhostDomain 
>
> **=**
>
>  '
>
> YOUR_BACK4APP_WEBHOST_DOMAIN
>
> ';
>
>
> **const**
>
>  consumer_key 
>
> **=**
>
>  '
>
> YOUR_CONSUMER_KEY
>
> ';
>
>
> **const**
>
>  consumer_secret 
>
> **=**
>
>  '
>
> YOUR_CONSUMER_SECRET
>
> ';

## 6 - Upload your code to Back4App server

To deploy your app with Back4App, you need to upload your code to Cloud Code. In order to do that, follow the steps below:

1. Go to your App at <a href="https://www.back4app.com/" target="_blank">Back4App website </a>and click on Server Settings.
2. Find the “Cloud Code” block and click on SETTINGS. The ''Cloud Code'' block looks like this:



::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eSoaWd-txhLEZ-MXoo941_image.png" signedSrc size="70" width="810" height="578" position="center" caption}

&#x20;    3\. Click on Choose Files and select all the files imported by index.html. Move them to public, then click SAVE, as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/n4p1MR8rvvC-w07KAloLp_image.png)

&#x20;      4\. Your files will look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_NIs0EluV1tao7gJH6mT__image.png" signedSrc size="70" width="533" height="538" position="center" caption}

## 7 - It’s done

After following the guide described above, you just need to open your browser with the Web Hosting address that you have created.

In case you need any help or a function/link doesn’t work, please contact our team via chat!

[title] Start from ObjC Template
[path] iOS/

# Start your iOS project from an App Template - ObjC

## Introduction

In this section you learn how to get started with an Xcode template and get ready to use Back4App in 3 easy steps.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
:::

## 1 - Get the template

Download the template at
<a href="https://github.com/back4app/ios-objective-c-quickstart-example/archive/master.zip" target="_blank">Back4App’s GitHub repository</a>, and unzip files in your project folder.

You can do that using the following command line:

> $ curl -LOk [https://github.com/back4app/ios-objective-c-quickstart-example/archive/master.zip](https://github.com/back4app/ios-objective-c-quickstart-example/archive/master.zip) && unzip master.zip

### Step 2 - Open the project template

1. Open Xcode.
2. Click on File->Open.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VlzfoMHxymrGCXz7VBH7a_image.png)

1. Navigate to the project folder and double click on QuickStartObjcExampleApp.xcworkspace.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/O6NlutV2l4eA0SrnALcNk_image.png)

1. Wait for Xcode to open the project.

## 3 - Setup app’s credentials

Update your App Delegate’s Parse Client Configuration values to set up the app’s credentials. Parse iOS SDK uses these settings to connect to the Back4App servers.

1. Open your App Delegate file: .../QuickStartObjcExampleApp/AppDelegate.m

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eucd2dITD5J6IDSBNpdRF_image.png)

1. Go to your App Dashboard at Back4App website.&#x20;
2. Navigate to app's settings: Click on Features > Core Settingsblock > Server.
3. Return to your AppDelegate.swift file and paste your applicationId and clientKey.

:::hint{type="success"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 4 - Test your connection

1. Build your app in a device or simulator (Command+R).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PUaUGxCIyzbqcPvPsZE42_image.png)

1. Wait until the Hello World! screen appears.
2. Login at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a>.
3. Find your app and click on Dashboard.
4. Click on Core.
5. Go to Browser.

If everything works properly, you should find a class named Installation as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/253dXzdiCT6_BMH1h1R8n_image.png)

## Next Steps

At this point, you have learned how to get started with iOS apps.

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Untitled
[path] /


[title] App security Guidelines
[path] Security & Privacy/

# How to make a Secure App using Parse

## Introduction

**Ahoy Back4app community!**

This is a guest tutorial from Joren Winge at [Startup Soul](http://startup-soul.com/). We help startups build and launch their products fast. Our friends @ Back4app asked us to show you how to build a secure app on top of Back4app.

In this post, we’ll walk you through the steps to make a secure To-Do app on back4app. Security is important. If your app takes off at all you will need to make sure your app’s data is secure and that your system can’t be hacked.

## Security features on Parse

Lets first talk about the first level of security, ACLs - (access control list). ACLs are basically just rules you set when you create an object. Let’s say you create a to-do item, at the time of creation you can say who the item can be read by, and who it can be written by. You can assign certain users to be able to read that item or write to that item or you can set either one to the public which allows access to anyone. But ACLs don’t always work.

There are instances where you might need to have some more sophisticated logic instead of a simple ACL. Sometimes you can also paint yourself in a corner where you may need to give someone access to an object on a conditional basis instead of an absolute basis like with ACLs. So let’s skip using ACLs. They are too rigid and at the same time allow for too much access to the data. So I’m about to give you the secret to building a secure app on Back4App and Parse Server. You ready?

Class level permissions! Yes, we will set class level permissions for each table in our database. And the level of permission we will set is no permission at all. We will lock down each and every table so that no read or write access is allowed to anyone! Sounds extreme, I know, but this is the first step in creating a secure app. The only permission we will allow is on the User table which will be for the creation of new user objects and for the user to view his own data which is required for refreshing the current user.

We will secure the user from being able to view other users data by using ACLs. This is the one time we will use ACLs so I guess they are not totally useless. They are good to have but don’t rely on them to do everything. But how will we access the data you ask? Good question, glad you are thinking about it! Well, the secret to letting clients access the data in a controlled manner is to make each and every single interaction between the client and database filtered through a cloud code function. Yes, anytime you do anything with your app it will now be through a custom cloud code function. No more client-based PFQueries.

You pretty much skip using the entire client based Parse SDK except for Sign Up functions, Sign In functions, Forgot Password functions, and Log Out functions. For these, we still will use the native client SDKs. It’s just easier. Have you ever written cloud code before? No, you say? Well, it’s pretty easy. It’s just Javascript and it uses the Parse Javascript SDK but internally on your own app’s server. In fact, since Parse Server is based on Node JS, it’s pretty similar to writing routes with Express, but even easier since your query language is already installed and cloud code functions are a lot easier to write than an entire Node JS Express app.

So here is what we will do. We will use an iOS todo app that I have already created. We won’t bother with showing you how I created it. Instead, we will focus on writing cloud code and securing the database. The todo app will be a secure app where you can only access your own todos and you can only write your own todos. The data will be secure on the server safe from malicious rogue clients. I will also show you how to write a secure Parse background job - basically, the same thing as a cron job - so that you can have automated services send manipulate your data on a schedule. Sounds complicated but it’s not. Just imagine little server robots that do whatever you want on an automated schedule. Sounds cool right? Ok, so here we go!!!!!!

## Let’s set up the Back4app Secure ToDo App

**1) Create an App on Back4App:**

1. Create a new app on Back4App. Call the app ‘Secure ToDo App’. \* Note: Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create an app at Back4App. .
2. Go into the app’s Core Settings page and then click on Edit App Details.
3. Disable the checkbox called ‘Allow Client Class Creation’ to disable client class creation and hit save. We want to limit what the client can do as a rule.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VNDlMYePW7A8bPrVK4-Yq_image.png)

**2) Set class level security permissions for the User class:**

1. Next we will set the permissions for the User class. Go into the Back4App database dashboard and click on the User class. Then click on the Security tab, then click on the gear icon in the top right. You should see a menu that says Simple/Advanced. Flip the slider to Advanced. You should then see the full class level permissions for this class. Disable the Find checkbox. Disable the Update and Delete checkbox. Finally, disable the Add Field checkbox. Then hit save. Your Security settings should look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FCpGNNzSvdsRQ8xJ1a_-G_image.png)

**3) Create the ToDo class:**

1. Hit Create a class and call it ToDo. Set the class type as custom.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eU61hrjMThuTLTBivShRg_image.png)

**4) Set class level security permissions for the ToDo class:**

1. Next we will set the permissions for the ToDo class. Go into the Back4App database dashboard and click on the ToDo class. Then click on the Security tab, then click on the gear icon in the top right. You should see a menu that says Simple/Advanced. Flip the slider to Advanced. You should then see the full class level permissions for this class. Disable everything then hit save. Your Security settings should look like this.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/DhWsbHPtTx_UEDJxuCyht_image.png)

**5) Let’s add some columns to the ToDo class:**

1. First let’s join the ToDo class to the User class. We will do that by adding 2 columns.
2. The first column will be called ‘user’ and will be a pointer back to the user class.
3. Next let’s create a column for the user’s object id who created it. It will be a string type and will be called ‘userObjectId’.
4. Next let’s create a column to hold our actual todo information. It will also be a string and will be call ‘todoDescription’.
5. Let’s create a Boolean to hold the state of the todo. Let’s call it ‘finished’.
6. Finally let’s add one more column to hold the date you finished your todo. Let’s call it ‘finishedDate’ and set it to a date type.
7. Your ToDo class should look like this

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/IPxsB-USsmjzrX1ySC2WW_image.png)

**6) Let’s go over the client:**

The client is a pretty basic to do app. It uses the built in parse functions to login, create a new user, and reset your password. Besides that everything is cloud code based and secure. The user’s ACL’s are also set as soon as they login or signup just to be 100% sure the system is secure. Let’s start by writing the cloud code function to set the user’s ACL upon logging in or signing up.

:::hint{type="info"}
**At any time, you can access the complete iOS Project built with this tutorial at this **<a href="https://github.com/mpc20001/secure-todo-app-swift-back4app-parse/tree/master/AddingParseSDK" target="_blank">**GitHub repository**</a>**.**
:::

:::hint{type="info"}
**You can also access the Main.js cloud code file built for this tutorial at this **<a href="https://github.com/mpc20001/SecureToDoCloudCodeRepo" target="_blank">**GitHub repository.**</a>****
:::



- 1 . In the client go to ToDoController.swift and look for the function setUsersAclsNow. This function is called when you login or view the LoggedInViewController.swift. The function checks to see if you are logged in and if you are it calls the cloud function to setup your personal user ACL’s.

:::CodeblockTabs
ToDoController.swift

```swift
1   func setUsersAclsNow(){
2           if PFUser.current() != nil{
3               let cloudParams : [AnyHashable:String] = ["test":"test"]
4               PFCloud.callFunction(inBackground: setUsersAcls, withParameters: cloudParams, block: {
5                   (result: Any?, error: Error?) -> Void in
6                   if error != nil {
7                       //print(error.debugDescription)
8                       if let descrip = error?.localizedDescription{
9                           print(descrip)
10                      }
11                  }else{
12                      print(result as! String)
13                  }
14              })
15          }
16      }
```
:::



- 2 . Now let’s write the cloud code function

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.define('setUsersAcls', async(request) => {
2       let currentUser = request.user;
3       currentUser.setACL(new Parse.ACL(currentUser));
4       return await currentUser.save(null, { useMasterKey: true });
5   });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define('setUsersAcls', function (request, response) {
2       var currentUser = request.user;
3       currentUser.setACL(new Parse.ACL(currentUser));
4       currentUser.save(null, {
5           useMasterKey: true,
6           success: function (object) {
7               response.success("Acls Updated");
8           },
9           error: function (object, error) {
10              response.error("Got an error " + error.code + " -  " + error.description);
11          }
12      });
13  });
```
:::

- 3 . This cloud code uses two key features of making your app secure, request.user and masterKey. Request.user let’s you access the user who is making the cloud code call and allows you to limit access for that user. In this case, we are using it to set the user’s acl’s to limit read access to the current user only. This way only the user can read their own information. The class level permissions prevent write access even for the current user. In this way users cannot modify their own information. They can only change things about their own user through cloud code. It is possible to import false information when the user first signs up, but I would reccomend writing a cloud code function to check the user’s information after a new user is created. The built in parse function for creating a new user is really helpful so i think it’s a decent tradeoff, but you can always set the default values for the user via cloud code right after they sign up. There are lots of fail safes you can also write into cloud code and have them run automatically and continously using background jobs to detect any malicious user information that was imported when the user was first created. If you want to be really secure, you can store any sensitive information like membership status or payment information on a separate table from the user table. That way the user cannot spoof any sensitive information on user creation.



- 4 . Next let’s look at creating a ToDo. In the client go to ToDoController.swift and look for the function saveToDo. This function is called when you create a new todo. The function takes a string that decribes the todo and saves it in the database.

:::CodeblockTabs
ToDoController.swift

```swift
1   func saveToDo(todoString:String, completion: @escaping (_ result: Bool, _ message:String, _ todoArray:[ToDo])->()){
2         var resultToDoArray:[ToDo] = []
3         let cloudParams : [AnyHashable:Any] = ["todoString":todoString]
4         PFCloud.callFunction(inBackground: createToDosForUser, withParameters: cloudParams, block: {
5             (result: Any?, error: Error?) -> Void in
6             if error != nil {
7                 if let descrip = error?.localizedDescription{
8                     completion(false, descrip, resultToDoArray)
9                 }
10            }else{
11                resultToDoArray = result as! [ToDo]
12                completion(true, "Success", resultToDoArray)
13            }
14        })
15    }
```
:::

- 5 . Now let’s write the cloud code function to save the todo in the database

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.define("createToDosForUser", async(request) => {
2       let currentUser = request.user;
3       let todoString = request.params.todoString;
4       let ToDo = Parse.Object.extend("ToDo");
5       let todo = new ToDo();
6       todo.set("user", currentUser);
7       todo.set("userObjectId", currentUser.id);
8       todo.set("todoDescription", todoString);
9       todo.set("finished", false);
10      return await todo.save(null, { useMasterKey: true });
11  });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define("createToDosForUser", function(request, response) {
2       var currentUser = request.user;
3       var todoString = request.params.todoString;
4       var ToDo = Parse.Object.extend("ToDo");
5       var todo = new ToDo();
6       todo.set("user", currentUser);
7       todo.set("userObjectId", currentUser.id);
8       todo.set("todoDescription", todoString);
9       todo.set("finished", false);
10      todo.save(null, {
11          useMasterKey: true,
12          success: function (object) {
13              response.success([todo]);
14          },
15          error: function (object, error) {
16              response.error("Got an error " + error.code + " - " + error.description);
17          }
18      });
19  });
```
:::



- 6 . This cloud code function creates a todo object and sets the current user as the owner of the object. This is important so that only the user who created it can find it or modify it. By not allowing todos to be created in the client we are forcing the todo object to conform to our standards and making sure the todos are owned by the user who created them.

&#x20;

- 7 . Next let’s look at retrieving the todos that you created from the server. In the client go to ToDoController.swift and look for the function getToDosForDate. This function is called when you retrieve your todos. The function takes a date as a parameter and uses it to retrieve a list of todos that were created by you before that date in descending order. Using a date is a great way to write a lazy loading query that doesn’t use skips. Skip can sometimes fail on a large dataset.

:::CodeblockTabs
ToDoController.swift

```swift
1   func saveToDo(todoString:String, completion: @escaping (_ result: Bool, _ message:String, _ todoArray:[ToDo])->()){
2         var resultToDoArray:[ToDo] = []
3         let cloudParams : [AnyHashable:Any] = ["date":date]
4         PFCloud.callFunction(inBackground: getToDosForUser, withParameters: cloudParams, block: {
5             (result: Any?, error: Error?) -> Void in
6             if error != nil {
7                 if let descrip = error?.localizedDescription{
8                     completion(false, descrip, resultToDoArray)
9                 }
10            }else{
11                resultToDoArray = result as! [ToDo]
12                completion(true, "Success", resultToDoArray)
13            }
14        })
15    }
```
:::



- 8 . Now let’s write the cloud code function to retrieve todos from the database based on a starting date. We query for todos that are created before the parameter date so we use ‘query.lessThan’ because dates are basically numbers that get larger the farther in the future you are. I also included some tricky code here. Say we are including the user object who created the todo but we don’t want to share sensitive information about that user with other users we need to strip it from the json response. So we have a for loop where we take the user object out of the todo, remove the email and username from the json and then put it back in the todo. This is handy for removing sensitive data from an api call in situations where you cannot control what fields you return - such as an included user object. In this case we don’t really need it because this function will only return todos you created yourself. We do this by using CurrentUser again to query for only todos created by the CurrentUser that was attached to the request. The results are returned in descending order so that the latest todos appear first. When you need to lazy load another batch of todos you take the createdAt date from the last todo and use it as the date parameter for the next request.

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.define("getToDosForUser", async(request) => {
2       let currentUser = request.user;
3       let date = request.params.date;
4       let query = new Parse.Query("ToDo");
5       query.equalTo("user", currentUser);
6       query.lessThan("createdAt", date);
7       query.descending("createdAt");
8       query.limit(100);
9       query.include("user");
10      let results = await query.find({ useMasterKey: true });
11      if(results.length === 0) throw new Error('No results found!');
12
13      let resultsArray = [];
14      for (let i = 0; i < results.length; i++) {
15          let todo = results[i];
16          let tempUser = todo.get("user");
17          let jsonUser = tempUser.toJSON();
18          delete jsonUser.email;
19          delete jsonUser.username;
20
21          jsonUser.__type = "Object";
22          jsonUser.className = "_User";
23
24          let cleanedTodo = todo.toJSON();
25          cleanedTodo.user = jsonUser;
26          cleanedTodo.__type = "Object";
27          cleanedTodo.className = "ToDo";
28          resultsArray.push(cleanedTodo);            
29      }
30      return resultsArray;
31  });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define("getToDosForUser", function(request, response) {
2       var currentUser = request.user;
3       var date = request.params.date;
4       var query = new Parse.Query("ToDo");
5       query.equalTo("user", currentUser);
6       query.lessThan("createdAt", date);
7       query.descending("createdAt");
8       query.limit(100);
9       query.include("user");
10      query.find({
11          useMasterKey: true,
12          success: function (results) {
13              var resultsArray = [];
14              for (var i = 0; i < results.length; i++) {
15                  var todo = results[i];
16                  var tempUser = todo.get("user");
17                  var jsonUser = tempUser.toJSON();
18                  delete jsonUser.email;
19                  delete jsonUser.username;
20
21                  jsonUser.__type = "Object";
22                  jsonUser.className = "_User";
23
24                  var cleanedTodo = todo.toJSON();
25                  cleanedTodo.user = jsonUser;
26                  cleanedTodo.__type = "Object";
27                  cleanedTodo.className = "ToDo";
28                  resultsArray.push(cleanedTodo);
29                }
30              response.success(resultsArray);
31          },
32          error: function (error) {
33              response.error("- Error - " + error.code + " " + error.message);
34          }
35     });
36  });
```
:::

- 9 . Now that we have the todos we can see them in the app and mark them as completed if we want. Lets cover that next.



- 10 . To mark a todo as completed just hit the ‘Mark As Completed’ button on any of the todos you created. This will fire off a method in the ToDoController.swift called ‘markToDosAsCompletedFor’ that takes the todo you selected as a parameter. It sends the todo.objectId to the server as a parameter and then returns the updated todo as a result.

:::CodeblockTabs
ToDoController.swift

```swift
1   func markToDosAsCompletedFor(todo:ToDo, completion: @escaping (_ result: Bool, _ message:String, _ todoArray:[ToDo])->()){
2           var resultToDoArray:[ToDo] = []
3           let cloudParams : [AnyHashable:Any] = ["todoId":todo.objectId ?? ""]
4           PFCloud.callFunction(inBackground: markToDoAsCompletedForUser, withParameters: cloudParams, block: {
5               (result: Any?, error: Error?) -> Void in
6               if error != nil {
7                   if let descrip = error?.localizedDescription{
8                       completion(false, descrip, resultToDoArray)
9                   }
10              }else{
11                  resultToDoArray = result as! [ToDo]
12                  completion(true, "Success", resultToDoArray)
13              }
14          })
15      }
```
:::



- 11 . Now we’ll write the cloud code to update this todo. It looks for the todo to update based on the objectId but it also uses the CurrentUser to make sure that the todo that is associated with the objectId was created by the user making the query. This makes sure that you can only view todos that you created and is thus secure. We include a limit of 1 result to make sure the server doesn’t continue to search after finding the todo. There’s another method for finding an object based on an objectId but I don’t like to use it since it can return weird results if it doesn’t find the object associated with the objectId. We are also setting the ‘finishedDate’ with the current date when the object was updated. By having the finishedDate set by this function only we’ve made sure the finishedDate is secure and cannot be faked or changed. We also used ‘query.equalTo(“finished”, false)’ to make sure that only an unfinished todo can be marked as finished and have the finishedDate set. That means once a todo has been marked as finished it can never be marked finished again at a later date.

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.define("markToDoAsCompletedForUser", async(request) => {
2     let currentUser = request.user;
3     let todoId = request.params.todoId;
4     let query = new Parse.Query("ToDo");
5     query.equalTo("user", currentUser);
6     query.equalTo("objectId", todoId);
7     query.equalTo("finished", false);
8     let todo = await query.first({ useMasterKey: true });
9     if(Object.keys(todo).length === 0)  throw new Error('No results found!');
10    todo.set("finished", true);
11    let date = new Date();
12    todo.set("finishedDate", date);
13    try {
14      await todo.save(null, { useMasterKey: true}); 
15      return todo;   
16    } catch (error){
17      return("getNewStore - Error - " + error.code + " " + error.message);
18    }
19  });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define("markToDoAsCompletedForUser", function(request, response) {
2       var currentUser = request.user;
3       var todoId = request.params.todoId;
4       var query = new Parse.Query("ToDo");
5       query.equalTo("user", currentUser);
6       query.equalTo("objectId", todoId);
7       query.equalTo("finished", false);
8       query.limit(1);
9       query.find({
10          useMasterKey: true,
11          success: function (results) {
12              if (results.length > 0) {
13                  var todo = results[0];
14                  todo.set("finished", true);
15                  var date = new Date();
16                  todo.set("finishedDate", date);
17                  todo.save(null, {
18                      useMasterKey: true,
19                      success: function (object) {
20                          response.success([todo]);
21                      },
22                      error: function (object, error) {
23                          response.error("Got an error " + error.code + " : " + error.description);
24                      }
25                  });
26              } else {
27                  response.error("ToDo not found to update");
28              }
29
30          },
31          error: function (error) {
32              response.error("- Error - " + error.code + " " + error.message);
33          }
34      });
35  });
```
:::

**7) Wrap Up!**

1. And that’s it. You have built a secure todo app. Again, the key to making a secure app on parse server is disabling all class level permissions for all classes except for the User class. On the User class, you disable all permissions except CREATE, and GET. Also make sure to set all user’s ACL’s so that the user can only GET their own data. Then all your interactions go through cloud code and are filtered using the request.user aka the CurrentUser. So there ya go, you can now build secure systems on top of Parse server and Back4App. But wait you say? What about Background Jobs and Live Queries. Well, you have a good point, so I will cover that in two bonus sections next.



**8) Bonus Sections**

- 1 . Background jobs - sometimes you need to create a background job to run every hour, or every day or every week. If you are running with all you class level permissions turned off, your background job will not be able to query the database unless it’s set up correctly. This is kind of tricky to do so I want to include an example of it here. In this case we will create a background job that checks the database for unfinshed todos that are more than 1 year old and then automatically marks them as finished. The trick here is using ‘useMasterKey’ correctly.
  It has to be added to the query before the .then promise. Just follow this template and you should be able to write secure background jobs easily. You always start with writing a query that you want to iterate over the entire database and then make sure to include status.error if there is an error and end it with a status.success to make sure it completes. You can watch the logs on Back4App to see the background job working while you run it.

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.job("markUnfinishedToDosOlderThan1YearAsFinished", async(request) => {
2       let date = new Date();
3       let intYear = date.getFullYear() - 1;
4       let query = new Parse.Query("ToDo");
5       query.equalTo("finished", intYear);
6       query.lessThan("createdAt", date);
7 
8       let todo = await query.find({ useMasterKey: true });
9       for (let i = 0; i < results.length; i++) {
10          let todo = results[i];
11          todo.set("finished", true);
12          todo.set("finishedDate", date);
13          try {
14              await todo.save(null, { useMasterKey: true});    
15          } catch (error){
16              console.log("getNewStore - Error - " + error.code + " " + error.message);
17          }
18      } 
19      return "Migration completed successfully.";
20  });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define("markToDoAsCompletedForUser", function(request, response) {
2       var currentUser = request.user;
3       var todoId = request.params.todoId;
4       var query = new Parse.Query("ToDo");
5       query.equalTo("user", currentUser);
6       query.equalTo("objectId", todoId);
7       query.equalTo("finished", false);
8       query.limit(1);
9       query.find({
10          useMasterKey: true,
11          success: function (results) {
12              if (results.length > 0) {
13                  var todo = results[0];
14                  todo.set("finished", true);
15                  var date = new Date();
16                  todo.set("finishedDate", date);
17                  todo.save(null, {
18                      useMasterKey: true,
19                      success: function (object) {
20                          response.success([todo]);
21                      },
22                      error: function (object, error) {
23                          response.error("Got an error " + error.code + " : " + error.description);
24                      }
25                  });
26              } else {
27                  response.error("ToDo not found to update");
28              }
29
30          },
31          error: function (error) {
32              response.error("- Error - " + error.code + " " + error.message);
33          }
34      });
35  });
```
:::



- 2 . Live Queries - sometimes you need to use Parse’s Live Query feature for something like a live chat app. You will want to use the live query to see when new messages for your user are created. Live Query is basically just Parse’s way of using sockets to get live updates. It’s pretty handy but it will not work with a class who’s FIND permissions have been turned off. So in this case we will turn the FIND permissions back on for the Message class and instead we will assign ACL’s for that message directly. The ACL should be set so that only the recipient can use a FIND to get the message from the server. Then you run your PF Live Query in your client looking for messages for your user and it will work flawlessly. If you are dealing with group messages, it’s a bit different. You can assign multiple people to be on the ACL but, it really doesn’t scale. Instead, there is a better way. You set the ACL to be based on a Role - Parse.Role - and then any user you want to have access to that message you just assign them to that Parse.Role. If you want to stop them from reading the messages for that group, you remove them from that role. This is way easier than removing them from every single message’s ACL and it scales for super large groups. This is the correct way to do it. I’m not going to leave a code sample for this as it’s too complex for this tutorial, but maybe I’ll explain how to do it in my next one. Thank you for reading this tutorial on security with Parse and Back4App. If you have questions feel free to <a href="mailto:iosdev22@gmail.com" target="_blank">contact me</a> and I’ll be happy to answer them.

Thanks! Joren

[title] WhatsApp
[path] Cloud Code Functions/Integrations/

# Using cloud functions to send WhatsApp messages through Twilio API

## Introduction

In this guide, we will explain how you can use Twilio’s streamlined REST API to send WhatsApp messages easily. After completing this tutorial, you can use a cloud code function to send WhatsApp messages to your customers. So, let’s get down to business.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:




- An app created at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Back4App Command Line Configured with the project.
- Follow the <a href="https://www.back4app.com/docs/local-development/parse-cli" target="_blank">Setting up Cloud Code tutorial</a> to learn how to set up cloud code for a project.
- Account created in <a href="https://login.twilio.com/u/signup?state=hKFo2SBSTHoyUXZ1NnUzTjZaMHk2Q3EtaXV5LUtnUlhaMzdtN6Fur3VuaXZlcnNhbC1sb2dpbqN0aWTZIE9JTzFKSU9LaVMzTV9fYVo0VDM0a2pnSEpZM09SYUo4o2NpZNkgTW05M1lTTDVSclpmNzdobUlKZFI3QktZYjZPOXV1cks" target="_blank">Twilio</a>.
:::

## Let’s get started!

As you may know, the Facebook-owned WhatsApp has recently rolled out its first version of API for businesses to integrate and communicate seamlessly with customers. By using WhatsApp this service to the Bussiness API, companies can send customized notifications with pertinent, non-promotional messages, such as booking confirmations, appointment reminders, and delivery alerts, to their opted-in customers.

Twilio is a cloud communication platform that offers a robust feature to communicate and prototype with WhatsApp Bussiness API immediately. For now, the Twilio API for WhatsApp is in BETA and only allows you to send text messages to a WhatsApp user. In other words with that service, it’s not yet possible to send images, audio, videos and pdf files to users.

The feature used to send or receive WhatsApp messages to the user is “Twilio Sandbox”. In order to send messages to a user’s WhatsApp number, you will need to activate the sandbox first, and need to activate the number that will join your sandbox. In step 1, we will explain how you can activate your Twilio Sandbox for WhatsApp.

:::hint{type="info"}
**Note: **The Twilio Sandbox has <a href="https://www.twilio.com/docs/whatsapp/api#sandbox-limitations" target="_blank">some limitations</a>, and the major one is that you can only send to or receive messages from those users who have joined your specific sandbox. However, this limitation can be overcome if you enable WhatsApp using your own Twilio number. In order to activate WhatsApp on your own number, you need to submit a request for approval directly at <a href="https://www.twilio.com/docs/whatsapp/api#enabling-whatsapp-with-a-twilio-number" target="_blank">Twilio Console</a>.
:::

In this guide, we will explain how you can use a simple REST API to send and receive messages directly on WhatsApp while employing as a middleware. We will write and implement a Cloud Function that will interact with a Twilio API to send those messages. Once this function will be triggered by a saving event, we call this function an AfterSave.

To create or access an Account in Twilio, check the links given below:

<a href="https://www.twilio.com/try-twilio" target="_blank">Create a new account</a> - <a href="https://www.twilio.com/login" target="_blank">Log in to your account</a>

## 1 - Activate your WhatsApp Beta

After logging in to an existing account, you will be redirected to your Project. But, if you are a new user, you’ll first need to create a project and select programmable SMS from products. You should now see the recently created project on your console. Next, you must click on the programmable SMS and select the 4th option WhatsApp Beta and then follow the Steps given in that section to activate the Twilio Sandbox for WhatsApp. Same as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Zh_JtnPvd9BXCUzForN8q_image.png)

## 2 - Get Account SID and Auth Token

To find your Account SID and Auth Token, log in to your Twilio Account, go to your Dashboard and click on Settings. All the important information about your Project will be available in that section. Make sure that all these instructions are followed as shown in the image below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kkaxBERJI8j7fUgZu6U8z_image.png" signedSrc size="70" width="450" height="425" position="center" caption}

Now, you can Copy your SID and Authentication Token for the Cloud Code.

## 3 - Install Module from Twilio

After configuring the environment for the Command Line Interface in your computer, create a file called package.json, and inside this file, you need to install the Twilio module, like:

```json
1   {
2     "dependencies": {
3       "twilio": "*"
4     }
5   }
```

## 4 - Implement Cloud Code

In this section, we will show you know how to work with the [Cloud Functions](https://docs.parseplatform.org/cloudcode/guide/#cloud-functions).

We will build an afterSave Trigger function to activate and send the confirmation message that the object has been saved.

```javascript
1   Parse.Cloud.afterSave("Contact", (request) => {
2
3     // Requiring the values to send
4     var
5         getPhoneTo   = request.object.get("phone"),
6         getFirstName = request.object.get("firstName"),
7         getPhoneFrom = "+Your Phone number", //Remember to replace your number enable on Twilio Sandbox
8         accountSid = 'AccountSID',
9         authToken  = 'AuthToken',
10        getMessage   = "Welcome " + getFirstName +", to Twilio App! Thank you for your interest, our team will contact you ASAP! ;)";
11
12    //require the Twilio module and create a REST client
13    var client = require('twilio')(accountSid, authToken);
14
15    client.messages
16        .create(
17        {
18            from: "whatsapp:" + getPhoneFrom,
19            body: getMessage,
20            to: "whatsapp:" + getPhoneTo
21        })
22        .then(message => console.log(message.sid))
23        .done();  
24  });
```

## 5 - Test the AfterSave Trigger

Now that we have created and activated the afterSave trigger, it’s time to test the function whether it’s working faultlessly or not. You can also test the function in client SDKs, but for now, we will use the REST API command to save a new User:

```curl
curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Natália", "phone":"+0000000000000"}' \
  https://parseapi.back4app.com/classes/Contact
```

And the result will be similar to the screenshot below.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/gxPyaasg1-D1aiHmAHNZ6_image.png" signedSrc size="50" width="338" height="600" position="center" caption}

## 6 - It’s done!

With the guide described above, you’ll be able to use Twilio with a Cloud Code Function in Back4App and send WhatsApp messages to your opted-in customers!

In case you need any help or a function/link doesn’t work, please contact our team via chat!

[title] SendGrid
[path] Cloud Code Functions/Integrations/

# Using Sendgrid email API

## Introduction

This section explains how you can integrate SendGrid with a Cloud Code function. After completing this guide with step-by-step instructions, you will be ready to use your function in your App and call it at your iOS or Android App.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:




- An app created at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Back4App Command Line Configured with the project.
- Follow the <a href="https://www.back4app.com/docs/local-development/parse-cli" target="_blank">Setting up Cloud Code tutorial</a> to learn how to set up cloud code for a project.
- Account created in <a href="https://login.twilio.com/u/signup?state=hKFo2SBSTHoyUXZ1NnUzTjZaMHk2Q3EtaXV5LUtnUlhaMzdtN6Fur3VuaXZlcnNhbC1sb2dpbqN0aWTZIE9JTzFKSU9LaVMzTV9fYVo0VDM0a2pnSEpZM09SYUo4o2NpZNkgTW05M1lTTDVSclpmNzdobUlKZFI3QktZYjZPOXV1cks" target="_blank">Twilio</a>.
:::

## Let’s get started!

We will write a function using SendGrid that you will be able to work with a lot from possibilities as Delivering messages to our customers and configuring parameters to use the SendGrid v3 REST API.

To learn how to create or access an Account in SendGrid, check the links given below:

<a href="https://signup.sendgrid.com/" target="_blank">Create a new account</a> - <a href="https://app.sendgrid.com/login" target="_blank">Log in to your account</a>

## 1 - Create a SendGrid API Key

The most important step before start to code is create the correct keys to setup your environment. After accessing your account, locate in the Settings drop-down menu, the API Keys option as in the picture below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/S5lS0LacIcaC-xBz3Krya_image.png" signedSrc size="30" width="162" height="207" position="center" caption}

After that, at the top right locate and choose an identification to theAPI Key Name, like as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/LqdAOYbNuSKMivIdMPwD8_image.png)

:::hint{type="info"}
As you can see at the image above, it’s necessary to select one option to allow the *Full Access *to the API Key.
:::

After click on the Create & View to proceed with creating of the key, you will be able to see the screen below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-saVIVCBs8al5vGK3c4In_image.png)

:::hint{type="warning"}
**Hint**: Be careful to write it down, as there is no way to retrieve it. Click on the text to copy it.
:::

## 2 - Add a function to the Cloud Code

The main strategy to this way of using SendGrid’s API is to create a function on the Cloud Code named sendgridEmail and call it from the App.

### **2.1 - Install module from Sendgrid**

Create a file called package.json, and inside this file, you need to install the Twilio module, like:

```json
1   { 
2	   "dependencies": {
3		   "@sendgrid/mail": "*"
4	   }
5   }
```

### **2.2 - Implement Cloud Code**

You should note that every email field has to be send by the App – from the subject to the content – as a parameters. The code is as follows:

:::CodeblockTabs
Parse Server 3.X

```javascript
1   Parse.Cloud.define("sendgridEmail", async(request) => {
2       const sgMail = require('@sendgrid/mail');
3
4       // Import SendGrid module and call with your SendGrid API Key
5       sgMail.setApiKey("your SendGrid API key here");
6    
7       const msg = {
8           to: request.params.toEmail,
9           replyTo: 'info@youremail.com',
10          from: 'info@youremail.com',
11           subject: request.params.subject,
12          text: request.params.body
13      };
14
15      try{
16        await sgMail.send(msg);
17        return 'OK'
18      } catch (e){
19        return `Error: ${e.message}`
20      }
21     
22  });
```

Parse Server 2.X

```javascript
1   Parse.Cloud.define("sendgridEmail", (request, response) => {
2       const sgMail = require('@sendgrid/mail');
3
4       // Import SendGrid module and call with your SendGrid API Key
5       sgMail.setApiKey("your SendGrid API key here");
6    
7       const msg = {
8           to: request.params.toEmail,
9           replyTo: 'info@youremail.com',
10          from: 'info@youremail.com',
11           subject: request.params.subject,
12          text: request.params.body
13      };
14    
15      sgMail.send(msg).then(() => {
16         response.success("The message was sent!");
17      })
18      .catch(error => {    
19          //Log friendly error
20          response.error(error.toString());
21      });
22  });
```
:::

:::hint{type="warning"}
**Hint**: Remember to change the fields from and reply\_to to your personal information.
:::

Then it is necessary to implement a call to the Cloud Code function on the app.

## 3 - Call Cloud Code function

In this current step, we can work with two possibilities to call our function, they’re: Android and iOS (Swift and Objective-C).

:::CodeblockTabs
Android

```java
1   Map<String, String> params = new HashMap<>();
2
3   // Create the fields "emailAddress", "emailSubject" and "emailBody"
4   // As Strings and use this piece of code to add it to the request
5   params.put("toEmail", emailAddress);
6   params.put("subject", emailSubject);
7   params.put("body", emailBody);
8
9   ParseCloud.callFunctionInBackground("sendgridEmail", params, new FunctionCallback<Object>() {
10      @Override
11      public void done(Object response, ParseException exc) {
12          if(exc == null) {
13              // The function executed, but still has to check the response
14          }
15          else {
16              // Something went wrong
17          }
18      }
19  });
```

iOS(Swift)

```swift
1   PFCloud.callFunctionInBackground("sendgridEmail", withParameters: [
2       // These fields have to be defined earlier
3       "toEmail": toEmail,
4       "subject": subject,
5       "body": body
6   ]) { (response, error) in
7       if error == nil {
8           // The function executed, but still has to check the response
9       } else {
10          // The function returned an error
11      }
12  }
```

iOS(Objective-C)

```objective-c
1   [PFCloud callFunctionInBackground:@"sendgridEmail"
2	   withParameters:@{@"toEmail": toEmail,
3	             	    @"subject": subject,
4	              	    @"body": body}
5	   block:^(NSString *myAlertMsg, NSError *error){
6		   if(!error) {
7		      // The function executed, but still has to check the response
8		   }
9		   else {
10		     // The function returned an error
11		  }
12	  }
13  ];
```
:::

## 4 - It’s done!

And that’s it for the SendGrid usage. Note that you might want to use some sort of authentication before allowing anyone to use your SendGrid API to send emails.

In case you need any help or a function/link doesn’t work, please contact our team via chat!

[title] Email Verification
[path] JS Framework/Ionic/

## User registration with email verification

### Introduction

This section explains how you can create an app with user registration and email verification using <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

This is how it will look like:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/c7_HTGlsLsEc0AcyZJ-Jk_image.png)

:::hint{type="success"}
At any time, you can access the complete Ionic Project built with this tutorial at our <a href="https://github.com/back4app/ionic-email-verification" target="_blank">GItHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://code.visualstudio.com/download" target="_blank">Visual Studio Code </a>(or any web IDE you like).
- <a href="https://ionicframework.com/getting-started/" target="_blank">Ionic Framework </a>
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- Followed the <a href="https://www.back4app.com/docs/js-framework/ionic/ionic-framework-login" target="_blank">User Registration</a> tutorial to learn how to implement sign up, login and log out with back4app.
:::

## 1 - Set up

In this tutorial, we will be starting from where we left off in the previous [User Registration](https://www.back4app.com/docs/ionic/ionic-framework-login-screen) one.

## 2 - Enable Email Verification

1. Go to your App at [Back4App Website ](https://www.back4app.com/)and click on Server Settings.
2. Find the “Verification emails” block and click on Settings. The “Verification emails” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3bu-2FLDQV0u_HLu1DlBq_image.png)

&#x20;     3\. Click on Verify User Email. It is right here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/AAm7YFMWHE0vI1U5gIl6P_image.png)



&#x20;   4\. Fill in the empty fields and modify the ones that have already been filled based on your preferences.

&#x20;   5\. Click on the SAVE button.

## 3 - Sign Up

The two fundamental attributes of ParseUser class are **username** and **password**. There’s a third special attribute that you should also set, i.e. the **email**.
To implement Sign Up with Email Verification, you will use the same method as the basic user registration. But this time, instead of sending the user to the Home page, you will ask him/her to verify his/her email to login.

Once the user creation is completed, it is automatically added to Parse Dashboard and its **emailVerified** Boolean attribute is set as **false**. At this point, the user should not be allowed to log into your platform. Once he/she verifies his/her e-mail, by clicking on the link sent to his/her mailbox, the **emailVerified** boolean will be automatically set to **true**, enabling him/her to access your platform entirely.

To make SignUpActivity work, follow these steps:

Add the isSigningup and email variables to login.ts to toggle and hold the e-mail input:

:::CodeblockTabs
login.ts

```typescript
1   // Parse Dependencies
2     email: string;
3     isSigningup: boolean;
```
:::

Make the signUp() method send the e-mail address to the parse User.signUp() function:

:::CodeblockTabs
login.ts

```typescript
1   signUp() {
2       Parse.User.signUp(this.username, this.password, {email: this.email}).then((resp) => {
3           console.log('Signed up successfully', resp);
4
5           // Clears up the form
6           this.username = '';
7           this.password = '';
8           this.email = '';
9
10          this.toastCtrl.create({
11          message: 'Account created successfully',
12          duration: 2000
13          }).present();
14
15          this.isSigningup = false;
16      }, err => {
17          console.log('Error signing in', err);
18
19          this.toastCtrl.create({
20          message: err.message,
21          duration: 2000
22          }).present();
23      });
24  }
```
:::

Now, let’s reflect those changes to the view login.html by adding \*ngIf to show/hide html elements whenever the user is signing up (**isSigningup** is equal to **true**) or signing in (**isSigningup** is equal to **false**).

:::CodeblockTabs
login.html

```html
1     <ion-item *ngIf="isSigningup">
2       <ion-label stacked>E-mail</ion-label>
3       <ion-input type="email" [(ngModel)]="email"></ion-input>
4     </ion-item>
5
6     <div text-center margin-top *ngIf="!isSigningup">
7       <button ion-button margin-right (click)="isSigningup = true">
8         SIGN UP
9       </button>
10
11      <button ion-button color="secondary" (click)="signIn()">
12        SIGN IN
13      </button>
14    </div>
15
16    <div text-center margin-top *ngIf="isSigningup">
17      <button ion-button (click)="signUp()">
18        SIGN UP
19      </button>
20    </div>
```
:::

## 4 - Log in

Now, let’s add the **emailVerified** boolean verification before sending the user to the Home page.

:::hint{type="info"}
**Note**: Although the user logs in when the function Parse.User.logIn() is called, he/she can’t access the app until the e-mail verification is done.
Also, because of a Session object is created in the database when calling logIn(), it’s important to call Parse.User.logout() every time a user who hasn’t verified his/her e-mail tries to access the application in order to not leave Sessions opened.
:::

Now, let’s implement the **emailVerified **verification to decide whether the user logs in or get an alert saying e-mail must be verified:

:::CodeblockTabs
login.ts

```typescript
1   // Parse Dependencies
2   signIn() {
3       Parse.User.logIn(this.username, this.password).then((user) => {
4           console.log('Logged in successfully', user);
5
6           if(user.get('emailVerified')) {
7               // If you app has Tabs, set root to TabsPage
8               this.navCtrl.setRoot('HomePage')
9           } else {
10              Parse.User.logOut().then((resp) => {
11                  console.log('Logged out successfully', resp);
12              }, err => {
13                  console.log('Error logging out', err);
14              });
15
16              this.alertCtrl.create({
17                  title: 'E-mail verification needed',
18                  message: 'Your e-mail address must be verified before logging in.',
19                  buttons: ['Ok']
20              }).present();
21          }
22      }, err => {
23          console.log('Error logging in', err);
24
25          this.toastCtrl.create({
26          message: err.message,
27          duration: 2000
28          }).present();
29      });
30  }
```
:::

## 5 - Test your app

1. Test your app by running ionic serve and creating a couple of users, also try logging in after registering without verifying the email to see if the error is actually displayed.
2. Login at [Back4App Website.](https://www.back4app.com/)&#x20;
3. Find your app and click on Dashboard > Core > Browser > User to see the users that you’ve created!

## It’s done!

At this stage, you can login, register or logout of your app using email verification with Parse Server core features through Back4App!

[title] using CC and ObjC
[path] iOS/Send Push Notifications/

# Sending push notifications using cloud code with Objective-C

## Introduction

This section explains how you can send push notifications using Cloud Code through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wKKlp6QQvyOFUTlwBBriR_image.png" signedSrc size="40" width="640" height="1136" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- An iOS app set up via <a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank">Back4App Push Notifications via Dashboard tutorial</a>.
- An iOS device, iphone or ipad, running iOS 10 or newer.
- A paid Apple developer Account.
:::

## 1 - Set up your iOS app to receive push

Every Parse application installed on a device registered for push notifications has an associated Installation object. The Installation object is where you store all the data needed to target push notifications. For example, in your app, you could store which teams one of your users is interested in to send updates about their performance. Saving the Installation object is also required for tracking push-related app open events.

The simplest way to start sending notifications is using channels. This allows you to use a publisher-subscriber model for sending pushes. Devices start by subscribing to one or more channels, and notifications can later be sent to these subscribers. The channels subscribed to by a given Installation are stored in the channels field of the Installation object.

After that we will go over sending targeted push notifications to a single user or to a group of users based on a query.

:::hint{type="danger"}
Going forward we are going to assume you have completed all steps of the<a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank"> Back4App Push Notifications via Dashboard tutorial</a>, even if you use the iOS Project built with this tutorial that is available at our <a href="https://github.com/mpc20001/ios-objc-push-cloud-code" target="_blank">GitHub repository</a>.
You should have basic push notifications working and also be able to send pushes out via the admin console.
:::

## 2 - Subscribe your device to the News channel

1. First we will add a channel to your installation object. We are going to do this by altering the method createInstallationOnParse in our App Delegate file. Open your project’s AppDelegate.m file, and add make sure your version of didRegisterForRemoteNotificationsWithDeviceToken is the same as the code below. We are adding one new line of code - ‘\[currentInstallation setObject:@\[@”News1”] forKey:@”channels”];’ - which will set the installation object’s channel array to contain one channel called ‘News’. This will allow us to send a message to everyone who subscribes to the channel called ‘News’ via cloud code.



> \- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
>  // Store the deviceToken in the current Installation and save it to Parse
>  PFInstallation *currentInstallation = [PFInstallation currentInstallation];
>  \[currentInstallation setDeviceTokenFromData:deviceToken];
>  \[currentInstallation setObject:@[@"News1"] forKey:@"channels"];
>  \[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>      if (!error) {
>          NSLog(@"installation saved!!!");
>      }else{
>          NSLog(@"installation save failed %@",error.debugDescription);
>      }
>  }];
> }

&#x20;      2\. Test it by running your app on a physical device. You cannot run this on simulator. You’ll need an actual push token to update your installation record so a physical device is a must. After it runs successfully you should see this in the installation section of your dashboard. You can check by going to <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and click on your app’s dashboard and then check the installation table. Under the the channels column you should see ‘News’, showing you that you are now subscribed to the News push channel.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Go0-0G9rO_d1fosUKlDFm_image.png)

## 3 - Create your Cloud Code

:::hint{type="info"}
To know more about how to get started with **Cloud Code **look at <a href="https://www.back4app.com/docs" target="_blank">Cloud Code for iOS Tutorial</a>.
:::

1. Create a .js file to put your Cloud Code into. You need to call it main.js in order for Back4App to know this is where you store your cloud code.
2.
   Define a Cloud function, using Parse.Cloud.Define, to call the push notification. Inside the function we will call Parse.Push.Send to send a pushes to the ‘News’ channel.

:::hint{type="danger"}
It is required to use the **master key **in this operation.
:::

The following code executes these steps:

:::CodeblockTabs
Parse Server 3.X

```javascript
// main.js
1   Parse.Cloud.define("pushsample", (request) => {
2
3       return Parse.Push.send({
4           channels: ["News"],
5           data: {
6               title: "Hello from the Cloud Code",
7               alert: "Back4App rocks!",
8           }
9       }, { useMasterKey: true });
10  });
```

Parse Server 2.X

```javascript
//main.js
1   Parse.Cloud.define("pushsample", function (request, response) {
2       Parse.Push.send({
3               channels: ["News"],
4               data: {
5                   title: "Hello from the Cloud Code",
6                   alert: "Back4App rocks!",
7               }
8          }, {
9               success: function () {
10                  // Push was successful
11                  response.success("push sent");
12                  console.log("Success: push sent");
13              },
14              error: function (error) {
15                  // Push was unsucessful
16                  response.error("error with push: " + error);
17                  console.log("Error: " + error);
18              },
19              useMasterKey: true
20         });
21  })
```
:::

## 4 - Upload to Cloud Code

1. Go to your App at<a href="https://www.back4app.com/" target="_blank"> Back4App Website</a> and click on Dashboard.
2. Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/cM-hOeH2GGq04PhYwvKyS_image.png)

&#x20;    3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click at Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/jdZ-bp3IcWh-8Qg42UJG9_image.png)

## 5 - Call Cloud Code from your iOS App

1. Next we are going to write some code to call this cloud function from your app. You will need both the simulator and a physical device to complete this task. You will call the cloud function from your app running in the simulator and you will see the push appear on your physical device. Your physical device should actually be closed with the lock screen on in order to see the push. A push will not appear on the screem if you are inside the app that is sending it when you receive the push.
2. Open your project’s ViewController.m file. We need to include Parse in the view controller by adding the following code - ‘#import \<Parse/Parse.h>’ at the top of the file.



> \#import "ViewController.h"
> \#import <Parse/Parse.h>

&#x20;    3\. Next in the ViewController.m file we will call an alert function from the viewDidAppear method. The alert will allow you to trigger the cloud code code which will send a push to your device. Be sure to include the following block of code after the viewDidLoad function.

<a href="https://github.com/mpc20001/ios-objc-push-cloud-code/blob/master/AddingParseSDKObjc/AppDelegate.m#L29-L62" target="_blank">`ViewController.m`</a>&#x20;

> \- (void)viewDidAppear:(BOOL)animated {
>  \[self askToSendPushnotifications];
> }
> \- (void)askToSendPushnotifications {
>  UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Send a push to the news channel"
>                                                                 message:nil
>                                                          preferredStyle:UIAlertControllerStyleAlert];
>  UIPopoverPresentationController *popPresenter = [alert popoverPresentationController];
>  popPresenter.sourceView = self.view;
>  UIAlertAction *Okbutton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
>      \[self sendPushNotifications];
>  }];
>  \[alert addAction:Okbutton];
>  UIAlertAction *cancelbutton = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
>
>  }];
>  \[alert addAction:cancelbutton];
>  popPresenter.sourceRect = self.view.frame;
>  alert.modalPresentationStyle = UIModalPresentationPopover;
>  \[self presentViewController:alert animated:YES completion:nil];
> }
>        \- (void)sendPushNotifications {
>  \[PFCloud callFunctionInBackground:@"pushsample"
>                     withParameters:@{}
>                              block:^(id object, NSError *error) {
>                                  if (!error) {
>                                      NSLog(@"PUSH SENT");
>                                  }else{
>                                      NSLog(@"ERROR SENDING PUSH: %@",error.localizedDescription);
>                                  }
>                              }];
> }

&#x20;   4\. Run your app in the simulator and when the alert asking to send the push pops up hit “OK”. On your physical device you should see the push appear on the lock screen.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ijZml6qcgZiYvbGwmfnWb_image.png" signedSrc size="40" width="640" height="1136" position="flex-start" caption}

## 6 - Call Cloud Code from REST API

The REST API provides a quick and easy way to test if your Cloud function is working.
Just use the code below in your terminal or command prompt:

:::hint{type="info"}
Click on to know more about how to get started with command line in <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-the-linux-terminal" target="_blank">Linux</a>, <a href="https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line" target="_blank">MacOS</a>
or <a href="https://www.bleepingcomputer.com/tutorials/windows-command-prompt-introduction/" target="_blank">Windows</a>.
:::

> curl -X POST -H "X-Parse-Application-Id: YOUR_APP_ID_HERE" \
> -H "X-Parse-REST-API-Key: YOUR_REST_API_KEY_HERE" \
> -H "Content-Type: application/json" \
> -d ‘{ // Put the function parameters here in JSON format }’ \
> https://parseapi.back4app.com/functions/pushsample

To test the push notifications, just use the REST code while the device is closed.

## 7 - Send Targeted Push Notifications Using a User Object

:::hint{type="info"}
Going forward we will be using a different iOS project that has a basic signup and signin features already built. We are going to be using this iOS project that we can show you how to detect if a user is logged in, and if so save their installation to with a link to their object id for querying in cloud code. You can download the complete iOS Project built with this section’s tutorial at our <a href="https://github.com/mpc20001/ios-objc-targeted-push-cloud-code" target="_blank">GitHub repository</a> but you will still have to do all of the setup from the previous tutorial that explains how to send pushes fromt he Back4App dashboard.
:::

1. Get the new version of the app setup and signup or login to the app. First make sure you download the working template from Github at <a href="https://github.com/mpc20001/ios-objc-targeted-push-cloud-code" target="_blank">GitHub repository</a>. We are not going to walk through all the steps of building this app, instead we will focus on setting up the cloud code and why it works. Once you open this new app be sure to put your own app’s credentials in the AppDelegate.m file.&#x20;



> \- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
>  // Override point for customization after application launch.
>  \[Parse initializeWithConfiguration:[ParseClientConfiguration configurationWithBlock:^(id<ParseMutableClientConfiguration> configuration) {
>      configuration.applicationId = @"PASTE_YOUR_APPLICATION_ID_HERE";
>      configuration.clientKey = @"PASTE_YOUR_CLIENT_ID_HERE";
>      configuration.server = @"https://parseapi.back4app.com/";
>  }]];
>  return YES;
> }

&#x20;    2\. This app has some major differences between the previous app. It features 2 sections, one for being logged into your app and one section when you are not logged in to your app. The next big change is the AppDelegate.m file’s function ‘didRegisterForRemoteNotificationsWithDeviceToken’. We’ve added 1 line that store’s the user’s object id as part of the installation object. That way we can know which user is associated with which installation object and can target them individually for pushes.

&#x20;

> \- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
>  // Store the deviceToken in the current Installation and save it to Parse
>  PFInstallation *currentInstallation = [PFInstallation currentInstallation];
>  \[currentInstallation setDeviceTokenFromData:deviceToken];
>  \[currentInstallation setObject:@[@"News"] forKey:@"channels"];
>  \[currentInstallation setObject:PFUser.currentUser.objectId forKey:@"userId"];
>  \[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>      if (!error) {
>          NSLog(@"installation saved!!!");
>      }else{
>          NSLog(@"installation save failed %@",error.debugDescription);
>      }
>  }];
> }

&#x20;    3\. Since we are now storing the user’s object id as part of the installation object we do not want to request a new push token until the user is logged in. We do not want to request a token directly from AppDelegate.m file’s function ‘application didFinishLaunchingWithOptions’ instead we want to call it from the LoggedInViewController’s function ‘viewDidAppear’. In ‘viewDidAppear’ we call a function on the AppDelegate to request access to a push notification token from Apple. Since you can only view this section once you are logged in we can assume the user is logged in when we create the installation object and retrieve the object id.



> \- (void)viewDidAppear:(BOOL)animated {
>  AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
>  \[delegate registerForRemoteNotifications];
> }



> \[currentInstallation setObject:PFUser.currentUser.objectId forKey:@"userId"];

&#x20;   4\. Ok, now to sign up or login. On your physical device - (iphone or ipad) start the app. You should see the image below. You should sign Up to create a new user or sign in if you have already created a user on your app.
This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nqoTbBkYjc1EgWNP83LsS_image.png" signedSrc size="30" width="750" height="1334" position="flex-start" caption}

You should now be able to see the LoggedInviewController. It should look like this.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3His1rs0WHDoel_4J70sq_image.png" signedSrc size="28" width="750" height="1334" position="flex-start" caption}

If you try to send pushes to yourself it won’t work yet because we haven’t added those methods to cloud code. So that’s what we will do next.

## 8 - Add the targeted Push Methods to Cloud Code

Open your Main.js file that you created previously and add the following functions to target installations by user id.

:::hint{type="danger"}
It is required to use the **master key **in this operation.
:::

The following code executes these steps:

:::CodeblockTabs
Parse Server 3.X

```javascript
// main.js
1   Parse.Cloud.define('sendPushToYourself', (request) => {
2       let userId = request.user.id;
3
4       let query = new Parse.Query(Parse.Installation);
5       query.equalTo("userId", userId);
6       query.descending("updatedAt");
7       return Parse.Push.send({
8           where: query,
9        data: {
10              title: "Hello from the Cloud Code",
11              alert: "Back4App rocks! Single Message!",
12          }
13      }, { useMasterKey: true });    
14  });
15
16  Parse.Cloud.define('sendPushToAllUsers', (request) => {
17      let currentUser = request.user;
18      let userIds = [currentUser.id];
19
20      let query = new Parse.Query(Parse.Installation);
21      query.containedIn('userId', userIds);
22      return Parse.Push.send({
23          where: query,
24          data: {
25              title: "Hello from the Cloud Code",
26              alert: "Back4App rocks! Group Message!",
27          }
28      }, { useMasterKey: true });
29  });
```

Parse Server 2.X

```javascript
//main.js
1   Parse.Cloud.define('sendPushToYourself', function (request, response) {
2       var currentUser = request.user;
3       var userId = currentUser.id;
4
5       var query = new Parse.Query("Installation");
6       query.equalTo("userId", userId);
7       query.descending("updatedAt");
8       Parse.Push.send({
9           where: query,
10          data: {
11              title: "Hello from the Cloud Code",
12              alert: "Back4App rocks! Single Message!",
13          }
14      }, {
15          useMasterKey: true,
16          success: function () {
17             response.success("success sending a single push!");
18          },
19          error: function (error) {
20              response.error(error.code + " : " + error.description);
21          }
22      });
23  });
24
25  Parse.Cloud.define('sendPushToAllUsers', function (request, response) {
26      var currentUser = request.user;
27      var userIds = [currentUser.id];
28  
29      var query = new Parse.Query(Parse.Installation);
30      query.containedIn('userId', userIds);
31      Parse.Push.send({
32          where: query,
33          data: {
34              title: "Hello from the Cloud Code",
35              alert: "Back4App rocks! Group Message!",
36          }
37      }, {
38          useMasterKey: true,
39          success: function () {
40              response.success('Success sending a group push!');
41          },
42          error: function (message) {
43              response.error(error.code + " : " + error.description);
44          }
45      });
46  });
```
:::

## 9 - Upload to Cloud Code

1. Go to your App at <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and click on Dashboard.
2.
   Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6z28wpouFf6ElQsP37Ycg_image.png" signedSrc size="50" width="810" height="578" position="center" caption}

3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click at Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8b26jNv2FJ4uenFGTATwU_image.png)

## 10 - Test that you can send Targeted push Notifications to yourself

Open your app from the simulator while leaving your physical device closed with the lock screen on. You can test that both push functions are working by pressing the ‘send push to a yourself’ button and the ‘send push to a group of people’ button. You should see the pushes appear on your devices lock screen.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/np9ER977_OIFEYdd2XAdt_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

## Final Thoughts

This concludes the tutorial. You should have a firm understanding of how to send pushes based on a user’s channel or a user’s object id or any other query that involves getting the user’s object id. Remember in order to store the user’s object id you must add it to the push installation and only request a push token when the user is logged in. When sending pushes via query be aware that it is limited by default to 100 results and some users may have more than one instllation object. Also it is not reccomended to send pushes to array of installation objects that are larger than 100 results. It could result in some pushes not getting sent. if you are dealing with large groups of people it is better to use channels or to send the pushes out in repeated requests.

## It’s done!

At this stage, you can send push notifications using Cloud Code through Back4App! Congratulations!

[title] User Registration - login
[path] JavaScript/

# Add JavaScript user registration and log in to your Parse App

## Introduction

This section explains how to do a basic user registration with email verification in a JavaScript environment through <a href="https://www.back4app.com/" target="_blank">Back4app</a>.

In this tutorial, you will use Parse.User object and will learn its most important functions.

:::hint{type="success"}
See more about Parse SDK at <a href="https://parseplatform.org/Parse-SDK-JS/api/4.3.1/" target="_blank">Parse JavaScript SDK API Reference</a> and <a href="https://docs.parseplatform.org/js/guide/" target="_blank">Parse open source documentation for JavaScript SDK</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- A basic JavaScript app connected with Back4app or JSBin connected with our Parse API.
- **Note: **You can use the app created in our <a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">JavaScript Install Parse SDK tutorial</a> or use the same online environment <a href="https://jsbin.com/?html,js,output" target="_blank">JSBin</a> with the setup done in the J<a href="https://www.back4app.com/docs/javascript/serverless-database" target="_blank">avaScript Database Operations tutorial</a>.
:::

## 1 - Sign Up

The user sign-up function is similar to the create function used in the <a href="https://www.back4app.com/docs/javascript/serverless-database" target="_blank">JavaScript Database Operations tutorial</a>, but it has some additional benefits:

- Check if the username and email are unique.
- Securely hashes the password in the cloud. Not even the developer can see the user’s password.
- Requires at least a username and a password. You can use the email as a username if you want to.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/guhikig/edit?html,js,console" target="_blank">Back4app JavaScript Sign Up Function</a> to see the code that has already been implemented.
:::

To make your own signUp function, you need to repeat the same steps of the create function explained in the <a href="https://www.back4app.com/docs/javascript/serverless-database" target="_blank">Javascript CRUD tutorial</a> but call the method user.signUp instead of the save method, as shown below:

:::CodeblockTabs
signUp.js

```javascript
signUp();

function signUp() {
    // Create a new instance of the user class
    var user = new Parse.User();
    user.set("username", "my name");
    user.set("password", "my pass");
    user.set("email", "email@example.com");

    // other fields can be set just like with Parse.Object
    user.set("phone", "415-392-0202");

    user.signUp().then(function(user) {
        console.log('User created successful with name: ' + user.get("username") + ' and email: ' + user.get("email"));
    }).catch(function(error){
        console.log("Error: " + error.code + " " + error.message);
    });
}
```
:::

:::hint{type="danger"}
- Be aware that **Error 202** or **Error 203** is likely to occur if you don’t change the username or the email.
-
  The **Error 209** *invalid season* token is also likely to occur when your browser cookies conflict
  &#x20;with your Parse’s Current Session. To bypass that, clear your browser cookies or open the
  &#x20;incognito mode of your browser.
  To confirm that the new user has been added to the database, you can access your Parse Dashboard or code the login function which will be provided ahead.
:::

## 2 - Email Verification

An important feature of a sign-up method is email verification.
Fortunately, it is easy to configure it using Back4App. To enable email verification, login to your account, find your app, and click on Server Settings.
Find the “Verification Emails” box and click on SETTINGS. Here’s how the “Verification Emails” box looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/NDs8FvH4tfewWxQZRzLaW_image.png)

Then enable the verification by checking the box below.

:::hint{type="info"}
If you are using the cloud environment of JSBin, then there is no need to complete this step.
:::

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7W_ZO1xEu0wKLOd2IZZWH_image.png)

:::hint{type="info"}
By enabling this, the user’s class in your database receives one additional field: verifiedEmail.
This field is set to true when the email is verified, false if the email isn’t verified, and
undefined if the user was created before this setting was checked.
:::

On that page, you can also customize the email, change the subject, the body, and the sender’s email and name.

:::hint{type="info"}
To see how the email looks like, just create a user, using the signUp function,
with an email that you can access. You should receive an email for verification.
:::

## 3 - Login

The login function is very simple and just needs a password and a username to run.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/delazew/edit?html,js,console" target="_blank">Back4app JavaScript Login Function</a> to see the code that has already been implemented.
:::

You need to call the Parse.User.logIn method is as follows:

:::CodeblockTabs
login.js

```javascript
logIn();

function logIn() {
    // Create a new instance of the user class
    var user = Parse.User
        .logIn("myname", "mypass").then(function(user) {
            console.log('User created successful with name: ' + user.get("username") + ' and email: ' + user.get("email"));
    }).catch(function(error){
        console.log("Error: " + error.code + " " + error.message);
    });
}
```
:::

## 4 - Reset Password

It’s **very important** to add the **Reset Password** option as users are likely to forget their password in the future.
The configuration for the email that will be sent in the reset password function is on the same
page as in the **Email Verification step**. There you can change the body and the subject of the email.

:::hint{type="info"}
You can open the <a href="https://jsbin.com/guwuben/edit?html,js,console,output" target="_blank">Back4app JavaScript Reset Password Function</a> to see the code that has already been implemented.
:::

To send the Reset Password email, just run the following code:

:::CodeblockTabs
resetpassword.js

```javascript
resetPassword();

function resetPassword() {
    Parse.User.requestPasswordReset("email@example.com").then(function() {
      console.log("Password reset request was sent successfully");
    }).catch(function(error) {
      console.log("The login failed with error: " + error.code + " " + error.message);
    });
}
```
:::

## It’s done!

At this point, you have learned not only how to do User Registration with JavaScript apps, but also how to send email verification and password reset emails.

[title] from client side (ObjC)
[path] iOS/Send Push Notifications/

# Send push notifications from client side in Objective-C

## Introduction

This section explains how you can send push notifications through your iOS client with Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/I1AU-eH4ONf-ogYS7R_8B_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- An iOS app set up via <a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank">Back4App Push Notifications via Dashboard tutorial</a>.
- An iOS device, iphone or ipad, running iOS 10 or newer.
- A paid Apple developer Account.
:::

:::hint{type="danger"}
Going forward we are going to assume you have completed all steps of the<a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank"> Back4App Push Notifications via Dashboard tutorial</a>, even if you use the iOS Project built with this tutorial that is available at our <a href="https://github.com/mpc20001/ios-objc-push-cloud-code" target="_blank">GitHub repository</a>.
You should have basic push notifications working and also be able to send pushes out via the admin console.
:::

## 1 - Enable Client Push

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website. </a>log in, find your app and click on Server Settings.
2. Find the “Core Settings” block and click on SETTINGS. The “Core Settings” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PuXmcH_B8_A-BdxssHm6n_image.png)

&#x20;    3\.  Scroll to the end of the page and click on the EDIT DETAILS button, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/fLsT6XfjxNTSJO1pGuPxC_image.png)

&#x20;    4\. You will see a checkbox called Allow Push Notification from Client in the end of the edit page, tick that box and click on the SAVE button, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0XGl4x5H8n2BX_LdQ-2EF_image.png)

## 2 - Subscribe your device to the News channel

1. Assuming you have completed the [Back4App Push Notifications via Dashboard tutorial](https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service), you will want to modify the completed project from that tutorial or download it from our <a href="https://github.com/back4app/ios-objc-push" target="_blank">GitHub repository</a>. First, you will add a channel to your Installation object. You are going to do this by altering the method createInstallationOnParse in your AppDelegate file. Open your project’s AppDelegate.m file and add the following line of code - ‘**\[currentInstallation setObject:@\[@”News1”] forKey:@”channels”];**’ - which will set the installation object’s channel array to contain one channel called News.

<a href="https://github.com/mpc20001/ios-objc-push-client/blob/master/AddingParseSDKObjc/AppDelegate.m#L52-L63" target="_blank">AppDelegate.m</a>

> \- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
>  // Store the deviceToken in the current Installation and save it to Parse
>  PFInstallation *currentInstallation = [PFInstallation currentInstallation];
>  \[currentInstallation setDeviceTokenFromData:deviceToken];
>  \[currentInstallation setObject:@[@"News"] forKey:@"channels"];
>  \[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
>      if (!error) {
>          NSLog(@"installation saved!!!");
>      }else{
>          NSLog(@"installation save failed %@",error.debugDescription);
>      }
>  }];
> }

:::hint{type="success"}
This will allow you to send a message to everyone who subscribes to the channel called News via cloud code
:::

:::hint{type="info"}
Make sure your version of didRegisterForRemoteNotificationsWithDeviceToken is the same as the code below.
:::

&#x20;      2\. Next, we will add a method to your app delegate to send a push to the News channel everytime the app launches. Open your project’s AppDelegate.m file and the method below and make sure this method is fired off everytime the app launches by calling it from didFinishLaunchingWithOptions.



> \- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
>  // Override point for customization after application launch.
>  \[Parse initializeWithConfiguration:[ParseClientConfiguration configurationWithBlock:^(id<ParseMutableClientConfiguration> configuration) {
>      configuration.applicationId = @"7Ez7z1DfvGFFAXFi8pjhYBOtTGqeU89eSccLBBVN";
>      configuration.clientKey = @"fySO7DEPIC39LmWJLvugLMTKdlWsLVOmsSZGksqq";
>      configuration.server = @"https://parseapi.back4app.com/";
>  }]];
>  \[self registerForRemoteNotifications];
>  \[self sendPushOnLaunch];
>  return YES;
> }
> \- (void)sendPushOnLaunch {
>  PFPush *push = [[PFPush alloc] init];
>  \[push setChannel:@"News"];
>  \[push setMessage:@"Push From Device"];
>  \[push sendPushInBackground];
> }

## 3 - Test that you can send targeted push notifications to yourself via the client

Open your app from the simulator while leaving your physical device closed with the lock screen on.

You should see the pushes appear on your device’s lock screen as soon as the app opens on the simulator.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZzLbDQRGFIwqM__UDcxqP_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

## Final Thoughts

You should have a firm understanding of how to send pushes from the client.

You can combine it with a pfquery to target users based on some sort of property like age, location, or object id.

:::hint{type="danger"}
Just remember that if client push is enabled it **can be exploited **and **can’t be turned off without restricting all client pushes**. It’s **recommended that you tick to pushes from Cloud Code**, but it’s still good to know.
:::

## It’s done!

At this stage, you can send push notifications using Client Push through Back4App!

[title] from client side (Swift)
[path] iOS/Send Push Notifications/

# Send push notifications from client side in Swift

## Introduction

This section explains how you can send push notifications through your iOS client with Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N6XcUW-VUuLNwzDiPqxOD_image.png" signedSrc="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N6XcUW-VUuLNwzDiPqxOD_image.png" size="36" width="640" height="1136" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- An iOS app set up via <a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank">Back4App Push Notifications via Dashboard tutorial</a>.
- An iOS device, iphone or ipad, running iOS 10 or newer.
- A paid Apple developer Account.
:::

:::hint{type="danger"}
Going forward we are going to assume you have completed all steps of the<a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank"> Back4App Push Notifications via Dashboard tutorial</a>, even if you use the iOS Project built with this tutorial that is available at our <a href="https://github.com/mpc20001/ios-objc-push-cloud-code" target="_blank">GitHub repository</a>.
You should have basic push notifications working and also be able to send pushes out via the admin console.
:::

## 1 - Enable Client Push

1. Go to <a href="https://www.back4app.com/" target="_blank">Back4App Website. </a>log in, find your app and click on Server Settings.
2. Find the “Core Settings” block and click on SETTINGS. The “Core Settings” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/br0y9ydiIJ-MGcuigWBh9_image.png)

&#x20;    3\.  Scroll to the end of the page and click on the EDIT DETAILS button, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/tA1g08ntmcgBz8DctuBWy_image.png)

&#x20;    4\. You will see a checkbox called Allow Push Notification from Client in the end of the edit page, tick that box and click on the SAVE button, as shown below:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PBxZPk1qKAcGHdU4ODAQI_image.png)

## 2 - Subscribe your device to the News channel

1. Assuming you have completed the [Back4App Push Notifications via Dashboard tutorial](https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service), you will want to modify the completed project from that tutorial or download it from our <a href="https://github.com/back4app/ios-objc-push" target="_blank">GitHub repository</a>. First, you will add a channel to your Installation object. You are going to do this by altering the method createInstallationOnParse in your AppDelegate file. Open your project’s AppDelegate.swift file and add the following line of code - ‘**Installation setObject(\[”News1”] forKey:”channels”];**’ - which will set the installation object’s channel array to contain one channel called News.

:::CodeblockTabs
AppDelegate.m

```swift
1   func createInstallationOnParse(deviceTokenData:Data){
2       if let installation = PFInstallation.current(){
3           installation.setDeviceTokenFrom(deviceTokenData)
4           installation.setObject(["News"], forKey: "channels")
5           installation.saveInBackground {
6               (success: Bool, error: Error?) in
7               if (success) {
8                   print("You have successfully saved your push installation to Back4App!")
9               } else {
10                  if let myError = error{
11                      print("Error saving parse installation \(myError.localizedDescription)")
12                  }else{
13                      print("Uknown error")
14                  }
15              }
16          }
17      }
18  }
```
:::

:::hint{type="success"}
This will allow you to send a message to everyone who subscribes to the channel called News via cloud code
:::

:::hint{type="info"}
Make sure your version of didRegisterForRemoteNotificationsWithDeviceToken is the same as the code below.
:::

&#x20;      2\. Next, we will add a method to your app delegate to send a push to the News channel everytime the app launches. Open your project’s AppDelegate.swift file and the method below and make sure this method is fired off everytime the app launches by calling it from didFinishLaunchingWithOptions.

:::CodeblockTabs
AppDelegate.m

```swift
1   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
2        let configuration = ParseClientConfiguration {
3            $0.applicationId = "PASTE_YOUR_APPLICATION_ID_HERE"
4            $0.clientKey = "PASTE_YOUR_CLIENT_ID_HERE"
5            $0.server = "https://parseapi.back4app.com"
6        }
7        Parse.initialize(with: configuration)
8
9        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge, .carPlay ]) {
10           (granted, error) in
11           print("Permission granted: \(granted)")
12           guard granted else { return }
13           self.getNotificationSettings()
14       }
15       sendPushOnLaunch()
16       return true
17   }
18   func sendPushOnLaunch(){
19       let push = PFPush()
20       push.setChannel("News")
21       push.setMessage("Push From Device")
22       push.sendInBackground()
23   }
```
:::

## 3 - Test that you can send targeted push notifications to yourself via the client

Open your app from the simulator while leaving your physical device closed with the lock screen on.

You should see the pushes appear on your device’s lock screen as soon as the app opens on the simulator.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/miCD1avjvfKiZPeDraEiY_image.png" signedSrc size="28" width="640" height="1136" position="flex-start" caption}

## Final Thoughts

You should have a firm understanding of how to send pushes from the client.

You can combine it with a pfquery to target users based on some sort of property like age, location, or object id.

:::hint{type="danger"}
Just remember that if client push is enabled it **can be exploited **and **can’t be turned off without restricting all client pushes**. It’s **recommended that you tick to pushes from Cloud Code**, but it’s still good to know.
:::

## It’s done!

At this stage, you can send push notifications using Client Push through Back4App!

[title] Untitled
[path] /


[title] MFA- User Account
[path] Security & Privacy/

# How to Implement Multi-Factor Authentication in your Back4App Account

## Introduction

This section explains how you can setup and configure MFA to your Back4App Account. After completing this, step-by-step, you will be one step closer to secure your accounts and data.

## Prerequisites

:::hint{type="info"}
**To get started with this tutorial, you will need:**

- An account created in <a href="https://www.back4app.com/" target="_blank">Back4App</a>
- You can download the app from <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en" target="_blank">Google Play Store</a><a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8" target="_blank"> or App Store</a>
:::

# What is MFA?

MFA is the abbreviation for Multi-Factor Authentication that is a method to confirm a user’s identify by validating several (two or more) authentication stages. We deliver strong authentication with the simple sign-in process to provide an extra layer of security to our clients. Also, we add additional layers of protection not just on top of clients’ username and password, but also on their sensitive information that only they need to know.

## Why is this necessary?

This newly implemented method will assegure more security for your account and prevent attackers to bypass the security filter. For example, if your account and password are compromised, you will have another layer of protection to keep cybercriminals at bay.

# 3 Steps to enable MFA on your device

After creating your account in Back4App and downloading the Google Authentication app (see [prereqs](https://www.back4app.com/docs/security/multi-factor-authentication#content-prerequisites)), you can easily configure it on your device.

## 1 - log in to your Back4App account and generate an access token

After successfully accessing your account with credentials, go to the menu and hover your cursor over theHello, \< username >button in the top-right corner, and then click on Multi-Factor Authentication:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Wc0AskPqaDaP5hHAYJUMO_image.png" signedSrc size="60" width="262" height="219" position="center" caption}

## 2 - Scan the generated access token

Next, you will need to click on the red button ‘+’ in your device and scan the code generated on the screen from Back4App. After scanning the code, it will auto-generate a six-digit number, like the image below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WWOihZDG3ws8JJ8OOeJCU_image.png" signedSrc size="80" width="600" height="338" position="center" caption}

Then, you need to insert into Multi-Factor Authentication in your Back4App Account and click on the button Confirm Multi-Factor Authorization.

## 3 - MFA Activated

If you follow all the above mentioned steps and enter the correct code, you will be redirected to a Back4App success page!

**Now, we’re ready to test it!**

Hover your cursor over the Hello, \< username > button in the top-right corner of your device and click on the Logout link. Afterward, when you’ll try to login to your account, you’ll be able to see a new field like in the image below:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FcnG0Gr_xzXqgWVDFMcSj_image.png" signedSrc size="40" width="351" height="515" position="center" caption}

Are you able to Log in? If yes, then congratulations! It seems like that you’ve followed this tutorial and all the steps splendidly. If you are facing any error or need any help to configure MFA on your device, please contact our Support Team!

[title] User Registration
[path] JS Framework/Ionic/

# How to add a login screen to your Ionic framework project

## Introduction

In this section you learn how to create a page, implement sign up, sign in and sign out to your Ionic app.

This is how it will look like:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PC-d303k9wdHVghTjEwg0_image.png)

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://code.visualstudio.com/download" target="_blank">Visual Studio Code </a>(or any web IDE you like).
- <a href="https://ionicframework.com/getting-started/" target="_blank">Ionic Framework </a>
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
:::

:::hint{type="success"}
At any time, you can access the complete Ionic Project built with this tutorial at our <a href="https://github.com/back4app/ionic-email-verification" target="_blank">GItHub repository</a>.
:::

## 1 - Install parse SDK

Considering you have an existing Ionic project, the first thing you need to do is to install parse SDK. You can do it by running:

> $ npm install parse

## 2 - Set up app’s credentials

1. Open your app.component.ts, import parse and initialize its connection to Back4App Parse server.

:::CodeblockTabs
app.component.html

```html
1   Parse.initialize("YOUR-APP-ID", "YOUR-JS-KEY");
2   Parse.serverURL = 'https://parseapi.back4app.com/';
```
:::

:::hint{type="info"}
If you don’t know how to find your keys, check out the first Ionic tutorial <a href="https://www.back4app.com/docs/js-framework/ionic/ionic-template#setup" target="_blank">Start From Template</a>.
:::

## 3 - Create the LogIn Page

Now, let’s create our LogIn page. Luckly, Ionic does everything to us. All we need to do is to run the following command:

> $ ionic generate page Login

In this page view, we need to add inputs for username and password and two buttons, one for signing up and another one for signing in.

:::CodeblockTabs
login.html

```html
1     <h4 text-center margin-top>Insert your credentials</h4>
2
3     <ion-item>
4       <ion-label stacked>Username</ion-label>
5       <ion-input [(ngModel)]="username"></ion-input>
6     </ion-item>
7
8     <ion-item>
9       <ion-label stacked>Password</ion-label>
10      <ion-input type="password" [(ngModel)]="password"></ion-input>
11    </ion-item>
12
13    <div text-center margin-top>
14      <button ion-button margin-right (click)="signUp()">
15        SIGN UP
16      </button>
17
18      <button ion-button color="secondary" (click)="signIn()">
19        SIGN IN
20      </button>
21    </div>
```
:::

Let’s implement now the methods signIn() and signUp() referred in our Login view.

:::CodeblockTabs
login.ts

```typescript
1     signUp() {
2       Parse.User.signUp(this.username, this.password).then((resp) => {
3         console.log('Logged in successfully', resp);
4
5         // Clears up the form
6         this.username = '';
7         this.password = '';
8
9         this.toastCtrl.create({
10          message: 'Account created successfully',
11          duration: 2000
12        }).present();
13      }, err => {
14        console.log('Error signing in', err);
15
16        this.toastCtrl.create({
17          message: err.message,
18          duration: 2000
19        }).present();
20      });
21    }
```
:::

:::hint{type="info"}
Learn more about signUp() at <a href="https://parseplatform.org/Parse-SDK-JS/api/v1.11.1/Parse.User.html#.signUp" target="_blank">Parse Documentation</a>.
:::

:::CodeblockTabs
login.ts

```typescript
1     signIn() {
2       Parse.User.logIn(this.username, this.password).then((resp) => {
3         console.log('Logged in successfully', resp);
4
5         // If you app has Tabs, set root to TabsPage
6         this.navCtrl.setRoot('HomePage')
7       }, err => {
8         console.log('Error logging in', err);
9
10        this.toastCtrl.create({
11          message: err.message,
12          duration: 2000
13        }).present();
14      });
15    }
```
:::

:::hint{type="info"}
Learn more about Parse.User.logIn() at <a href="https://parseplatform.org/Parse-SDK-JS/api/v1.11.1/Parse.User.html#.signUp" target="_blank">Parse Documentation</a>.
:::

This is how it should look like.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/efmW3s-Z-wu4kLeQlhUL__image.png" signedSrc size="50" width="413" height="731" position="center" caption}

## 4 - Implement Log out

Let’s move to our HomePage (or the page the user will be directed after logging in) and implement the sign out.

First, go ahead, open home.html and add a button for doing so.

:::CodeblockTabs
login.html

```html
1     <h2 text-center>You are logged in!</h2>
2
3     <div margin-top text-center>
4       <button ion-button (click)="logOut()">
5         Log out
6       </button>
7     </div>
```
:::

Now, let’s implement the logOut() method and also add a Toast component if the request fails.

:::CodeblockTabs
home.ts

```typescript
1     logOut() {
2       Parse.User.logOut().then((resp) => {
3         console.log('Logged out successfully', resp);
4
5         this.navCtrl.setRoot('LoginPage');
6       }, err => {
7         console.log('Error logging out', err);
8
9         this.toastCtrl.create({
10          message: 'Error logging out',
11          duration: 2000
12        }).present();
13      })
14    }
```
:::

:::hint{type="info"}
Learn more about Parse.User.logOut() at <a href="https://parseplatform.org/Parse-SDK-JS/api/v1.11.1/Parse.User.html#.signUp" target="_blank">Parse Documentation</a>.
:::

It should look like this.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HB337gxfN6oOM-D7B-dCl_image.png" signedSrc size="50" width="417" height="737" position="center" caption}

## 5 - Set root page

An important feature of parse is that it remembers if a user is logged or not in a device. It means that even if the user closes the app, you can still restore his session when the app is open.

With that, we can determine if the app initial page will be our LoginPage or HomePage.

To do so, we just need to call currentAsync(). It will return the user logged in or null.

:::CodeblockTabs
app.component.ts

```typescript
1     Parse.User.currentAsync().then(user => {
2       console.log('Logged user', user);
3
4       this.rootPage = user ? 'HomePage' : 'LoginPage';
5     }, err => {
6       console.log('Error getting logged user');
7
8       this.rootPage = 'LoginPage';
9     })
```
:::

:::hint{type="info"}
Learn more about Parse.User.currentAsync() at <a href="https://parseplatform.org/Parse-SDK-JS/api/v1.11.1/Parse.User.html#.signUp" target="_blank">Parse Documentation</a>.
:::

## Finally, it’s all set up!

At this point, just run ionic serve and you will have a sign in, sign up and sign out features working that also remembers the logged user until he/she logs out.

[title] Facebook Login
[path] JS Framework/Ionic/

# How to add facebook login to your Ionic App

## Introduction

This section explains how you can create an app with user registration using Facebook Login and <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

After following this tutorial, you will be able to do this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PrNRMaA0rfyc0wtY3KPiY_image.png" signedSrc size="50" width="360" height="640" position="center" caption}

:::hint{type="success"}
At any time, you can access the complete Ionic Project built with this tutorial at our <a href="https://github.com/back4app/ionic-email-verification" target="_blank">GItHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://code.visualstudio.com/download" target="_blank">Visual Studio Code </a>(or any web IDE you like).
- <a href="https://ionicframework.com/getting-started/" target="_blank">Ionic Framework </a>
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- A <a href="https://www.facebook.com/login/?next=https%3A%2F%2Fdevelopers.facebook.com%2F" target="_blank">Facebook</a> account for creating the app.
:::

## 1 - Facebook Set up

To start using Facebook functions, you need to:

1. Go to the <a href="https://developers.facebook.com/" target="_blank">Facebook Developer</a> Website and create an account and log in.
2. Go to <a href="https://www.facebook.com/login.php?next=https%3A%2F%2Fdevelopers.facebook.com%2Fapps%2F" target="_blank">My Apps</a> and click on Add a New App:
3. At the left panel, click on Settings > Basic. In this page:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xsVB-JyllSoKNbm2bJLrm_image.png)

- Take note of your App ID
- Add a Privacy Police URL
- Select a Category
- Scroll down and hit Save changes

4\. At the top of the same page, click on the Off button and **Confirm** to make your app live.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SSBOjQ0Rl5I0638Yp0xUJ_image.png" signedSrc size="50" width="391" height="160" position="center" caption}



&#x20;   5\. Scroll down at the same page and click on Add platform.

- For this tutorial, let’s choose **Android**.
- Add your Google Play Package Name which in our case is com.back4app.myapp
- Add the Key Hashes of your machine (run keytool -exportcert -alias androiddebugkey -keystore \~/.android/debug.keystore | openssl sha1 -binary | openssl base64 to find out yours)
- Save changes

&#x20;   6\. At the left panel, to back to Dashboard, scroll down and click on Facebook Login.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6YRZiQ5uSVljKxiN8oF5y_image.png" signedSrc size="80" width="548" height="299" position="center" caption}

## 2 - Link your Facebook App with Back4App

1. Go to your App dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App Website</a> and click on Server Settings.
2. Find the “Facebook Login” block and click on Settings. The “Facebook Login” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/WIYx7akL5P5bxjmbBPmIo_image.png)

&#x20;    3\. Add the Facebook App ID taken note in the previous step.

## 3 - Set up

In this tutorial, we will be starting from where we left off in the previous [User registration with email verification](https://www.back4app.com/docs/ionic/user-registration-email-verification) one.

## 4 - Facebook Login

Let’s first install Facebook cordova plugins:

> $ ionic cordova plugin add cordova-plugin-facebook4 --variable APP_ID="XXXXXXXX" --variable APP_NAME="XXXXXXXX"
> $ npm install --save @ionic-native/facebook

Now, let’s implement the facebookLogin() method:

:::CodeblockTabs
login.ts

```typescript
1     async facebookLogin() {
2       try {
3         // Log in to Facebook and request user data
4         let facebookResponse = await this.facebook.login(['public_profile', 'email']);
5         let facebookAuthData = {
6           id: facebookResponse.authResponse.userID,
7           access_token: facebookResponse.authResponse.accessToken,
8         };
9
10        // Request the user from parse
11        let toLinkUser = new Parse.User();
12        let user = await toLinkUser._linkWith('facebook', {authData: facebookAuthData});
13
14        // If user did not exist, updates its data
15        if (!user.existed()) {
16          let userData = await this.facebook.api('me?fields=id,name,email,first_name,picture.width(720).height(720).as(picture)', []);
17          user.set('username', userData.name);
18          user.set('name', userData.name);
19          user.set('email', userData.email);
20          await user.save();
21        }
22
23        this.navCtrl.setRoot('HomePage');
24      } catch (err) {
25        console.log('Error logging in', err);
26
27        this.toastCtrl.create({
28          message: err.message,
29          duration: 2000
30        }).present();
31      }
32    }
```
:::

Finally, let’s add a button to our Login page and make it call the method we just created:

:::CodeblockTabs
login.html

```html
1   <div text-center>
2         <button ion-button color="facebook" (click)="facebookLogin()">
3           LOG IN WITH FACEBOOK
4         </button>
5       </div>
```
:::

## 5 - Test your app

1. Since Facebook log in does’t work on a browser, test your app by running ionic cordova run android.
2. Login at [Back4App Website](https://www.back4app.com/).
3. Find your app and click on Dashboard > Core > Browser > User to see the user that you’ve created!

## It’s done!

At this stage, you can log in, register and log out of your app with Facebook using Parse Server core features through Back4App!

[title] Start from template
[path] Xamarin/

# Start your Xamarin project using a pre built template

## Introduction

In this section you learn how to get started with a Visual Studio template and get ready to use Back4App in 5 easy steps.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://visualstudio.microsoft.com/pt-br/" target="_blank">Visual Studio</a> and <a href="https://dotnet.microsoft.com/pt-br/apps/xamarin" target="_blank">Xamarin Dependency</a>.
- An app created at Back4App. See more on <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a>.
:::

## 1 - Get the template

Download the template at <a href="https://github.com/back4app/xamarin-quickstart-example" target="_blank">Back4App’s GitHub repository</a>.

You can do that using the following command line:

>  $ curl -LOk https://github.com/back4app/xamarin-quickstart-example/archive/master.zip && unzip master.zip

## 2 - Open the project template

1. Open Visual Studio.
2. Click on Open Project / Solution

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/so-iCN0G6PN0X9fptkLwd_image.png)

&#x20;    3\. Navigate to the project folder and click on Open.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZjUdMLg52SFaMIchsINc1_image.png)

## 3 - Install Required NuGet Packages

We need some additional libraries for this example. We will get them through Nuget Packages.

1. Right click on App’s name in the Solution Explorer and click on Manage NuGet Packages....

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xEdKcXEhcPA4BaJeMmpMr_image.png)

&#x20;    2\. Press on Browse, search and install the packages parse by Parse and Xamarin.Android.Support.v7.AppCompat by Xamarin Inc..

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/q9KsmkOqBA37bCbY_7tkd_image.png)

&#x20;    3\. Wait for the installations to complete.

## 4 - Setup app’s credentials

Update your strings values to set up the app’s credentials. Parse Xamarin SDK uses these settings to connect to the Back4App servers.

1. Open your strings file: .../App1/Resources/values/strings.xml&#x20;

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/FQxsAoO4h5vUvcQ7g2lzP_image.png)

&#x20;    2\. Go to your App Dashboard at Back4App website.

&#x20;    3\. Navigate to app’s settings: Click on Features > Core Settings and press Settings.

&#x20;    4\. Return to your strings.xml file and paste your applicationId and dotNetKey.

:::hint{type="info"}
&#x20;See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Back4App’s Creating New App guide</a>.
:::



## 5 - Testing your connection

1. Build your app in a device or virtual device ( Shift + F10).

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/T6hqZuL1cps6rAtgNCpEG_image.png)

&#x20;       2\. Wait until the Hello World! screen appears as below.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/n0SHe6T2l7E_KiwP2mQ8d_image.png" signedSrc size="50" width="720" height="1280" position="center" caption}

&#x20;       3\. Login at <a href="https://www.back4app.com/" target="_blank">Back4App</a>.

&#x20;       4\. Find your app and click on Dashboard.

&#x20;       5\. Click on Core.

&#x20;       6\. Go to Browser.

If everything works properly, you should find a class named Installation as follows:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZLsk-nIXi_Kx43i0IjbXc_image.png)

## Next Steps

At this point, you have learned how to get started with Android apps. You are now ready to explore [Parse Server core features](https://www.back4app.com/product/parse-server) and [Back4App add-ons](https://www.back4app.com/product/addons).

Learn more by walking around <a href="http://docs.parseplatform.org/dotnet/guide/" target="_blank">Parse open source documentation for .NET SDK</a>.


[title] Express.js
[path] JS Framework/

# Hosting your Node.JS web application on Back4App servers

## Introduction

This tutorial explains how you can set up a subdomain and easily host static pages. After completing this step-by-step guide, you will be able to use a Node JS App to Register and Login Users.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- If you want to test this App locally, you need to install Node JS in your local environment. You can follow the
- An app created with Back4App.
  - Check out
- Back4App Command Line Configured with the project.
  - Go through the
:::

## First off, let’s talk about our new simple application!

We’ll describe an example about the usage of Web hosting with Node JS!

Let’s imagine that we need to build a simple Log in, Register and request password from Users included in your Back4App Dashboard. We will be able to use Bootstrap, Static files (CSS and Images) and usign Express in the Project.

See the live app here: <a href="https://nodeapplication.back4app.io/" target="_blank">nodeapplication.back4app.io</a>.

:::hint{type="info"}
You can clone this complete application in the App code templates on the Back4App Platform <a href="https://www.back4app.com/database/back4app/node-app-template" target="_blank">here</a>.
:::

Firstly, complete the set up using the Command Line Interface ([see prerequisites](https://www.back4app.com/docs/js-framework/node-web-server#content-prerequisites)), to understand how it will work with the final structure from the files:

> ├── NodeJSWebApp/
> │  ├── cloud/
> │  │   ├── app.js
> │  │   ├── routes.js
> │  │   ├── package.json
> │  │   ├── views/
> │  │   │   ├── head.ejs
> │  │   │   ├── index.ejs
> │  │   │   ├── reset_password.ejs
> │  ├── public/
> │  │   ├── images/
> │  │   ├── css/
> │  │   │   ├── style.css

## 1 - Enable your subdomain name on Back4app

Enable your Web Hosting feature, please follow the steps available in the guide below:

&#x20;                                  <a href="https://www.back4app.com/docs/platform/parse-web-hosting" target="_blank">Activating your Web Hosting and Live Query</a>

## 2 - Create and upload files

Before getting started with this step, we recommend you using Command Line Tool ([see prereqs](https://www.back4app.com/docs/js-framework/node-web-server#content-prerequisites)) to upload your files easily!

First off, create the files called as app.js and package.json inside the cloud directory!

::::ExpandableHeading
**Install modules in Back4App**

Inside the cloud folder (in your terminal), you need to write:

> $ touch package.json

Now, insert the npm module ‘body-parser’ inside the file package.json:

```json
1   {
2     "dependencies": {
3       "body-parser": "*"
4     }
5   }
```

:::hint{type="danger"}
**Troubleshooting: **It is not necessary to run npm install inside the cloud folder because the Back4App server will install it automatically
:::
::::

:::ExpandableHeading
**app.js**

```javascript
1   // Require the module
2   var bodyParser = require("body-parser");
3
4   // Set up the views directory
5   app.set('views', __dirname + '/views');
6   app.set('view engine', 'ejs');
7
8   // Set up the Body Parser to your App
9   app.use(bodyParser.json());
10  app.use(bodyParser.urlencoded({extended: true})); 
11
12  //Require the routes.js file
13
14  require('./routes');
```
:::

## 3 - Create the views to your App

We’ll provide template EJS files to make the template App, you can change it anytime at your end. :)

**Returning to the terminal**

Inside the Cloud directory, it is necessary to create the views folder and the following EJS files:

- head.ejs- We will use it to add content to head of HTML structure.
- index.ejs- We will use it to Register and Log in users.
- reset\_password.ejs- We will use it for the user request the Password Reset.

:::hint{type="warning"}
**Hint: **We will construct the views using Bootstrap, <a href="https://getbootstrap.com/" target="_blank">click here</a> to read more about it.
:::

You can add content to your respective views. You can use the below templates without any hassle:

:::CodeblockTabs
head.ejs

```nodejs
1   <title>Back4App - Node JS Application</title>
2     <meta charset="utf-8">
3     <meta name="viewport" content="width=device-width, initial-scale=1">
4     <link rel="shortcut icon" type="image/png" href="/favicon.png"/>
5     <!-- Bootstrap CSS--> 
6     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
7
8     <!-- jQuery--> 
9     <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
10
11    <!-- Bootstrap JS--> 
12    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
13
14    <!-- Stylesheet Pages -->
15    <link rel="stylesheet" type="text/css" href="/css/style.css">
```

index.ejs

```nodejs
1   <!DOCTYPE html>
2     <html>
3     <head>
4       <% include head.ejs %>
5     </head>
6     <body>
7       <section id="header">
8         <div class="container">
9           <div class="row">
10            <div class="col-sm-6">
11              <div class="box-settings box-register">
12                <div class="wrap-login100">
13                  <form class="validate-form" method="POST" action="/users/register">
14                    <h2 class="title-screen">
15                      Register a new account
16                    </h2>
17                    <div class="form-group">
18                      <input type="text" name="usernameRegister" placeholder="Username" class="form-control" value="<%= infoUser.usernameRegister %>">
19                    </div>
20                    <div class="form-group">
21                      <input type="text" name="emailRegister" placeholder="Email" class="form-control" value="<%= infoUser.emailRegister %>">
22                    </div>
23                    <div class="form-group">
24                      <input type="password" name="passwordRegister" placeholder="Password" class="form-control" value="<%= infoUser.passwordRegister %>">
25                    </div>
26                    <% if (RegisterMessage != 'undefined' ? RegisterMessage  : '' ) { %>
27                      <div class="alert alert-<%= typeStatus %>" role="alert">
28                        <p><%= RegisterMessage %></p>
29                      </div>
30                    <% } %>
31                    <div class="form-group">
32                      <button class="btn-login btn">
33                        Register
34                      </button>
35                    </div>
36                  </form>
37                </div>            
38              </div>
39            </div>
40            <div class="col-sm-6">
41              <div class="box-settings box-login">
42                <div class="wrap-login100">
43                  <form class="validate-form" method="POST" action="/users/login">
44                    <h2 class="title-screen">
45                      Access your account
46                    </h2>
47                    <div class="form-group">
48                      <input type="text" name="usernameLogin" placeholder="Username" class="form-control" value="<%= infoUser.usernameLogin %>">
49                    </div>
50  
51                    <div class="form-group">
52                      <input type="password" name="passwordLogin" placeholder="Password" class="form-control" value="<%= infoUser.passwordLogin %>">
53                    </div>
54                    <% if (loginMessage != 'undefined' ? loginMessage  : '' ) { %>
55                      <div class="alert alert-<%= typeStatus %>" role="alert">
56                        <p><%= loginMessage %></p>
57                      </div>
58                    <% } %>
59                    <div class="form-group">
60                      <button class="btn-login btn">
61                        Login
62                      </button>
63                    </div>
64  
65                    <div class="text-forgot">
66                      <a href="/users/forgot-password">
67                        Forgot Password? Click here to retrive your access!
68                      </a>
69                    </div>
70                  </form>
71                </div>            
72               </div>
73            </div>
74          </div>
75        </div>
76      </section>
77    </body>
78    </html>
```

reset\_password.ejs

```nodejs
1   <!DOCTYPE html>
2     <html>
3     <head>
4       <% include head.ejs %>
5     </head>
6     <body>
7       <section id="header">
8         <div class="container">
9           <div class="row">
10            <div class="col-sm-offset-3 col-sm-6" align="center">
11              <div class="box-settings box-password">
12                <div class="wrap-login100">
13                  <form class="validate-form" method="POST" action="/users/forgot-password">
14                    <h2 class="title-screen">
15                      Retrieve your access
16                    </h2>
17                    <p>Insert your email address</p>
18                    <div class="form-group">
19                      <input type="text" name="email" placeholder="Email" class="form-control" value="<%= infoUser.email %>">
20                    </div>
21                    <% if (resetPass != 'undefined' ? resetPass  : '' ) { %>
22                      <div class="alert alert-<%= typeStatus %>" role="alert">
23                        <p><%= resetPass %></p>
24                      </div>
25                    <% } %>
26                    <div class="form-group">
27                      <button class="btn-login btn">
28                        Reset Password
29                      </button>
30                    </div>
31                  </form>
32                </div>            
33              </div>
34            </div>
35          </div>
36        </div>
37      </section>
38    </body>
39    </html>
```
:::

## 4 - Create the routes to render the views

Now, we need to configure the Routes to render the views that were created previously. The routes will be built using [Express](https://expressjs.com/).

```nodejs
1   // Index
2   app.get('/', (req, res) => {
3     res.render('index', {loginMessage: '', RegisterMessage: '', typeStatus: '',  infoUser: ''});
4   });
5   // Request Password
6   app.get('/users/forgot-password', (req, res) => {
7     res.render('reset_password', { resetPass: '', typeStatus: '', infoUser: ''});
8   });
```

:::hint{type="warning"}
**Hint: **As you can see, we are configuring variable as parameters, which will be used to display alerts on the page.
:::

## 5 - Create the routes to save the informations to your database

We will use the [Parse Server Javascript Guide](https://docs.parseplatform.org/js/guide/) as a reference for developing our functions for Register, Login and Request the Password.

```javascript
1   // Request the Log in passing the email and password
2   app.post('/users/login', async(req, res) => {
3     let infoUser = req.body;
4  
5     try{
6       let user = await Parse.User.logIn(infoUser.usernameLogin, infoUser.passwordLogin)
7       res.render('index', { loginMessage: "User logged!", RegisterMessage: '', typeStatus: "success",  infoUser: infoUser });
8     } catch (error){
9       res.render('index', { loginMessage: error.message, RegisterMessage: '', typeStatus: "danger",  infoUser: infoUser});
10    }
11  });
12
13  // Register the user passing the username, password and email
14  app.post('/users/register', async(req, res) => {
15    let infoUser = req.body;    
16    let user = new Parse.User();
17  
18    user.set("username", infoUser.usernameRegister);
19    user.set("password", infoUser.passwordRegister);
20    user.set("email", infoUser.emailRegister);
21
22    try{
23      await user.signUp();
24      res.render('index', { loginMessage : '', RegisterMessage: "User created!", typeStatus: "success",  infoUser: infoUser});
25    } catch (error) {
26      res.render('index', { loginMessage : '', RegisterMessage: error.message, typeStatus: "danger",  infoUser: infoUser});
27    }
28  });
29
30  // Request the Password reset passing the email
31  app.post('/users/forgot-password', function(req, res) {
32    let infoUser = req.body;
33    try{  
34      await Parse.User.requestPasswordReset(infoUser.email);
35      res.render('reset_password', { resetPass: "Check your email!", typeStatus: "success", infoUser: infoUser});
36    } catch (e){
37      res.render('reset_password', { resetPass: error.message, typeStatus: "danger", infoUser: infoUser});
38    }
39  });
```

## 6 - Almost there! Static files in public folder

It’s not over yet! In the public folder, you can insert static files such as CSS and images to require this on the views :)

When you add files to CSS and images, you are able to provide different stylesheets to your Website created!

## 7 - It’s done!

Up to this point, you have splendidly learned how to create a Node JS Application using Cloud code.

:::hint{type="info"}
Click on <a href="https://nodeapplication.back4app.io/" target="_blank">this link</a> to access the complete Project in Back4App at anytime.
:::

With the steps described above, you’ll be able to work with the Web Hosting when using a Node JS application!

[title] Security & Privacy
[path] /


[title] CLI Migration
[path] App Migration/

# Migrate your Parse App to Back4App using CLI

## Introduction

In this guide, you will learn how to migrate your Parse App to Back4App using the migration CLI.

The migration process consists of transferring the Database, cloud functions, and files from your current Parse App to a Back4App App. The CLI migration tool will help you with the Database and files migration.&#x20;

It will pick a copy of your database (a dump file) and restore the data in your Back4App App. Then will pick your files from a local folder and upload them to Back4App S3 Bucket. The Cloud Code migration is not covered for this CLI migration tool but you can use the [Back4App CLI](https://www.back4app.com/docs/platform/parse-cli) (other CLI) to migrate them or do it manually using the Dashboard.

## Prerequisites

:::hint{type="info"}
**To begin with this tutorial, you will need:**

- A dump of your Parse App in your own machine.
- [Node JS (>=8.0) ](https://nodejs.org/en/)and <a href="https://docs.npmjs.com/getting-started" target="_blank">NPM</a>.
:::

## 1 - Install our CLI Tool

First of all, it’s necessary to install @back4app/m2b4a on your machine. As described below:

> npm install -g @back4app/m2b4a

## 2 - Start the Parse App Migration

You will now migrate your Database. Go to your current Parse App and download a copy of your Database. Then go to the folder that contains your dump files and run:

> migrate-to-back4app

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VpC-RFB0Yx7swgG1uDUWg_image.png)

## 3 - Access your Account

You can sign up or log in to your account and choose if you want to save your session. Then, the next time you use this tool you won’t need to put your account credentials again.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zpsCrjT1ppBqkmStGbOuu_image.png)

## 4 - Choose a Back4App App

You can migrate your Parse App to an existing app or a new one.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aERuzGAQUZI3LUBqZ9ux3_image.png)

If you decide to use an existing app, be careful, it’s possible to decide if you’ll overwrite the existing data or just insert new objects.

For new apps, you must set a name and press ENTER.

## 5 - Restore your data

Once you’re already in the folder where your dump files are, you just need to press ENTER.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zvjxl4vEjtSsmoW2XO2O3_image.png)

## 6 - Migrate your Parse Files

In case you have Parse Files, browse in the folder where your files are, and press ENTER to continue. The files will be automatically associated with your Parse Objects.

Otherwise, just type n to skip this step and start the restoration process.

Here’s what the end of the migration will look like:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/W-sLU5PGCZ2RCPlEDFhEe_image.png)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/CWc_z5WYj6PX87rFGP2Eb_image.png)

:::hint{type="danger"}
Never share these credentials to anyone. You can <a href="https://help.back4app.com/hc/en-us/articles/115000808951-How-to-add-Collaborators-to-my-app-" target="_blank">add collaborators</a> in your project and they can get these keys <a href="https://help.back4app.com/hc/en-us/articles/115000754772-Where-are-my-Keys-and-ID-" target="_blank">here</a>.
:::

## 7 - Test your cloud code

Check [this guide](https://www.back4app.com/docs/platform/parse-cli) to learn how to deploy your cloud code files.

Here are some pieces of information you must know:

1 - Back4App uses 2 main folders: public/ to public files, like HTML, CSS, etc., and cloud/ to private cloud code.

2 - Inside cloud/ Back4App import 2 files. One named app.js for your custom API (app.get(‘/my-custom-api’) for ex.) and main.js for Parse.Cloud functions and jobs.

3 - **VERY IMPORTANT!** app (the express.js instance) and Parse variables are global. **Do not install them** on your package.json or require them in your cloud code.

4 - **Do not use **require('express')** or **require('parse/node')**, just use app and Parse variables**.

## 8 - Connect your current API to your new database at Back4App

Once you finished all App tests, it’s time to point your current Parse Server to use the Back4App Database. You have two possibilities here. The first is to point the Parse Server to the Back4App Database and then make an incremental Database restore (as described in **Step 5**). The second is to turn off your Parse Server, make a complete database restore, and then turn it on again, pointing to the Back4App Database. Our suggestion here is the first step, which will avoid downtime to your App.

To proceed with the first option, copy your Back4App Connection String at Server Settings > Settings > Core Settings > Connection String and paste it into your old Parse App settings.

Now you need to make a new dump of your data and restore them in your application **(step 5)**, but, instead of creating a new app, choose YES, I want to update one of them! and NO! Only insert new ids, afterward. It will help to prevent data inconsistency for your users.

After concluding this step, your users will be using the Back4App Database instead of the old one.

## 9 - Updating your frontend

Now, you need to update your front end to connect to the Back4App App. You can get your app ID, keys, and API Address in the core settings section at Server Settings > Settings > Core Settings. Update your frontend/App connection (pointing to Back4App now) and release a new application version to your users.

Now you have a new app pointing to the Back4App API and database and an old app pointing to the previous Parse API but using the Back4App database.

## 10 - Finishing the process

We recommend only turning off your old Parse API when most of your users are using the new App version (pointing to Back4App API). Once you turn off the old API, users who use the old version will lose access to the backend.

## It’s done!

Now you know how to migrate your Parse App to the Back4App using the CLI. In case you need any help or a link doesn’t work, please [contact our team!](https://www.back4app.com/support)

[title] using CC and Swift
[path] iOS/Send Push Notifications/

# Sending push notifications using cloud code with Swift

## Introduction

This section explains how you can send push notifications using Cloud Code through Back4App.

This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/oKdSS5kYQeYrCYw-6RP0G_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

:::hint{type="success"}
At any time, you can access the complete Project built with this tutorial at our <a href="https://github.com/templates-back4app/iOS-install-SDK" target="_blank">GitHub repository</a>.
:::

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to Back4App.
- An iOS app set up via <a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank">Back4App Push Notifications via Dashboard tutorial</a>.
- An iOS device, iphone or ipad, running iOS 10 or newer.
- A paid Apple developer Account.
:::

## 1 - Set up your iOS app to receive push

Every Parse application installed on a device registered for push notifications has an associated Installation object. The Installation object is where you store all the data needed to target push notifications. For example, in your app, you could store which teams one of your users is interested in to send updates about their performance. Saving the Installation object is also required for tracking push-related app open events.

The simplest way to start sending notifications is using channels. This allows you to use a publisher-subscriber model for sending pushes. Devices start by subscribing to one or more channels, and notifications can later be sent to these subscribers. The channels subscribed to by a given Installation are stored in the channels field of the Installation object.

After that we will go over sending targeted push notifications to a single user or to a group of users based on a query.

:::hint{type="danger"}
Going forward we are going to assume you have completed all steps of the<a href="https://www.back4app.com/docs/ios/push-notifications/best-ios-push-notification-service" target="_blank"> Back4App Push Notifications via Dashboard tutorial</a>, even if you use the iOS Project built with this tutorial that is available at our <a href="https://github.com/mpc20001/ios-objc-push-cloud-code" target="_blank">GitHub repository</a>.
You should have basic push notifications working and also be able to send pushes out via the admin console.
:::

## 2 - Subscribe your device to the News channel

1. First we will add a channel to your installation object. We are going to do this by altering the method createInstallationOnParse in our App Delegate file. Open your project’s AppDelegate.m file, and add make sure your version of createInstallationOnParseis the same as the code below.

:::CodeblockTabs
AppDelegate.swift

```swift
1   func createInstallationOnParse(deviceTokenData:Data){
2        if let installation = PFInstallation.current(){
3            installation.setDeviceTokenFrom(deviceTokenData)
4            installation.setObject(["News"], forKey: "channels")
5            installation.saveInBackground {
6                (success: Bool, error: Error?) in
7                if (success) {
8                    print("You have successfully saved your push installation to Back4App!")
9                } else {
10                   if let myError = error{
11                       print("Error saving parse installation \(myError.localizedDescription)")
12                   }else{
13                       print("Uknown error")
14                   }
15               }
16           }
17       }
18   }
```
:::

:::hint{type="success"}
We are adding one new line of code - ‘installation.setObject(\[“News”], forKey: “channels”)’ - which will set the installation object’s channel array to contain one channel called ‘News’.
This will allow us to send a message to everyone who subscribes to the channel called News via cloud code.
:::

&#x20;    2\. Test it by running your app on a physical device

:::hint{type="danger"}
**You may not run this on simulator.**

You’ll need an actual push token to update your installation record, so a physical device is a must.
:::

&#x20;    3\. After it runs successfully, you should see something similar to the image below in the Installation section of your Dashboard. You can check it by going to your app’s Dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and then checking the Installation table. Under the channels column you should see News, showing you that you are now subscribed to the News push channel.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/h9FqeiNZvOHqHN6zcWtuC_image.png)

## 3 - Create your Cloud Code

:::hint{type="info"}
To know more about how to get started with **Cloud Code **look at <a href="https://www.back4app.com/docs" target="_blank">Cloud Code for iOS Tutorial</a>.
:::

1. Create a .js file to put your Cloud Code into. You need to call it main.js in order for Back4App to know this is where you store your cloud code.
2.
   Define a Cloud function, using Parse.Cloud.Define, to call the push notification. Inside the function we will call Parse.Push.Send to send a pushes to the ‘News’ channel.

:::hint{type="danger"}
It is required to use the **master key **in this operation.
:::

The following code executes these steps:

:::CodeblockTabs
Parse Server 3.X

```javascript
// main.js
1   Parse.Cloud.define("pushsample", (request) => {
2
3       return Parse.Push.send({
4           channels: ["News"],
5           data: {
6               title: "Hello from the Cloud Code",
7               alert: "Back4App rocks!",
8           }
9       }, { useMasterKey: true });
10  });
```

Parse Server 2.X

```javascript
//main.js
1   Parse.Cloud.define("pushsample", function (request, response) {
2       Parse.Push.send({
3               channels: ["News"],
4               data: {
5                   title: "Hello from the Cloud Code",
6                   alert: "Back4App rocks!",
7               }
8          }, {
9               success: function () {
10                  // Push was successful
11                  response.success("push sent");
12                  console.log("Success: push sent");
13              },
14              error: function (error) {
15                  // Push was unsucessful
16                  response.error("error with push: " + error);
17                  console.log("Error: " + error);
18              },
19              useMasterKey: true
20         });
21  })
```
:::

## 4 - Upload to Cloud Code

1. Go to your App at<a href="https://www.back4app.com/" target="_blank"> Back4App Website</a> and click on Dashboard.
2. Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/msnbrVj-RbkPo8LhM5OzB_image.png" signedSrc size="60" width="810" height="578" position="center" caption}

&#x20;    3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click at Deploy as shown here:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7KGXK1l0iJ5zfJ29pnC4Z_image.png" signedSrc size="90" width="2482" height="1616" position="center" caption}

## 5 - Call Cloud Code from your iOS App

1. Now, we are going to write some code to call this cloud function from your app. **You will need both the simulator and a physical device to complete this task.**You will call the cloud function from your app running in the simulator and you will see the push appear on your physical device.

:::hint{type="danger"}
**Your physical device should actually be closed with the lock screen on in order to see the push. **

A push will not appear on the screem if you are inside the app that is sending it when you receive the push.
:::

&#x20;    2\. Open your project’s ViewController.swift file. We need to include Parse in the view controller by adding the following code - ‘import Parse’- at the top of the file.

<a href="https://github.com/mpc20001/ios-swift-push-cloud-code/blob/master/AddingParseSDK/ViewController.swift#L10" target="_blank">`ViewController.swift`</a>

> **1   import**
>
>  
>
> **UIKit**
>
>
>
>
> **2   import**
>
>  
>
> **Parse**

&#x20;    3\. Next in the ViewController.swift file we will call an alert function from the viewDidAppear method. The alert will allow you to trigger the Cloud Code code which will send a push to your device. Be sure to include the following block of code **after** the viewDidLoad function.



:::CodeblockTabs
ViewController.swift&#x20;

```swift
1   override func viewDidAppear(_ animated: Bool) {
2        askToSendPushnotifications()
3    }
4
5    func askToSendPushnotifications() {
6        let alertView = UIAlertController(title: "Send a push to the news channel", message: nil, preferredStyle: .alert)
7        let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction) in
8            self.sendPushNotifications()
9        }
10       alertView.addAction(OKAction)
11       let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action:UIAlertAction) in
12       }
13       alertView.addAction(cancelAction)
14       if let presenter = alertView.popoverPresentationController {
15           presenter.sourceView = self.view
16           presenter.sourceRect = self.view.bounds
17       }
18       self.present(alertView, animated: true, completion:nil)
19   }
20  
21   func sendPushNotifications() {
22       let cloudParams : [AnyHashable:String] = [:]
23       PFCloud.callFunction(inBackground: "pushsample", withParameters: cloudParams, block: {
24           (result: Any?, error: Error?) -> Void in
25           if error != nil {
26               if let descrip = error?.localizedDescription{
27                   print(descrip)
28               }
29           }else{
30               print(result as! String)
31           }
32       })
33   }
```
:::

&#x20;   4\. Run your app in the simulator and when the alert asks to send the push pops up, hit “OK”. On your physical device you should see the push appear on the lock screen, like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aCc1kxznZ0qOcv5nZTh15_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

## 6 - Call Cloud Code from REST API

The REST API provides a quick and easy way to test if your Cloud function is working.
Just use the code below in your terminal or command prompt:

:::hint{type="info"}
Click on to know more about how to get started with command line in <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-the-linux-terminal" target="_blank">Linux</a>, <a href="https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line" target="_blank">MacOS</a>
or <a href="https://www.bleepingcomputer.com/tutorials/windows-command-prompt-introduction/" target="_blank">Windows</a>.
:::

> curl -X POST -H "X-Parse-Application-Id: YOUR_APP_ID_HERE" \
> -H "X-Parse-REST-API-Key: YOUR_REST_API_KEY_HERE" \
> -H "Content-Type: application/json" \
> -d ‘{ // Put the function parameters here in JSON format }’ \
> https://parseapi.back4app.com/functions/pushsample

To test the push notifications, just use the REST code while the device is closed.

## 7 - Send targeted Push Notifications using an User Object

:::hint{type="danger"}
Going forward we will be using a different iOS project that has a basic signup and signin features already built. We are going to be using this iOS project that we can show you how to detect if a user is logged in, and if so save their installation to with a link to their object id for querying in cloud code. You can download the complete iOS Project built with this section’s tutorial at our <a href="https://github.com/mpc20001/ios-objc-targeted-push-cloud-code" target="_blank">GitHub repository</a> but you will still have to do all of the setup from the previous tutorial that explains how to send pushes fromt he Back4App dashboard.
:::

:::hint{type="info"}
You can download the complete iOS Project built with this section’s tutorial at our  <a href="https://github.com/mpc20001/ios-swift-targeted-push-cloud-code" target="_blank">GitHub repository</a> but you will still have to do all of the setup from the previous tutorial that explains how to send pushes fromt he Back4App dashboard.
:::

1. Get the new version of the app setup and signup or login to the app. First make sure you download the working template from our <a href="https://github.com/mpc20001/ios-swift-targeted-push-cloud-code" target="_blank">GitHub repository</a>. We are not going to walk through all the steps of building this app, instead we will focus on setting up the cloud code and why it works. Once you open this new app be sure to put your own app’s credentials in the AppDelegate.swift file.&#x20;

:::CodeblockTabs
AppDelegate.swift

```swift
1   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
2        let configuration = ParseClientConfiguration {
3            $0.applicationId = "PASTE_YOUR_APPLICATION_ID_HERE"
4            $0.clientKey = "PASTE_YOUR_CLIENT_ID_HERE"
5            $0.server = "https://parseapi.back4app.com"
6        }
7        Parse.initialize(with: configuration)
8        return true
9   }
```
:::

&#x20;    2\. This app has some major differences between the previous app. It features 2 sections, one for being logged into your app and one section when you are not logged in to your app. The next big change is the AppDelegate.swift file’s function ‘createInstallationOnParse’. We’ve added 1 line that store’s the user’s object id as part of the installation object. That way we can know which user is associated with which installation object and can target them individually for pushes.

:::CodeblockTabs
AppDelegate.swift

```swift
1   func createInstallationOnParse(deviceTokenData:Data){
2        if let installation = PFInstallation.current(){
3            installation.setDeviceTokenFrom(deviceTokenData)
4            installation.setObject(["News"], forKey: "channels")
5            if let userId = PFUser.current()?.objectId {
6                installation.setObject(userId, forKey: "userId")
7            }
8            installation.saveInBackground {
9                (success: Bool, error: Error?) in
10               if (success) {
11                   print("You have successfully saved your push installation to Back4App!")
12               } else {
13                   if let myError = error{
14                       print("Error saving parse installation \(myError.localizedDescription)")
15                   }else{
16                       print("Uknown error")
17                   }
18               }
19           }
20       }
21   }
```
:::

&#x20;    3\. Since we are now storing the user’s object id as part of the installation object we do not want to request a new push token until the user is logged in. We do not want to request a token directly from AppDelegate.swift file’s function ‘application didFinishLaunchingWithOptions’ instead we want to call it from the LoggedInViewController’s function ‘viewDidAppear’. In ‘viewDidAppear’ we call a function on the AppDelegate to request access to a push notification token from Apple. Since you can only view this section once you are logged in we can assume the user is logged in when we create the installation object, but just to be safe we used an ‘if let statement’ to unwrap the Parse.currentUser() object and retrieve the object id.

:::CodeblockTabs
LoggedInViewController.swift

```swift
1    override func viewDidAppear(_ animated: Bool) {
2        appDelegate?.startPushNotifications()
3    }
```
:::

:::CodeblockTabs
AppDelegate.swift

```swift
1    if let userId = PFUser.current()?.objectId {
2        installation.setObject(userId, forKey: "userId")
3    }
```
:::

&#x20;    4\. Ok, now, to sign up or login. On your physical device - (iphone or ipad) start the app. You should see the image below. You should sign Up to create a new user or sign in if you have already created a user on your app.
This is how it will look like:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yaBf-y_QBeSoIu6akA8nP_image.png" signedSrc size="30" width="750" height="1334" position="flex-start" caption}

You should now be able to see the LoggedInviewController. It should look like this.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/N5uvCp8cJii3VL8GONPnN_image.png" signedSrc size="30" width="750" height="1334" position="flex-start" caption}

If you try to send pushes to yourself it won’t work yet because we haven’t added those methods to cloud code. So that’s what we will do next.

## 8 - Add the targeted Push Methods to Cloud Code

Open your Main.js file that you created previously and add the following functions to target installations by user id.

:::hint{type="danger"}
It is required to use the **master key **in this operation.
:::

:::CodeblockTabs
Parse Server 3.X

```javascript
// main.js
1   Parse.Cloud.define('sendPushToYourself', (request) => {
2       let userId = request.user.id;
3
4       let query = new Parse.Query(Parse.Installation);
5       query.equalTo("userId", userId);
6       query.descending("updatedAt");
7       return Parse.Push.send({
8           where: query,
9        data: {
10              title: "Hello from the Cloud Code",
11              alert: "Back4App rocks! Single Message!",
12          }
13      }, { useMasterKey: true });    
14  });
15
16  Parse.Cloud.define('sendPushToAllUsers', (request) => {
17      let currentUser = request.user;
18      let userIds = [currentUser.id];
19
20      let query = new Parse.Query(Parse.Installation);
21      query.containedIn('userId', userIds);
22      return Parse.Push.send({
23          where: query,
24          data: {
25              title: "Hello from the Cloud Code",
26              alert: "Back4App rocks! Group Message!",
27          }
28      }, { useMasterKey: true });
29  });
```

Parse Server 2.X

```javascript
//main.js
1   Parse.Cloud.define('sendPushToYourself', function (request, response) {
2       var currentUser = request.user;
3       var userId = currentUser.id;
4
5       var query = new Parse.Query("Installation");
6       query.equalTo("userId", userId);
7       query.descending("updatedAt");
8       Parse.Push.send({
9           where: query,
10          data: {
11              title: "Hello from the Cloud Code",
12              alert: "Back4App rocks! Single Message!",
13          }
14      }, {
15          useMasterKey: true,
16          success: function () {
17             response.success("success sending a single push!");
18          },
19          error: function (error) {
20              response.error(error.code + " : " + error.description);
21          }
22      });
23  });
24
25  Parse.Cloud.define('sendPushToAllUsers', function (request, response) {
26      var currentUser = request.user;
27      var userIds = [currentUser.id];
28  
29      var query = new Parse.Query(Parse.Installation);
30      query.containedIn('userId', userIds);
31      Parse.Push.send({
32          where: query,
33          data: {
34              title: "Hello from the Cloud Code",
35              alert: "Back4App rocks! Group Message!",
36          }
37      }, {
38          useMasterKey: true,
39          success: function () {
40              response.success('Success sending a group push!');
41          },
42          error: function (message) {
43              response.error(error.code + " : " + error.description);
44          }
45      });
46  });
```
:::

## 9 - Upload to Cloud Code

1. Go to your App at <a href="https://www.back4app.com/" target="_blank">Back4App website</a> and click on Dashboard.
2.
   Find the Cloud Code and click on Functions & Web Hosting. It looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dZUKc-Yh-z-Vk40qGTB9-_image.png)

3\. Upload or create a new file (you can also edit the current main.js file directly on the browser). Then, click at Deploy as shown here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ws6sHt37u2aHhs4hjgHt7_image.png)

## 10 - Test that you can send Targeted push Notifications to yourself

Open your app from the simulator while leaving your physical device closed with the lock screen on.

You can test that both push functions are working by pressing the ‘send push to a yourself’ button and the ‘send push to a group of people’ button. You should see the pushes appear on your devices lock screen.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/v2cCkqHWTlFonYttZHX0D_image.png" signedSrc size="30" width="640" height="1136" position="flex-start" caption}

## Final Thoughts

Now, you should have a firm understanding of how to send pushes based on a user’s channel or a user’s object id or any other query that involves getting the user’s object id.

Remember that in order to store the user’s object id you must add it to the push installation and only request a push token when the user is logged in. When sending pushes via query be aware that it is limited by default to 100 results and some users may have more than one instllation object.

Also it is not reccomended to send pushes to array of installation objects that are larger than 100 results. It could result in some pushes not getting sent. If you are dealing with large groups of people it is better to use channels or to send the pushes out in repeated requests.

## It’s done!

At this stage, you can send push notifications using Cloud Code through Back4App! Congratulations!

[title] Overview
[path] Database Hub/

# Learn about the Hub, sign up for an account, and explore the Database Hub community

## Introduction

The Database Hub is a collaboration tool where Back4app users can share and use data from the available datasets freely.

The main idea behind the hub is to allow data exchange and make the creation of Apps easier as you will find many pre-loaded datasets that you can integrate and use into your application.

Back4app has already published lots of databases that can help you getting started, such as:

- <a href="https://www.back4app.com/database/back4app/list-of-all-continents-countries-cities" target="_blank">A list of Wolrd’s Continents, Countries and Cities</a>
- <a href="https://www.back4app.com/database/paul-datasets/dataset-with-all-movies" target="_blank">A list of over 500.000 Movies, Stars and Directors</a>
- <a href="https://www.back4app.com/database/back4app/car-make-model-dataset" target="_blank">A list of Cars and Manufacturers</a>
- <a href="https://www.back4app.com/database/search" target="_blank">Over 150 other databases that might interest you</a>

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/3L6bCNm0REjpb2Dg2L-H7_image.png)

## Prerequisites

:::hint{type="info"}
**In order to use or share a database, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## Cloning, Connecting and Sharing

With the Hub, you can clone, connect and share your databases.

Cloning a database means a copy of that database will be made (as in that moment of time) and linked to your Back4app account.
That means you will have a full copy available to you, where you can insert, update and delete records as you wish.
That also means that new updates made from the database owner will NOT show up in your copy.

Connecting a database, on the other hand, means that your App will access the original database. The clear benefit here is that all updates made by the database owner over time will reflect in your database. This is useful if the database reflects data that changes over time, for instance, a list of the champions of the NBA championship. Every time a new champion enters the database, it will be available to you immediately.

Sharing a database means you will make one or more of your own databases available to other Back4app users. This empowers the community helping people to start out and makes us a stronger community. This also means you can get noticed by other users and promote your own brand/company to others, also, new ideas to improvements can show up from people using your databases over time.

## Benefits of Sharing in the Hub

By sharing your datasets with other developers you can :

- Help other people develop their apps using ready-to-use datasets;
- Share your data as a service in an easy-to-use format (APIs - REST & GraphQL, SDKs to more than 13 technologies);
- Build your dataset portfolio;
- Ask for collaboration to build a brand-new dataset;
- Get backlinks from the Back4App Database HUB directly to your website;

## Benefits of using shared databases from the Hub

By using the shared databases you:

- Start from a pre-loaded source with consistent data
- Ensure data is up to date with the original database
- Give and take feedback from other users on how to make the database better

## Benefits of cloning shared databases from the Hub

By cloning the shared databases you:

- Start from a pre-loaded source with consistent data
- Ensure a snapshot of the data is copied to your account so it will not change over time if you don’t want it to

## Browsing available Databases

Every time you want to see the available databases you can go to the <a href="https://www.back4app.com/database/search" target="_blank">Database Hub</a> and browse.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/L7Yfe3khqXxW3GQkmtana_image.png)

Once you find something that interests you, you can enter that database to check its Classes:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Uyxj-xCZrzXiz81g_gSKB_image.png)

You can even enter a Class to check its data

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_UduMxFdTibXxfLIS35Yb_image.png)

And its Schema

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/OUa9Ue-ZByaWP_70X7_i0_image.png)

## GraphQL Playground

In order for you to test and play around with data before cloning or connecting a shared database, a fully working GraphQL playground is available in the <a href="https://www.back4app.com/database/search" target="_blank">Database Hub</a>.

You can access it going to the API Playground, where you can write and execute queries to ensure the data will fit your needs:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/O8xpdqf5C5TEunTB4PShp_image.png)

## Reporting Issues

If you find any issues or want to check the issues history for a database or its queries, you can refer to the Issues tab.
There you can add new issues by clicking the New Issue button and check for Open or Closed issues.

The owner and other users will be reported about any issues and can take action on those:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nWsBzHVW9dhC2jInktYnP_image.png)


[title] Using GraphQL Apollo iOS Client in a Swift Project
[path] iOS/

## Introduction

In this section you will learn how to install the Apollo iOS Client on your Swift project and query data from Back4app using it.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
:::

- A Class with some data stored (so you can retrieve it).

## 1 - Getting Apollo Client into your XCode Project

The easiest way to integrate the Apollo iOS Client is by using [CocoaPods](https://cocoapods.org/). In order to integrate it, follow these steps:

1. Create your XCode project and in the same folder of your .xcodeproj file, create a new file named Podfile

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/PEUfa1YovxMtVmHjTMbSx_image.png)

1. Edit the Podfile file and add the following code, changing the string YourProjectNameHere to your project’s name:

```ruby
# Uncomment the next line to define a global platform for your project
 platform :ios, '12.0'

 target 'YourProjectNameHere' do
 # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
 use_frameworks!

 # Pods for ConferencePlanner
 pod 'Apollo'

 end
```

1. Save the file and open a terminal. Go to that folder and type:

> pod install

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZZl780kGZg-WYcpBiKM-5_image.png)

1. When the installation finishes, you should have a new file with the format .xcworkspace. Open that file with Xcode.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QfRd01OyKsYTkJg_1Ukjj_image.png)

## 2 - Retrieve your Schemas

You need a file name schema.json containing the Schemas for your GraphQL endpoint. There are two ways for you to retrieve your full Schema:

1. Using the Back4app GraphQL Console
2. Using Apollo

We will discuss both. Choose the one you like the best.

### 2.1 - Retrieve your Schemas with the Back4app GraphQL Console

Go to your GraphQL Console for the App you want to retrieve the schema from and on, the right hand under the Schema tab, click the Download button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9xb-cnkV0mslqAG6-jcq5_image.png)

### 2.2 - Retrieve your Schemas with Apollo

If you prefer to use Apollo, first you have to install the Desktop version by typing:

> npm install -g apollo

Then, run the following command replacing the values for the headers with your AppId and Masterkey:

> apollo client --endpoint=[https://parseapi.back4app.com/graphql](https://parseapi.back4app.com/graphql) --header="X-Parse-Application-Id: YourAppIdHere" --header="X-Parse-Master-Key: YourMasterKeyHere"

This will generate aschema.json file as output

## 3 - Add your Schema.json file to the project

Add the schema.json file that you downloaded or retrieved to your project in the root directory. This is the same folder where your AppDelegate.swift file is located.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/v9dpqhKpw7IaDvEF7ssNE_image.png)

## 4 - Create your GraphQL files

You now can create your GraphQL files with your files and mutations.
Those file must have the .graphql extension and cointain at least one query or mutation in order for Apollo to crate the Swift code from.

A useful convention is to colocate queries, mutations or fragments with the Swift code that uses them by creating \<name>.graphql next to \<name>.swift.

If you don’t have pre-existing .graphql files in your file tree, create a very simple query and add it to a .graphql file in your file tree so that when you run the code generation build step, it actually finds something. If you don’t, you’ll get the error No operations or fragments found to generate code for.

Here is a simple query that as an example, that returns Parse users:

```graphql
1   query findAllUsers{
2     objects{
3       find_User{
4         count
5         results{
6           username
7         }
8       }
9     }
10  }
```

Add that file to your Target directory at the same level your schema.json file is:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uWcuy8aTL1cGATaqNejYM_image.png)

## 5 - Add a code generation build step

You can invoke apollo as part of the Xcode build process, which will retrieve and update the schema.json file automatically so your classes will always reflect any changes you make by calling the check-and-run-apollo-cli.sh wrapper script.

The wrapper checks if the version of Apollo installed on your system is compatible with the framework version in your project.
If you don’t check that, you could potentially generate code that is incompatible with the runtime code in the framework.

The steps are:

1. On your application target’s Build Phases settings tab, click the + icon and choose New Run Script Phase.
2. In the created Run Script, change its name to Generate Apollo GraphQL API.
3. Drag this new run script just above Compile Sources in your list of Build Phases so that it executes before your code is compiled.
4. Add the following to the Run Script:

> SCRIPT\_PATH="$\{PODS\_ROOT}/Apollo/scripts"
> cd "$\{SRCROOT}/$\{TARGET\_NAME}"
> "$\{SCRIPT\_PATH}"/check-and-run-apollo-cli.sh codegen --target=swift --includes=./\*\*/\*.graphql --localSchemaFile="schema.json" API.swift

If you are using XCode 11 Beta, add this script instead:

> \# Go to the build root and go back up to where SPM keeps the apollo iOS framework checked out.
> cd "$\{BUILD\_ROOT}"
> cd "../../SourcePackages/checkouts/apollo-ios/scripts"
>
> APOLLO\_SCRIPT\_PATH="$(pwd)"
>
> if \[ -z "$\{APOLLO\_SCRIPT\_PATH}" ]; then
> echo "error: Couldn't find the CLI script in your checked out SPM packages; make sure to add the framework to your project."
> exit 1
> fi
>
> cd "$\{SRCROOT}/$\{TARGET\_NAME}"
> "$\{APOLLO\_SCRIPT\_PATH}"/check-and-run-apollo-cli.sh codegen --target=swift --includes=./\*\*/\*.graphql --localSchemaFile="schema.json" API.swift

## 6 - Build and add your API file to the target

Build your project and a file named API.swift should be created on your Target’s directory:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/thnYgBrzLVGMa9km99xEM_image.png)

Drag the generated API.swift file to your target and make sure to uncheck the “Copy Files If Needed” checkbox:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VyC5uBcLau99P9-M6EPvg_image.png)

Make sure you checked all the Targets the API file needs to be included in.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/xaX63Ky-ADCvHITAbPCC2_image.png" signedSrc size="60" width="266" height="394" position="center" caption alt}

## 7 - Configure the Client

Now on your ViewController.swift, create your Apollo configuration and change your AppId and ClientKey values:

```swift
1   let apollo: ApolloClient = {
2       let configuration = URLSessionConfiguration.default
3       configuration.httpAdditionalHeaders = [
4           "X-Parse-Application-Id": "YourAppIdHere",
5           "X-Parse-Client-Key": "YourClientKeyHere"
6       ]
7
8       let url = URL(string: "https://parseapi.back4app.com/graphql")!
9   
10      return ApolloClient(
11          networkTransport: HTTPNetworkTransport(
12              url: url,
13              configuration: configuration
14          )
15      )
16  }()
```

## 8 - Configure the Client

On your viewDidLoad, call your GraphQL query from the Apollo client:

```swift
1   apollo.fetch(query: FindAllUsersQuery()) { result in
2       guard let data = try? result.get().data else { return }
3       print(data.objects?.findUser.results[0].username)
4   }
```

If you have any users in your User class, the first one (index 0) should be retrieved:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/8QfZCn__MDZsnj1VrTftc_image.png)

## Optional Step - Get syntax highlighting

You can have GraphQL syntax highlighting on XCode if you wish. To achieve that:

1. Clone the [xcode-apollo repository](https://github.com/apollostack/xcode-apollo) to your computer.
2. Close Xcode if it is currently running.
3. You may need to create these folders inside of \~/Library/Developer/Xcode:

> mkdir \~/Library/Developer/Xcode/Plug-ins \~/Library/Developer/Xcode/Specifications

&#x20;   4\. Copy GraphQL.ideplugin to \~/Library/Developer/Xcode/Plug-ins.

> cp -R GraphQL.ideplugin \~/Library/Developer/Xcode/Plug-ins

&#x20;    5\. Copy GraphQL.xclangspec to \~/Library/Developer/Xcode/Specifications.

> cp -R GraphQL.xclangspec \~/Library/Developer/Xcode/Specifications

You may receive a warning the first time you start up Xcode after installing these add-ons. Once you agree to load the plugin, you will no longer see this warning.

[title] Facebook login
[path] iOS/

# Add Facebook login to your iOS App using Swift tutorial

## Introduction

This section explains how you can create an app with user registration using Facebook Login and <a href="https://www.back4app.com/product/parse-server" target="_blank">Parse Server core features</a> through Back4App.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- <a href="https://developer.apple.com/xcode/" target="_blank">Xcode</a>.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at Back4App.
- An iOS app connected to Back4App.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-objc-sdk" target="_blank">Install Parse SDK (ObjC) Tutorial</a> to create an Xcode Project connected to Back4App.
:::

## 1 - Facebook Set up

To start using Facebook functions, you need to:

1. Go to the <a href="https://developers.facebook.com/" target="_blank">Facebook Developer Website </a>and create an account and an app.
2. Add your application’s Facebook Application ID on your Parse application’s settings page.
3. Follow Facebook’s instructions for [getting started with the Facebook SDK ](https://developers.facebook.com/docs/ios/getting-started)to create an app linked to the Facebook SDK.

## 2 - Link your Facebook App with Back4App

1. Go to your App dashboard at <a href="https://www.back4app.com/" target="_blank">Back4App Website </a>and click on Server Settings.
2. Find the “Facebook Login” block and click on Settings. The “Facebook Login” block looks like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/np6VzJ6Qoxfr8mKrVtYw4_image.png)

&#x20;    3\. Go back to your XCode Project, open your info.plist copy the code from <a href="https://developers.facebook.com/docs/facebook-login/ios" target="_blank">Facebook Configuration</a>, Step 4a, item 2, and paste it in the \<dict>...\</dict>  part of your info.plist.

&#x20;    4\. In order to use a dialog box from Facebook, also copy and paste the code from section 4a, item 3 into your info.plist file.

&#x20;    5\. Save&#x20;

## 3 - Setting up your App

1. Add the following to your application\:didFinishLaunchingWithOptions: method, after you’ve initialized the Parse SDK:

```swift
1    import FBSDKCoreKit
2    import Parse
3
4    // AppDelegate.swift
5    func application(application: UIApplicatiofunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
6    // Initialize Parse.
7    let parseConfig = ParseClientConfiguration {
8        $0.applicationId = "parseAppId"
9        $0.clientKey = "parseClientKey"
10       $0.server = "parseServerUrlString"
11   }
12   Parse.initialize(with: parseConfig)
13   PFFacebookUtils.initializeFacebook(applicationLaunchOptions: launchOptions)
14   }
```

&#x20;      2\. Add the following handlers in your app delegate:

```swift
1    func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
2
3    return FBSDKApplicationDelegate.sharedInstance().application(
4                application,
5                open: url,
6                sourceApplication: sourceApplication,
7                annotation: annotation
8        )
9  
10   }
11
12   func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
13
14   return FBSDKApplicationDelegate.sharedInstance().application(
15               app,
16               open: url,
17               sourceApplication: options[.sourceApplication] as? String,
18               annotation: options[.annotation]
19       )
20
21   }
22
23   //Make sure it isn't already declared in the app delegate (possible redefinition of func error)
24   func applicationDidBecomeActive(_ application: UIApplication) {
25   FBSDKAppEvents.activateApp()
26   }
```

## &#x20;4 - Log in & Sign Up

PFUser provides a way to allow your users to log in or sign up through Facebook. This is done by using the logInInBackgroundWithReadPermissions method like so:

```swift
1    PFFacebookUtils.logInInBackground(withReadPermissions: permissions) {
2    (user: PFUser?, error: Error?) in
3    if let user = user {
4        if user.isNew {
5        print("User signed up and logged in through Facebook!")
6        } else {
7        print("User logged in through Facebook!")
8        }
9     } else {
10        print("Uh oh. The user cancelled the Facebook login.")
11    }
12    }
```

When this code is run, the following happens:

1. The user is shown the Facebook login dialog.
2. The user authenticates via Facebook, and your app receives a callback using handleOpenURL
3. Our SDK receives the user’s Facebook access data and saves it to a PFUser. If no PFUser exists with the same Facebook ID, then a new PFUser is created.
4. Your code block is called with the user.
5. The current user reference will be updated to this user.

The permissions argument is an array of strings that specifies what permissions your app requires from the Facebook user. These permissions must only include read permissions.

To acquire publishing permissions for a user so that your app can, for example, post status updates on their behalf, you must call \[PFFacebookUtils logInInBackgroundWithPublishPermissions:]:

```swift
1   PFFacebookUtils.logInInBackgroundWithPublishPermissions(["publish_actions"], {
2     (user: PFUser?, error: NSError?) -> Void in
3     if user != nil {
4       // Your app now has publishing permissions for the user
5     }
6   })
```

## 5 - Linking

If you want to associate an existing PFUser to a Facebook account, you can link it like so:

```swift
1   if !PFFacebookUtils.isLinkedWithUser(user) {
2     PFFacebookUtils.linkUserInBackground(user, withReadPermissions: nil, {
3       (succeeded: Bool?, error: NSError?) -> Void in
4       if succeeded {
5         print("Woohoo, the user is linked with Facebook!")
6       }
7     })
8   }
```

## 6 - UnLinking

If you want to unlink Facebook from a user, simply do this:

```swift
1   PFFacebookUtils.unlinkUserInBackground(user, {
2     (succeeded: Bool?, error: NSError?) -> Void in
3     if succeeded {
4       print("The user is no longer associated with their Facebook account.")
5     }
6   })
```


[title] Facebook Login
[path] JavaScript/

# Add facebook login to your javascript App

## Introduction

This section guides you on how to use the Facebook API for JavaScript in a <a href="http://parseplatform.org/" target="_blank">Parse</a> environment through Back4App.

In this tutorial, you will learn how to link the Facebook SDK to your Parse dashboard and how to implement Facebook login, signup, logout, link and unlink functions.

By following the below-mentioned steps, you will be capable of building a system like this: <a href="http://js-fb-login.back4app.io/" target="_blank">Back4App JavaScript Example Facebook Login App</a>.

:::hint{type="info"}
**At any time, you can access the complete Android Project built with this tutorial at our **<a href="https://github.com/back4app/javascript-facebook-login" target="_blank">**GitHub repository**</a>**.**
:::

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you will need:**

- Basic JavaScript App connected with Back4App.
  - **Note: **You can use the app created in our <a href="https://www.back4app.com/docs/javascript/parse-javascript-sdk" target="_blank">JavaScript Install Parse SDK tutorial</a> or any app connected to Back4App.
- A domain for your app.
  - **Note: **It’s necessary to have a domain to start using the Facebook Login API. To know more about web hosting take a look at <a href="https://help.back4app.com/hc/en-us/articles/360002120991-How-can-I-use-Back4App-webhosting" target="_blank">Back4App WebHosting tutorial</a>.
- A Parse Server at version 2.6.5 or higher.
  - **Note: **The Parse Facebook SDK only works on Parse Server version higher than 2.6.5, which this tutorial will be using. So, if you’re using a lower version of Parse, consider upgrading it.
:::

## 1 - Facebook Set up

To start using the Facebook SDK for JavaScript, you need to follow these steps:

1. Go to the <a href="https://developers.facebook.com/" target="_blank">Facebook Developer Website</a>, create an account and an App.
2. Set up your Facebook App.
3. Activate the Facebook Login by clicking on Facebook login > Quickstart, which is present on the left menu, then follow the <a href="https://developers.facebook.com/docs/facebook-login/web" target="_blank">Facebook Quickstart Documentation</a> carry out the activation.
4. To link Back4app with your Facebook App, log in to your <a href="https://www.back4app.com/" target="_blank">Back4App </a>account, click on Server Settings of your App and click on Settings of the ''Facebook Login'' block. It should look like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ixUc4J1oLUcmumlAis20W_image.png)

&#x20;   5\. Then, add your Facebook App ID, which can be found on the <a href="https://www.facebook.com/login.php?next=https%3A%2F%2Fdevelopers.facebook.com%2Fapps" target="_blank">Dashboard</a> of your Facebook App.
&#x20;   6\. Follow <a href="https://developers.facebook.com/docs/javascript/quickstart/" target="_blank">these instructions</a> for loading the Facebook JavaScript SDK into your application.
&#x20;   7\. Replace your call to FB.init() with a call to Parse.FacebookUtils.init(). For example,
&#x20;if you load the Facebook JavaScript SDK asynchronously, your fbAsyncInit function will look like this:

:::CodeblockTabs
init.js

```javascript
1   // Initialize Parse
2   Parse.initialize("Your Parse App Id Here", "Your JavaScript Key Here"); // don't forget to change these keys
3   Parse.serverURL = "https://parseapi.back4app.com/";
4
5   // Load the Facebook API asynchronous when the window starts loading
6   window.fbAsyncInit = function() {
7       Parse.FacebookUtils.init({ // this line replaces FB.init({
8           appId      : '{facebook-app-id}', // Facebook App ID
9           cookie     : true,  // enable cookies to allow Parse to access the session
10          xfbml      : true,  // initialize Facebook social plugins on the page
11          version    : 'v2.3' // point to the latest Facebook Graph API version, found in FB's App dashboard.
12      });
13
14      // Put here code to run after the Facebook SDK is loaded.
15  };
16
17  // Include the Facebook SDK
18  (function(d, s, id){
19      var js, fjs = d.getElementsByTagName(s)[0];
20      if (d.getElementById(id)) {return;}
21      js = d.createElement(s); js.id = id;
22      js.src = "//connect.facebook.net/en_US/sdk.js";
23      fjs.parentNode.insertBefore(js, fjs);
24  }(document, 'script', 'facebook-jssdk'));
```
:::

## 2 - Log in

Start by making a *log in with Facebook* function that saves the user to the Parse
database.

Unfortunately, it’s not possible to use the login button provided by Facebook, as logging in
with it would not save the new user to the Parse Dashboard. However, when you use the <a href="http://docs.parseplatform.org/js/guide/#facebook-users" target="_blank">Parse SDK for Facebook</a>, it solves the problem on the server side.

:::hint{type="info"}
For an easy design of the Facebook Login button, using HTML and CSS, look at this implementation: <a href="https://codepen.io/davidelrizzo/pen/vEYvyv" target="_blank">Facebook Login Button</a>.
:::

To start the Login Implementation, assign an onClick event that calls the following logIn function to your Facebook Login button. To build this function, follow the steps mentioned below:

1. Use the Parse.FacebookUtils.logIn to perform the Facebook log in together with Parse, this function receives Facebook’s permissions as arguments. In this example, these permissions correspond to the public profile and email.

:::hint{type="info"}
Note: See <a href="https://developers.facebook.com/docs/permissions" target="_blank">Facebook Login Permission Reference</a> for more details.
:::

&#x20;     2\. Check if the user is already in the database. If he is, just redirect him to another page.
&#x20;     3\. Make a call to FB.api to get information about the new user. In this example, it’s possible to access the ID, name, email and permissions of users.

:::hint{type="info"}
**Note**: To know more about this function click <a href="https://developers.facebook.com/docs/javascript/reference/FB.api" target="_blank">here</a>.
:::

&#x20;    4\. Set the properties, username and email of the user and save it to the database.
&#x20;    5\. Redirect the page.

The *logIn() *code is the following:

:::CodeblockTabs
login.js

```javascript
1   function logIn() {
2       Parse.FacebookUtils.logIn("public_profile,email", {
3           success: function(user) {
4               if (!user.existed()) {
5                   FB.api('/me?fields=id,name,email,permissions', function (response) {
6                       user.set('username', response.name);
7                       user.set('email', response.email);
8
9                       user.save(null, {
10                          success: function(user) {
11                              alert('User logged in and sign up through Facebook, with username: ' + user.get('username') + ' and email: ' + user.get('email'));
12  
13                              // You should redirect the user to another page after a successful login.
14                              window.location.replace('http://js-fb-login.back4app.io/logout.html');
15                          },
16                          error: function(user, error) {
17                              alert('Failed to save user to database with error: ' + error.message);
18                          }
19                      });
20                  });
21              } else {
22                  alert("User logged in through Facebook!");
23                  // You should redirect the user to another page after a successful login.
24                  window.location.replace('http://js-fb-login.back4app.io/logout.html');
25              }
26          },
27          error: function(user, error) {
28              console.log("User cancelled the Facebook login or did not fully authorize.");
29          }
30      });
31  }
```
:::

## 3 - Log out

The log out function is way simpler than the log in one. This time, you will be able to use the button provided by Facebook. However, it will be used just for log out purposes, because it’s necessary to use the Parse’s function to log in. Thus, you should only use this button when the user is already logged in to Facebook and want to log out.

:::hint{type="info"}
To see the official Facebook reference to the Facebook button click <a href="https://developers.facebook.com/docs/facebook-login/web/login-button" target="_blank">here</a>.
:::

Here’s how you can implement this button using the Facebook SDK:

:::CodeblockTabs
logout.html

```html
1   <div class="fb-login-button" data-max-rows="1" data-size="large" data-button-type="login_with"
2        data-show-faces="false" data-auto-logout-link="true" data-use-continue-as="false"
3        onlogin="logOutWithFacebook()"></div>
```
:::

:::hint{type="info"}
**Note**: this element has a callback onlogin, which calls our function logOutWithFacebook when the user logs out. Yeah, that’s right: the onlogin event is fired on log out.
:::

The logOutWithFacebook function will simply log out the user from his current Parse session and redirect him to another page, as shown below:

:::CodeblockTabs
logout.js

```javascript
1   function logOutWithFacebook() { // The callback function
2       Parse.User.logOut(); // Delete the current session for the user
3       alert('User logged out of Facebook!');
4       window.location.replace('http://js-fb-login.back4app.io'); // Redirects the user to another webpage
5   }
```
:::

## 4 - Link and Unlink

The last features available for Parse Facebook are link and unlink functions.

While **Linking** is the act of associating an existing Parse.User to his Facebook account and **Unlinking **refers to removing this association. This association can be seen in your Parse Dashboard, in the authData column, right here:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uZ1mtNyTrWfLZTcBs0mon_image.png)

You can use the data in the column to validate your link and unlink functions.

### **Step 4.1 - Link**

The link function checks if the current user is linked before trying to link him again. It is quite
&#x20;simple and uses the Parse.FacebookUtils SDK, as shown below:

:::CodeblockTabs
link.js

```javascript
1   function link() {
2       var user = Parse.User.current();
3       if (!Parse.FacebookUtils.isLinked(user)) {
4           Parse.FacebookUtils.link(user, null, {
5               success: function(user) {
6                   alert("Woohoo, user logged in with Facebook!");
7               },
8               error: function(user, error) {
9                   alert("User cancelled the Facebook login or did not fully authorize.");
10              }
11          });
12      }
13      else {
14          alert("Can't link user to facebook because he is already linked");
15      }
16  }
```
:::

### **Step 4.2 - Unlink**

For the unlink function, simply call Parse.FacebookUtils.unlink on the current Parse User, as you can see:

:::CodeblockTabs
unlink.js

```javascript
1   function unlink() {
2       var user = Parse.User.current();
3       Parse.FacebookUtils.unlink(user, {
4           success: function(user) {
5               alert("The user is no longer associated with their Facebook account.");
6           }
7       });
8   }
```
:::

:::hint{type="info"}
Go to the authData column in your Parse Dashboard to confirm that it is empty after the function call.
:::

## It’s done!

At this point, you have learned not only how to configure and use the Facebook login and logout functions with your app, but also how to use them with Back4App and Parse.

You now master the use of the Parse Facebook SDK and can start using it at will.

:::hint{type="success"}
See more about Parse SDK at J<a href="https://parseplatform.org/Parse-SDK-JS/api/4.3.1/" target="_blank">avaScript SDK API Reference</a> and <a href="https://docs.parseplatform.org/js/guide/#facebook-users" target="_blank">Parse open source documentation for Javascript SDK</a>.
:::


[title] Sign In with Apple
[path] JavaScript/

# Sign In with Apple - Javascript

## Introduction

In this section you learn how to get started with an Javascript template in a few easy steps.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Set up Sign In with Apple in your Apple Developer account.
  - Follow the <a href="https://www.back4app.com/docs/platform/sign-in-with-apple" target="_blank">Sign In with Apple Tutorial</a> to learn how to set up Sign In with Apple in your Apple Developer account.
:::

## 1 - Get the template

Clone or download the template at
<a href="https://github.com/back4app/SignInWithApple" target="_blank">Back4App’s GitHub repository</a>, open the Javascript foder.

## 2 - Open the project template

1. Open your editor choice.
2. Click on File=>Open
3. Navigate to the project folder and open the fileindex.html

## 3 - Edit the template

Find in the template the following function

```swift
1   AppleID.auth.init({
2       clientId : 'com.back4app.app.servicesid',
3       scope : 'email',
4       redirectURI: 'https://example-app.com/redirect',
5       state = random
6   });
```

and replace the values for the clientID and redirectURI for the values you set in your Services ID in your Apple Developer account.&#x20;

Save and close.

:::hint{type="info"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 4 - Test your App

1. Open your browser of choice
2. Click onFile->Open
3. Wait until theSign in with Apple screen appears.
4. Click the button and do the login process.

If everything works properly, you should be redirected to your URI.

## Next Steps

At this point, you have learned how to get started with Javascript.

:::hint{type="success"}
Learn more by walking around our <a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank">iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Encrypted Chat
[path] Security & Privacy/

## How to make a GDPR compliant chat app

## Introduction

**Ahoy Back4app community!**

This is a guest tutorial from the team at [Virgil Security, Inc.](https://virgilsecurity.com/?utm_source=back4app\&utm_medium=blog\&utm_campaign=e2eechat): we’re the crypto tech behind [Twilio’s End-to-End Encrypted Messaging](https://www.twilio.com/blog/2016/05/introducing-end-to-end-encryption-for-twilio-ip-messaging-with-virgil-security.html). Our friends @ Back4app asked us to show you how to build an End-to-End Encrypted chat app on top of Back4app.

In this post, we’ll walk you through the steps to make a simple Back4App Android Messenger app End-to-End Encrypted! Are you ready? PS: If you don’t care about the details, simply skip to the end of the post and download the final product.

## What is End-to-End Encryption?

First, let’s start with a quick refresher of what E2EE (End-to-End Encryption) is and how it works. E2EE is simple: when you type in a chat message, it gets encrypted on your mobile device (or in your browser) and gets decrypted only when your chat partner receives it and wants to display it in chat window.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/9MxLtJtygPyFxE2Hc-onc_image.png)

The message remains encrypted while it travels over Wi-Fi and the Internet, through the cloud / web server, into a database, and on the way back to your chat partner. In other words, none of the networks or servers have a clue of what the two of you are chatting about.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ZSXJ30mbsiszJn8VS7Kgf_image.png)

What’s difficult in End-to-End Encryption is the task of managing the encryption keys in a way that only the users involved in the chat can access them and nobody else. And when I write “nobody else”, I really mean it: even insiders of your cloud provider or even you, the developer, are out; [no accidental mistakes](https://techcrunch.com/2017/11/29/meet-the-man-who-deactivated-trumps-twitter-account/) or legally enforced peeking are possible. Writing crypto, especially for multiple platforms is hard: generating true random numbers, picking the right algorithms, and choosing the right encryption modes are just a few examples that make most developers wave their hands in the air and end up just NOT doing it.

This blog post will show you how to ignore all these annoying details and quickly and simply End-to-End Encrypt using Virgil’s SDK.

:::hint{type="info"}
**For an intro, this is how we’ll upgrade Back4app’s messenger app to be End-to-End Encrypted:**

1. During sign-up: we’ll generate the individual private & public keys for new users (remember: the recipient’s public key encrypts messages and the matching recipient’s private key decrypts them).
2. Before sending messages, you’ll encrypt chat messages with the recipient’s public key.
3. After receiving messages, you’ll decrypt chat messages with the recipient’s private key.
:::

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/s-adqgg_eoOkic1lKZpLF_image.png)

We’ll publish the users’ public keys to Virgil’s Cards Service so that chat users are able to look up each other and able to encrypt messages for each other. The private keys will stay on the user devices.

**Keep it simple**

This is the simplest possible implementation of E2EE chat and it works perfectly for simple chat apps between 2 users
where conversations are short-lived and it’s okay to lose the message history if a device is lost with the private key on it.

**OK, enough talking! Let’s get down to coding.**

- We’ll start by guiding you through the Android app’s setup,
- Then, we’ll add some code to make the app end-to-end encrypted.

## Prerequisites

:::hint{type="info"}
**To complete this tutorial, you need:**

- Android Studio.
- An app created at Back4App.
  - Follow the<a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank"> Create New App tutorial</a> to learn how to create an app at Back4App.
- Sign up for a <a href="https://developer.virgilsecurity.com/account/signup?utm_source=back4app&utm_medium=blog&utm_campaign=e2eechat" target="_blank">Virgil Security account</a> (we’ll create the app later).
:::

## Let’s set up the “clean” Back4app messenger app

### **1: Set up your App Server**&#xD;

Let’s start with deploying the cloud function. For this, you will need to:

- Find main.js and package.json in scripts directory;
- Open main.js with your favorite editor.

**1.1) Get Back4App credentials
**

- Open Dashboard of your app > App Settings > Security & Keys:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Vd_coxs_eWmZ4oh8zDTS1_image.png)

- In main.js, replace PARSE\_APP\_ID with your Application ID and PARSE\_REST\_API\_KEY with your REST API key:

> **1   const**
>
>  PARSE_APP_ID 
>
> **=**
>
>  "YOUR_PARSE_APP_ID"
>
> **;**
>
>
>
>
> **2   const**
>
>  PARSE_REST_API_KEY 
>
> **=**
>
>  "YOUR_PARSE_REST_API_KEY"
>
> **;**

**1.2) Get Virgil credentials**

- Create an application at [Virgil Dashboard](https://dashboard.virgilsecurity.com/)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/SRkmXhqhh3ZK8ugdd3X1__image.png)

- Open your new Virgil application, navigate to E3Kit section and and generate a .env file under the E3Kit section in the left side bar

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/pv-zBaQQSXccASrxCeDnV_image.png)

- Copy the values of APP\_ID, APP\_KEY, and APP\_KEY\_ID from the .env file

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/kNkwijbS8tAt4U62FwAeG_image.png" signedSrc size="60" width="498" height="765" position="center" caption}

- Replace the copied values in your main.js file in the corresponding fields (main.js of scripts directory):

> **1   const**
>
>  APP_ID 
>
> **=**
>
>  "YOUR_VIRGIL_APP_ID"
>
> **;**
>
>
>
>
> **2   const**
>
>  APP_KEY 
>
> **=**
>
>  "YOUR_VIRGIL_APP_KEY"
>
> **;**
>
>
>
>
> **3   const**
>
>  APP_KEY_ID 
>
> **=**
>
>  "YOUR_VIRGIL_APP_ID"
>
> **;**

**1.3) Deploy cloud code function**

- Open Back4App “Dashboard” of your app -> “Core” -> Cloud code functions;

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/_k7MdNBlcB6pLGRsGvCfA_image.png)

- Click +ADD and select your main.js and package.json (from scripts directory), after that move both of them to the cloud folder;
- Click DEPLOY.

### **2: Start clean Back4app Kotlin Demo app**

Don’t forget to set up Back4App cloud code function first. It is a mandatory part of this demo. After this, go through the following steps:

**2.1) Import Project in Android Studio**

- Open Android Studio -> File > New > Project from Version Control > Git
- Git Repository URL: https\://github.com/VirgilSecurity/chat-back4app-android
- Check out the clean-chat-kt branch

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/H40FYle-nJ0mx_2TpiBQR_image.png)

**Important!**

Select “Project” type of file tree. It will be used all-through the tutorial:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/4vzq9wbFy-VdgKU6MHY_w_image.png" signedSrc size="50" width="730" height="256" position="center" caption}

**2.2) Setup Back4App credentials in project**

- Open Back4App “Dashboard” of your app -> “App Settings” -> “Security & Keys”;

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ni7M9NXyzAQQ1xAeRPNYI_image.png)

Go to /app/src/main/res/values/strings.xml file in your android project and replace your\_back4app\_app\_id with your Application ID and your\_back4app\_client\_key with your Client Key:

> 1   <string name="back4app_app_id">your_back4app_app_id</string>
> 2   <string name="back4app_client_key">your_back4app_client_key</string>

**2.3) Setup DB**

- Open Back4App “Dashboard” -> “Core” -> “Database Browser” -> “Create a class” and create classes of Custom type named Message and ChatThread;

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/e9oZV71XFXwRSgAVm2Ckd_image.png)

**2.4) Setup live query**

- Go back to your [Back4App account](https://dashboard.back4app.com/apps/#!/admin)
- Press the Server Settings button on your Application
- Find the “Web Hosting and Live Query” block
- Open the Live Query Settings and check the Activate Hosting option.
- Choose a name for your subdomain to activate Live Query for the 2 classes you created: Message and ChatThread.
- Copy your new subdomain name and click the SAVE button:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/UMjEBWfBaonEDrKWAh1VK_image.png)

Return to /app/src/main/res/values/strings.xml and paste “Subdomain name” you have entered above into the back4app\_live\_query\_url instead of “yourSubdomainName”:

> \<string name="back4app_live_query_url">wss://yourSubdomainName.back4app.io/</string>

After these steps you will be able to hit the Run button in Android Studio and get the sample to work. Use emulator or real device to test it out.

### **3: Run the clean demo**

To see the result of running the clean version of the demo, you’ll need to:

1. Sign up 2 users;
2. Start a conversation between them and send a couple of messages;

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-Yd80ckKrGkU8Vlujv9Lx_image.png" signedSrc size="60" width="419" height="851" position="center" caption}

&#x20;     3\. Open Back4App “Dashboard” -> “Core” -> “Database Browser” -> “Message”.

If it all worked out, you should see the chat messenger app popping up. Register two users and send a few messages to each other: you should see new data showing up in the Message class.

**Note that you can see on the server what your users are chatting about:**

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/yLYDjH3zMY1BEGFyM4Z8v_image.png)

**Next**: Close your chat interface and move on to the next step – adding E2EE encryption.

## Now, let’s End-to-End Encrypt those messages!

By the end of this part, this is how your chat messages will look like on the server: can you spot the difference?

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nScf6epGgfptXK0D6iSv8_image.png)

How do we get there? Obviously, we need to implement end-to-end encryption, which means that our app needs to:

- Generate the private & public key pair as part of signup
- Store the private key in the key storage on user’s device
- Publish the public key in Virgil’s Card Service as a “Virgil Card” for other users to download & encrypt messages with it
- Encrypt messages with public key and sign with private key; decrypt messages with private key and verify with public key

For this, we will need to add E3Kit to our clean demo application and some more code to implement all that was described above.

But before we begin, let’s clear two important terms for you: what’s a Virgil Card and a private key?

- **Virgil Card - **Virgil Сards carry the users’ private keys. Virgil Cards are published to Virgil’s Cards Service (imagine this service is like a telephone book) for other users to retrieve them: Alice needs to retrieve Bob’s public key in order to encrypt a message for Bob using that key.
- **Private key - **a private part of the encryption key. Remember, private keys can decrypt data that was encrypted using the matching public key.

## **1: Add E3Kit to the clean E3Kit Back4App Kotlin Demo**

- In the app-level (Module: app) gradle at /app/build.gradle add (but don’t sync gradle scripts just yet):

> implementation "com.virgilsecurity:ethree:$rootProject.ext.ethree"

- Add the following to the end of your project-level /build.gradle (Project: chat-back4app-android):

> 1   ext {
> 2   ethree = "2.0.5"
> 3   }
>

- Now you can synchronize gradle scripts;
- Update your AppVirgil class by adding new fields:

> 1
>
> **   companion**
>
>  
>
> **object**
>
>  \{
> 2     
>
> **lateinit**
>
>  
>
> **var**
>
>  eThree: 
>
> **EThree**
>
>
> 3     
>
> **fun**
>
>  
>
> **isEthreeInitialized**
>
> () = 
>
> **::**
>
> eThree.isInitialized
> 4   }

- Press Alt+ Enter to import the necessary libraries into the class.

## **2: Authenticate users with Back4App Cloud Code**

- In ../virgilsecurity/virgilback4app/model/ directory, create data classes AuthenticateResponse and VirgilJwtResponse that represent responses from Cloud Code functions:

> 1
>
> **    data class**
>
>  
>
> **AuthenticateResponse**
>
> (
>
> **val**
>
>  authToken: 
>
> **String**
>
> )
> 2
> 3
>
> **    data class**
>
>  
>
> **VirgilJwtResponse**
>
> (
>
> **val**
>
>  virgilToken: 
>
> **String**
>
> )

- In ../virgilsecurity/virgilback4app/util/ create AuthRx object that implements calls to Cloud Code functions (don’t forget to import all the necessary libraries afterwards):

```javascript
1   object AuthRx {
2
3       /**
4        * You can call it only after successful [authenticate]
5        */
6       fun virgilJwt(sessionToken: String) = Single.create<String> { emitter ->
7           val requestParams = mutableMapOf<String, String>().apply {
8               put("sessionToken", sessionToken)
9           }
10
11          ParseCloud.callFunctionInBackground<Map<String, Any>>(
12              KEY_VIRGIL_JWT,
13              requestParams
14          ) { virgilJwt, exception ->
15              if (exception == null)
16                  emitter.onSuccess(virgilJwt[KEY_TOKEN].toString())
17              else
18                  emitter.onError(exception)
19
20          }
21      }
22
23      private const val KEY_VIRGIL_JWT = "virgil-jwt"
24      private const val KEY_TOKEN = "token"
25  }
```

## **3: Store Virgil JWT locally**

Virgil token received from Cloud Code functions needs to be stored locally. Let’s update Preferences class in ../virgilsecurity/virgilback4app/util/:

- Define constant:

> **1   private**
>
>  
>
> **const**
>
>  
>
> **val**
>
>  KEY_VIRGIL_TOKEN = "KEY_VIRGIL_TOKEN"



- Add functions setVirgilToken, virgilToken and clearVirgilToken:

\~\~\~kotlin

fun setVirgilToken(virgilToken: String) \{
with(sharedPreferences.edit()) \{
putString(KEY\_VIRGIL\_TOKEN, virgilToken)
apply()
}
}



fun virgilToken(): String? \{
with(sharedPreferences) \{
return getString(KEY\_VIRGIL\_TOKEN, null)
}
}

fun clearVirgilToken() \{
with(sharedPreferences.edit()) \{
remove(KEY\_VIRGIL\_TOKEN)
apply()
}
}

```kotlin
1   Virgil token should be reset on logout. Let's add `Preferences.instance(this).clearVirgilToken()` line into `initDrawer` function of `ThreadsListActivity` class  (in `../virgilsecurity/virgilback4app/chat/contactsList/`):
2   ~~~kotlin
3   private fun initDrawer() {
4       ...
5       nvNavigation.setNavigationItemSelectedListener { item ->
6           R.id.itemLogOut -> {
7               dlDrawer.closeDrawer(GravityCompat.START)
8               presenter.disposeAll()
9               showBaseLoading(true)
10              // New code >>
11              Preferences.instance(this).clearVirgilToken()
12              // << New code
13              ...
14          }
15      }
16  }
```

## **4: Modify user registration**

E3Kit takes care about your private and public keys. To generate them during the registering process, we’ll need to do the following:

- In ../virgilsecurity/virgilback4app/util/create RxEthree class:

> 1
>
> **   class**
>
>  
>
> **RxEthree**
>
> (
>
> **val**
>
>  context: 
>
> **Context**
>
> ) {
> 2
> 3       
>
> **private**
>
>  
>
> **val**
>
>  preferences = 
>
> **Preferences**
>
> .
>
> **instance**
>
> (context)
> 4   }

- Now, add initEthree function that initializes E3Kit instance in RxEthree class:

```none
1      import com.virgilsecurity.android.common.model.EThreeParams
2      import com.virgilsecurity.android.ethree.interaction.EThree
3
4
5      fun initEthree(identity: String, verifyPrivateKey: Boolean = false): 6Single<EThree> = Single.create { e ->
6       val params = EThreeParams(identity, {preferences.virgilToken()!!}, context)
7       val ethree = EThree(params)
8       if (verifyPrivateKey) {
9           if (ethree.hasLocalPrivateKey()) {
10              e.onSuccess(ethree)
11          } else {
12              e.onError(KeyEntryNotFoundException())
13          }
14      } else {
15          e.onSuccess(ethree)
16      }
17  }
```

- Add registerEthree function that registers a new user to RxEthree class.
  E3Kit generates a key pair during a sign up. The generated private key then is stored in local storage, and public key is published to Virgil Services as a Virgil Card.

```none
1   import com.android.virgilsecurity.virgilback4app.AppVirgil
2   import com.virgilsecurity.common.callback.OnCompleteListener
3   import io.reactivex.Completable
4
5
6   fun registerEthree(): Completable = Completable.create { e ->
7       AppVirgil.eThree.register().addCallback(object : OnCompleteListener {
8           override fun onError(throwable: Throwable) {
9               e.onError(throwable)
10          }
11
12          override fun onSuccess() {
13              e.onComplete()
14          }
15
16      })
17  }
```

- Let’s make some updates to LogInPresenter class (in ../virgilsecurity/virgilback4app/auth/).
- Add rxEthree field:

```none
1   private val rxEthree = RxEthree(context)
```

Update the requestSignUp function to run registration with E3Kit

```javascript
1   fun requestSignUp(identity: String, onSuccess: () -> Unit, onError: (Throwable) -> Unit) {
2     val password = generatePassword(identity.toByteArray())
3
4     val disposable = RxParse.signUp(identity, password)
5             .subscribeOn(Schedulers.io())
6             .observeOn(Schedulers.io())
7             // New code >>
8             .toSingle { ParseUser.getCurrentUser() }
9             .flatMap { AuthRx.virgilJwt(it.sessionToken) }
10            .map { preferences.setVirgilToken(it) }
11            .flatMap { rxEthree.initEthree(identity) }
12            .map { AppVirgil.eThree = it }
13            .flatMap { rxEthree.registerEthree().toSingle { Unit } }
14            // << New code
15            .observeOn(AndroidSchedulers.mainThread())
16            // Updated code >>
17            .subscribeBy(
18                onSuccess = {
19                    onSuccess()
20                },
21                onError = {
22                    onError(it)
23                }
24            )
25            // << Updated code
26
27    compositeDisposable += disposable
28  }
```

## **5: Modify sign in functions**

Now, let’s make changes to requestSignIn method of LogInPresenter class (in ../virgilsecurity/virgilback4app/auth/):

```javascript
1      fun requestSignIn(identity: String,
2                     onSuccess: () -> Unit,
3                     onError: (Throwable) -> Unit) {
4
5       val password = generatePassword(identity.toByteArray())
6
7       val disposable = RxParse.logIn(identity, password)
8               .subscribeOn(Schedulers.io())
9               .observeOn(Schedulers.io())
10              // New code >>
11              .flatMap { AuthRx.virgilJwt(it.sessionToken) }
12              .map { preferences.setVirgilToken(it) }
13              .flatMap { rxEthree.initEthree(identity, true) }
14              .map { AppVirgil.eThree = it }
15              // << New code
16              .observeOn(AndroidSchedulers.mainThread())
17              // Updated code >>
18              .subscribeBy(
19                  onSuccess = {
20                      onSuccess()
21                  },
22                  onError = {
23                      onError(it)
24                  }
25              )
26              // << Updated code
27
28      compositeDisposable += disposable
29  }
```

## **6: Get the list of existing chat**

Next, add functions that handle E3Kit initialization into ThreadsListFragment class (in ../virgilsecurity/virgilback4app/chat/contactsList/):

```javascript
1   private fun onInitEthreeSuccess() {
2       presenter.requestThreads(ParseUser.getCurrentUser(),
3                                20,
4                                page,
5                                Const.TableNames.CREATED_AT_CRITERIA,
6                                ::onGetThreadsSuccess,
7                                ::onGetThreadsError)
8   }
9
10  private fun onInitEthreeError(throwable: Throwable) {
11      showProgress(false)
12      if (adapter.itemCount == 0)
13          tvError.visibility = View.VISIBLE
14
15      Utils.toast(activity, Utils.resolveError(throwable))
16  }
```

Update postCreateInit function to initialize E3Kit:

```javascript
1   override fun postCreateInit() {
2       ...
3       presenter = ThreadsListFragmentPresenter(activity)
4
5       showProgress(true)
6       // Updated code >>
7       if (AppVirgil.isEthreeInitialized()) {
8           presenter.requestThreads(ParseUser.getCurrentUser(),
9                                    20,
10                                   page,
11                                   Const.TableNames.CREATED_AT_CRITERIA,
12                                   ::onGetThreadsSuccess,
13                                   ::onGetThreadsError)
14      } else {
15          presenter.requestEthreeInit(ParseUser.getCurrentUser(), ::onInitEthreeSuccess, ::onInitEthreeError)
16      }
17      // << Updated code
18  }
```

And add the following code into ThreadsListFragmentPresenter class in virgilsecurity.virgilback4app.chat.contactsList/:

- Add field:

```javascript
1   private val rxEthree = RxEthree(context)
```

- and function

```javascript
1   fun requestEthreeInit(currentUser: ParseUser, onSuccess: () -> Unit, onError: (Throwable) -> Unit) {
2     val disposable = rxEthree.initEthree(currentUser.username)
3             .subscribeOn(Schedulers.io())
4             .observeOn(AndroidSchedulers.mainThread())
5             .subscribeBy(
6                 onSuccess = {
7                     AppVirgil.eThree = it
8                     onSuccess()
9                 },
10                onError = {
11                    onError(it)
12                }
13            )
14
15    compositeDisposable += disposable
16  }
```

At this point we are able to Sign up/Sign in a user and create a new chat with other user.

Now let’s add encryption for our messages.

## **7: Message encryption and decryption**

Let’s add findCard function to RxEthree class (in ../virgilsecurity/virgilback4app/util/) that will help us to get the latest Virgil Card by user name:

```javascript
1   fun findCard(identity: String): Single<Card> = Single.create { e ->
2       AppVirgil.eThree.findUser(identity).addCallback(object : OnResultListener<Card> {
3           override fun onError(throwable: Throwable) {
4                e.onError(throwable)
5           }
6
7           override fun onSuccess(result: Card) {
8               e.onSuccess(result)
9           }
10
11      })
12  }
```

When a chat is opened, we should obtain Virgil Card of the recipient. Edit postCreateInit of ChatThreadFragment class (in ../virgilsecurity/virgilback4app/chat/thread/) by replacing

```javascript
1   presenter.requestMessages(thread,
2                             50,
3                             page,
4                             Const.TableNames.CREATED_AT_CRITERIA,
5                             ::onGetMessagesSuccess,
6                             ::onGetMessagesError)
```

code with a new one

```javascript
1   presenter.requestCard(recipientId,
2                                      ::onGetCardSuccess,
3                                      ::onGetCardError)
```

And add two functions:

```javascript
1   private fun onGetCardSuccess(card: Card) {
2       showProgress(false)
3       adapter.interlocutorCard = card
4       presenter.requestMessages(thread,
5                                 50,
6                                 page,
7                                 Const.TableNames.CREATED_AT_CRITERIA,
8                                 ::onGetMessagesSuccess,
9                                 ::onGetMessagesError)
10  }
11
12  private fun onGetCardError(t: Throwable) {
13      if (t is VirgilCardIsNotFoundException || t is VirgilCardServiceException) {
14          Utils.toast(this,
15                      "Virgil Card is not found.\nYou can not chat with user without Virgil Card")
16          activity.onBackPressed()
17      }
18      showProgress(false)
19      srlRefresh.isRefreshing = false
20      lockSendUi(lock = false, lockInput = false)
21
22      Utils.toast(this, Utils.resolveError(t))
23  }
```

Now let’s change ChatThreadPresenter:

- Add fields:

```javascript
1   private val eThree = AppVirgil.eThree
2   private lateinit var userCard: Card
3   private val rxEthree = RxEthree(context)
```

- Add a function that obtains a Virgil Card of the recipient:

```javascript
1   fun requestCard(identity: String,
2                 onSuccess: (Card) -> Unit,
3                 onError: (Throwable) -> Unit) {
4
5     val disposable = rxEthree.findCard(identity)
6             .subscribeOn(Schedulers.io())
7             .observeOn(AndroidSchedulers.mainThread())
8             .subscribeBy(
9                 onSuccess = {
10                    userCard = it
11                    onSuccess(it)
12                },
13                onError = {
14                    onError(it)
15                }
16            )
17
18    compositeDisposable += disposable
19  }
```

- Add encryption of outcoming messages in requestSendMessage function:

```javascript
1   fun requestSendMessage(text: String,
2                        thread: ChatThread,
3                        onSuccess: () -> Unit,
4                        onError: (Throwable) -> Unit) {
5
6     val encryptedText = eThree.authEncrypt(text, userCard)
7     val disposable = RxParse.sendMessage(encryptedText, thread)
8     ...
9   }
```

Add decryption of all the incoming messages into ChatThreadRVAdapter class (in ../virgilsecurity/virgilback4app/chat/thread/):

- Add fields:

```javascript
1   private var eThree: EThree = AppVirgil.eThree
2   lateinit var interlocutorCard: Card
```

-
  Implement message decryption in onBindViewHolder function

```javascript
1   override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
2     when (viewHolder) {
3         is HolderMessageMe -> {
4             val decryptedText = eThree.authDecrypt(items[position].body)
5             viewHolder.bind(decryptedText)
6         }
7         is HolderMessageYou -> {
8             val decryptedText = eThree.authDecrypt(items[position].body, interlocutorCard)
9             viewHolder.bind(decryptedText)
10        }
11    }
12  }
```

## **8: Run the complete end-to-end encrypted demo**

Now to see the result of our fully end-to-end encrypted demo, go through these steps again:

1. Log out the previous user
2. Sign up 2 new users;
3. Start a conversation between them and send a couple of messages;
4. Open Back4App “Dashboard” -> “Core” -> “Database Browser” -> “Message”.

**Important! **You have to **Log Out **the current user and register two new users, after that you can start E2EE chat with those two new users. The reason is that your first two users have no Virgil Card’s, so you can not use encrypt\decrypt for them.\{: .blockquote-tip}

Done! Now you can see that users’ messages are encrypted and can only be accessed in app by users themselves.

## HIPAA & GDPR compliance:

End-to-End Encryption is a way to meet the technical requirements for HIPAA (the United States’ Health Insurance Portability and Accountability Act of 1996) & GDPR (the European Union’s General Data Protection Regulation). If you need more details, sign up for a free [Virgil account](https://developer.virgilsecurity.com/account/signup?utm_source=back4app\&utm_medium=blog\&utm_campaign=e2eechat), join our Slack community and ping us there: we’re happy to discuss your own privacy circumstances and help you understand what’s required to meet the technical HIPAA & GDPR requirements.

## Where to go from here?

[Final project](https://github.com/VirgilSecurity/chat-back4app-android/). If you missed pieces from the puzzle, open the E2EE project branch. You can insert your application credentials in this code (as you did during the article) and build the project.

You can find more information about what you can build with Virgil Security [here](https://virgilsecurity.com/?utm_source=back4app\&utm_medium=blog\&utm_campaign=e2eechat).


[title] Cloud Code Unit Tests
[path] Cloud Code Functions/

# How to Create a Unit Test through their Cloud Code Functions

## Introduction

This section will allow you to check the operation and testing of your functions locally using the library <a href="https://github.com/AmpMe/parse-server-test-runner" target="_blank">parse-server-test-runner</a>.

## Prerequisites

:::hint{type="info"}
- To complete this tutorial, you will need:

- A local environment with Node.js installed to apply unit tests. You can follow the <a href="https://nodejs.org/en/download/package-manager/" target="_blank">Official NodeJS tutorial</a> to successfully install Node.js at your terminal.
- An app created at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Back4App Command Line Configured with the project.
- Follow the <a href="https://www.back4app.com/docs/local-development/parse-cli" target="_blank">Setting up Cloud Code tutorial</a> to learn how to set up cloud code for a project.
:::

## First off, we need to talk about Unit Test!

When developers start writing a function with different intentions in mind, a major point evident in the software community is the application of imaginary scenarios for the created code to be tested. It is necessary to perform the Unit Tests procedure as it allows you to test the code in parts, which ensures that your main code remains intact and uncompromised.

### **And now, how about a simple practical example?**

Let’s assume that you need to write a function to show in a complete sentence, the name from the worker, the position and company. We’ll need to write the function to get the following input items:

- Company
- Position
- Worker name

## Steps to complete the simple test

In your terminal, initially, we’ll create a directory and configure your test App (package.json) first, using the following command:

> $ mkdir unit-test-sample && cd unit-test-sample
> $ npm init

:::hint{type="warning"}
Hint:
Using the npm init command, you’ll be able to create the package.json file. It only covers the most common items and tries to guess sensible defaults. Because of this, we’ll make available the dependencies necessary for the code to work.
:::

The result from “package.json” will be something like the example below:

```json
{
  "name": "yourfoldername",
  "version": "1.0.0",
  "description": "Just a unit test with using a simple function.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "author": "Your Name",
  "license": "ISC",
  "bugs": {
    "url": "{url}/issues"
  },
  "homepage": ""
}
```

Now, let’s create the file (index.js) using the command below:

> \~/unit-test-sample$ touch index.js

We will now insert the code below into the previously created file, and the function will insert an example that can demonstrate this test with the code:

```javascript
1   // index.js
2     module.exports = function(name, position, company) {
3     let sentence = "Hi, " + name + "! You are " + position + " in " + company + " company.";
4       return sentence;
5     };
```

:::hint{type="info"}
**In NodeJS, the module encapsulates the related code into a single unit of code, and when you’re using *****module.exports*****, it increases encapsulated code that can be utilized in other files.**
:::

### **Let’s test 0/**

Finally, you can work with your terminal:

> \~/unit-test-sample$ node
> \> var moduleBack4App = require('./index.js');
> undefined
> \> moduleBack4App("Jonathan", "Developer", "Back4App")
> Hi, Jonathan! You are Developer in Back4App company.

:::hint{type="warning"}
Hint:
&#x20;Using the require() function, you’re able to import and export modules, and in the case above, we are using this function to require a file inside an application.
:::

# And using Parse, can I test my function?

Of course, as a test parameter, we will create a backend to control the information of a company’s employees.

# Let’s structure the classes as:

### Parse.User (Reference for employees)

- username, email, password (required)

We recommend you to have a detailed look at the <a href="http://docs.parseplatform.org/js/guide/#users" target="_blank">Parse Server</a> guide in order to get some more information about User properties.

### infoEmployee

- Position
- Department
- WorkShift
- userId (Pointer)

## 1 - Understand our final structure

Let’s get started! We will use the <a href="https://www.back4app.com/docs/cloud-code-functions/docs.parseplatform.org/js/guide/" target="_blank">Parse Server Javascript Guide</a> as a parameter for the development of our functions. Firstly, after completing the setup using the Command Line Interface [(see prereqs](https://www.back4app.com/docs/cloud-code-functions/unit-tests#content-prerequisites)), we’ll understand how it will work with the final structure from the files:

> ├──Back4AppProject
> │  ├── cloud
> │  │   ├── functions.js
> │  │   ├── main.js
> │  ├── public
> │  ├── package.json
> │  ├── index.js
> │  ├── node_modules
> │  ├── src
> │  │   ├── jasmine.js

:::hint{type="info"}
**Notice: **When you upload the files to your Cloud Code, the Command Line Interface (See <a href="https://www.back4app.com/docs/cloud-code-functions/unit-tests#content-prerequisites" target="_blank">prereqs</a>) will ignore the other files and upload only the ones that are in the public and cloud folder.
:::

## 2 - Writing our function

After configuring the environment for the Command Line Interface, we’ll write the function to build the process to register the Employee and save the additional information. By refactoring the code, at the main.js file, we’ll import these functions into main, like:

```javascript
1   //In 1cloud/main.js
2
3   var cloudFunctions = require("./functions");
4
5   /* It's necessary to insert the Parse Instance in our code,
6   because at local context not is referenced.*/
7
8   Parse.Cloud.define("registerUser",  cloudFunctions.registerUser(Parse));
9
10  Parse.Cloud.beforeSave("infoEmployee", infoEmployee.infoEmployee(Parse));
```

The idea is to decouple the functions from the cloud interface so we may test them without sending HTTP requests inefficiently. This will make a lot of sense as we create the test suite.

:::CodeblockTabs
Parse Server 3.X

```javascript
1   //In cloud/functions.js
2
3   module.exports.registerUser = function(Parse){
4     return async(request) =>{
5       let params = request.params; //Parameters received  
6       let infoEmployee = Parse.Object.extend("infoEmployee"); //Store Information      
7
8       let userCreated = new Parse.User({
9         "email" : params.email,
10        "username": params.username,
11        "password" : params.password
12      })
13
14      //Save relation
15      try {
16        let result = await userCreated.save();
17
18        let information = new infoEmployee({
19          "position"   : params.position,
20          "department" : params.department,
21          "workShift"  : params.shift,
22          "user" : result
23        });
24
25        return information.save();
26      } catch (e) {
27          return e.message;
28      }
29    }
30  }
31
32  module.exports.infoEmployee = function(Parse){
33    return async (request) =>{
34      let req = request.object;
35
36      if (!req.get("position") || !req.get("department") || !req.get("workShift")) {
37        throw new Error("Missing params! The required parameters are: position, department. workShift");
38      } else {
39        return;
40      }
41    }
42  }
```

Parse Server 2.X

```javascript
1   //In cloud/functions.js
2
3   module.exports.registerUser = function(Parse){
4     return function (request, response){
5       var params = request.params; //Parameters received
6
7       var infoEmployee = Parse.Object.extend("infoEmployee"); //Store Information      
8
9       var userCreated = new Parse.User({
10        "email" : params.email,
11        "username": params.username,
12        "password" : params.password
13      })
14
15      //Save relation
16      userCreated.save().then((updatedUser) => {
17        var information = new infoEmployee({
18          "position"   : params.position,
19          "department" : params.department,
20          "workShift"  : params.shift,
21          "user" : updatedUser
22        });
23        return information.save();
24      }).then((info) => response.success(info))
25        .catch((e) => {
26          response.error(e.message);
27        })
28    }
29  }
30
31  module.exports.infoEmployee = function(Parse){
32    return function (request, response){
33      var req = request.object;
34
35      if (!req.get("position") || !req.get("department") || !req.get("workShift")) {
36        response.error("Missing params! The required parameters are: position, department. workShift");
37      } else {
38        response.success();
39      }
40    }
41  }
```
:::

## &#x20;3 - Setting up the environment to test the above code!

For our test suite, we will be using <a href="https://jasmine.github.io/" target="_blank">Jasmine</a>, a highly popular JavaScript testing framework. However, our code so far is completely agnostic of our tests, so you may use whatever framework or platform you prefer.

### **Install the development dependencies**

Let’s install Jasmine globally and use, with the commands below:

> \~/Back4AppProject$ sudo npm install -g jasmine
> \~/Back4AppProject$ jasmine
> Randomized with seed 48094
> Started
>
>
> No specs found
> Finished in 0.002 seconds
> Incomplete: No specs found

## 4 - Configuring Parse Server Test Runner in the folder

With our methods implemented in the project Cloud folder in Back4App, we will create new files on our project on Node.js that will configure this interaction.

> \~/Back4AppProjectsrc$ touch index.js
> \~/Back4AppProject$ mkdir src && cd src
> \~/Back4AppProject/src$ touch jasmine.js

Now, we will configure the files created above with the codes shown below:

:::CodeblockTabs
index.js

```javascript
1   const Promise = require('bluebird');
2     const express = require('express');
3     const http = require('http');
4     const {MongoClient} = require('mongodb');
5     const {ParseServer} = require('parse-server');
6
7     const mongoDBRunnerStart = require('mongodb-runner/mocha/before').bind({
8      timeout() {
9       },
10      slow() {
11      },
12    });
13    const mongoDBRunnerStop = require('mongodb-runner/mocha/after');
14
15    const startDB = () => (
16      new Promise((done, reject) => {
17        done.fail = reject;
18        mongoDBRunnerStart(done);
19      })
20    );
21
22    const stopDB = () => (
23      new Promise((done, reject) => {
24        done.fail = reject;
25        mongoDBRunnerStop(done);
26      })
27    );
28
29    const connectDB = (databaseURI) => new Promise((resolve, reject) => {
30      MongoClient.connect(databaseURI, (err, db) => {
31        if (err) {
32          reject(err);
33        } else {
34          resolve(db);
35        }
36      });
37    });
38
39    let parseServerState = {};
40
41    const dropDB = () => {
42      const {mongoConnection} = parseServerState;
43      return mongoConnection.dropDatabaseAsync();
44    };
45
46    /**
47      * Starts the ParseServer idropDatabaseAsyncnstance
48      * @param {Object} parseServerOptions Used for creating the `ParseServer`
49      * @return {Promise} Runner state
50     */
51    function startParseServer(parseServerOptions = {}) {
52      const mongodbPort = process.env.MONGODB_PORT || 27017;
53      const {
54        databaseName = 'parse-test',
55        databaseURI = 'mongodb://localhost:${mongodbPort}/${databaseName}',
56        masterKey = 'test',
57        javascriptKey = 'test',
58        appId = 'test',
59
60        port = 30001,
61        mountPath = '/1',
62        serverURL = 'http://localhost:${port}${mountPath}',
63      } = parseServerOptions;
64
65      return startDB()
66        .then(() => connectDB(databaseURI))
67        .then((mongoConnection) => {
68         parseServerOptions = Object.assign({
69            masterKey, javascriptKey, appId,
70            serverURL,
71            databaseURI,
72            silent: process.env.VERBOSE !== '1',
73          }, parseServerOptions);
74          const app = express();
75          const parseServer = new ParseServer(parseServerOptions);
76
77          app.use(mountPath, parseServer);
78
79          const httpServer = http.createServer(app);
80
81          Promise.promisifyAll(httpServer);
82          Promise.promisifyAll(mongoConnection);
83
84          return httpServer.listenAsync(port)
85            .then(() => Object.assign(parseServerState, {
86              parseServer,
87              httpServer,
88              mongoConnection,
89              expressApp: app,
90              parseServerOptions,
91            }));
92        });
93    }
94
95  /**
96   * Stops the ParseServer instance
97   * @return {Promise}
98   */
99    function stopParseServer() {
100     const {httpServer} = parseServerState;
101     return httpServer.closeAsync()
102       .then(stopDB)
103       .then(() => parseServerState = {});
104   }
105 
106   module.exports = {
107     dropDB,
108     startParseServer,
109     stopParseServer,
110     parseServerState,
111   };
```

jasmine.js

```javascript
1   const { startParseServer, stopParseServer, dropDB } = require('parse-server-test-runner');
2
3   describe('registerUser', () => {
4      beforeAll((done) => {
5       const appId = 'test';
6       const masterKey = 'test';
7       const javascriptKey = 'test';
8
9       startParseServer({ appId, masterKey, javascriptKey })
10        .then(() => {
11          Parse.initialize(appId, masterKey, javascriptKey);
12          Parse.serverURL = 'http://localhost:30001/1';
13        })
14        .then(done).catch(done.fail);
15    }, 300 * 60 * 2);
16
17    afterAll((done) => {
18      stopParseServer()
19        .then(done).catch(done.fail);
20    });
21
22    beforeEach((done) => {
23      dropDB()
24        .then(done).catch(done.fail);
25    });
26
27    it('should work', (done) => {
28      const q = new Parse.Query('_User')
29      q.limit(5)
30        .find({ useMasterKey: true })
31        .then(console.log)
32        .then(done).catch(done.fail);
33    });
34  });
```
:::

The last step is to configure the package.json, using the command: $ npm init in the root directory (the file below is just an example with the modules required):

```json
1   {
2     "name": "back4approject",
3     "version": "1.0.0",
4     "description": "Back4App guide using for reference the Parse Server Test Runner",
5     "main": "index.js",
6     "engines": {
7       "node": ">=6"
8     },
9     "repository": {
10      "type": "",
11      "url": ""
12    },
13    "keywords": [
14      "parse",
15      "parse-server",
16      "testing",
17      "tests"
18    ],
19    "author": "",
20    "license": "ISC",
21    "dependencies": {
22      "bluebird": "^3.5.0",
23      "express": "latest",
24      "mongodb": "^2.2.30",
25      "mongodb-runner": "^3.5.0",
26      "parse": "^1.10.0",
27      "parse-server": "^2.5.3",
28      "parse-server-test-runner": "^1.0.0"
29    }
30  }
```

And now, you’ll check that we approach the structure described in [these previous](https://www.back4app.com/docs/cloud-code-functions/unit-tests#content-structure) steps :)

## 5 - Returning to the terminal

We’ll start to configure the local testing, for this we’ll follow the command below to the code for set up programmatically for testing purposes.

> \~/Back4AppProject$ # in the same directory from package.json
> \~/Back4AppProject$ npm install

After the successful installation, you’re able to check your unit test locally with the command described and receive the result, such as:

> \~/Back4AppProject$ jasmine src/jasmine.js
> Randomized with seed 79055
> Started
> ✔  Downloaded MongoDB 3.6.5
> \[]
> .
>
>
> 1 spec, 0 failures
> Finished in 19.983 seconds
> Randomized with seed 79055 (jasmine --random=true --seed=79055)




## Wonderful, it’s ready!

With the guide described above, you’re able to work with the Parse Server Test Runner and your functions developed for the Cloud Code to Back4App.

[title] Web Application hosting
[path] Advanced Guides/

# Creating and hosting a full-stack web application

## Introduction

Launching a full-stack application can be daunting. You have to worry about hosting your front end, configuring/provisioning a server, and tying everything together. You may not have known it, but Back4App provides an optimal infrastructure for all of the above.

You can easily serve your frontend HTML (including frontend frameworks like React and Vue) with Back4App’s Web Hosting. Cloud Code makes an excellent backend that launches quickly. In this guide, we’ll build a complete, albeit rudimentary, web application on Back4App.

This is a guest tutorial written by [John Considine](https://github.com/considine), lead developer at [K-Optional](https://koptional.com/).

## Goals

Launch a full-stack web application on Back4App

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you need:



- To be familiar with the command-line
- <a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git" target="_blank">Git</a> and <a href="https://docs.npmjs.com/getting-started" target="_blank">NPM</a> should be installed
- Have a Back4App Account, with the CLI tool installed and configured, see <a href="https://blog.back4app.com/cli-parse-server/" target="_blank">here for help</a>
- Create a brand new project on your Back4App dashboard- see <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">here for help</a>.
- This tutorial must be set to the Parse Server Version 3.1.x. See directly below for more details
:::

:::hint{type="danger"}
This project will use the newly released version 3.1 Parse Server. This means you need to make sure your Back4App project is set to this release- it won’t work otherwise. On your project dashboard, go to Server Settings » Manage Parse Server(settings) and select 3.1.1 (it may be in Beta).
For more information on migrating to Parse Server 3.1.x, see <a href="https://www.back4app.com/docs/advanced-guides/parse-server-3" target="_blank">this guide</a>.
See this guide if you do not understand the syntax of the cloud code for this project.
:::

## Project Background

We will launch a rudimentary ticket exchange application. It allows users to sign up, login, and post tickets they are selling to different events- which admins can create using the Dashboard. Other users can contact them by their email or phone number, depending on what the poster chooses to display.

I have launched the app [here](http://ticketlister.koptional.com/), using the same code we explore in this guide. You are free to create an account, post tickets, and see what the app looks like.

The point of this tutorial is to demonstrate how to efficiently launch an app. Thus rather than dwelling on each line of code, we will start with a mostly finished codebase and focus on the ease of deployment. There is only one place where you’ll need to edit code:

- In step 1 you’ll need to add your project settings (Application ID, JavaScript Key, and server URL).

However, you’re welcome to edit and extend this application in any way you like.

## Project Structure

Before you start preparing code, it’s important to understand the file structure of this project. I’ll use this as a reference throughout this guide. Here’s what your directory will look like when everything is finished:

> 1   project
> 2   │   .parse.local
> 3   │   .parse.project
> 4   │   .gitignore
> 5   │   README.md
> 6   └───public
> 7   │   │   index.html
> 8   │   │   login.html
> 9   │   │   signup.html
> 10  │   └───css
> 11  │       │   signin.css
> 12  │       │   bootstrap.min.css
> 13  │   └───js
> 14  │       │   main.js
> 15  │       │   parse.js
> 16  │       │   signin.js
> 17  │       │   signup.js
> 18  │
> 19  └───cloud
> 20      │   main.js

The main takeaways from this setup are:

1. The frontend code lives in the **public** directory. A frontend is simply the part of an application that your end user will interact with
2. The backend code lives in the **cloud** directory. The backend does the behind-the-scenes work in an application. This includes saving things to the database and sending data.

*The frontend tells the backend what to do by sending HTTP requests. In our case, this means running cloud functions*

:::hint{type="info"}
**Please also note the simplicity of this setup. Three HTML files represent the three pages in this application. Our whole backend is a single file!**
:::

In Step 2 we will take a brief look at the frontend code- that is, the public directory. In Step 3 we move to the backend.&#x20;

## 1 - Preparing the files

**As mentioned in the **[**prerequisites**](https://www.back4app.com/docs/advanced-guides/web-application-hosting#prerequisites)**, you should have a fresh project on Back4App created AND **[**your CLI tool configured**](https://blog.back4app.com/2017/01/20/cli-parse-server/)

Before visiting any of the codes, you’ll have to have it downloaded and ready. In this step, we do just that. Please note that you will run several commands on your command line. I will give you each of them to copy and run. If you feel confused during this step, don’t worry; this is just the process necessary to connect a Back4App app to a project I have on Git. It is not important to know what’s going on.

In this step we:

1. Initialize a local directory with your Back4App project using the CLI
2. Pull the example project files into this directory using .git

**Initialization with Back4App**

On your command line run

> 1 b4a new

You should be prompted:

> 1   Would you like to create a new app, or add Cloud Code to an existing app?
> 2   Type "(n)ew" or "(e)xisting":

Go with “e” for existing. Then select the application that you created from the list.

Next, you’ll be asked to name the directory where the code will be installed. You can just hit ‘enter’ if you don’t have a preference. For the sake of this project, I will assume the directory is called “ticketlister”. Finally, when asked:

> 1   Directory Name:
> 2   You can either set up a blank project or download the current deployed Cloud Code
> 3   Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER

Just hit enter (do NOT hit blank). When this command returns, you can cd into the new directory. You should see two directories, one called “cloud” the other called public”.

**Your entire output should look something like this:**

> 1   $ b4a new
> 2   Would you like to create a new app, or add Cloud Code to an existing app?
> 3   Type "(n)ew" or "(e)xisting": e
> 4   1: ticketlister
> 5   Select an App to add to config: 11
> 6   Please enter the name of the folder where we can download the latest deployed
> 7   Cloud Code for your app "ticketlister"
> 8
> 9   Directory Name: 
> 10  You can either set up a blank project or download the current deployed Cloud Code
> 11  Please type "(b)lank" if you wish to setup a blank project, otherwise press ENTER: 
> 12  Successfully configured email for current project to: "JACKCONSIDINE3@GMAIL.COM"
> 13  Your Cloud Code has been created at /tmp/ticketlister.
> 14
> 15  This includes a "Hello world" cloud function, so once you deploy,
> 16  you can test that it works, with the printed curl command.
> 17
> 18  Next, you might want to deploy this code with:
> 19
> 20	  cd /tmp/ticketlister
> 21	  b4a deploy
> 22
> 23  Once deployed you can test that it works by running:
> 24  curl -X POST \
> 25   -H "X-Parse-Application-Id: Ivy4QAJQuAjDhpqQ2D3MCR4jlrCvDcVVH6yom1kk" \
> 26   -H "X-Parse-REST-API-Key: yLgPHNEnt0jnzWy9BYt6ZCWHqmsWRyvCfsmqrvuS" \
> 27   -H "Content-Type: application/json" \
> 28   -d "{}" \
> 29   https://parseapi.back4app.com/functions/hello
> 30
> 31  $ cd ticketlister 
> 32  $ ls
> 33  cloud  public

**Syncing the app with the project files**

In addition to the cloud and public folders, there will be two files in your directory:

- .parse.local
- .parse.project

These hold data pertaining to the Back4App project. Everything else should be overwritten with the existing project files from [the repo](https://github.com/back4app/back4app-ticketlister). The following is the easiest way to do this:

> 1   cd ticketlister
> 2   git init
> 3   git remote add origin https://github.com/back4app/back4app-ticketlister
> 4   git fetch origin
> 5   git checkout --force -b  master --track origin/master

If everything has worked, you should now be set with the following files:

> $ ls -R
> README.md         cloud             index.js          package-lock.json package.json      public
>
> ./cloud:
> main.js
>
> ./public:
> css         index.html  js          login.html  signup.html
>
> ./public/css:
> bootstrap.min.css signin.css
>
> ./public/js:
> main.js   parse.js  signin.js signup.js

Don’t worry- that was the hard part! Now we can focus on the project.

## 2 - The Frontend

As a reminder, the frontend code for this app lives in the **public** directory. To keep things relatively simple, I opted not to use a front-end framework like React, Angular, or Vue. This way, there are no external dependencies or builds.

:::hint{type="info"}
The project does use HTML5 Web Components. These are supported natively in the browser. They help encapsulate the functionality of different parts of the user interface. They allow the developer to declare new HTML elements (think ‘\<p>’). Otherwise, they just use plain-old JavaScript.
:::

In the** public/js** directory, there are 4 JavaScript files:

> $ ls public/js
> \# main.js   parse.js  signin.js signup.js

1. **main.js** is the code loaded by the main page, **index.html**. This page is where users list tickets etc.
2. **signup.js** is the code loaded by the signup page, **signup.html**
3. **signin.js** is the code loaded by the sign in page, **login.html**
4. **parse.js** is a simple file that all the pages use. It creates a connection to the backend. This is the only file you will need to edit and the project will not work unless you do!

**Adding your Back4App credentials**

First, you’ll need to grab your **Application ID** and your **JavaScript Key** from your Back4App project. After logging in to Back4App, select your project. Then click **App Settings** on the left-hand side, and select **Security & Keys**.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/wm1jDb3MY4-REOljU0KMQ_image.png" signedSrc size="70" width="602" height="852" position="flex-start" caption}

You should see several keys displayed. Grab the **Application ID** and **JavaScript Key** and keep them handy.

Finally, open up **public/js/parse.js** and place each of the strings in the proper place. Remember to make sure the serverURL is **https\://parseapi.back4app.com**.

```javascript
1   // PART 1: Put your APP ID, JS Key, and SERVER URL
2   Parse.initialize(
3     '', // YOUR APP ID
4     '' // YOUR Javascript  KEY
5   );
6   // YOUR SERVER URL
7   Parse.serverURL = 'https://parseapi.back4app.com';
```

The application now can communicate with the server!

&#x20;**A shallow dive into the code.**

Though all of the code in this project is outside the scope of this guide, I encourage you to browse each of the files. Nothing is to complex, and I’d like to take a quick minute to give a 1,000-foot view.

- This project uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" target="_blank">HTML5 Web Components</a> to encapsulate each logical section of the interface.
- The important markup in the HTML files resides within the HTML \<template> tags. This is how we describe the layout
- The “functionality” of the application occurs in the JavaScript files. This is where the app describes what to do when a form is submitted, or a button is clicked etc.

For example, take the login component. The markup (**public/login.html**) looks like this:

```html
1   <template id="signin-template">
2     <style>
3       @import 'css/signin.css';
4       @import 'css/bootstrap.min.css';
5     </style>
6     <div class="signin-wrapper text-center">
7       <form class="auth-form">
8         <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
9         <label for="inputEmail" class="sr-only">Email address</label>
10        <input
11          type="email"
12          id="inputEmail"
13          class="form-control"
14          placeholder="Email address"
15          required=""
16          autofocus=""
17        />
18        <label for="inputPassword" class="sr-only">Password</label>
19        <input
20          type="password"
21          id="inputPassword"
22          class="form-control"
23          placeholder="Password"
24          required=""
25        />
26        <button class="btn btn-lg btn-primary btn-block" type="submit">
27          Sign in
28        </button>
29        <div class="text-center"><a href="/signup.html"> Sign Up</a></div>
30      </form>
31    </div>
32  </template>
33  <!-- This tag below simply tells the browser to render the component we declared above -->
34  <login-form></login-form>
```

And the functionality appears in the JavaScript file (**public/signin.js**)

```javascript
1   // Code above ^
2       // When the code is ready, listen for the form to be submitted. When it is,
3       //  call the 'onSubmit' method
4       connectedCallback() {
5         const form = this.shadowRoot.querySelector('form');
6         form.addEventListener('submit', this.onSubmit.bind(this), false);
7       }
8     // Obtain the email and password from the markup inputs
9       onSubmit(e) {
10        e.preventDefault();
11        //   Get inputs
12        const email = this.shadowRoot.querySelector('#inputEmail').value;
13        const password = this.shadowRoot.querySelector('#inputPassword').value;
14        this.login(email, password);
15      }
16    // Send a request to the backend, attempting to login. If the login
17    //  is successful, go to the index.html page. Otherwise, give the user
18    // an alert explaining what went wrong
19      login(email, password) {
20        // Add login method here
21        Parse.User.logIn(email, password)
22          .then(user => {
23            window.location.href = 'index.html';
24          })
25          .catch(e => {
26            alert(e.message);
27          });
28      }
29      // More code below
30
```

The whole application takes this general structure. Keep an eye out for the times the front end talks to the backend like this: (**public/js/main.js**).

In the next step, we’ll look into how these functions are declared.

## 3 - The Backend

The entire backend will live in **cloud/main.js**, the Cloud Code functions file. It consists of a very modest amount of code, attesting to how much we can accomplish for so little with Back4App.

Part of the app (creating events that tickets can list under) will simply use the Back4App dashboard. This awesome functionality comes with our project, so no need to reinvent the wheel!

Again, examining each line of code is outside our scope. We will, however, take another broad view of how the code works.



- You declare **Cloud Functions** in the **cloud/main.js** file. These functions can be invoked from the front end (see **Step 2**). For more information on Cloud Functions, see <a href="https://docs.parseplatform.org/cloudcode/guide/#cloud-functions" target="_blank">the documentation</a>.
- Furthermore, these Cloud Functions are run on a Parse Server. This <a href="https://www.back4app.com/docs/advanced-guides/parse-server-3" target="_blank">guide</a> discusses some of the syntax that’s used, so it may be helpful to have a look.

More specifically, the functions we define our:

1. ‘**user\:signup**’ - Code for handling user signup flow
2. ‘**tickets\:list**’ - Code for retrieving all listed tickets
3. ‘**tickets\:create**’ - Code for creating a new ticket
4. ‘**events\:list**’ - Code for listing all events

And one last code note: I added a simple method towards the top of the file:

```javascript
1   const requireAuth = user => {
2     if (!user) throw new Error('User must be authenticated!');
3   };
```

Certain Cloud Functions require a user to be logged in. By calling this function with the user property of the request, we ensure that no one can make unauthorized requests.

I highly encourage you to skim the rest of the functions to see how they work. Now that you know what they do, we can deploy!

## 4 - Deploying

We’ve buttoned up all the code, and now the app can be deployed to Back4App. The following command will upload all public and cloud files:

> b4a deploy

**Local website hosting**

To obtain a public domain to view your uploaded web app, you will need to switch on Web Hosting from your Back4App dashboard.

First, open “Server Settings” on the left side of the dashboard:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/V5-yoz42if7bmLRlv-pU3_image.png" signedSrc size="70" width="500" height="715" position="flex-start" caption}

Next, click the “Settings” link under “Web Hosting and Live Query”

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/VOlHTaDxFfwg6chUQZRnT_image.png" signedSrc size="70" width="500" height="559" position="flex-start" caption}

And finally, check “Activate Back4App Hosting”. You’ll need to pick a unique subdomain; I already claimed ticketlister for this project so pick something different. Optionally, you can configure a domain you own to “point” to this back4app domain. I did this for [http://ticketlister.koptional.com](https://www.back4app.com/docs/advanced-guides/my%20website) and my settings look like this:&#x20;

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/YlLshpzkdaVyh9C-gVoQA_image.png" signedSrc size="70" width="500" height="484" position="flex-start" caption}

Please note the text below “Custom Domain”, if you plan to launch off your website.

If you complete this step properly, you can go to your domain and use the app. If you don’t have a custom domain, just open http\://\<YOUR\_SUBDOMAIN>.back4app.io, where YOUR\_SUBDOMAIN is the name you just selected.

## 5 - Usage and the Dashboard

To start listing tickets, you’ll have to create an event from the admin dashboard on Back4App.

Go to the data browser, and create an ‘Event’ class. Add the columns ‘name’ (a string), and ‘when’ (a date). Then you can add an event directly. Remember to fill out all columns. It should look something like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/eSQqyJ80U9z16k7RVRxGq_image.png" signedSrc size="70" width="1086" height="636" position="flex-start" caption}

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/M8tpIQJ10s21Q5x7xTWJo_image.png" signedSrc size="70" width="1082" height="636" position="flex-start" caption}

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HyMOBwx-36Yst_44UDAG6_image.png)

Now, on your web app, you can log in and list a ticket with that event.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/7Q7WtLTkpTcsr8EXY-LaS_image.png)

This admin functionality that comes with Parse / Back4App is another shortcut that decreases your workload.

## Conclusion

Creating a web application with a backend is something that often takes weeks and months. We took advantage of Back4App’s powerful infrastructure and the Parse SDK to launch one much quicker. Using this approach for any application allows you to build amazing things without wasting time.

[title] Using Geopoints
[path] JS Framework/Ionic/

# Using geo points with Ionic framework

## Introduction

Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a **ParseGeoPoint** to a **ParseUser**, you will be able to easily find out which user is closest to another, show the locations of the users of your app and also store the user’s location information, among other possibilities.

You can also associate a **ParseGeoPoint** to any **ParseObject**. For example, if your app is associated with a store with physical affiliates, you will be able to create a page to show the location of those stores or to show the user which store is closest to him. Another example of this association usage: if your app is a game in which you created ParseObjects to represent characters, adding ParseGeoPoints to these characters would allow them to be shown along the player’s path.

This tutorial explains how to use some features of ParseGeoPoint through Back4App.

After following this tutorial, you will be able to do this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/lC9NUmr8W2RwRGh9nPGSM_image.png" signedSrc size="40" width="359" height="639" position="center" caption}

## Prerequisites

## 1 - Set up Google API Key

Install <a href="https://angular-maps.com/api-docs/agm-core/" target="_blank">Angular Google Maps - AGM</a>.

> $ npm install @agm/core

Import the SDK adding the generated key to ./src/app/app.module.ts.

## 2 - Retrieve user’s location

To retrieve user’s location, we’ll need to install <a href="https://ionicframework.com/docs/native/geolocation/" target="_blank">Ionic Geolocation</a> plugin.

> $ ionic cordova plugin add cordova-plugin-geolocation --variable GEOLOCATION\_USAGE\_DESCRIPTION="To locate you"
> $ npm install --save @ionic-native/geolocation

Now that we installed and imported Geolocation to providers at ./src/app/app.module.ts, let’s implement the get location method and store the results to a class attribute.

## 3 - Create a page to display the map

Now that we have the user location, let’s create a page to display the map, using the already mentioned \[Angular Google Maps].

First, run the helper:

> $ ionic generate page Maps

Let’s now make it receive a Marker that holds the **current** position the map is displaying and an array of Markers that will be the pin points around the map.

Let’s pull up the maps.html now and make it display the data.

## 4 - Showing current user’s location in map

Now we have a page to display the Map locations.

1. Let’s head back to home.ts and implement a method to display the user’s location on the map
2. Let’s make use of Parse easiness now and look for the peers closest to the user location

You can see how simple it is. Basically all we had to do is instantiate a query object for the User collection and make it look for the closest geo location by calling near().

## 5 - Set up Back4App to associate ParseGeoPoint to ParseObjects

Suppose that the example app you are building is associated with a group of stores with physical affiliates. It would be interesting to show all the physical affiliates of this store in the map. In order to do so create a Stores class on Back4pp Dashboard, save the existing stores as ParseObjects, their locations and after that the app will be able to query the physical affiliates and display their possitions on the map. The following steps will help you with that.

Go to <a href="https://www.back4app.com/" target="_blank">Back4App website </a>login, find your app and open its Dashboard.



Go to Core > Browser > Create a class, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/T9gMl256s_tvoPbKNEYt8_image.png)

In the field What type of class do you need?, choose the Custom option

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XrAghJP5HgDEEJYbZ6HGq_image.png)

In the field What should we call it?, type “**Stores**” and then click on Create class button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/5AoEZ5Geao4gHETUuWh-N_image.png)

Then a new class called “**Stores**” will be shown and you should insert 2 columns on it: a collumn with data type String called Name, as well as another column with data type GeoPoint called Location. Your class should end up like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/Ze2MXhV5cInRg6nDKzxcp_image.png)

Now, fill this class with information. To do so, click on Add a row button in the menu on the top right or in the middle of the page, as shown in the image below.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/rF9YhyrN4W_s3DOobD5tn_image.png)

Then fill the row with the informations required: the name of the store and its latitude and longitude. After inserting some data, your Stores class should look like the image below. If you want to insert more data, you can click on the Add a row button on the top, or in the + button bellow the last row of data.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/HIHbksg6Y7-XCkykTqGoP_image.png)

Now you are ready to use the location information of the stores in your app.

## 6 - Display all the stores location on Map

Now that we have the stores location stored, let’s code a method to get the objects and display them.

Easy, right? Now, let’s get the CLOSEST one.

You can see how it’s just like getting the closest User as we have done earlier.

## 7 - Add buttons to Home Page

You probably noticed we have no way of calling those methods, let’s add the buttons to the home.ts then and make it happen!

## 8 - Test your app

1. Login at [Back4App Website](https://www.back4app.com/).
2. Find your app and click on Dashboard > Core > Browser > User and create some users with location associated to them to allow the method getClosestUser to work.
3. Run your app in a real device or in the browser to sign up and log in. Try every feature!

## It’s done!

At this stage, you can use some features of ParseGeoPoint through Back4App!

[title] Sign In with Apple
[path] iOS/Parse Swift SDK/Users/

# Sign In with Apple

## Introduction

When integrating third-party sign-in methods on an iOS app, it is mandatory to add the sign in with Apple option as an additional alternative. For this purpose, Apple introduced the **AuthenticationServices** framework. This framework allows developers to seamlessly integrate a **Sign in with Apple** button on any Xcode project.

In [this repository](https://github.com/templates-back4app/ios-sign-in-with-apple) we provide a simple Xcode template where you can test the different sign in methods we are implementing. This example was already introduced in the [log in guide](https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-log-in). You can revisit it for more details about the project.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- A recent version of Xcode.
- An Apple developer account with non-personal developer team.
- An app created at Back4App.
  - Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">New Parse App tutorial</a> to learn how to create a Parse app at **Back4App**.
  - **Note: **Follow the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk" target="_blank">Install Parse SDK (Swift) Tutorial</a> to create an Xcode Project connected to **Back4App**.
:::

## Goal

To integrate a user sign-in feature using the **AuthenticationServices** framework and **ParseSwift SDK**.

## 1 - Setting up Sign in with Apple

- Once we have the Xcode project <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/install-sdk" target="_blank">linked</a> to the **Back4App** application, we proceed to add the Sign in with Apple capability. To do this, select your project from the project navigator and go to the targets section. Select the main target and go to the **Signing & Capabilities** tab, then click on the **+ Capability** button and add the **Sign in with Apple** capability:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/-eKZlYW8EWK01XSPlf1uG_image.png)

This is the only configuration needed to start integrating a **Sign in with Apple** method on an iOS app.

## 2 - Using the AuthenticationServices framework with ParseSwift

The sign in with Apple flow can be completed in three stages. But before, let us add and set up the button to be used to present the **Sign in with Apple** flow. In the LogInControllerclass we add this button:

```swift
1   // LogInController.swift file
2   import AuthenticationServices
3   ...
4
5   class LogInController: UIViewController {
6     ...
7
8     private let signInWithAppleButton: UIButton = {
9       let button = UIButton(type: .system)
10      button.setImage(UIImage(named: "appleIcon"), for: .normal)
11      button.imageView?.contentMode = .scaleAspectFit
12      return button
13    }()
14
15    override func viewDidLoad() {
16      super.viewDidLoad()
17      // ...
18      // Layout configuration
19      // ...
20
21      signInWithAppleButton.addTarget(self, action: #selector(handleSignInWithApple), for: .touchUpInside)
22    }
23  }
24
25  // MARK: - Sign in with Apple section
26  extension LogInController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
27    @objc fileprivate func handleSignInWithApple() {
28      // TODO: Here we will implement the sign in procedure
29    }
30
31     // ASAuthorizationControllerDelegate
32    
33    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
34      // TODO: Handle the sign-in result
35    }
36    
37    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
38      showMessage(title: "Error", message: error.localizedDescription)
39    }
40    
41    // ASAuthorizationControllerPresentationContextProviding
42  
43    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
44      guard let window = view.window else { fatalError("No UIWindow found!") }
45      return window
46    }
47  }
```

Note that the LogInController conforms to two new protocols: ASAuthorizationControllerDelegate and ASAuthorizationControllerPresentationContextProviding. The first protocol allows us to delegate the sign-in result to the LogInController class. The second protocol is to determine the UIWindow where the sign-in sheet is displayed.

We now implement the flow in the handleSignInWithApple() method.

- In the first stage, we prepare the request and the form. The request is constructed by the ASAuthorizationAppleIDRequest class. We get an instance of this class from the ASAuthorizationAppleIDProvider provider and the form by the ASAuthorizationController class. Once we have an instance of the request, we have to provide the scopes we are interested in. So far Apple only gives access to the user’s full name and email. Thus, a standard way to create a request is:

```swift
1   @objc fileprivate func handleSignInWithApple() {
2     let provider = ASAuthorizationAppleIDProvider()
3     let request: ASAuthorizationAppleIDRequest = provider.createRequest()
4     request.requestedScopes = [.fullName, .email]
5     ...
6   }
```

- With this request, we construct an ASAuthorizationController controller. This controller is in charge of displaying a sheet where the user authenticates and gives the corresponding permissions to complete the sign-in process:

```swift
1   @objc fileprivate func handleSignInWithApple() {
2     // As requested by apple, we set up the necessary objects to implement the sign in with Apple flow.
3     // See https://help.apple.com/developer-account/#/devde676e696 for more details
4     let provider = ASAuthorizationAppleIDProvider()
5     let request: ASAuthorizationAppleIDRequest = provider.createRequest()
6     request.requestedScopes = [.fullName, .email]
7 
8     let authorizationController = ASAuthorizationController(authorizationRequests: [request])
9     authorizationController.delegate = self // Here self is a reference to LoginController
10    authorizationController.presentationContextProvider = self // Here self is a reference to LoginController
11
12    // Presents the sign in with Apple sheet and the result will be handled by the ASAuthorizationControllerDelegate delegate
13    authorizationController.performRequests()
14  }
```

- In the last stage we handle the sign-in result. This is returned via the delegate method authorizationController(controller\:didCompleteWithAuthorization:). The last argument in this method is an ASAuthorization class containing all the necessary information about the Apple ID and credentials. This stage is where we associate a User object and perform the log in to the Back4App application. This User object has the following structure (see the <a href="https://www.back4app.com/docs/ios/parse-swift-sdk/users/user-log-in" target="_blank">Login guide</a> for more details):

```swift
1   import ParseSwift
2
3   struct User: ParseUser {
4     ...
5  
6     var username: String?
7     var email: String?
8     var emailVerified: Bool?
9     var password: String?
10  
11    var age: Int?
12  }
```

Now, we create a User object from the data contained in the ASAuthorization result. We accomplish this by instantiating a ParseApple object (from User.apple) and call the login(user\:identityToken:) method:

```swift
1   // MARK: - Sign in with Apple section
2   extension LogInController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
3     ...
4     // ASAuthorizationControllerDelegate
5  
6     func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
7       // We cast the (ASAuthorization) authorization object to an ASAuthorizationAppleIDCredential object
8       guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else {
9         return showMessage(title: "Sign in with Apple", message: "Invalid credential")
10    }
11    
12      guard let identityToken = credential.identityToken else {
13        return showMessage(title: "Sign in with Apple", message: "Token not found")
14      }
15        
16      // We log in the user with the token generated by Apple
17      User.apple.login(user: credential.user, identityToken: identityToken) { [weak self] result in
18        switch result {
19        case .success(let user):
20          // After the login succeeded, we send the user to the home screen
21          // Additionally, you can complete the user information with the data provided by Apple
22          let homeController = HomeController()
23          homeController.user = user
24          
25          self?.navigationController?.pushViewController(homeController, animated: true)
26        case .failure(let error):
27          self?.showMessage(title: "Error", message: error.message)
28        }
29      }
30    }
31  }
```

## 3 - Verifying user sign in and session creation

To make sure that the Google sign-in worked, you can look at your **Back4App** application dashboard and see the new User containing the Facebook authData parameters.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/0YRyPhXNe7GIB2DQTTOAu_image.png)

You can also verify that a valid session was created in the dashboard, containing a pointer to the corresponding User object.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/aCG29yJZGv_WG6O4nZoit_image.png)

## 4 - Linking an existing User to an Apple ID

In case your iOS App requires relating an Apple ID to an existing user in your **Back4App** platform, the ParseApple\<User> object implements the method link(user\:identityToken\:completion:) where you pass the user value and the identityToken from a ASAuthorizationAppleIDCredential credential

```swift
1   let credential: ASAuthorizationAppleIDCredential
2
3   guard let identityToken = credentials.identityToken else {
4     return showMessage(title: "Sign in with Apple", message: "Token not found")
5   }
6
7   User.apple.link(user: credential.user, identityToken: identityToken){ result in
8     switch result {
9     case .success(let user):
10      // Linking succeeded, user object now is linked to the corresponding Apple ID
11    case .failure(let error):
12      // Linking failed, handle the error
13    }
14  }
```

## 5 - Run the app

You can go to this <a href="https://github.com/templates-back4app/ios-sign-in-with-apple" target="_blank">repository</a> and download the example project. Before running the project, make sure you set up the provisioning profiles with the ones associated with your developer account.

## Conclusion

At the end of this guide, you learned how to sign in or link existing **Back4App** users on iOS using the sign in with Apple.

[title] Sign In with Apple
[path] iOS/

# Sign In with Apple - Swift

## Introduction

In this section you learn how to get started with Sign In with Apple using our Swift template and get ready to use Back4App in a few easy steps.

## Prerequisites

:::hint{type="info"}
**To complete this quickstart, you need:**

- Xcode 11
- Set up Sign In with Apple in your Apple Developer account.
  - Follow the <a href="https://www.back4app.com/docs/platform/sign-in-with-apple" target="_blank">Sign In with Apple Tutorial</a> to learn how to set up Sign In with Apple in your Apple Developer account.
:::

## 1 - Get the template

Clone or download the template at
<a href="https://github.com/back4app/SignInWithApple" target="_blank">Back4App’s GitHub repository</a>, open the Swift foder.

## 2 - Open the project template

1. Open Xcode.
2. Click on File>Open.
3. Navigate to the project folder and double click on app.xcworkspace.
4. Wait for Xcode to open the project.

## 3 - Setup app’s credentials

Update your App Delegate’s Parse Client Configuration values to set up the app’s credentials. Parse iOS SDK uses these settings to connect to the Back4App servers.

1. Open your App Delegate file: AppDelegate.swift
2. Go to your App Dashboard at Back4App website.
3. Navigate to app’s settings: Click on Features>Core Settingsblock>Server
4. Return to your AppDelegate.swift file and paste your applicationId and clientKey.

:::hint{type="info"}
See more at our <a href="https://www.back4app.com/docs/get-started/new-parse-app#creating-new-app-find-your-appid" target="_blank">New Parse App guide</a>.
:::

## 4 - Replace the Bundle Identifier

In your target, replace the Bundle Identifier by the one you created when setting up Sign In with Apple in your Apple Developer account

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/uCKcoA_WBChh7qZpG2ShU_image.png)

Also add the Sign In with Apple in the Capabilities of your App

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/o-eW51KbTABmtE9tduXeb_image.png)

## 5 - Test your App

1. Build your app in a device or simulator (Command+R)

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/dXkrh-tGuuFhtkGlvSVvm_image.png)

&#x20;    2\. Wait until the Sign in with Apple screen appears.

&#x20;    3\. Click the button and do the login process.

&#x20;    4\. Find your app and click on Dashboard.

&#x20;    5\. Click on Core.

&#x20;    6\. Go to Browser.

If everything works properly, you should find a class named User as follows:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/QCW4CPWjJRms0Y3yHP7XH_image.png" signedSrc size="40" width="295" height="381" position="center" caption}

## Next Steps

At this point, you have learned how to get started with iOS apps.

:::hint{type="info"}
Learn more by walking around our<a href="https://www.back4app.com/docs/ios/ios-app-template" target="_blank"> iOS Tutorials</a> or check <a href="https://docs.parseplatform.org/ios/guide/" target="_blank">Parse open source documentation for iOS SDK</a>.
:::


[title] Relay Compatibility
[path] Advanced Guides/

# Relay Compatibility

## Introduction

The Parse Server GraphQL API follows latest standards currently available for highly-scalable APIs and ambitious front-end projects.

The Parse Open Source Team choose to follow the GraphQL Server [Relay](https://relay.dev/docs/en/graphql-server-specification) Specification.

Relay is a JavaScript framework for building data-driven React applications powered by GraphQL, designed from the ground up to be easy to use, extensible and, most of all, performant. Relay accomplishes this with static queries and ahead-of-time code generation.

Starting from Parse 3.10, full compatibility with [Relay](https://relay.dev/docs/en/graphql-server-specification) is implemented.
This document will walk you through those implementations

## Prerequisites

:::hint{type="info"}
**To begin with this tutorial, you will need:**

- An app created at Back4App
- See the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
:::

## 1 - Create a New Back4App App

First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check [this tutorial](https://www.back4app.com/docs/get-started/new-parse-app) to learn how to create one.

## 2 - Create a few Classes

In your newly created App, go to the Database Browser and click the Create a class button

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zAfYMf6sbeh5o9VjBeIkd_image.png" signedSrc size="40" width="448" height="579" position="center" caption}

Choose to create a Custom class and give it a name.
Following the [Relay example schema](https://relay.dev/docs/en/graphql-server-specification#schema), I created the classes Faction, Ship, and others as described with matching properties, but you can create your classes to follow this documentation. Just change your queries and mutations accordingly.
Remember that by convention classes start with an Uppercase letter, are CamelCase, and do not contain special characters such as spaces and symbols.
Click Create class when you’re done.

## 3 - GraphQL Console

With your Classes and Properties created, you can go to the API Console and then GraphQL Console to execute your queries and mutations.

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/ozv6WReeEgNChHI3CjB3h_image.png" signedSrc size="70" width="1606" height="922" position="center" caption}

## 4 - Queries

Our very first query will retrieve an object based on its objectId (not to confuse with id).
Parse has evolved and now queries support both ObjectId, formerly known as id in previous versions, but now also supports Global Id, known as id, which refers to Relay’s global ID and has a longer format as it contains the classname encrypted into it.

Example of ObjectId: EaT0dDk09v
Example of id (a.k.a. Global id): RmFjdGlvbjpFYVQwZERrMDl2

Let’s make our very first query retrieving an object by its ObjectId:

```graphql
1   query RebelsQuery {
2     faction(id:"EaT0dDk09v") {
3       objectId #ObjectId for Parse
4       id #GlobalID for Relay
5       name #n
6     }
7   }
```

That will output

```graphql
1   {
2     "data": {
3       "faction": {
4         "objectId": "EaT0dDk09v",
5         "id": "RmFjdGlvbjpFYVQwZERrMDl2",
6         "name": "Galactic Empire"
7       }
8     }
9   }
```

Now, let’s change that for the GlobalId for Relay:

```graphql
1   query RebelsQuery {
2     faction(id:"RmFjdGlvbjpFYVQwZERrMDl2") {
3       objectId #ObjectId for Parse
4       id #GlobalID for Relay
5       name #n
6     }
7   }
```

And notice that the result will be the same:

```graphql
1   {
2     "data": {
3       "faction": {
4         "objectId": "EaT0dDk09v",
5         "id": "RmFjdGlvbjpFYVQwZERrMDl2",
6         "name": "Galactic Empire"
7       }
8     }
9   }
```

This happens because the Global Id works, as its name implies, globally, and has the class name encrypted into it, so Parse knows where to search for that ID.

## 5 - Refetching

Also with the Global Id you can prefetch like [Relay’s specification](https://relay.dev/docs/en/graphql-server-specification#object-identification) as follows:

```graphql
1   query RefetchQuery{
2     node(id:"RmFjdGlvbjpFYVQwZERrMDl2"){
3       id ... on Faction{
4         name
5       }
6     }
7   }
```

which will result in

```graphql
1   {
2     "data": {
3       "node": {
4         "id": "RmFjdGlvbjpFYVQwZERrMDl2",
5         "name": "Galactic Empire"
6       }
7     }
8   }
```

## 6 - Connections

[Relay’s connections](https://relay.dev/docs/en/graphql-server-specification#connections) work the same way in Parse with GraphQL, so, if you need to retrieve the Rebel’s ships:

```graphql
1   query RebelsShipsQuery {
2     faction(id: "RmFjdGlvbjphcTAzNklSZ2RQ"){
3       objectId
4       name
5       ships{
6         edges{
7           node{
8             name
9           }
10        }
11      }
12    }
13  }
```

which will result:

```graphql
1   {
2     "data": {
3       "faction": {
4         "objectId": "aq036IRgdP",
5         "name": "Alliance to Restore the Republic",
6         "ships": {
7           "edges": [
8             {
9               "node": {
10                "name": "Y-Wing"
11              }
12            },
13            {
14              "node": {
15                "name": "X-Wing"
16              }
17            }
18          ]
19        }
20      }
21    }
22  }
```

You can also retrieve the Nth ships:

```graphql
1   query RebelsShipsQuery {
2     faction(id: "RmFjdGlvbjphcTAzNklSZ2RQ"){
3       objectId
4       name
5       ships(first: 1){
6         edges{
7           node{
8             name
9           }
10        }
11      }
12    }
13  }
```

resulting in

```graphql
1   {
2     "data": {
3       "faction": {
4         "objectId": "aq036IRgdP",
5         "name": "Alliance to Restore the Republic",
6         "ships": {
7           "edges": [
8             {
9               cursor
10              "node": {
11                "name": "Y-Wing"
12              }
13            }
14          ]
15        }
16      }
17    }
18  }
```

Or retrieve the Nth ship after a specific one, using its cursor value:

```graphql
1   query RebelsShipsQuery {
2     faction(id: "RmFjdGlvbjphcTAzNklSZ2RQ"){
3       objectId
4       name
5       ships(first: 1 after: "YXJyYXljb25uZWN0aW9uOjA"){ #cursor for the Y-wing ship
6         edges{
7           cursor
8           node{
9             name
10          }
11        }
12      }
13    }
14  }
```

which will retrieve:

```graphql
1   {
2     "data": {
3       "faction": {
4         "objectId": "aq036IRgdP",
5         "name": "Alliance to Restore the Republic",
6         "ships": {
7           "edges": [
8             {
9               "cursor": "YXJyYXljb25uZWN0aW9uOjE=",
10              "node": {
11                "name": "X-Wing"
12              }
13            }
14          ]
15        }
16      }
17    }
18  }
```

## 7 - Mutations

We can also use Mutations compatible with [Relay’s mutations](https://relay.dev/docs/en/graphql-server-specification#mutations).
Let’s create a new Ship:

```graphql
1   mutation createBWing($input: CreateShipInput!){
2     createShip(input: $input){
3       ship{
4         id
5         name
6       }
7     }
8   }
```

That needs the following Query Variable:

```graphql
1   {
2     "input":{
3       "fields": {
4         "name": "B-Wing"
5       }
6     }
7   }
```

And will return the Global Id and name as specified in the mutation:

```graphql
1   {
2     "data": {
3       "createShip": {
4         "ship": {
5           "id": "U2hpcDpXb21QZnVzeXVF",
6           "name": "B-Wing"
7         }
8       }
9     }
10   }
```


[title] Complexity Report
[path] Cloud Code Functions/

# How to Create a Report showing the Complexity of your Cloud Code

## Introduction

This section will teach you to generate a Code Complexity report of your Cloud Code using <a href="https://www.npmjs.com/package/plato" target="_blank">Plato</a>.

Cloud Code must be efficient from design. As it is called many many times, a slightly worse performance can become a huge problem and affect your production environment badly.

If you take your time to design your cloud code efficiently, you will be able to serve more requests using smaller servers, which can lead to huge savings over time.
On the other hand, badly designed cloud code can only scale up in bigger, more expensive machines, which also has limitations. This situation can and probably will lead to the necessity of rewriting code and more spendings over time.

Please take your time to test, load test and constantly check reports on code complexity.

## Prerequisites

:::hint{type="info"}
- To complete this tutorial, you will need:

- A local environment with Node.js installed to apply unit tests. You can follow the <a href="https://nodejs.org/en/download/package-manager/" target="_blank">Official NodeJS tutorial</a> to successfully install Node.js at your terminal.
- An app created at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.
- Back4App Command Line Configured with the project.
- Follow the <a href="https://www.back4app.com/docs/local-development/parse-cli" target="_blank">Setting up Cloud Code tutorial</a> to learn how to set up cloud code for a project.
:::

## First off, we need to talk about Plato

We usually start developing by creating a smaller set of functions that break a big problem into smaller, easier to address ones.
This approach is usually fine and these initial smaller functions grow over time, making more complex operations and dealing with more data.
As data grows in your application, computing intensive tasks such as loops and recursive calls get called more and more, which tends to slow the application. In severe cases it might even freeze the application completely.
This is where [Plato](https://www.npmjs.com/package/plato) comes in.

[Plato](https://www.npmjs.com/package/plato) is a JavaScript source code visualization, static analysis, and complexity tool that generates reports showing how complex your application is getting and where to address fixes to potentially speed up processes.

### 1 - Installing Plato

If you have [NodeJS](https://nodejs.org/en/download/package-manager/) and [NPM](https://www.npmjs.com/) installed in your system, installing [Plato](https://www.npmjs.com/package/plato) is as easy as typing

> 1   npm install -g plato

If you don’t, please install those before proceeding.

### 2 - Running Plato

Running [Plato](https://www.npmjs.com/package/plato) after installation consists of typing the following command from the directory where your Cloud Code is:

> 1   plato -r -d MyReportFolder -t "My Report for this App" -x .json *.js

the options mean:

- -r: Recursive, meaning it will go into directories and subdirectories looking for files
- -d MyReportFolder: (output) Directory. Plato will create a directory named MyReportFolder where it will store its results
- -t “My Report for this App”: Title. Plato will name this report My Report for this App. This is useful to create multiple reports over time and keep track
- -x .json: Exclude .json files. You can tell Plato to ignore file types so it runs faster
- \*.js: Look for anything with the extension .js to be evaluated

### 3 - Getting results

In the MyReportFolder created by the command above, you will find an index.html containing the report. Open that file in a browser and you will find something like this:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/m7y26fYbFw_pxzXbDa9E-_image.png)

In my case, I only had a file named main.js, but depending on your code, you can have more files.
Scroll down to the Files section and click the file name you want to open (main.js in my case). This will open the report for that file:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/TqMI00Zb4hLBqZul0SQmX_image.png)

- Maintainability is a value between 0 and 100 that represents the relative ease of maintaining the code. A high value means better maintainability.
- Difficulty measure is related to the difficulty of the program to write or understand.
- Estimated Errors is Halstead’s delivered bugs is an estimate for the number of errors in the implementation.

The Function Weight has two metrics:

- By Complexity: This metric counts the number of distinct paths through a block of code. Lower values are better.
- By SLOC: Source Lines of Code / Logical Lines of Code

Now you can scroll down and watch the alerts and possible fixes that are suggested:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/nf44nHZSFHVQYA2bY-0DO_image.png)

In my case, it is telling that arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6'), which is not a problem.
But let’s add some very inefficient code to that function and re-evaluate:

```javascript
1function getSquareRootOf(numberOne, numberTwo, numberThree){
2	var finalResult;
3
4	var i = 0;
5	var j = 0;
6	var k = 0;
7
8	for (i = 0; i < 100; i ++){
9		for (j = 0; j < 100; i ++){
10			for (k = 0; k < 100; k++){
11				var resultOne = getSquareRootOf(numberOne);
12				var resultTwo = getSquareRootOf(numberTwo);
13				var resultThree = getSquareRootOf(numberThree);
14				finalResult = resultOne + resultTwo + resultThree;
15			}
16		}
17	}
18}
```

And evaluate the result:

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/izF-yrFGm7JcbDQ6XZV1X_image.png)

As we can see, the complexity of this function is 4, which is OK. The higher the number you get, the more complex the function is and the more you should ensure it is efficient.

[Plato](https://www.npmjs.com/package/plato) will also warn you of missing semicolons and other potential Javascript errors.

## Conclusion

Having a tool such as [Plato](https://www.npmjs.com/package/plato) checking the complexity of your code and continuously reworking cloud code to be as fast, and efficient as possible can lead to huge savings over time.
You and all developers should include this step or something similar in your development process to ensure you get the most bang for your buck serving requests.

[title] Stripe
[path] Cloud Code Functions/Integrations/

# Stripe Integration using Cloud Functions

## Introduction

In this guide, we are going to show you how to integrate a hosted backend in Back4App with Stripe API. The best architecture choice to build it on Back4App is using a very powerful feature called <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">Cloud Functions</a>. Once you finish the cloud function integration with Stripe you will be able to use this integration for all your front-end projects (Web, iOS, Android). This guide presents a complete Stripe integration using a web example.

## Prerequisites

:::hint{type="info"}
To complete this tutorial, you will need:




- An app created at Back4App.
- Follow the <a href="https://www.back4app.com/docs/get-started/new-parse-app" target="_blank">Create New App tutorial</a> to learn how to create an app at Back4App.

- Account created in <a href="https://stripe.com/br" target="_blank">Stripe</a>.
:::

## Goal

Integrate Stripe on a web project using Back4App Cloud Functions.

## What is Stripe?

<a href="https://stripe.com/" target="_blank">Stripe</a> is a tech company operating in over 25 countries, which allows both individuals and businesses to accept payments over the Internet. Stripe focuses on providing the technical, fraud prevention, and banking infrastructure required to operate online payment systems.

This tutorial will walk you through the steps of creating functions and integrating Stripe API to your Parse Server into your web app.

More specifically, in this guide, we will create an environment in which a user can log in or sign up, register credit cards, and generate example purchases with them via Stripe Payments. Also, you will be guided on how to set up the whole process as well as test if the connection to the server and the Stripe API is working properly.

## 1 - Create a Stripe Account

Go to Stripe and click on the sign up to <a href="https://dashboard.stripe.com/register" target="_blank">create an account</a>. There, you just need to provide your personal information and to which country your account belongs.

Next, verify your Stripe account (you will receive an email containing a verification link from Stripe). Click on that link, and then follow the steps to confirm your Stripe email address.

## 2 - Setting up your database classes

After configuring the Stripe environment for Step 1, go to your Back4App app dashboard so you can set up your database. This step is not obligatory since Parse will automatically create the classes while the cloud functions try to create a new object, but we will go through them to explain which fields will be created and why.

There will be two classes that will hold your app Stripe related data: PaymentMethod and Payment:

Here is how the classes are laid out:

- PaymentMethod
  - type(String): its value will always be “card”;
  - card(Object): will hold the complete Stripe data regarding the registered card;
  - stripeID(String): id referencing this PaymentMethod on the Stripe backend;
  - user(Pointer to Parse.User): direct reference to which Use this PaymentMethod belongs.
- Payment
  - data(Object): will hold the complete Stripe data regarding the payment;
  - user(Pointer to Parse.User): direct reference to which User this Payment belongs.

We will also add two new String value columns in your app default User class called setupSecret and customerId that will contain the Stripe ids that relate the User to its Stripe counterpart.

## 3 - Implementing Cloud Code

Let’s configure the [Cloud Code functions](https://docs.parseplatform.org/cloudcode/guide/#cloud-functions) in the app, installing the Stripe module and deploying the Code.

:::hint{type="info"}
**If you want to better understand the Cloud Code enviroment, check **<a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">**this guide**</a>**.**&#x20;
:::

### **3.1 - Get your Stripe Key**

Now, open your Stripe dashboard, navigate to the Developers page at the top and then select API Keys on the left menu. In that section, you will be able to see your Publishable key and your Secret key.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/XdeM4pK8gQagvIiLc0vd6_image.png)

Write down these keys as you will need them later.

### **3.2 - Cloud Code files**

On your computer, create the following files, that will be responsible to install the module and adding your Cloud Code functions to Back4App.

```json
1   {
2     "dependencies": {
3       "stripe": "*"
4     }
5   }
```

Add the code below to a new file and don’t forget to paste your Stripe secret key on the top.

:::CodeblockTabs
main.js

```javascript
1   const STRIPE_SECRET_KEY =
2     "YOUR_STRIPE_SECRET_KEY";
3   const stripe = require("stripe")(STRIPE_SECRET_KEY);
4
5   // Stripe needs an unique customer id to create payments, so after
6   // signing up, this function should be called to do this operation
7   Parse.Cloud.define("createStripeCustomer", async (request) => {
8     // Get Parse.User object
9     const userQuery = new Parse.Query(Parse.User);
10    userQuery.equalTo("objectId", request.params.userId);
11    let user = await userQuery.first();
12  
13    const customer = await stripe.customers.create({ email: user.get("email") });
14    // Creates an stripe setupIntent, that will enable the stripe lib to perform
15    // a singel operation related to payments
16    const intent = await stripe.setupIntents.create({
17      customer: customer.id,
18    });
19    // Set and save the stripe ids to the Parse.User object
20    user.set({
21      customerId: customer.id,
22      setupSecret: intent.client_secret,
23    });
24    return await user.save(null, { useMasterKey: true });
25  });
26
27  // Creates new payment method for a registered customer
28  Parse.Cloud.define("addNewPaymentMethod", async (request) => {
29    // Get Parse.User object
30    const userQuery = new Parse.Query(Parse.User);
31    userQuery.equalTo("objectId", request.params.userId);
32    let user = await userQuery.first();
33
34    // Retrieve complete stripe payment method by its id
35    const stripePaymentMethod = await stripe.paymentMethods.retrieve(
36      request.params.paymentMethodId
37    );
38
39    // Create a new SetupIntent so the customer can add a new method next time.
40      const intent = await stripe.setupIntents.create({
41      customer: `${stripePaymentMethod.customer}`,
42    });
43    user.set("setupSecret", intent.client_secret);
44    user = await user.save(null, { useMasterKey: true });
45
46    // Creates a new Parse object in the PaymentMethod class
47    let PaymentMethod = new Parse.Object("PaymentMethod");
48    PaymentMethod.set({
49      user: user,
50      type: "card",
51      stripeId: stripePaymentMethod.id,
52      card: stripePaymentMethod.card,
53    });
54    PaymentMethod.save();
55    return true;
56  });
57
58  // Creates a new payment using a valid payment method
59  Parse.Cloud.define("createNewPayment", async (request) => {
60    // Get Parse.User object
61    const userQuery = new Parse.Query(Parse.User);
62    userQuery.equalTo("objectId", request.params.userId);
63    let user = await userQuery.first();
64
65    const { amount, currency, payment_method } = request.params.paymentData;
66
67    // Look up the Stripe customer id.
68    const customer = user.get("customerId");
69
70    // Create a charge using an unique idempotency key
71    // to protect against double charges.
72    const idempotencyKey = new Date().getTime();
73    const payment = await stripe.paymentIntents.create(
74      {
75        amount,
76        currency,
77        customer,
78        payment_method,
79        off_session: false,
80        confirm: true,
81        confirmation_method: "manual",
82      },
83      { idempotencyKey }
84    );
85    // If the result is successful, write it back to the database.
86    let Payment = new Parse.Object("Payment");
87    Payment.set({
88      user: user,
89      data: payment,
90    });
91    await Payment.save();
92
93    return true;
94  });

```
:::

## 4 - Upload functions to Cloud Code

Go to the Back4App website, log in and then find your app. After that, click on Dashboard link and you will end up on the page shown below. To deploy your Cloud Code, click on the + ADD button and find the main.js and package.json files that you created in the previous step, then click on the DEPLOY button.

![](https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/6TdryEG35btnknL02_lCa_image.png)

:::hint{type="info"}
You have just configured Cloud Code functions that you can use on any platform! Check the <a href="https://www.back4app.com/docs/get-started/cloud-functions" target="_blank">Deploy & call functions</a> guide to learn how to call them. On the next step, you will work with a JavaScript project that calls them.
:::

## 5 - Integrating a JavaScript app with Cloud Code

Now you will see an example of a straightforward HTML page with JavaScript that has three main features: signing in or up a user on Parse, creating valid payment methods (credit cards), and creating new payments, charging these methods belonging to the user.

Go ahead and create a new directory in your computer and a new HTML file with the following code inside it:

:::CodeblockTabs
index.html

```html
1   <!DOCTYPE html>
2   <html lang="en">
3
4   <head>
5       <meta charset="UTF-8" />
6       <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7       <title>Back4App Stripe Payments</title>
8       <link rel="stylesheet" href="app.css">
9   </head>
10
11  <body>
12      <header class="header">
13          <img class="header_logo"
14              src="https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png" alt="">
15          <p class="header_text_bold">React on Back4App</p>
16          <p class="header_text">Back4App Stripe Payments</p>
17      </header>
18      <section id="auth">
19          <div class="container">
20              <div class="form_wrapper">
21                  <form id="auth-signin-form">
22                      <h2 class="form_heading">Sign In</h2>
23                      <input class="form_input" type="email" name="email" placeholder="Email" required />
24                      <input class="form_input" type="password" name="password" placeholder="Password" required />
25                      <button class="form_button" type="submit">
26                          Sign in
27                      </button>
28                      <a class="form_hint" id="signup-toggle" href="#">Don't have an account yet? Sign up instead.</a>
29                  </form>
30                  <form id="auth-signup-form" hidden>
31                      <h2 class="form_heading">Sign Up</h2>
32                      <input class="form_input" type="email" name="email" placeholder="Email" required />
33                      <input class="form_input" type="password" name="password" placeholder="Password" required />
34                      <button class="form_button" type="submit">
35                          Sign up
36                      </button>
37                      <a class="form_hint" id="signin-toggle" href="#">Already have an account? Sign in instead.</a>
38                  </form>
39              </div>
40          </div>
41      </section>
42      <section id="content" style="display: none;">
43          <div class="container">
44              <div>
45                  <h2 class="form_heading">Add new payment method</h2>
46                  <p>
47                      Use any of the
48                      <a href="https://stripe.com/docs/testing#international-cards" target="_blank">Stripe test cards</a>
49                      for this demo!
50                  </p>
51                  <form class="form" id="payment-method-form">
52                      <input class="form_input" type="text" name="name" placeholder="Cardholder name" required />
53                      <div class="card_element_wrapper">
54                          <div id="card-element"></div>
55                      </div>
56                      <button class="form_button">Save Card</button>
57                  </form>
58              </div>
59              <div>
60                  <h2 class="form_heading">Create new payment</h2>
61                  <form class="form" id="payment-form">
62                      <select class="form_input" name="payment-method" required>
63                          <option disabled selected>Card (payment method):</option>
64                      </select>
65                      <div>
66                          <input class="form_input" name="amount" type="number" min="1" max="99999999"
67                              placeholder="Amount" required />
68                          <select class="form_input" name="currency">
69                              <option disabled selected>Currency:</option>
70                              <option value="usd">USD</option>
71                              <option value="brl">BRL</option>
72                              <option value="eur">EUR</option>
73                          </select>
74                      </div>
75                      <button class="form_button">Charge selected card</button>
76                  </form>
77              </div>
78              <div class="form">
79                  <h2 class="form_heading">Payments</h2>
80                  <ul id="payments-list"></ul>
81              </div>
82              <button type="button" id="signout">
83                  Sign out
84              </button>
85          </div>
86      </section>
87      <script type="text/javascript" src="https://npmcdn.com/parse/dist/parse.min.js"></script>
88      <script type="text/javascript" src="https://js.stripe.com/v3/"></script>
89      <script type="text/javascript" src="index.js"></script>
90  </body>
91
92  </html>
```
:::

In this file you will find two main sections, the first one being the authentication one, which will be rendered by default if the user is not logged in yet. After logging in, the payment section will be shown, containing all the forms responsible for creating data on Stripe and also communicating with the Cloud Code functions on Back4App.

We now need to create a JavaScript function containing the code that ties it all together, called index.js:

:::CodeblockTabs
index.js

```javascript
1   // Define and initialize Parse and Stripe libs
2   const PARSE_APP_ID = "YOUR_PARSE_ID";
3   const PARSE_JS_KEY = "YOUR_PARSE_JS_KEY";
4   Parse.initialize(PARSE_APP_ID, PARSE_JS_KEY);
5   Parse.serverURL = "https://parseapi.back4app.com/";
6
7   const STRIPE_PUBLISHABLE_KEY = "YOUR_STRIPE_PUBLISHABLE_KEY";
8   const stripe = Stripe(STRIPE_PUBLISHABLE_KEY);
9
10  // Holds the currentUser complete Parse object
11  let currentUser = null;
12  const setCompleteCurrentUser = async () => {
13    // Called when user is already signed in, completing
14    // the currentUser object with the full one
15    currentUser = await new Parse.Query(Parse.User)
16      .equalTo("objectId", Parse.User.current().id)
17      .first();
18    // Retrieve and render user cards and payments
19    retrieveCurrentUserPaymentMethods();
20    retrieveCurrentUserPayments();
21  };
22
23  const retrieveCurrentUserPaymentMethods = async () => {
24    // Query and render user PaymentMethods
25    const PMQuery = new Parse.Query("PaymentMethod");
26    PMQuery.equalTo("user", Parse.User.current());
27    paymentMethods = await PMQuery.find();
28    renderPaymentMethodOptions(paymentMethods);
29  };
30
31  const retrieveCurrentUserPayments = async () => {
32    // Query and render user Payments
33    const paymentsQuery = new Parse.Query("Payment");
34    paymentsQuery.equalTo("user", Parse.User.current());
35    payments = await paymentsQuery.find();
36    renderPayments(payments);
37  };
38  
39  const renderPaymentMethodOptions = async (paymentMethods) => {
40    for (let paymentMethod of paymentMethods) {
41      const optionId = `card-${paymentMethod.get("stripeId")}`;
42      let optionElement = document.getElementById(optionId);
43  
44      // Add a new option if one doesn't exist yet.
45      if (!optionElement) {
46        optionElement = document.createElement("option");
47        optionElement.id = optionId;
48        document
49          .querySelector("select[name=payment-method]")
50          .appendChild(optionElement);
51      }
52 
53      optionElement.value = paymentMethod.get("stripeId");
54      optionElement.text = `${paymentMethod.get("card").brand} •••• ${
55        paymentMethod.get("card").last4
56      } | Expires ${paymentMethod.get("card").exp_month}/${
57        paymentMethod.get("card").exp_year
58      }`;
59    }
60  };
61
62  const renderPayments = (payments) => {
63    for (let payment of payments) {
64      let liElement = document.getElementById(`payment-${payment.id}`);
65      if (!liElement) {
66        liElement = document.createElement("li");
67        liElement.id = `payment-${payment.id}`;
68      }
69
70      const paymentData = payment.get("data");
71      let content = "";
72      if (
73        paymentData.status === "new" ||
74        paymentData.status === "requires_confirmation"
75      ) {
76        content = `Creating Payment for ${formatAmount(
77          paymentData.amount,
78          paymentData.currency
79        )}`;
80      } else if (paymentData.status === "succeeded") {
81        const card = paymentData.charges.data[0].payment_method_details.card;
82        content = `Payment for ${formatAmount(
83          paymentData.amount,
84          paymentData.currency
85        )} on ${card.brand} card •••• ${card.last4} ${paymentData.status}!`;
86      } else {
87        content = `Payment for ${formatAmount(
88          paymentData.amount,
89          paymentData.currency
90        )} ${paymentData.status}`;
91      }
92      liElement.innerText = content;
93      document.querySelector("#payments-list").appendChild(liElement);
94    }
95  };
96
97  // Checks if user is already signed in on Parse
98  if (Parse.User.current() !== null) {
99    setCompleteCurrentUser();
100   // Hide auth screen and show payment fields
101   document.getElementById("auth").style.display = "none";
102   document.getElementById("content").style.display = "block";
103 }
104
105 // Toggle signin and signup forms
106 document
107   .querySelector("#signup-toggle")
108   .addEventListener("click", async (_event) => {
109     document.getElementById("auth-signin-form").style.display = "none";
110     document.getElementById("auth-signup-form").style.display = "block";
111     clearAuthFormFields();
112   });
113 document
114   .querySelector("#signin-toggle")
115   .addEventListener("click", async (_event) => {
116     document.getElementById("auth-signup-form").style.display = "none";
117     document.getElementById("auth-signin-form").style.display = "block";
118     clearAuthFormFields();
119   });
120 
121  // Clear auth form fields
122 const clearAuthFormFields = () => {
123   document
124     .querySelector("#auth")
125     .querySelectorAll("input")
126     .forEach((input) => (input.value = ""));
127 };
128 
129 // Handle auth forms
130 document
131   .querySelector("#auth-signin-form")
132   .addEventListener("submit", async (event) => {
133     event.preventDefault();
134     toggleAllButtonsEnabled(false);
135     const form = new FormData(event.target);
136     const email = form.get("email");
137     const password = form.get("password");
138
139    // Try to signin on Parse
140     try {
141       let user = await Parse.User.logIn(email, password);
142       if (user !== null) {
143         currentUser = user;
144         // Hide auth screen and show payment fields
145         document.getElementById("auth").style.display = "none";
146         document.getElementById("content").style.display = "block";
147         clearAuthFormFields();
148       }
149     } catch (error) {
150       alert(error);
151     }
152     toggleAllButtonsEnabled(true);
153   });
154 
155 document
156   .querySelector("#auth-signup-form")
157   .addEventListener("submit", async (event) => {
158     event.preventDefault();
159     toggleAllButtonsEnabled(false);
160     const form = new FormData(event.target);
161     const email = form.get("email");
162     const password = form.get("password");
163
164     // Try to signup on Parse
165     try {
166       let user = await Parse.User.signUp(email, password, { email: email });
167       // Cloud code to create Stripe user and intent
168       user = await Parse.Cloud.run("createStripeCustomer", { userId: user.id });
169       if (user !== null) {
170         currentUser = user;
171         // Hide auth screen and show payment fields
172         document.getElementById("auth").style.display = "none";
173         document.getElementById("content").style.display = "block";
174         clearAuthFormFields();
175       }
176     } catch (error) {
177       alert(error);
178     }
179     toggleAllButtonsEnabled(true);
180   });
181 
182 // Signout from Parse
183 document.querySelector("#signout").addEventListener("click", async (_event) => {
184   await Parse.User.logOut();
185   currentUser = null;
186   // Show auth screen and hide payment fields
187   document.getElementById("auth").style.display = "block";
188   document.getElementById("content").style.display = "none";
189 });
190
191 // Creates stripe card UI element
192 const elements = stripe.elements();
193 const cardElement = elements.create("card");
194 cardElement.mount("#card-element");
195
196 // Handle add new card form
197 document
198   .querySelector("#payment-method-form")
199   .addEventListener("submit", async (event) => {
200    event.preventDefault();
201     toggleAllButtonsEnabled(false);
202     if (!event.target.reportValidity()) {
203       return;
204     }
205    const form = new FormData(event.target);
206     const cardholderName = form.get("name");
207 
208     const result = await stripe.confirmCardSetup(
209       currentUser.get("setupSecret"),
210       {
211         payment_method: {
212           card: cardElement,
213           billing_details: {
214             name: cardholderName,
215           },
216         },
217       }
218     );
219 
220     if (result.error) {
221       alert(result.error.message);
222       toggleAllButtonsEnabled(true);
223       return null;
224     }
225
226     let setupIntent = result.setupIntent;
227 
228     // Cloud code to add new payment method
229     let cloudCodeResult = await Parse.Cloud.run("addNewPaymentMethod", {
230       userId: currentUser.id,
231       paymentMethodId: setupIntent.payment_method,
232     });
233 
234     toggleAllButtonsEnabled(true);
235     alert("Success on creating a new payment method!");
236
237     // Update payment method options
238     retrieveCurrentUserPaymentMethods();
239   });
240 
241 // Handles new payment form
242 document
243   .querySelector("#payment-form")
244   .addEventListener("submit", async (event) => {
245     event.preventDefault();
246     toggleAllButtonsEnabled(false);
247     const form = new FormData(event.target);
248     const amount = Number(form.get("amount"));
249     const currency = form.get("currency");
250     // Gets selected card option id
251     const paymentMethod = form.get("payment-method");
252     const paymentData = {
253       payment_method: paymentMethod,
254       currency,
255       amount: formatAmountForStripe(amount, currency),
256       status: "new",
257     };
258     // Cloud code to create new payment
259     let cloudCodeResult = await Parse.Cloud.run("createNewPayment", {
260       userId: currentUser.id,
261       paymentData: paymentData,
262     });
263 
264     toggleAllButtonsEnabled(true);
265     alert("Success on creating a new payment!");
266 
267     retrieveCurrentUserPayments();
268   });
269  
270 // Helper functions
271 const toggleAllButtonsEnabled = (enabledValue) => {
272   document
273     .querySelectorAll("button")
274     .forEach((button) => (button.disabled = !enabledValue));
275 };
276
277 const formatAmount = (amount, currency) => {
278   amount = zeroDecimalCurrency(amount, currency)
279     ? amount
280     : (amount / 100).toFixed(2);
281   return new Intl.NumberFormat("en-US", {
282     style: "currency",
283     currency,
284   }).format(amount);
285 };
286
287 // Format amount for Stripe
288 const formatAmountForStripe = (amount, currency) => {
289   return zeroDecimalCurrency(amount, currency)
290     ? amount
291     : Math.round(amount * 100);
292 };
293
294 // Check if we have a zero decimal currency
295 // https://stripe.com/docs/currencies#zero-decimal
296 const zeroDecimalCurrency = (amount, currency) => {
297   let numberFormat = new Intl.NumberFormat(["en-US"], {
298     style: "currency",
299     currency: currency,
300     currencyDisplay: "symbol",
301   });
302   const parts = numberFormat.formatToParts(amount);
303   let zeroDecimalCurrency = true;
304   for (let part of parts) {
305     if (part.type === "decimal") {
306       zeroDecimalCurrency = false;
307     }
308   }
309   return zeroDecimalCurrency;
310 };
```
:::

Make sure to add your Stripe publishable key and also your Parse app ID and JS key to the top of the file. These are some of the key elements to check out and understand in this script:

- Check the usage of the Parse.User.current method when loading the script for the first time to render the correct part of the page;
- The form submit action listeners that will perform actions on Parse, like signing in or up and calling the Cloud Code functions to create the Stripe related objects and save on your Back4App database;
- The “retrieve” and “return” methods that make queries on Parse to retrieve the current user’s Payment and PaymentMethod objects.

Before testing the app, add the following stylesheet in a CSS file called app.css inside the same directory:

```css
1   /* Back4App Guide */
2 
3   *,
4   *:before,
5   *:after {
6     margin: 0;
7     padding: 0;
8     box-sizing: inherit;
9   }
10
11  html {
12    font-family: sans-serif;
13    box-sizing: border-box;
14    outline: none;
15    overflow: auto;
16  }
17
18  body {
19    margin: 0;
20    background-color: #fff;
21  }
22  
23  h1,
24  h2,
25  h3,
26  h4,
27  h5,
28  h6 {
29    margin: 0;
30  }
31
32  p {
33    margin: 0;
34  }
35
36  .container {
37    width: 100%;
38    max-width: 600px;
39    margin: auto;
40    padding: 20px 0;
41  }
42  
43  .wrapper {
44   width: '90%';
45    align-self: 'center';
46  }
47
48  .header {
49    display: flex;
50    flex-direction: column;
51    align-items: center;
52    padding: 25px 0;
53    background-color: #208AEC;
54  }
55
56  .header_logo {
57    height: 55px;
58    margin-bottom: 20px;
59    object-fit: contain;
60  }
61  
62  .header_text_bold {
63    margin-bottom: 3px;
64    color: rgba(255, 255, 255, 0.9);
65    font-size: 16px;
66    font-weight: bold;
67  }
68  
69  .header_text {
70    color: rgba(255, 255, 255, 0.9);
71    font-size: 15px;
72  }
73
74  .flex_row {
75    display: flex;
76  }
77  
78  .flex_between {
79    display: flex;
80    align-items: center;
81    justify-content: space-between;
82  }
83
84  .form_wrapper {
85    margin-top: 20px;
86    margin-bottom: 10px;
87  }
88
89  .form_heading {
90    margin-bottom: 10px;
91  }
92
93  .form {
94    padding-bottom: 25px;
95    margin: 25px 0;
96    border-bottom: 1px solid rgba(0, 0, 0, 0.12);
97  }
98
99  .form_input {
100   display: block;
101   width: 100%;
102   height: 46px;
103   padding: 0 15px;
104   background-color: #e6e6e6;
105   border: 1px solid #ccc;
106   border-radius: 999px;
107   margin-bottom: 20px;
108   font-size: 16px;
109 }
110
111 .form_button {
112   display: block;
113   width: 100%;
114   height: 42px;
115   padding: 0 15px;
116   background-color: #208AEC;
117   border: 1px solid #208AEC;
118   border-radius: 999px;
119   color: #fff;
120   font-size: 16px;
121   cursor: pointer;
122 }
123
124  .form_hint {
125   display: block;
126   margin-top: 20px;
127   color: rgba(0, 0, 0, 0.5);
128   font-size: 16px;
129   text-align: center;
130   text-decoration-color: rgba(0, 0, 0, 0.4);
131 }
132
133 .card_element_wrapper {
134   padding: 13px 15px;
135   margin-bottom: 20px;
136   background-color: #e6e6e6;
137   border: 1px solid #ccc;
138   border-radius: 999px;
139 }
```

Test the app using any kind of local HTTP server, like the <a href="https://github.com/http-party/http-server" target="_blank">http-server node package</a>. After signing up, your app should look like this:

::Image[]{src="https://archbee-image-uploads.s3.amazonaws.com/yD3zCY-NNBBIfd0uqcfR5/zmw8j8fXi-Ghyk53M-7KG_image.png" signedSrc size="80" width="800" height="673" position="center" caption}

## Conclusion

With the guide described above, you got to use Stripe with a Cloud Code function in Back4App as well as integrate payments to a simple JavaScript app!

In case you face any trouble while integrating Stripe or a function doesn’t work, please contact our team via chat!