Sending push notifications via Firebase using AWS Lambda

This is Part 2 of my tutorial on Push notifications. Please click the link for Part 1 of the tutorial. We are building a Retweeter app. The Retweeter app is a React native app which displays a list of tweets it receives from a push notification. The user has an option to retweet some of the tweets.

In Part 2, we are going to build the server infrastructure for push notifications using AWS Lambda. So far, we have created a lambda function which searches tweet by a query or hashtag. Next, we are going to build an API gateway which calls this lambda function.

Build an API gateway

API gateway is another service provided by AWS to build REST endpoints quickly. The documentation and help provided by AWS is sufficient to build an API quickly.

The /tweets resource has a GET method. This calls the lambda function – search-tweets. As you recall, the lambda function expects the query to be passed as a parameter. Our GET method will accept a query string named query. Here is where it gets a bit AWSish. The integration request in the above screen shot is useful to define a template that maps the query string parameter in the API to the query parameter in the lambda function.

Based on the advice provided in the StackOverflow answer, I was able to do this quickly. This obscure piece of code will do the mapping. Thanks AWS!

{ "query": "$input.params('query')" }

Now, we are ready to test the API gateway. Press the Test button. Pass the query that you want tweets for. For example, ReactJS. Press Test again.

It works. But as an experienced professional, don’t expect to get it right the first time!

UI for the App

The easiest part of the tutorial is building the UI. We have a text input which gets the query or hashtag with which we want to search tweets. There is a button to set the query.

<View style={styles.row}>
    <TextInput 
        style={styles.input}
        value={this.state.query}
        onChangeText={query => this.setState({ query })}
    />
    <TouchableHighlight style={styles.button} onPress={this.handleSetPress.bind(this)}>
        <Text style={styles.buttonText}>Set</Text>
    </TouchableHighlight>
</View>

The styles for the components are shown below.

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'stretch',
        justifyContent: 'flex-start',
    },
    row: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        margin: 20
    },
    input: {
        flex: 1,
        fontSize: 20
    },
    button: {
        width: 100,
        backgroundColor: '#0047AB',
        justifyContent: 'center',
        alignItems: 'center',
        borderRadius: 20,
        padding: 10
    },
    buttonText: {
        color: '#FFF',
        fontSize: 20
    }
});

The event handler for the button does the bulk of the work. It marks the query as set. And retrieves the tweets for the last hour using our API gateway.

handleSetPress() {
    fetch(`https://fvmylcig0b.execute-api.us-west-2.amazonaws.com/prod/tweets?query=${encodeURIComponent(this.state.query)}`)
        .then(response => response.json())
        .then(json => {
            this.setState({
                isQuerySet: true,
                tweets: json
            });
        });
}

Fetch at the API endpoint returns all the tweets as a list. These tweets are displayed in a FlatList component.

<View style={styles.column}>
    <Text style={styles.heading}>Tweets in the last one hour</Text>
    <FlatList
        data={this.state.tweets}
        keyExtractor={(item, index) => index}
        renderItem={({ item }) => (
            <View style={{ marginBottom: 20 }}>
                <Text style={styles.name}>{item.name}</Text>
                <Text style={styles.text}>{item.text}</Text>
                <Button 
                    title="Retweet"  
                    onPress={this.handleRetweet.bind(this)}
                />
            </View>
        )}
    />
</View>

The renderItem prop in FlatList renders the tweet with a Retweet button.

Now, we are all set to send push notifications via Lambdas.

Sending push notification using Lambda

Sending a push notification to FCM is trivial. POST request to an API endpoint with JSON payload sends the notification to the device.

request({
    url: 'https://fcm.googleapis.com/fcm/send',
    method: 'POST',
    headers: {
        Authorization: `key=${FIREBASE_API_KEY}`
    },
    json: true,
    body: {
        notification: {
            title: 'Your Tweets',
            text: `There are ${tweets.length} tweets`,
        },
        data: {
            message: JSON.stringify(tweets)
        },
        to: fcm_token
    }
}, function (error, response, body) {
    if (!error && response.statusCode === 200) {
        
    }
});

There are two points to note in the above code. The request header has the Firebase API key. Part 1 of the Tutorial covers how to retrieve the FCM token. Provide the token in the request body.

From where does the tweets come? The tweets are retrieved by invoking the lambda function search_tweets we wrote earlier.

const aws = require('aws-sdk');
const lambda = new aws.Lambda();
lambda.invoke({
    FunctionName: 'search-tweets',
    Payload: JSON.stringify(event)
}, function(error, data) {
    const tweets = JSON.parse(data.Payload);
});

Let us put the above code snippets in a new lambda handler – index.js.  Install the npm modules – aws-sdk and request. Create a deployment package by using the zip utility.

zip -r ../send_notification.zip *

Upload the zip as a new lambda function. Since the lambda invokes another lambda, create a new role. The new role should have the AWSLambdaRole permission set. Run the lambda using the following event.

{
    query: '#ReactJS',
    fcm_token: 'xyz'
}

If everything went well, you should receive a push notification on your device.

Next Up

In this part of the tutorial, we learnt how to wrap our search-tweets lambda with an API gateway. Our UI called the API gateway, retrieved the tweets and displayed it in a FlatList component. Finally, we deployed another lambda send-notifications which invoked the search-tweets lambda and sent a push notification to our device using the Firebase API endpoint.

Now, it is time to pause. The next tutorial in the series will cover:

  • How to render the UI after the user clicks on the push notification.
  • Deploy another lambda function which sets a hourly trigger for send-notifications lambda.
  • Retweet the tweet rendered in the UI

Tutorial Series

Part 1: Sending Remote notifications using Firebase console

Part 2: Sending Remote notifications using AWS Lambda

Part 3: Displaying data from push notifications

Related Posts

Leave a Reply

Your email address will not be published.