Aviato – A Prototype Built with Stream’s React Native Components & Serverless on AWS

Nick P.
Nick P.
Published October 4, 2018 Updated November 8, 2019

The Story Behind Aviato

With our recent announcement about the React Native Components for activity feeds, we wanted to showcase them with a project that was not only functional but also fun to build. For those of you that haven’t seen HBO’s comedy sitcom Silicon Valley, Aviato is an application that is inspired by that of the TV series’ “angel investor” Erlich Bachman (TJ Miller). Erlich’s love for Aviato in the show is downright hilarious – it’s his baby and only entrepreneurial success, or, at least, we are lead to believe that this is the case.

💬 Pssst: Are you interested in building a chat app? If you are, make sure you check out our new React Native Chat components.

According to the interwebs, Aviato is a software aggregation program that takes all the information from social media whenever Frontier Airlines is mentioned and organizes it by region and airport, as well as lets you know which airports are hubs and which aren’t. It was also said to have been acquired in 2008 by Frontier Airlines for "a low seven figures”…

This inspired me to build a mockup app that utilized much of the same technology said to be in the app (albeit a stripped down version, for the sake of time). In the Stream version of Aviato, you’re immediately taken to a page where a paginated list of Frontier Airlines related Tweets is visible to you. The app also boasts a “like” button (in case you feel compelled to “like” a Tweet), in addition to full support for commenting your heart out and sharing your thoughts about Frontier Airlines. 😉

This tutorial assumes the following:

  1. You have a basic understanding of Node.js, React, and JSX (with Node 10.11.0 or latest installed – brew install node)
  2. You're running macOS or can follow along on another OS (note that you'll have to run the Android emulator in this case)
  3. You have Xcode installed with Command Line Tools installed as well (https://developer.apple.com/download/more/)
  4. You have an Amazon AWS account (https://aws.amazon.com)
  5. Expo CLI installed (yarn global add expo-cli)

What You’ll Learn

In this tutorial, you’ll learn the ins and outs of building an application using our brand new React Native Components, as well as spinning up an environment on AWS (in minutes!).

The backend is powered by Serverless, a powerful cross-cloud framework that allows you to spin up a serverless API and set of workers in just a couple of minutes. For our use-case, we decided to go with Amazon Web Services (AWS).

The Config File

Throughout this of this tutorial, you’ll be required to enter values into a config.js file that is located in the root directory. Once complete, you’ll need to copy this config.js file into your serverless directory so that Lambda can pick up on the values.

Setting up a Twitter App

A Twitter app is required for this installation. Luckily, it’s rather straightforward to do.

Note that you WILL need to have a verified account in order to create an app.

To get started, head over to https://apps.twitter.com. Follow the necessary prompts and then collect the keys as required in config.js of the aviato directory.

Creating a Stream Account & App

Stream is the backbone of this application, so we’ll need to create an account and app for Aviato. Creating an account is easy, just head over to https://getstream.io and click “Sign Up” in the top right corner.

Once your account is provisioned, you’ll want to create a new app. You can do this by clicking “Create App” in the top right-hand corner. Your app name must be globally unique, so pick something interesting, and I’d recommend choosing “Development” for the environment – it makes truncating data a lot easier when getting set up.

Grab your App ID, Key, and Secret and drop it in the config.js file.

Now that you have your credentials saved, create a new “flat” feed called “timeline”. This timeline feed will store all of the Tweets that are scraped from Twitter for the @FlyFrontier handle.

Installing the AWS CLI

To get this application up and running, it’s important that you first install and configure the AWS CLI. Don’t worry, it’s an easy task. Follow the instructions below, or visit this site for detailed instructions.

Prerequisites:

Python 2 version 2.6.5+ or Python 3 version 3.3+. To check your Python version, run the following command:

python --version

To install the AWS CLI using the bundled installer:

1. Download the AWS CLI Bundled Installer

curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"

2. Unzip the package

unzip awscli-bundle.zip

3. Run the install executable

sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws

Once the AWS CLI is installed, you can run aws configure in your terminal and you’ll be prompted with several questions. Be sure to have your AWS Key and AWS Secret available.

Note: You will also want to make sure that you have AdministratorAccess permissions under your IAM profile.

Now that everything is set up, Serverless will be able to successfully deploy to AWS without any hiccups. If you run into issues, double check that the AWS CLI was set up properly, that your credentials are correct, and that you have the correct AdministratorAccess permissions under your IAM profile.

Cloning the Aviato Repo from GitHub

As with any repository on git, we’ll use the git clone command to pull down the code. I’d recommend cloning the code into a directory where you usually work from, such as ~/code.

git clone git@github.com:GetStream/Aviato.git

Installing Dependencies

Your code is cloned down to your local machine and you’re ready to go. Let’s move on and get the necessary node modules installed.

Note that this will require the latest version of Node.js. Currently, Node.js is at v10.11.0 at the time of writing this post.

React Native

cd aviato && yarn install

Serverless

From there, we’re just one directory away from installing our Serverless requirements! However, we will first need to install Serverless (the node module) globally:

yarn global add serverless

Now you can install the required modules in the ./serverless directory:

cd aviato/serverless && yarn install

Now you can go ahead and back out to the base aviato directory.

Looking at the Serverless Setup

As previously mentioned, the Serverless project makes it extremely easy to get off the ground and running with a Serverless environment. For our setup, we have two primary files that make up our environment.

  1. The handler.js file which has all of the logic in it for telling the Lambda processes what to do.
  2. The second important file (arguably the more important file) is the serverless.yml file. This file is essentially the director to handler.js, telling it when to fire (whether it be on a cron, like for a schedule, or when a certain endpoint is hit).

For example, here’s a Lambda function that is triggered by an HTTP request to the AWS API Gateway, returning a Stream JWT token for use on the client side (React Native):

Building your own app? Get early access to our Livestream or Video Calling API and launch in days!
module.exports.token = (event, context, callback) => {
	const data = JSON.parse(event.body);

	const token = streamClient.createUserSessionToken(data.user);

	const response = {
		statusCode: 200,
		body: JSON.stringify({ token: token })
	};

	callback(null, response);
};

And, here’s a longer example on what a regular time-based (cron-like) Lambda looks like (please note that this also contains logic for adding to Stream, etc.):

module.exports.crawl = () => {
	twitterClient.get(
		'search/tweets',
		{ q: '@FlyFrontier', count: 100 },
		(err, data, res) => {
			data.statuses.map(status => {
				streamClient
					.feed('timeline', 'all')
					.addActivity({
						actor: streamClient.collections.createUserReference(
							status.user.screen_name
						),
						verb: 'tweet',
						object: status.text,
						attachments: {
							og: {
								title: status.text,
								description: 'via Aviato for Twitter',
								url: `https://twitter.com/${
									status.user.screen_name
								}/status/${status.id_str}`,
								images: [
									{
										image:
											'https://pbs.twimg.com/profile_images/700447615062642688/fXsrshge_400x400.jpg'
									}
								]
							}
						},
						time: moment(
							status.created_at,
							'ddd MMM DD HH:mm:ss ZZ YYYY'
						).toISOString(),
						foreign_id: status.id_str
					})
					.then(res => {
						streamClient.collections
							.upsert('user', {
								id: status.user.screen_name,
								name: '@' + status.user.screen_name,
								profileImage:
									status.user.profile_image_url_https
							})
							.then(res => {
								callback(null, res);
							})
							.catch(err => {
								callback(err);
							});
					})
					.catch(err => {
						callback(err);
					});
			});
		}
	);
};

If you’d like to do a test run a Twitter crawl locally, you can do so with the following commands:

serverless invoke local --function crawl

Note: This will create entries in Stream but will not create duplicate entries.

Dissecting the React Native Directories

Our Aviato build of React Native is rather simple, and it’s meant to be that way. The more complex your application, the harder it is to develop and to explain your thought process behind it.

Because React Native is a relatively self-serving, we’re able to focus on what matters – the aesthetics. With that said, App.js and screens will be our working directories.

App.js

The file App.js contains all of our logic for navigating throughout the application. In our case, we only have two files to navigate between, so, in theory, we could get away with this file entirely. However, for learning purposes, I’ve decided to keep it in. Here are the contents of the App.js file:

import React, { Component } from 'react';
import { createStackNavigator } from 'react-navigation';
import browserPollyfill from 'react-native-browser-polyfill';

import { Welcome, Feed } from './screens';

const RootStack = createStackNavigator({
	Welcome: {
		screen: Welcome,
		navigationOptions: {
			header: null
		}
	},
	Feed: {
		screen: Feed,
		navigationOptions: {
			header: null
		}
	}
});

export default class App extends Component {
	render() {
		return <RootStack />;
	}
}

This file relies heavily on react-navigation to handle switching screens. It creates what’s called a “RootStack” which is essentially just an object of directives for the application to read.

Screens

Screens is an important directory as it holds the files for visualizing and composing the app. Both feed.js and welcome.js use exports. The file index.js then pulls these files in and exports them again so that they can be consumed by components (e.g. the navigation/App.js file).

The Stream React Native Components Used

1. StreamApp

StreamApp is the most basic component and is what allows you to define your application parameters (e.g. appId, apiKey, token, etc.).

<StreamApp
	appId={config.stream.appId}
	apiKey={config.stream.apiKey}
	token={this.state.token}
/>

More information can be found here: https://getstream.github.io/react-native-activity-feed/#context

2. FlatFeed

FlatFeed renders a feed of activities; this component is a StreamApp consumer and must always be a child of the element.

<FlatFeed
    feedGroup="timeline"
    Activity={props => (
        ...
    )}
/>

More information can be found here: https://getstream.github.io/react-native-activity-feed/#flatfeed

3. Activity

An Activity is responsible for actually rendering feed activities.

Activity={props => (
    <Fragment>
        <Activity
            {...props}
            Footer={
                <View
                    style={{
                        flexDirection: 'row',
                        alignItems: 'center'
                    }}
                >
                    <LikeButton {...props} />
                    <ReactionIcon
                        icon={ReplyIcon}
                        labelSingle="comment"
                        labelPlural="comments"
                        counts={
                            props.activity
                                .reaction_counts
                        }
                        kind="comment"
                    />
                </View>
            }
        />
    </Fragment>
)}

More information can be found here: https://getstream.github.io/react-native-activity-feed/#activity

4. LikeButton

A LikeButton provides the ability for a user to recognize or show off that specific content is pleasing to them.

<LikeButton {...props} />

More information can be found here: https://getstream.github.io/react-native-activity-feed/#likebutton

5. CommentList

A CommentList is a rather simple component – it’s pre-styled to hold onto all comments that are made on an element.

<CommentList
    reactions={props.activity.latest_reactions}
/>

More information can be found here: https://getstream.github.io/react-native-activity-feed/#commentlist

6. CommentBox

The CommentBox comes with support out of the box ( 😉) for keyboard control, an avatar and text input.

<CommentBox
    onSubmit={text =>
        props.onAddReaction(
            'comment',
            props.activity,
            {
                data: { text: text }
            }
        )
    }
    avatarProps={{
        source:
            'https://i.imgur.com/T4kO8H2.png'
    }}
    styles={{ container: { height: 80 } }}
/>

More information can be found here: https://getstream.github.io/react-native-activity-feed/#commentbox

For a full list of UI components offered by Stream’s React Native Components, have a look here: https://getstream.github.io/react-native-activity-feed/#ui-components. Our team has also put together a great tutorial on how to use these components here.

Deploying to AWS Lambda with Serverless

Assuming that you’ve already configured AWS CLI using the aws configure command as mentioned above, and you have installed the serverless framework globally with yarn global add serverless, this step should be a breeze.

  1. Create an account on https://serverless.com
  2. Create a new project, where “name” is something unique
  3. In your terminal, type serverless login and it will take you through an auth flow
  4. Once logged in, open serverless.yml and specify your service as the name of the app that you chose (e.g. aviato), the app as the name of the app that you chose (e.g. aviato), and the tenant as your username (e.g. stream)
  5. Save the file and go back to your terminal
  6. Making sure that you’re inside of the serverless directory, type sls deploy and Serverless will automatically read and parse the serverless.yml file, generating the entire infrastructure for you on AWS

Note: You’ll want to grab the POST endpoint and paste it into your config files apiToken value.

Starting the React Native App

Now that we have everything in place, let’s go ahead and start the React Native app! You can do this one of two ways: you can simply type yarn start OR you can type expo start -c (if you have the Expo CLI tools installed).

Note: If you’re on macOS, press the “i” key and Expo will automatically open the iOS Simulator. If you would like to look at the app on your phone, download the Expo app from the Android Play Store or iOS App Store and use the app to snap a photo of the provided QR code.

Final Thoughts

If you were able to get the app up and running, congratulations! You should see a similar screen to the screenshot below!

Thanks for reading and I hope you take the time to have a look at React Native Components by Stream. We have a great tutorial here and a link to another GitHub example here.

If you have any questions, please don’t hesitate to reach out in the comments below or via Twitter @nickparsons. And, if you're feeling extra nice, an upvote on Expo (https://expo.io/@nickparsons/aviato) would be greatly appreciated!

Happy coding! 🍻

Integrating Video With Your App?
We've built a Video and Audio solution just for you. Check out our APIs and SDKs.
Learn more ->