Files
nhost/docs/getting-started/tutorials/reactnative.mdx
2025-09-24 14:50:55 +02:00

444 lines
14 KiB
Plaintext

---
title: Get up and running with Nhost and React Native
sidebarTitle: React Native
description: In this quickstart guide, we'll demonstrate how to build a simple To-Do feature using Nhost and React Native.
icon: mobile-notch
---
<Card>
Throughout this guide, we'll utilize the **@nhost/react-native-template**, which comes pre-configured with
authentication and storage capabilities provided by Nhost.
</Card>
<br />
<Note>
Before starting this quickstart, ensure that your environment is set up to work with React Native.
Follow the [setup guide](https://reactnative.dev/docs/next/set-up-your-environment) available on
the official React Native website.
</Note>
<Steps>
<Step title="Create Nhost Project">
Create your project through the [Nhost Dashboard](https://app.nhost.io).
</Step>
<Step title="Setup Database">
Navigate to the **SQL Editor** of the database and run the following SQL to create a new table `todos`.
<Warning>Make sure the option `Track this` is enabled</Warning>
```sql SQL Editor
CREATE TABLE todos (
id uuid NOT NULL DEFAULT gen_random_uuid(),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
user_id uuid NOT NULL,
contents text NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE cascade ON DELETE cascade
);
```
![Create Todos Table](/images/quickstarts/react-native/create-table-todos.png)
</Step>
<Step title="Configure the todos table permissions">
To set permissions for the new `todos` table, select the table, click on the `...` to open the actions dialog,
then click on **Edit Permissions**. Set the following permissions for the `user` role:
1. `Insert`
- Set `Row insert permissions` to `Without any checks`
- Select all columns except `user_id` on `Column insert permissions`
- Add a new `Column preset` and set `Column Name` to `user_id` and `Column Value` to `X-Hasura-User-Id`
- Save
![Insert Permissions](/images/quickstarts/react-native/todos-insert-permissions.png)
2. `Select`
- Set `Row select permissions` to `With custom check` and fill in the following rule:
- Set `Where` to `todos.user_id`
- Set the operator to `_eq`
- Set the value to `X-Hasura-User-Id`
- Select all columns except `user_id` on `Column select permissions`
- Save
![Select Permissions](/images/quickstarts/react-native/todos-select-permissions.png)
3. `Update`
- Set `Row update permissions` to `With custom check` and fill in the following rule:
- Set `Where` to `todos.user_id`
- Set the operator to `_eq`
- Set the value to `X-Hasura-User-Id`
- Select all columns except `user_id` on `Column select permissions`
- Save
![Update permissions](/images/quickstarts/react-native/todos-update-permissions.png)
4. `Delete`
- Set `Row delete permissions` to `With custom check` and fill in the following rule:
- Set `Where` to `todos.user_id`
- Set the operator to `_eq`
- Set the value to `X-Hasura-User-Id`
- Save
![Delete permissions](/images/quickstarts/react-native/todos-delete-permissions.png)
</Step>
<Step title="Configure permissions to enable user file uploads">
To enable file uploads by users, set the permissions as follows:
1. Edit the **files** table permissions
1. Navigate to the files table within the [Database tab](https://app.nhost.io/_/_/database/browser/default/storage/files)
2. Click on the three dots (...) next to the files table
3. Click on **Edit Permissions**
2. Modify the `Insert` permission for the `user` role:
1. Set `Row insert permissions` to `Without any checks`
2. Select all columns on `Column insert permissions`
4. Save
![Insert Permissions](/images/quickstarts/react-native/files-insert-permissions.png)
3. `Select`
- Set `Row select permissions` to `With custom check` and fill in the following rule:
- Set `Where` to `files.uploaded_by_user_id`
- Set the operator to `_eq`
- Set the value to `X-Hasura-User-Id`
- Select all columns on `Column select permissions`
- Save
![Select permissions](/images/quickstarts/react-native/files-select-permissions.png)
</Step>
<Step title="Bootstrap your React Native app">
Intialize a new React Native project using the template `@nhost/react-native-template`
```bash Terminal
npx react-native init myapp --template @nhost/react-native-template
```
</Step>
<Step title="Connect your React Native app to the Nhost project">
Copy your project's `<subdomain>` and `<region>` values available on the dashboard overview
```tsx src/root.tsx
const nhost = new NhostClient({
subdomain: "<subdomain>", // replace the subdomain value e.g. "hjcuuqweqwezolpolrep"
region: "<region>", // replace the region value e.g. "eu-central-1"
clientStorageType: 'react-native',
clientStorage: AsyncStorage,
});
```
</Step>
<Step title="Add the GraphQL queries">
Create a new file `src/graphql/todos.ts` that will expose the graphql queries needed to `list`, `add` and `delete` To-Do's.
```ts src/graphql/todos.ts
import {gql} from '@apollo/client';
export const GET_TODOS = gql`
query listTodos {
todos(order_by: { created_at: desc }) {
id
contents
}
}
`;
export const ADD_TODO = gql`
mutation addTodo($contents: String!) {
insert_todos_one(object: { contents: $contents }) {
id
contents
}
}
`;
export const DELETE_TODO = gql`
mutation deleteTodo($id: uuid!) {
delete_todos_by_pk(id: $id) {
__typename
}
}
`;
```
</Step>
<Step title="Add a form to insert a To-Do">
```tsx src/components/AddTodoForm.tsx
import React from 'react';
import {useMutation} from '@apollo/client';
import Button from '@components/Button';
import ControlledInput from '@components/ControlledInput';
import {ADD_TODO, GET_TODOS} from '@graphql/todos';
import {useForm} from 'react-hook-form';
import {StyleSheet, View} from 'react-native';
interface AddTodoFormValues {
contents: string;
}
export default function AddTodoForm() {
const {control, handleSubmit, reset} = useForm<AddTodoFormValues>();
const [addTodo, {loading}] = useMutation(ADD_TODO, {
refetchQueries: [{query: GET_TODOS}],
});
const onSubmit = async (values: AddTodoFormValues) => {
const {contents} = values;
await addTodo({variables: {contents}});
reset();
};
return (
<View style={styles.wrapper}>
<View style={styles.inputWrapper}>
<ControlledInput
control={control}
name="contents"
placeholder="New To-Do"
autoCapitalize="none"
rules={{
required: true,
}}
/>
</View>
<View style={styles.buttonWrapper}>
<Button
label="Add"
onPress={handleSubmit(onSubmit)}
disabled={loading}
loading={loading}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
gap: 12,
padding: 12,
flexDirection: 'row',
backgroundColor: 'white',
},
inputWrapper: {
flex: 3,
},
buttonWrapper: {
flex: 1,
},
});
```
</Step>
<Step title="Add the Todo component and the screen to list all the todos">
<CodeGroup>
```tsx src/components/Todo.tsx
import React from 'react';
import {useMutation} from '@apollo/client';
import {DELETE_TODO, GET_TODOS} from '@graphql/todos';
import {StyleSheet, Text, View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import Button from './Button';
export interface TodoItem {
id: string;
contents: string;
}
export default function Todo({todo: {id, contents}}: {todo: TodoItem}) {
const [deleteTodo] = useMutation(DELETE_TODO, {
variables: {id},
refetchQueries: [{query: GET_TODOS}],
});
const handleDeleteTodo = async () => {
await deleteTodo();
};
return (
<View style={styles.wrapper}>
<View style={styles.todoContentWrapper}>
<Icon name="check" size={25} />
<Text style={styles.todoContent}>{contents}</Text>
</View>
<View style={styles.buttonWrapper}>
<Button
label={<Icon name="trash-can-outline" size={20} />}
color="#f1f1f1"
onPress={handleDeleteTodo}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
padding: 14,
flexDirection: 'row',
alignItems: 'center',
},
todoContentWrapper: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
gap: 20,
},
todoContent: {flex: 1},
buttonWrapper: {
width: 50,
},
});
```
```tsx src/screens/Todos.tsx
import React from 'react';
import {useQuery} from '@apollo/client';
import AddTodoForm from '@components/AddTodoForm';
import Todo, {type TodoItem} from '@components/Todo';
import {GET_TODOS} from '@graphql/todos';
import {useEffect} from 'react';
import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native';
export default function Todos() {
const {loading, data, client} = useQuery<{todos: TodoItem[]}>(GET_TODOS);
const todos = data?.todos || [];
useEffect(() => {
return () => client.stop();
}, [client]);
if (loading) {
return (
<View style={styles.loadingViewWrapper}>
<ActivityIndicator />
</View>
);
}
const renderTodo = ({item}: {item: TodoItem}) => <Todo todo={item} />;
const itemSeperator = () => <View style={styles.separator} />;
return (
<View style={styles.wrapper}>
<AddTodoForm />
<FlatList
data={todos}
keyExtractor={item => item.id}
renderItem={renderTodo}
ItemSeparatorComponent={itemSeperator}
/>
</View>
);
}
const styles = StyleSheet.create({
loadingViewWrapper: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
wrapper: {
flex: 1,
backgroundColor: 'white',
},
separator: {
height: 1,
backgroundColor: '#f1f1f1',
},
});
```
</CodeGroup>
</Step>
<Step title="Reference the new Todos components in the Drawer Navigator">
```tsx src/screens/Main.tsx
function DrawerNavigator() {
return (
<Drawer.Navigator
screenOptions={screenOptions}
drawerContent={drawerContent}>
<Drawer.Screen name="Profile" component={Profile} />
{/* Add the Todos component here */}
<Drawer.Screen name="Todos" component={Todos} />
<Drawer.Screen name="Storage" component={Storage} />
</Drawer.Navigator>
);
}
```
</Step>
<Step title="Run the app on the emulator">
<Tabs>
<Tab title="Android">
1. Open a terminal and start the metro bundler
```bash Terminal
cd myapp
npm start
```
2. Open a new terminal and run the app on Android
```bash Terminal
cd myapp
npm run android
```
</Tab>
<Tab title="iOS">
1. Make sure the iOS project cocopods are installed
```bash Terminal
cd ios
pod install
```
1. Install the `ios-deploy` CLI
```bash Terminal
npm install -g ios-deploy
```
2. Start the metro bundler
```bash Terminal
cd myapp
npm start
```
3. Open a new terminal and run the app on Android
```bash Terminal
cd myapp
npm run ios --interactive
```
</Tab>
</Tabs>
</Step>
<Step title="Demo">
<iframe
width="486"
height="864"
src="https://www.youtube.com/embed/gfzksbce2G4"
title="demo react native"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
/>
</Step>
</Steps>
<Note>
### Next Steps: enabling Google and Apple Sign-In
The template is preconfigured to allow users to sign in with Google and Apple. To enable this feature, follow these steps:
1. Navigate to your Nhost project's [Sign-In Methods settings](https://app.nhost.io/_/_/settings/sign-in-methods).
2. Enable Google and/or Apple sign-in.
3. Fill in the necessary credentials.
For detailed instructions on generating the required credentials, refer to the following guides:
- [Google Sign-In Guide](https://docs.nhost.io/products/auth/social/sign-in-google)
- [Apple Sign-In Guide](https://docs.nhost.io/products/auth/social/sign-in-apple)
</Note>