apps/docs/content/guides/functions/examples/push-notifications.mdx
Push notifications are an important part of any mobile app. They allow you to send notifications to your users even when they are not using your app. This guide will show you how to send push notifications to different mobile app frameworks from your Supabase edge functions.
<Tabs scrollable size="large" type="underlined" defaultActiveId="expo" queryGroup="platform"
<TabPanel id="expo" label="Expo Push Notifications"> [Expo](https://docs.expo.dev/push-notifications/overview/) makes implementing push notifications easy. All the hassle with device information and communicating with Firebase Cloud Messaging (FCM) or Apple Push Notification Service (APNs) is done behind the scenes. This allows you to treat Android and iOS notifications in the same way and save time both on the frontend and backend.
Find the example code on [GitHub](https://github.com/supabase/supabase/blob/master/examples/user-management/expo-push-notifications/).
## Supabase setup
- [Create a new Supabase project](https://database.new).
- Link your project: `supabase link --project-ref your-supabase-project-ref`
- Start Supabase locally: `supabase start`
- Push up the schema: `supabase db push` (schema is defined in [supabase/migrations](https://github.com/supabase/supabase/blob/master/examples/user-management/expo-push-notifications/supabase/migrations/))
## Expo setup
To utilize Expo's push notification service, you must configure your app by installing a set of libraries, implementing functions to handle notifications, and setting up credentials for Android and iOS. Follow the official [Expo Push Notifications Setup Guide](https://docs.expo.dev/push-notifications/push-notifications-setup/) to get the credentials for Android and iOS. This project uses [Expo's EAS build](https://docs.expo.dev/build/introduction/) service to simplify this part.
1. Install the dependencies: `npm i`
1. Create a [new Expo project](https://expo.dev/accounts/_/projects)
1. Link this app to your project: `npm install --global eas-cli && eas init --id your-expo-project-id`
1. [Create a build for your physical device](https://docs.expo.dev/develop/development-builds/create-a-build/#create-a-build-for-the-device)
1. Start the development server for your project: `npx expo start --dev-client`
1. Scan the QR code shown in the terminal with your physical device.
1. Sign up/in to create a user in Supabase Auth.
## Enhanced security for push notifications
1. Navigate to your [Expo Access Token Settings](https://expo.dev/accounts/_/settings/access-tokens).
1. Create a new token for usage in Supabase Edge Functions.
1. Toggle on "Enhanced Security for Push Notifications".
1. Create the local `.env` file: `cp .env.local.example .env.local`
1. In the newly created `.env.local` file, set your `EXPO_ACCESS_TOKEN` value.
## Deploy the Supabase Edge Function
The database webhook handler to send push notifications is located in [supabase/functions/push/index.ts](https://github.com/supabase/supabase/blob/master/examples/user-management/expo-push-notifications/supabase/functions/push/index.ts). Deploy the function to your linked project and set the `EXPO_ACCESS_TOKEN` secret.
1. `supabase functions deploy push`
1. `supabase secrets set --env-file .env.local`
```ts supabase/functions/push/index.ts
import { createClient } from 'npm:@supabase/supabase-js@2'
console.log('Hello from Functions!')
interface Notification {
id: string
user_id: string
body: string
}
interface WebhookPayload {
type: 'INSERT' | 'UPDATE' | 'DELETE'
table: string
record: Notification
schema: 'public'
old_record: null | Notification
}
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
Deno.serve(async (req) => {
const payload: WebhookPayload = await req.json()
const { data } = await supabase
.from('profiles')
.select('expo_push_token')
.eq('id', payload.record.user_id)
.single()
const res = await fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${Deno.env.get('EXPO_ACCESS_TOKEN')}`,
},
body: JSON.stringify({
to: data?.expo_push_token,
sound: 'default',
body: payload.record.body,
}),
}).then((res) => res.json())
return new Response(JSON.stringify(res), {
headers: { 'Content-Type': 'application/json' },
})
})
```
## Create the database webhook
Navigate to the [Database Webhooks settings](/dashboard/project/_/integrations/webhooks/overview) in your Supabase Dashboard.
1. Enable and create a new hook.
1. Conditions to fire webhook: Select the `notifications` table and tick the `Insert` event.
1. Webhook configuration: Supabase Edge Functions.
1. Edge Function: Select the `push` edge function and leave the method as `POST` and timeout as `1000`.
1. HTTP Headers: Click "Add new header" > "Add auth header with service key" and leave Content-type: `application/json`.
1. Click "Create webhook".
## Send push notification
1. Navigate to the [table editor](/dashboard/project/_/editor) in your Supabase Dashboard.
1. In your `notifications` table, insert a new row.
1. Watch the magic happen 🪄
This guide will show you how to send push notifications to your app when a new row is inserted into a table using FCM, Supabase Edge Functions, and database web hooks.
## Supabase setup
We will create two tables. One to store the user's FCM token and a `notifications` table. The edge function will be triggered when a new row is inserted into the `notifications` table and sends a push notification to the user.
Create a `notifications` table. Also create a `profiles` table if you don't already have one:
```sql
create table public.profiles (
id uuid references auth.users(id) not null primary key,
fcm_token text
);
create table public.notifications (
id uuid not null default gen_random_uuid(),
user_id uuid references auth.users(id) not null,
created_at timestamp with time zone not null default now(),
body text not null
);
```
If you already have a `profiles` table, alter it to include an `fcm_token` column:
```sql
ALTER TABLE public.profiles
ADD COLUMN fcm_token text;
```
With the tables created, we can now create the edge function that will be triggered by database webhook when a notification is inserted.
Create the function using the following command:
```bash
# Initialize Supabase in your working directory
supabase init
# Create the push edge function
supabase functions new push
```
Add the following code to `supabase/functions/push/index.ts`:
```ts supabase/functions/push/index.ts
import { createClient } from 'npm:@supabase/supabase-js@2'
import { JWT } from 'npm:google-auth-library@9'
import serviceAccount from '../service-account.json' with { type: 'json' }
interface Notification {
id: string
user_id: string
body: string
}
interface WebhookPayload {
type: 'INSERT'
table: string
record: Notification
schema: 'public'
}
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
Deno.serve(async (req) => {
const payload: WebhookPayload = await req.json()
const { data } = await supabase
.from('profiles')
.select('fcm_token')
.eq('id', payload.record.user_id)
.single()
const fcmToken = data!.fcm_token as string
const accessToken = await getAccessToken({
clientEmail: serviceAccount.client_email,
privateKey: serviceAccount.private_key,
})
const res = await fetch(
`https://fcm.googleapis.com/v1/projects/${serviceAccount.project_id}/messages:send`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
message: {
token: fcmToken,
notification: {
title: `Notification from Supabase`,
body: payload.record.body,
},
},
}),
}
)
const resData = await res.json()
if (res.status < 200 || 299 < res.status) {
throw resData
}
return new Response(JSON.stringify(resData), {
headers: { 'Content-Type': 'application/json' },
})
})
const getAccessToken = ({
clientEmail,
privateKey,
}: {
clientEmail: string
privateKey: string
}): Promise<string> => {
return new Promise((resolve, reject) => {
const jwtClient = new JWT({
email: clientEmail,
key: privateKey,
scopes: ['https://www.googleapis.com/auth/firebase.messaging'],
})
jwtClient.authorize((err, tokens) => {
if (err) {
reject(err)
return
}
resolve(tokens!.access_token!)
})
})
}
```
## FCM setup
1. Follow the official [FCM Setup Guide](https://firebase.google.com/docs/cloud-messaging) to set up FCM for your client side application.
1. Generate a new service account private key from the Firebase console `Project Settings > Service Accounts > Generate new private key`.
1. Save the service account private key as `service-account.json` under `supabase/functions` directory.
## Deploy the function
Deploy the function with the following command:
```bash
# Link your local Supabase project to the remote Supabase project
supabase link
# Deploy the function
supabase functions deploy push --no-verify-jwt
```
## Create the database webhook
Navigate to the [Database Webhooks settings](/dashboard/project/_/database/hooks) in your Supabase Dashboard.
1. Enable and create a new hook.
1. Conditions to fire webhook: Select the `public.notifications` table and tick the `Insert` event.
1. Webhook configuration: Supabase Edge Functions.
1. Edge Function: Select the `push` edge function and leave the method as `POST` and timeout as `1000`.
1. Click "Create webhook".
## Send push notification
1. Make sure you have a user with an FCM token in the `profiles` table.
1. Navigate to the [table editor](/dashboard/project/_/editor) in your Supabase Dashboard.
1. In your `notifications` table, insert a new row.
1. Watch the magic happen 🪄
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/CiSv9E6ZKVc"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>