Navigation

PlateSpace Tutorial (Web App)

This tutorial demonstrates how to use MongoDB Stitch to integrate the following services for a Web application:

  • Facebook user authentication services
  • Email/password authentication
  • Anonymous authentication
  • MongoDB Service

PlateSpace is a sample native application built around a social, mobile, local restaurant search concept. The user will see a list of restaurants, sorted by their physical proximity to the user’s location. The user can browse through the restaurants, in either map view or a list view, and use advanced filtering and keywords to search for more entries.

All users can view restaurant details, but only users who have logged in through Facebook or with Stitch named users credentials are able to add/edit ratings and reviews to restaurants.

Prerequisites

For this app, you need the following:

  • A MongoDB Atlas cluster using MongoDB version 3.4+. The tutorial uses an Atlas Free Tier cluster.
  • A developer account with Facebook for user authentication services.
  • A developer account with Yelp to load restaurant data.
  • Node v6 or above to run the application.
  • Yarn

Procedure

A. Create a MongoDB Stitch App

Estimated Time to Complete: ~3 minutes

1

Log into Atlas.

To use MongoDB Stitch, you must be logged into MongoDB Atlas. If you do not have an Atlas account, follow the instructions in the Atlas documentation to create an account.

2

Create an Atlas cluster.

If you do not already have an Atlas cluster for use with MongoDB Stitch, create a cluster.

Important

You must deploy the Atlas cluster with MongoDB version 3.4 or greater.

Atlas provides a Free Tier M0 replica set as well as paid M10+ clusters. Free Tier deployments have restrictions as compared to paid M10+ deployments but will work for the purposes of this tutorial. For complete documentation on these restrictions, see Atlas M0 (Free Tier) Limitations.

3

Add a MongoDB Stitch application.

  1. In Atlas, click Stitch Apps in the left-hand navigation.
  2. Click the Create New Application. The name can only contain ASCII letters, numbers, underscores, and hyphens.
  3. Select your Atlas cluster for your MongoDB service.
  4. Click Create.

Upon creation of your app, you will be redirected to the MongoDB Stitch console. Your app is created with a MongoDB service named mongodb-atlas.

B. Define Pipelines

Estimated Time to Complete: ~5 minutes

1

Define a pipeline named geoNear.

Define a pipeline that reads from the platespace.restaurants collection to find the nearest restaurants given the input arguments.

  1. Click Pipelines and then click New Pipeline.

  2. Enter the following properties for the pipeline:

    Name geoNear
    Private Leave unselected. If selected, the pipeline is inaccessible by a client and is only accessible in other MongoDB Stitch definitions, such as webhooks and other pipelines.
    Skip Rules Leave unselected. If selected, rules do not apply to the service actions in the pipeline stages.
    Can Evaluate Leave as {}. The Can Evaluate condition determines whether the pipeline can be run. An empty document {} always evaluates to true, indicating that the pipeline can always be run.
    Parameters

    Add the following parameters:

    • longitude
    • latitude
    • query
    • limit
    • minDistance

    Select all except the query parameter as Required. Clients calling the pipeline must include the required parameters.

    Note

    In the pipeline stage, you bind parameters to stage-defined variables to reference them in the stage. See the stage edit step below for details.

  3. For the output type, select Array

  4. For the displayed stage, edit the following:

    Service Select mongodb-atlas
    Action

    Select aggregate

    In the text box that appears, enter the following aggregation:

    {
      "database": "platespace",
      "collection": "restaurants",
      "pipeline": [
        {
          "$geoNear": {
            "near": {
              "coordinates": [
                "%%vars.longitude",
                "%%vars.latitude"
              ],
              "type": "Point"
            },
            "query": "%%vars.query",
            "limit": "%%vars.limit",
            "minDistance": "%%vars.minDistance",
            "distanceField": "dist",
            "spherical": true
          }
        }
      ]
    }
    
    Bind data to %%vars

    You cannot directly reference the parameters in the stage. Instead, define variables that reference the parameters, and reference the variables in the stage.

    Enable Bind data to %%vars and copy the following in the Bind data to %%vars text box:

    {
      "longitude": "%%args.longitude",
      "latitude": "%%args.latitude",
      "query": "%%args.query",
      "limit": "%%args.limit",
      "minDistance": "%%args.minDistance"
    }
    

    Click Done

  5. Click Save.

See Named Pipelines for more information.

2

Define a pipeline named aggregateRestaurant.

Define a pipeline that calculates the number of reviews and the average rating for a specified restaurant.

  1. Click Pipelines and then click New Pipeline.

  2. Enter the following properties for the pipeline.

    Name aggregateRestaurant
    Private Leave unselected. If selected, the pipeline is inaccessible by a client and is only accessible in other MongoDB Stitch definitions, such as webhooks and other pipelines.
    Skip Rules Leave unselected. If selected, rules do not apply to the service actions in the pipeline stages.
    Can Evaluate Leave as {}. The Can Evaluate condition determines whether the pipeline can be run. An empty document {} always evaluates to true, indicating that the pipeline can always be run.
    Parameters

    Add the following parameter:

    • restaurantId

    Set the parameter as required. Clients calling the pipeline must include this parameter.

    Note

    In the pipeline stage, you bind parameters to stage-defined variables to reference them in the stage. See the edit stage step below for details.

  3. For the output type, ensure that Single Document is selected.

  4. For the displayed stage, edit the following:

    Service Select mongodb-atlas.
    Action

    Select aggregate.

    In the text box that appears, enter the following aggregation:

    {
      "database": "platespace",
      "collection": "reviewsRatings",
      "pipeline": [
        {
          "$match": {
            "restaurantId": "%%vars.restaurantId",
            "rate": {
              "$exists": true
            }
          }
        },
        {
          "$group": {
            "_id": "$restaurantId",
            "average": {
              "$avg": "$rate"
            },
            "count": {
              "$sum": 1
            }
          }
        }
      ]
    }
    
    Bind data to %%vars

    You cannot directly reference the parameter restaurantId in the stage. Instead, define a variable that references the parameter, and then reference the variable in the stage.

    Enable Bind data to %%vars and copy the following in the Bind data to %%vars text box:

    {
      "restaurantId": "%%args.restaurantId"
    }
    

    Click Done.

  5. Click Save.

See Named Pipelines for more information.

3

Define a pipeline named updateRatings.

Define a pipeline that updates the platespace.restaurants collection with the number of reviews and the average rating for a specified restaurant.

  1. Click Pipelines and then click New Pipeline.

  2. Enter the following properties for the pipeline.

    Name updateRatings
    Private Leave unselected. If selected, the pipeline is inaccessible by a client and is only accessible in other MongoDB Stitch definitions, such as webhooks and other pipelines.
    Skip Rules Enable skip rules. In a later step, you will set up write rules that prevents write operations to this collection. As such, in order for the pipeline to bypass this rule and write to the collection, you need to skip rules.
    Can Evaluate Leave as {}. The Can Evaluate condition determines whether the pipeline can be run. An empty document {} always evaluates to true, indicating that the pipeline can always be run.
    Parameters

    Add the following parameter:

    • restaurantId

    Select the parameter as required. Clients calling the pipeline must include this parameter.

    Note

    In the pipeline stage, you bind parameters to stage-defined variables to reference them in the stage. See the edit stage step below for details.

  3. For the output type, ensure that Single Document is selected.

  4. For the displayed stage, edit the following:

    Service Select mongodb-atlas.
    Action

    Select update.

    In the text box that appears, enter the following aggregation:

    {
      "database": "platespace",
      "collection": "restaurants",
      "query": {
        "_id": "%%vars.restaurantId"
      },
      "update": {
        "$set": {
          "averageRating": "%%vars.pipelineResult.average",
          "numberOfRates": "%%vars.pipelineResult.count"
        }
      },
      "upsert": false,
      "multi": false
    }
    
    Bind data to %%vars

    You cannot directly reference the parameter restaurantId in the stage. Instead, define a variable that references the parameter, and then reference the variable in the stage. You can also reference other named pipelines using the %pipeline expansion.

    Enable Bind data to %%vars and copy the following in the Bind data to %%vars text box:

    {
      "restaurantId": "%%args.restaurantId",
      "pipelineResult": {
        "%pipeline": {
          "name": "aggregateRestaurant",
          "args": {
            "restaurantId": "%%args.restaurantId"
          }
        }
      }
    }
    

    Click Done.

  5. Save.

See Named Pipelines for more information.

C. Define Rules

Estimated Time to Complete: ~5 minutes

1

Add rules for each collection the MongoDB service must access.

  1. Click on the MongoDB service mongodb-atlas.

  2. In the Rules tab, add the following namespaces:

    1. Database: platespace

      Collection: restaurants

    2. Database: platespace

      Collection: reviewsRatings

Note

You must add rules for each collection the MongoDB service accesses.

See MongoDB Service Rules for more information.

2

Review the rules for platespace.restaurants collection.

Click on platespace.restaurants to view the read rule, write rule, the validation rule, and the filter rule.

MongoDB Authorization

MongoDB Stitch rules do not override the read and write access (i.e. authorization) that may have been set up separately in MongoDB. That is, MongoDB Stitch rules determine whether the fields are readable or writable; not whether the client has authorization to read or write to a particular database or collection.

Similarly, MongoDB Stitch validation rules do not override document validation rules set up separately in MongoDB.

  1. Review the read rule for the top-level document of platespace.restaurants.

    Modify the read rule for the top-level document to the following and click Save:

    { }
    

    The new rule specifies that all the fields in the documents are always readable. For more information on MongoDB read rules, see MongoDB Service Read Rule.

  2. Review the write rule for the top-level document of platespace.restaurants.

    Modify the write rule for the top-level document to the following and click Save:

    {
       "%%true": false
    }
    

    The new rule specifies that no fields are writable . For more information on MongoDB write rules, see MongoDB Service Write Rule.

  3. Delete the owner_id field.

    For this collection, the owner_id field does not exist. Delete the owner_id field by hovering over it and clicking the x on the right-hand side.

  4. Allow all other fields for platespace.restaurants.

    By default, Allow All Other Fields flag is enabled so that any unlisted fields are readable/writeable as long as the document meets the read/write rules. Alternatively, you can explicitly define all the fields for this collection and disable Allow All Other Fields to control the shape of the documents.

  5. Review filters for platespace.restaurants.

    1. Click on the Filters.

    2. Delete the default filter.

      For more information on filters, see MongoDB Service Filters.

  6. Save your changes.

3

Review the rules for platespace.reviewsRatings collection.

Click on platespace.reviewsRatings to view the read rule, write rule, the validation rule, and the filter rule.

MongoDB Authorization

MongoDB Stitch rules do not override the read and write access (i.e. authorization) that may have been set up separately in MongoDB. That is, MongoDB Stitch rules determine whether the fields are readable or writable; not whether the client has authorization to read or write to a particular database or collection.

Similarly, MongoDB Stitch validation rules do not override document validation rules set up separately in MongoDB.

  1. Review the read rule for the top-level document of platespace.reviewsRatings.

    Modify the read rule for the top-level document to the following and click Save:

    { }
    

    The new rule specifies that all the fields in the documents are always readable. For more information on MongoDB read rules, see MongoDB Service Read Rule.

  2. Review the write rule for the top-level document of platespace.reviewsRatings.

    Leave the rule as is.

    {
       "owner_id": "%%user.id"
    }
    

    The rule specifies that all fields in the documents are writable if the id of the current user matches the owner_id field of the document. For more information on MongoDB write rules, see MongoDB Service Write Rule.

  3. Review the validation rule on the owner_id field of platespace.reviewsRatings. To view the rule, click on the owner_id field.

    {
      "%or": [
        { "%%prev": "%%user.id" },
        { "%%prev": { "%exists": false } }
      ]
    }
    

    The rule specifies that a write operation is valid if either of the following conditions are true:

    • The operation modifies a document where the value of the owner_id field matches the %%user.id.
    • The operation modifies a document that previously did not have an owner_id field, such as inserting a new document.

    For more information on MongoDB validation rules, see MongoDB Service Validation.

  4. Allow all other fields.

    By default, Allow All Other Fields flag is enabled so that any unlisted fields are readable/writeable as long as the document meets the read/write rules. Alternatively, you can explicitly define all the fields for this collection and disable Allow All Other Fields to control the shape of the documents.

  5. Review the filters for platespace.reviewsRatings.

    1. Click on Filters.

      By default, the collection has the following filter rule:

      When Match Expression
      { "%%true": true } { "owner_id": "%%user.id" }

      This filter indicates that when %%true equals true (i.e. always), apply the following filter { "owner_id": "%%user.id" } to the read and write operations. This filter is in addition to the query predicates for read and write operations.

    2. Delete the default filter.

      For more information on filters, see MongoDB Service Filters.

  6. Save your changes.

D. Set Up Facebook Authentication

Estimated Time to Complete: ~10 minutes

1

Set up an app in Facebook.

  1. Log in to your Facebook Developer account. If you do not have a Facebook developer account or even a Facebook account, see Facebook - Register and Configure an App.

  2. Add a new Facebook application.

  3. Once created, your application view opens to Product Setup. If not, go to your newly added app and click Add Product.

  4. In Product Setup, for Facebook Login, click Set Up. This brings up the Quickstart for Facebook Login. Skip the Quickstart.

  5. Instead, in the left-hand navigation menu, under Products > Facebook Login, click Settings.

  6. Under Valid OAuth redirect URIs, add the following entry:

    https://stitch.mongodb.com/api/client/v1.0/auth/callback
    
  7. Save changes.

See Facebook Authentication for more information.

2

Retrieve the App ID and App Secret for your Facebook app.

  1. Click Settings for your Facebook application (i.e. not the Settings under Facebook Login).
  2. Note the App ID and App Secret. You will use the information in the Authentication section of the MongoDB Stitch Admin Console.
3

Configure MongoDB Stitch for Facebook Authentication.

  1. Click Authentication in the left side navigation in the MongoDB Stitch console.

    The Authentication page displays the Authentication Providers information.

  2. For Facebook, click the Edit button.

  3. In the Edit Provider dialog,

    • Switch Facebook to enabled.

    • Enter your new Facebook App ID in the Client ID field and Facebook App Secret in Client Secret field.

    • In the Redirect URIs, add the following:

      Important

      Enter the URI exactly, including the trailing slash /.

      http://localhost:3000/
      
    • In the Metadata Fields, select name and email.

  4. Click Save.

E. Set Up Anonymous Authentication

Estimated Time to Complete: ~1 minutes

1

Click Authentication in the left side navigation in the MongoDB Stitch console.

The Authentication page displays the Authentication Providers information.

2

Enable Anonymous Authentication.

  1. For Allow users to log in anonymously, click the Edit button.
  2. Switch Allow users to log in anonymously to enabled.
  3. Close the dialog.

F. Download the PlateSpace Application Source

Estimated Time to Complete: ~2 minutes

Download from the MongoDB Stitch GitHub example repository and unzip. The source code for the PlateSpace application can be found in the PlateSpace directory.

G. Populate the restaurants Collection from Yelp.

Estimated Time to Complete: ~12 minutes

1

Create a new Yelp app.

  1. Log in to your Yelp developer account.
  2. Create a new Yelp app.
2

Get an OAuth token for your Yelp app.

To get your auth token, issue the following POST operation, substituting <CLIENT_ID> with your client id and <CLIENT_SECRET> with your client secret:

curl -X POST https://api.yelp.com/oauth2/token -d 'grant_type=client_credentials&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>'

For more information, see https://www.yelp.com/developers/documentation/v3/authentication

3

Edit the config.js file with your Yelp token.

  1. From the downloaded examples directory, navigate to the PlateSpace/ETL folder.

  2. Edit the config.js file:

    For YELP_AUTH_TOKEN, enter "Bearer <YOUR_YELP_TOKEN>" where <YOUR_YELP_TOKEN> is the auth token for your app.

4

Edit the config.js file with your Atlas cluster connection info.

  1. Go to Atlas, and click on Clusters.

  2. For your cluster linked to the MongoDB Stitch app, click Connect to view the Connect dialog.

  3. Click Connect your application and copy the URI Connection string.

  4. Edit the config.js file:

    For MONGODB_ATLAS_URI, enter the connection string to your Atlas cluster.

    Substitute <PASSWORD> for your password and test database with the target database platespace.

5

Add IP Address to your Atlas cluster’s IP Whitelist.

Add to your Atlas cluster’s IP Whitelist the IP address from which you will run the load script.

  1. From the Connect dialog (see previous step), you can add to the IP Whistelist.
  2. Add the IP address from which you will run the load script.
6

Load data into restaurants collection.

  1. Install dependencies for the script.

    yarn
    
  2. Run the load script to populate the platespace.restaurants collection:

    node index.js
    

If you wish to delete the collection, you can run node clearRestCollection.js.

H. Set Up and Run PlateSpace App

Estimated Time to Complete: ~11 minutes

1

Go to the PlateSpace web app directory.

Navigate to the downloaded example directory and go to the PlateSpace/Web directory.

2

Ensure MongoDB Stitch SDK library is a dependency for the project.

The MongoDB Stitch SDK library has already been added to the package.json file as a dependency:

"mongodb-stitch": "0.0.16"

Additional depencies used by the app are listed in the file.

3

Update the application configuration with your MongoDB Stitch App Id.

In the /src/config.js file, replace <YOUR_STITCH_APP_ID> with your MongoDB Stitch app ID:

module.exports.STITCH_APP_ID = '<YOUR_STITCH_APP_ID>';
module.exports.MONGODB_SERVICE_NAME = 'mongodb-atlas';
module.exports.DB_NAME = 'platespace';
module.exports.STITCH_ENDPOINT= 'https://stitch.mongodb.com';

You can find your App ID in the Clients view in the MongoDB Stitch console.

4

Review code to connect to MongoDB Stitch.

Use the StitchClient class to connect to your MongoDB Stitch application.

The src/mongodb-manager/mongodb-manager.js file has the following code:

import { StitchClient, builtins } from 'mongodb-stitch';

const config = require('./../config.js');

//...
const stitchClient = new StitchClient(config.STITCH_APP_ID, options);
5

Review code to authenticate application users.

StitchClient provides various functions to authenticate. For the three used in this tutorial:

  • StitchClient.login() for anonymous login,
  • stitchClient.login(email, password) for email/password login and
  • StitchClient.authenticate('facebook') for Facebook Login.

The mongodb-manager.js file has the following code:

stitchClient.login();
//...
stitchClient.login(email, password)
//...
stitchClient.authenticate('facebook');
6

Review code to access the MongoDB service and collections.

To access MongoDB database and collection using the MongoDB service, use:

For example, in the project, the mongodb-manager.js file has the following code:

const COLLECTIONS = {
  RESTAURANTS: 'restaurants',
  REVIEWS_RATINGS: 'reviewsRatings'
};

// ...

const db = stitchClient.service('mongodb', config.MONGODB_SERVICE_NAME).db(config.DB_NAME);
const restaurants = db.collection(COLLECTIONS.RESTAURANTS);
const reviewsRatings = db.collection(COLLECTIONS.REVIEWS_RATINGS);

The /src/config.js file contains the following values for the MONGODB_SERVICE_NAME and DB_NAME:

module.exports.MONGODB_SERVICE_NAME = 'mongodb-atlas';
module.exports.DB_NAME = 'platespace';
7

Review code to execute pipelines.

To execute pipelines, use StitchClient.executePipeline().

For example, the mongodb-manager.js contains code to execute a pipeline to find nearby restaurants:

function geoNear(latitude, longitude, query = {}, limit, minDistance = 0) {
  return stitchClient.executePipeline([
    builtins.namedPipeline('geoNear', {
      latitude: latitude,
      longitude: longitude,
      minDistance: new Double(minDistance),
      query: query,
      limit: limit
    })
  ]);
}
8

Review code to query a collection.

The Collection provides various CRUD function for operating on the collection.

For example, the mongodb-manager.js contains the following code that uses the Collection.find() function to query the restaurants collection for a specific restaurant:

return restaurants
  .find({ _id: new ObjectID(restaurantId) })
  .then(data => data.map(convertToRestaurantModel))
  .then(firstOrUndefined);

where restaurant was initialized in an earlier step.

9

Run the PlateSpace Web application.

Go to the PlateSpace Web application directory.

To run using npm
  1. Install dependencies:

    npm install
    
  2. Start a development server locally and launch the app:

    npm start
    

To create a production build, run npm run build to create a build folder with the site’s static assets.

- or-

To run using yarn
  1. Install dependencies:

    yarn install
    
  2. Start a development server locally and launch the app:

    yarn start
    

To create a production build, run yarn build to create a build folder with the site’s static assets.

Collection Information

Each document in the platespace.restaurants collection has the form:

{
   "_id" : <ObjectId>,
   "name" : <String>,
   "address" : <String>,
   "phone" : <String>,
   "Image_url" : <String>,
   "website" : <String>,
   "averageRating" : <Double>,
   "numberOfRates" : <Double>,
   "openingHours" :  { "end" : <String>, "start" : <String> },
   "attributes" : {
      "veganFriendly" : <Boolean>,
      "openOnWeekends" : <Boolean>,
      "hasParking" : <Boolean>,
      "hasWifi" : <Boolean>
   },
   "location" : {
      "coordinates" : [ "longitude" : <Double>, "latitude" : <Double> ],
      "type" : "Point"
   }
}

Note

openingHours and attributes are custom fields added by our sample script to load data and not from Yelp. The field values are generated with dummy data.

Each document in the platespace.reviewsRatings collection has the form:

{
   "_id" : <ObjectId>,
   "owner_id" : <string>,
   "restaurantId" : <ObjectId>,
   "nameOfCommenter" : <String>,
   "comment": <String>,
   "rate" : <Integer>,
   "dateOfComment" : <Date>
}

Additional Information