Create a stocks application for Android featuring push notifications
You need Android Studio 3+ and Node.js installed on your machine. Some familiarity with Android development and Android Studio is required.
A stock market, equity market or share market is the aggregation of buyers and sellers (a loose network of economic transactions, not a physical facility or discrete entity) of stocks (also called shares), which represent ownership claims on businesses; these may include securities listed on a public stock exchange as well as those only traded privately. - Wikipedia
Building a stock market application has some fundamental requirements. Apart from accuracy, the application needs to also be able to update prices in realtime as the changes occur. It should also have an option to notify the user if there is a change in the price of a stock, even when they are not currently looking at the application.
In this post, we will see how we can achieve this using Kotlin, Pusher Beams, and Pusher Channels.
When we are done with the article, here is what our app will look like:
Prerequesites
To follow along in this tutorial, you need the following:
- Android Studio (v3.x or later) installed on your machine. Download here.
- Knowledge of Android development and the Android Studio IDE.
- Knowledge of Kotlin. Visit the docs.
- Node.js and NPM installed on your machine. Download any stable release here.
Creating your Android application
The first thing we want to do is create our Android application. Open Android Studio and create a new application.
Enter the name of your application, StockExchangeApp
, and then enter the package name, which is com.example.stockexchangeapp
. Make sure the Enable Kotlin Support checkbox is selected, choose the minimum SDK, we chose API 19, click Next. Choose an Empty Activity template and click Finish.
Setting up Pusher Channels
For this article, you need a Pusher application. To get this, log in to your Pusher dashboard and Create a new channel app. You can do this by clicking the Create new Channels app card at the bottom right. When you create a new app, you are provided with the keys. We will need them later.
Getting your FCM key
For Pusher Beams to work, we need an FCM key and a google-services.json
file. Go to your Firebase console and click the Add project card to initialize the app creation wizard. Add the name of project, read and accept the terms and conditions. After this, you will be directed to the project overview screen. Choose the Add Firebase to your Android app option.
The next screen will require the package name of your app. You can find your app’s package name is your app-module build.gradle
file. Look for the applicationId
value. When you enter the package name and click Next, you will be prompted to download a google-services.json
file. Download the file and skip the rest of the process. Add the downloaded file to the app folder of your project - StockExchangeApp/app
.
To get the FCM key, go to your project settings on Firebase, under the Cloud messaging tab, copy out the server key.
Setting up Pusher Beams
Now that we have set up our Firebase application, log in to the Pusher Beams dashboard and click on the CREATE button on the BEAMS card.
After creating the instance, you will be presented with a quickstart guide. Select the ANDROID quickstart.
The next screen requires the FCM key which you copied earlier. After you add the FCM key, you can exit the guide.
Adding application dependencies
Since this application will depend on other libraries to function, let’s pull in these dependencies so they are available to the project.
Open the project build.gradle
file and add the add the following:
// File: ./build.gradle
buildscript {
// [...]
dependencies {
// [...]
classpath 'com.google.gms:google-services:4.0.0'
}
}
And these other dependencies to the app-module build.gradle
file:
// File: ./app/build.gradle
dependencies {
// [...]
// Support library
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
implementation 'com.android.support:preference-v7:28.0.0-rc01'
// Pusher Channel
implementation 'com.pusher:pusher-java-client:1.8.0'
// Pusher Beams
implementation 'com.google.firebase:firebase-messaging:17.3.0'
implementation 'com.pusher:push-notifications-android:0.10.0'
}
// Add this line to the end of the file
apply plugin: 'com.google.gms.google-services'
Building the application
So far, we have been setting up the project. Now let’s start building the application. Let’s start by tweaking the colors of the application.
Open the colors.xml
file and add the following code to it:
<!-- File: ./app/src/main/res/values/colors.xml -->
<color name="colorPrimary">#9E9E9E</color>
<color name="colorPrimaryDark">#424242</color>
<color name="colorAccent">#607D8B</color>
Next, open your styles.xml
file and replace the parent theme on the app theme with this - Theme.AppCompat
.
Apart from the initial MainActivity
already created for us, we will need a screen to manage the settings for the application.
Create a new Empty Activity named SettingsActivity
. Open the layout created for it - activity_settings
and replace everything except the first line of the file with the following code:
<!-- File: ./app/src/main/res/layout/activity_settings.xml -->
<FrameLayout
android:background="#000"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/frame_layout"
android:layout_height="match_parent"
tools:context=".SettingsActivity"/>
Next, open the SettingsActivity
file and set it up like this:
// File: ./app/src/main/java/com/example/stockexchangeapp/SettingsActivity.kt
// [...]
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
class SettingsActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
supportFragmentManager.beginTransaction()
.replace(R.id.frame_layout, SettingsFragment())
.commit()
}
}
In the code above, we replaced the frame layout with a fragment. This is the recommended practice when creating a settings page. Before creating the fragment, let’s create a preference file.
Create a new file in the xml
directory named preference
and paste the following:
<!-- File: ./app/src/main/res/xml/preference.xml -->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="amazon_preference"
android:title="Amazon"
android:summary="Receive stock updates for Amazon"
android:defaultValue="true" />
<CheckBoxPreference
android:key="apple_preference"
android:title="Apple"
android:summary="Receive stock updates for Apple"
android:defaultValue="true" />
</PreferenceScreen>
In this file, we have two checkboxes to control the updates for two stocks, Amazon and Apple.
Next, create a new class named SettingsFragment
and paste the following code:
// File: ./app/src/main/java/com/example/stockexchangeapp/SettingsFragment.kt
// [...]
import android.os.Bundle
import android.support.v7.preference.PreferenceFragmentCompat
class SettingsFragment: PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preference, rootKey)
}
}
The code above loads the settings from the preference file created earlier. With this, we are done implementing the settings screen.
The next screen to be added will be a list of stock prices, which will be shown in the MainActivity
. To do this, we need a list. Open the activity_main.xml
file and paste the following:
<!-- File: ./app/src/main/res/layout/activity_main.xml -->
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
This layout has a ConstraintLayout
housing a RecyclerView
. Since we are using a list, we need some other utilities. One of those utilities is a data object. The object is what every item on the list will hold.
Related: Getting started with ConstraintLayout in Kotlin.
Create a new class named StockModel
and paste this:
// File: ./app/src/main/java/com/example/stockexchangeapp/StockModel.kt
data class StockModel(var name: String, var currentValue: Double, var changeValue:Double)
A data class in Kotlin generates some other useful methods we would have had to create manually if we were using Java.
Next, let’s design a layout for how each list item will look. Create a new layout file named list_row
and paste this:
<!-- File: ./app/src/main/res/layout/list_row.xml -->
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="10dp"
android:id="@+id/stockName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
tools:text="Amazon"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/changeValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="+5%"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="20dp"
android:paddingEnd="5dp"
android:paddingStart="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/currentValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
tools:text="1234.9"
app:layout_constraintEnd_toStartOf="@id/changeValue"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
From this layout, each list item will show a company name, the current stock price, and it’ll show the change percentage.
Next, let’s create the adapter for the list. Create a new class named StockListAdapter
and paste this:
// File: ./app/src/main/java/com/example/stockexchangeapp/StockListAdapter.kt
// [...]
import android.support.v4.content.res.ResourcesCompat
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
class StockListAdapter(private val stockList:ArrayList<StockModel>) : RecyclerView.Adapter<StockListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.list_row, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(stockList[position])
override fun getItemCount(): Int = stockList.size
fun addItem(item:StockModel){
stockList.add(item)
notifyDataSetChanged()
}
fun updateItem(item:StockModel) {
stockList.forEachIndexed { index, element ->
if (element.name == item.name){
stockList[index].changeValue = item.changeValue
stockList[index].currentValue = item.currentValue
notifyItemChanged(index)
}
}
}
fun contains(item: StockModel):Boolean{
for (stock in stockList){
if (stock.name==item.name){
return true
}
}
return false
}
fun removeItem(name: String) {
val it = stockList.iterator()
while (it.hasNext()) {
val value = it.next()
if (value.name == name){
it.remove()
}
}
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val changePercent: TextView = itemView.findViewById(R.id.changeValue)
private val stockName: TextView = itemView.findViewById(R.id.stockName)
private val currentValue: TextView = itemView.findViewById(R.id.currentValue)
fun bind(item: StockModel) = with(itemView) {
stockName.text = item.name
currentValue.text = item.currentValue.toString()
val fmt = "%s%s"
changePercent.text = String.format(fmt, item.changeValue.toString(), "%")
if (item.changeValue.toString().contains("-")){
changePercent.background = ResourcesCompat.getDrawable(resources, android.R.color.holo_red_dark, null)
} else {
changePercent.background = ResourcesCompat.getDrawable(resources, android.R.color.holo_green_dark, null)
}
}
}
}
This class manages the display of stock items on the list. It collects an initial list passed from the constructor and uses the size of that to know how many items we have.
The list can be updated with the additem
, updateItem
, and removeItem
methods we created. The list_row
layout we designed earlier is used in the onCreateViewHolder
method. In the bind
method of the ViewHolder
class, apart from adding the values to the necessary text views, we apply a green or red background to the changePercent
text view if it is a positive or negative value.
For uniformity, we will create a new class that will hold the list items we will use throughout the client app. Create a new class named MyStockList
and paste this:
// File: app/src/main/java/com/example/stockexchangeapp/MyStockList.kt
// [...]
class MyStockList{
companion object {
val stockList = ArrayList<StockModel>()
init {
stockList.add(StockModel("Apple", 0.0, 0.0))
stockList.add(StockModel("Amazon", 0.0, 0.0))
}
}
}
For this demo, we are considering two stocks only. You can add more if you like. These stocks have a default value of 0.0 for change percent and value.
Next, we will add some logic to our MainActivity
file. Open the file and paste the following:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
// [...]
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.preference.PreferenceManager
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import com.pusher.client.Pusher
import com.pusher.client.PusherOptions
import com.pusher.pushnotifications.PushNotifications
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
private val mAdapter = StockListAdapter(ArrayList())
private lateinit var sharedPreferences: SharedPreferences
private val options = PusherOptions().setCluster("PUSHER_CLUSTER")
private val pusher = Pusher("PUSHER_KEY", options)
private val channel = pusher.subscribe("stock-channel")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupPrefs()
pusher.connect()
setupRecyclerView()
setupPusherChannels()
setupPushNotifications()
}
}
This class implements the SharedPreferences.OnSharedPreferenceChangeListener
interface because we will add settings functionality in the app and the callback will tell us when the settings have been updated.
We created instance variables for our Pusher Channel object and the list adapter. We subscribed to the stock-channel
channel to listen for stock updates.
Replace the Pusher holders with the keys on your Pusher Channel dashboard
Other methods called in the onCreate
method include:
setupPrefs()
- this method initializes thesharedPreferences
variable and initializes our settings with the default values. Add the method to the class:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
private fun setupPrefs() {
PreferenceManager.setDefaultValues(this, R.xml.preference, false)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
}
setupRecyclerView()
- this method initializes ourRecyclerView
. Add the method to the class:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
private fun setupRecyclerView() {
with(recyclerView) {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = mAdapter
addItemDecoration(
DividerItemDecoration(recyclerView.context, DividerItemDecoration.VERTICAL)
)
}
}
setupPusherChannels()
- this method loops through the stock list and looks for the stocks enabled in our settings page. If any of the stock is enabled, we subscribe to receive updates. Add the method to the class:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
private fun setupPusherChannels(){
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
MyStockList.stockList.forEachIndexed { index, element ->
val refKey = element.name.toLowerCase() + "_preference"
val refValue = sharedPref.getBoolean(refKey, false)
if (refValue) {
if (!mAdapter.contains(element)) {
mAdapter.addItem(element)
channel.bind(element.name) { channelName, eventName, data ->
val jsonObject = JSONObject(data)
runOnUiThread {
mAdapter.updateItem(
StockModel(
eventName,
jsonObject.getDouble("currentValue"),
jsonObject.getDouble("changePercent")
)
)
}
}
}
} else {
mAdapter.removeItem(element.name)
channel.unbind(element.name){ _, _, _ -> }
}
}
}
setupPushNotifications()
- this method initializes Pusher Beams and listens to stock interests. Add the method to the class:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
private fun setupPushNotifications() {
PushNotifications.start(applicationContext, "PUSHER_BEAMS_INSTANCEID")
PushNotifications.subscribe("stocks")
}
Replace the
PUSHER_BEAMS_INSTANCEID
placeholder with the Pusher Beams instance ID on your dashboard.
Remember we created a settings page earlier? Let’s inflate our menu and link it to the settings page. To do this, first, let’s first create a menu file in the menu resource folder.
Create a new menu file named menu_main.xml
and paste this:
<!-- File: app/src/main/res/menu/menu_main.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/settings"
app:showAsAction="collapseActionView"
android:title="@string/settings" />
</menu>
Now, add these methods to your MainActivity
file:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.settings -> {
startActivity(Intent(this@MainActivity,SettingsActivity::class.java))
true
}
else -> super.onOptionsItemSelected(item)
}
}
These methods add the menu to the toolbar of our main application screen and add an action when settings is selected. We then register and unregister the listener in the appropriate callback methods like so:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
override fun onStart() {
super.onStart()
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onDestroy() {
super.onDestroy()
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
Finally, let’s add the callback for the shared preference listener:
// file: app/src/main/java/com/example/stockexchangeapp/MainActivity.kt
override fun onSharedPreferenceChanged(sharedPref: SharedPreferences?, key: String?) {
setupPusherChannels()
}
When the settings change, we call the setupPusherChannels
method again so it binds to the stock reports we enabled and unbind from those we disabled.
To complete our Pusher Beams setup, we need a service that will handle incoming notifications. Create a new class named NotificationsMessagingService
and paste this:
// file: app/src/main/java/com/example/stockexchangeapp/NotificationsMessagingService.kt
// [...]
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.os.Build
import android.preference.PreferenceManager
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import android.util.Log
import com.google.firebase.messaging.RemoteMessage
import com.pusher.pushnotifications.fcm.MessagingService
class NotificationsMessagingService : MessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
MyStockList.stockList.forEachIndexed { index, element ->
val refKey = element.name.toLowerCase() + "_preference"
val refValue = sharedPref.getBoolean(refKey, false)
if (refValue && element.name == remoteMessage.notification!!.title!!){
setupNotifications(remoteMessage)
}
}
}
private fun setupNotifications(remoteMessage: RemoteMessage) {
val notificationId = 10
val channelId = "stocks"
lateinit var channel:NotificationChannel
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val mBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(remoteMessage.notification!!.title!!)
.setContentText(remoteMessage.notification!!.body!!)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = applicationContext.getSystemService(NotificationManager::class.java)
val name = getString(R.string.channel_name)
val description = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
channel = NotificationChannel("stock-exchange", name, importance)
channel.description = description
notificationManager!!.createNotificationChannel(channel)
notificationManager.notify(notificationId, mBuilder.build())
} else {
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(notificationId, mBuilder.build())
}
}
}
In the code above, when a new message comes, we check if we enabled price reporting for that stock. With this information, we know whether to display the notification for it or not.
Next, open the strings.xml
file and add the following to the file:
<!-- File: app/src/main/res/values/strings.xml -->
<string name="settings">Settings</string>
<string name="channel_name">Stock-Exchange</string>
<string name="channel_description">To receive updates about stocks</string>
Next, open the AndroidManifest.xml
file and update as seen below:
<!-- File: ./app/main/AndroidManifest.xml -->
<application
[...]
>
// [...]
<service android:name=".NotificationsMessagingService">
<intent-filter android:priority="1">
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
In the AndroidManifest.xml
file, add the internet permission as seen below:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.stockexchangeapp">
<uses-permission android:name="android.permission.INTERNET"/>
[...]
</manifest>
With this, our Android application is complete. Let us now build our backend server.
Building our backend server
Now that we have completed building the application, let us build the backend of the application. We will build our backend with Node.js.
Create a new folder for your project. Navigate into the folder and create a new package.json
file, then paste the following code:
// File: ./package.json
{
"name": "stockexchangeapp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@pusher/push-notifications-server": "^1.0.0",
"body-parser": "^1.18.3",
"express": "^4.16.3",
"pusher": "^2.1.2"
}
}
This file contains the meta data for the Node application. It also contains the list of dependencies the application relies on to function properly.
Next, let’s create a configuration file that will hold all our sensitive keys. Create a file named config.js
and paste this:
// File: ./config.js
module.exports = {
appId: 'PUSHER_CHANNELS_APPID',
key: 'PUSHER_CHANNELS_KEY',
secret: 'PUSHER_CHANNELS_SECRET',
cluster: 'PUSHER_CHANNELS_CLUSTER',
secretKey: 'PUSHER_BEAMS_SECRET',
instanceId: 'PUSHER_BEAMS_INSTANCEID'
};
Replace the placeholders above with keys from your Pusher dashboard.
Finally, let’s create another file named index.js
and paste this:
// File: ./index.js
// import dependencies
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const Pusher = require('pusher');
const PushNotifications = require('@pusher/push-notifications-server');
// initialise express
const app = express();
const pusher = new Pusher(require('./config.js'));
const pushNotifications = new PushNotifications(require('./config.js'))
function handleStock(req, res, stock) {
let loopCount = 0;
let sendToPusher = setInterval(() => {
loopCount++;
const changePercent = randomIntFromInterval(-10, 10)
const currentValue = randomIntFromInterval(2000, 20000);
const stockName = (stock === 'amazon') ? 'Amazon' : 'Apple'
const price = currentValue.toString()
// Send to pusher
pusher.trigger('stock-channel', stockName, {currentValue, changePercent})
pushNotifications.publish(
['stocks'],{
fcm: {
notification: {
title: stockName,
body: `The new value for ${stockName} is: ${price}`
}
}
}).then((publishResponse) => {
console.log('Just published:', publishResponse.publishId);
});
if (loopCount === 5) {
clearInterval(sendToPusher)
}
}, 2000);
res.json({success: 200})
}
app.get('/stock/amazon', (req, res) => handleStock(req, res, 'amazon'));
app.get('/stock/apple', (req, res) => handleStock(req, res, 'apple'));
function randomIntFromInterval(min,max) {
return Math.floor(Math.random()*(max-min+1)+min);
}
const port = 5000;
app.listen(port, () => console.log(`Server is running on port ${port}`));
This code above contains the endpoints for our application. We have two endpoints, one to handle all the processes for the amazon
stock, and the other for the apple
stock. We have the handleStock
method that basically does all the work.
In the folder directory, run this to install the modules:
$ npm install
Then run the following code to start the application:
$ node index.js
Now, if you run your app, you should see something like this:
Conclusion
In this post, we have learned how to leverage the power of Pusher to create powerful engaging applications.
You can find the source code on GitHub. Feel free to clone and explore further.
21 September 2018
by Neo Ighodaro