text: This article was originally posted on my personal blog
  Please participate in this survey to voice your opinion as a developer for an upcoming article!.
  You can find the code for this tutorial here.
  Create a MongoDB Realm App
  Create a MongoDB Cluster
  Before we can create a MongoDB Realm App, we need to create a MongoDB Cluster. To do that, go to the Atlas portal. If you don't have an account or you're not already logged in, you need to do that first.
  If you're not redirected to the Projects page, click on the logo at the top left.
  Once you're on the Projects page, click on the New Project button at the right.
  Then you will be asked to enter a project name. You can name it whatever you want. After that, you'll be asked to add members, if you need to. Once done, click on Create Project.
  Once the project is created, you will be redirected to the Clusters page. Click on "Build a Cluster"
  You'll be asked to pick a cluster plan. For this tutorial, you can just choose the free plan.
  Then after that you can just click on "Create Cluster"
  After this, your cluster will take some time to deploy. You need to wait until it's created and deploy, which can take a couple of minutes.
  The next step will be to add a sample dataset to our Cluster. If you already have a dataset, you can add your own data instead.
  To start adding data, click on Collections in the Cluster you created.
  Then, click on Load a Sample Dataset.
  A popup window will open to ask for confirmation. Once you confirm, a sample dataset will be installed in your cluster. This dataset contains a bunch of useful databases and collections for different use cases.
  It will take a minute or two to finish installing the sample dataset. Once it's done, you'll see that now you have a few databases now.
  We'll only be using the
  sample_restaurants database, so you can go ahead and delete the rest by clicking on the trash icon that appears when you hover a database name.
  Now that our MongoDB Cluster is ready, let's go ahead and create a MongoDB Realm App.
  Create a MongoDB Realm App
  To go to MongoDB Realm, click on "Realm" in the tab bar next to "Atlas"
  A dialog will show to start creating MongoDB Realm App. You'll need to enter a name for the Realm Application which can be whatever you want. Then, you'll need to choose a cluster to link the Realm App to. You'll need to choose the cluster we just created. Once you do that, click on Create Realm Application.
  Next, we'll need to choose a collection from the cluster to add access to from the Realm app. To do that, click on Get Started under Add a Collection on the dashboard.
  You'll have to pick the database, which is
  sample_restaurants. Then choose a collection, which will be
  restaurants.
  Next, we need to select a permission template. Permission templates allow to easily restrict read and write access as necessary.
  In the website we're creating, all users can read all data about restaurants, and they can write reviews in their own account.
  For now, we'll just choose "Users can only read all data" from the dropdown. Once you're done, click "Add Collection." Next, click on
  neighborhoods from the sidebar and choose the same template then Add Collection.
  Every time you make changes to the Realm App, you have to deploy it for changes to take effect. To deploy the changes we just made, click on "Review Draft & Deploy" in the blue banner at the top.
  And that's it! We created a Realm App that's linked to our cluster and the collections in it. This will provide a serverless backend that allows us to retrieve and write data to our cluster easily.
  Generate Schemas
  To be able to query our collections and documents, and to be able to apply certain roles, permissions and restrictions, we need to generate Schema definitions for each of the collections. To do that, click on Schema in the sidebar.
  Then, click on Generate Schema button. This will generate the schema based on the data that's already in the collection.
  Under "Generate schema(s) for:" choose "all unconfigured collections" and for Sample type in "20" as we don't need to sample so many documents considering our data is simple. Then, click on Generate Schema.
  Once it's done, you'll see the Schema generated with all the fields and their respective types.
  Setup Authentication in Realm App
  In our Realm app, we'll be using two Authentication providers:
  - Anonymous Login: Allow the user to view all data without actually having to login.
  - Email & Password Login: Users have to login with email and password to write their reviews.
  This means that users have permission to read all data, but only write their own data.
  In the Realm Portal, click on Authentication in the sidebar. You'll see a few Authentication providers that are all disabled.
  We'll first enable "Allow users to login anonymously." Click on the edit button for this one and just toggle it on.
  Then go back to the Authentication page. We'll now click on Edit for the second one which is "Email/Password."
  First, enable the provider. Next, for "User Confirmation Method," choose "Automatically confirm users." MongoDB Realm provides a user confirmation workflow for your app, but in our case we don't need it.
  Next comes "Password Reset Method." MongoDB Realm also provides a password reset method for your users. We won't be implementing it, but because we need to enter the configuration, just enter in "Password Reset URL."
  Once you're done, click Save. Our users are now able to login with an email and password.
  The last step for setting up authentications is to allow users who are logged in with email and password to write their own reviews. To do that, go to rules in the sidebar, then choose the
  restaurantscollection, then click on "New Role" in the table.
  A popup will open. You first need to enter the role name. We'll name it "Users"
  Next, we'll need to enter "Apply When" condition, which means when should the user be considered as part of this role. We want users who are logged in with their email and password to be able to write their reviews. Enter the following:
  { "%%user.data.email": { "%exists": true } }
  Then, for the "Document-Level Permissions" choose "Insert Documents." Once you're done, click "Done Editing."
  Then, in the table click "Add Field" and type in "grades" and click the checkmark. Then check for both Read and Write for the User role. This adds the double restriction which is users are only able to write in grades, nothing else. As for Read, you can check for all fields. Then, click on the left Arrow under the "User" role name to give the User role a higher priority when matching the logged in user with the correct role. Once you're done, click on Save. The table should look like this:
  And with this done, we can now anonymous and logged in users can read all data, but only users logged in can write their own reviews.
  One last thing to do is make sure to click on Review Draft & Deploy for all the changes to take effect.
  Now, we have our MongoDB Realm app ready for integration with React. Next, we'll go over how to integrate it with React and use all the functionalities we've been setting up.
  React Setup
  In case you don't have a React project ready, run the following to create one:
  npm create-react-app restaurants-reviews cd restaurants-reviews
  Next, we'll install the MongoDB Realm Web SDK:
  npm install --save realm-web
  That's all we need to start using Realm with React. We'll also install React Bootstrapto make styling easier:
  npm install react-bootstrap bootstrap@4.6.0
  and React Routerto add different pages:
  npm install react-router-dom
  Let's first start by modifying creating the
  Homecomponent which will be the home page. The home page will just show a list of restaurants and their ratings.
  Create the file
  src/pages/Home.js and the following basic component:
  function Home () { return ( <div></div> ) } export default Home
  For now, it's just a component that shows a
  <div>element. We need to make it show a list of restaurants instead.
  Since we're going to fetch the restaurants from our MongoDB Realm App later on, we'll be using a state for restaurants:
  function Home () { const [restaurants, setRestaurants] = useState([]) //... }
  Then, we'll loop over the restaurants and display them:
  <div className="mt-3"> {restaurants.map((restaurant) => ( <RestaurantCard key={restaurant._id} restaurant={restaurant} /> )) } </div>
  Let's create
  src/components/RestaurantCard.js with the following content:
  import { Badge } from 'react-bootstrap' import Card from 'react-bootstrap/Card' function RestaurantCard ({restaurant}) { //get average of grades let sum = 0; restaurant.grades.forEach(element => { sum += element.score }); const avg = Math.round(sum / (restaurant.grades.length)) return ( <Card className="m-3"> <Card.Body> <Card.Title>{restaurant.name} <Badge variant="warning">{avg}</Badge></Card.Title> </Card.Body> </Card> ) } export default RestaurantCard
  We're first calculating the average grade for the restaurant, then we're just displaying a card with the restaurant's name and the average grade.
  So, our home page should show a list of cards with restaurant names and grades. What's left is to actually link it to the data in our Realm app.
  Let's go over how to connect to Realm Apps first. You first need an App ID. You'll find the App ID on the Dashboard or you can click the copy icon in the sidebar.
  Then, create a
  .envfile in the root directory with the following content:
  REACT_APP_REALM_APP_ID=<YOUR_APP_ID>
  Make sure to replace
  <YOUR_APP_ID>with the App ID you copied. This helps changing App IDs easy just by changing it in
  .env.
  Back to
  src/pages/Home.js, we first need to import the SDK:
  import * as Realm from 'realm-web'
  Then, initialize the Realm App:
  const app = new Realm.App({id: process.env.REACT_APP_REALM_APP_ID})
  Notice we're using the environment variable we set earlier.
  Then inside the
  Homecomponent, we'll use
  useEffectto fetch the data on first render:
  useEffect(() => { }, [])
  Inside, we'll log in the user anonymously and then fetch the
  restaurantsdata. Since earlier we allowed all users to read all data, even users who aren't logged in can read the data.
  To login a user anonymously:
  useEffect(() => { async function getData () { const user = await app.logIn(Realm.Credentials.anonymous()) } getData(); }, [])
  After that, we'll get the MongoDB client for our collection using the user we just logged in:
  const client = app.currentUser.mongoClient('mongodb-atlas')
  As you can tell, by using
  app.currentUserwe're referring to the currently logged-in user. Then, we get the MongoDB client for that user. This means that the access to the data is restricted based on the user that is logged in, just like we defined above.
  Next step would be to get the restaurants from
  restaurantscollection and set the restaurants state:
  const rests = client.db('sample_restaurants').collection('restaurants') setRestaurants((await rests.find()).slice(0, 10))
  And with this, our code will display the restaurants once we retrieve them from MongoDB Realm App. We'll add also some loading to make sure we can see the loading:
  const [restaurants, setRestaurants] = useState([]) const [loading, setLoading] = useState(true) useEffect(() => { async function getData () { //... const rests = client.db('sample_restaurants').collection('restaurants') setRestaurants((await rests.find()).slice(0, 10)) setLoading(false) } if (loading) { getData(); } }, [loading]) return ( <div className="mt-3"> {loading && ( <div className="text-center"> <Loading /> </div> )} {restaurants.map((restaurant) => ( <RestaurantCard key={restaurant._id} restaurant={restaurant} /> ))} </div> );
  We'll also create
  src/components/Loading.js:
  import { Spinner } from "react-bootstrap"; function Loading () { return ( <Spinner animation="border" variant="primary"> <span className="sr-only">Loading...</span> </Spinner> ) } export default Loading
  And that's it! The home page now is ready. Only thing left is to use
  react-router in
  src/App.js to ensure multiple pages:
  import { BrowserRouter as Router, Switch, Route, } from "react-router-dom" import Home from "./pages/Home" import 'bootstrap/dist/css/bootstrap.min.css' import { Container } from "react-bootstrap" function App() { return ( <Router> <Container> <Switch> <Route path="/" component={Home} /> </Switch> </Container> </Router> ); } export default App;
  Let's now run the server:
  npm start
  After some loading, you'll see the restaurants with their average grades:
  Next, we'll create authentication forms to allow users to create accounts and login.
  Authentication Page
  Since the user just needs to enter the email and password to Sign up and Log in, we'll just create one Authentication component that changes behavior based on
  typeprop that determines whether the form is being used to create an account or login.
  Before we start, let's install Formik and Yupto make creating a form easier:
  npm i formik yup
  Then, create
  src/pages/Authentication.js with the following content:
  import { Formik } from 'formik' import { Button, Form } from 'react-bootstrap' import * as yup from 'yup' import { useState } from 'react' import Loading from '../components/Loading' const userSchema = yup.object().shape({ email: yup.string().email().required(), password: yup.string().required().min(8) }) function Authentication ({type = 'login'}) { const [loading, setLoading] = useState(false) async function submitHandler (values) { setLoading(true) //TODO handle login/create } return ( <Formik initialValues={{ email: '', password: '' }} validationSchema={userSchema} onSubmit={submitHandler} > {({errors, touched, handleSubmit, values, handleChange}) => ( <Form noValidate onSubmit={handleSubmit}> {loading && <Loading />} {!loading && (<div> <h1>{type === 'login' ? 'Login' : 'Sign Up'}</h1> <Form.Row> <Form.Label>Email</Form.Label> <Form.Control <Button variant="primary" type="submit">Submit</Button> </div> </div>)} </Form> )} </Formik> ) } export default Authentication
  We're using Formik to create a form that has two fields, email, and password. We're also using
  yup it to create a validation schema. On form submit if everything is valid, the function
  submitHandler will run which accepted the
  values object.
  Inside
  submitHandler, we need to check the
  type prop. If it equals
  create, then we need to create a new user and login the user after that. If it's
  login then we just need to login the user.
  But before we start, as it will be a hassle to use the
  user object, the MongoDB
  client, and the Realm
  app, let's create a Context that allows us to use the same data throughout the components easily.
  Create
  src/MongoContext.js with the following content:
  import React from 'react' const MongoContext = React.createContext({ app: null, client: null, user: null, setApp: () => {}, setClient: () => {}, setUser: () => {} }) export default MongoContext
  We're creating a Context that has the objects
  app,
  client, and
  userand their setter functions
  setApp,
  setClient and
  setUser.
  Next, let's move declarations and intialization of
  user,
  app and
  client that we did in
  Home to
  App:
  const [client, setClient] = useState(null) const [user, setUser] = useState(null) const [app, setApp] = useState(new Realm.App({id: process.env.REACT_APP_REALM_APP_ID})) useEffect(() => { async function init () { if (!user) { setUser(app.currentUser ? app.currentUser : await app.logIn(Realm.Credentials.anonymous())) } if (!client) { setClient(app.currentUser.mongoClient('mongodb-atlas')) } } init(); }, [app, client, user])
  As you can see, we're creating states for each of them and setting them in
  App. Then, we'll wrap our routes with
  MongoContext.Provider:
  return ( <Router> <MongoContext.Provider value={{app, client, user, setClient, setUser, setApp}}> <Container> <Switch> <Route path="/" component={Home} /> </Switch> </Container> </MongoContext.Provider> </Router> );
  Now, we need to pass the context to each of the components using
  MongoContext.Consumer. To avoid repetition, let's create a function inside
  App that does this:
  function renderComponent (Component, additionalProps = {}) { return <MongoContext.Consumer>{(mongoContext) => <Component mongoContext={mongoContext} {...additionalProps} />}</MongoContext.Consumer> }
  This will wrap a component with
  MongoContext.Consumer then pass it the
  mongoContext prop, which will hold all the objects we're storing in the context and their setters.
  Back to the return statement in
  App, instead of passing
  component={Home} to the route, we'll pass a render function:
  <Route path="/" render={() => renderComponent(Home)} />
  Now, we have a context that holds all the objects and their setters, then we're passing it to a route's component.
  Let's make changes in
  src/pages/Home.js where instead of initializing
  app,
  user, and
  client, it'll receive them as props:
  import { useEffect, useState } from 'react' import RestaurantCard from '../components/RestaurantCard' import Loading from '../components/Loading' function Home ({mongoContext: {client, user}}) { const [restaurants, setRestaurants] = useState([]) const [loading, setLoading] = useState(true) useEffect(() => { async function getData () { const rests = client.db('sample_restaurants').collection('restaurants') setRestaurants((await rests.find()).slice(0, 10)) setLoading(false) } if (loading && user && client) { getData() } }, [client, loading, user]) return ( <div className="mt-3"> {loading && ( <div className="text-center"> <Loading /> </div> )} {restaurants.map((restaurant) => ( <RestaurantCard key={restaurant._id} restaurant={restaurant} /> ))} </div> ) } export default Home
  If you try running the server and going to the website, you'll see that everything is working perfectly as before.
  Back to the
  Authentication component, we'll now pass it the
  mongoContext prop:
  function Authentication ({mongoContext: {app, user, setUser}, type = 'login'})
  Inside
  submitHandler, if the
  type is create we'll register a new user, then for both types we'll login the user with their credentials:
  async function submitHandler (values) { setLoading(true) if (type === 'create') { //create await app.emailPasswordAuth.registerUser(values.email, values.password); } //login user and redirect to home const credentials = Realm.Credentials.emailPassword(values.email, values.password); setUser(await app.logIn(credentials)) setLoading(false) }
  As you can see, we're using
  appand
  setUserfrom the context. When we use
  setUser, the user will be updated for all components using the context.
  Last thing we need to add is to redirect the user if they're already logged in. To do that, first create
  src/utils.jswhich will hold the function
  isAnonto determine whether the user is logged in:
  module.exports = { isAnon: function (user) { return !user || user.identities[0].providerType === 'anon-user' } }
  Where
  providerType will be
  anon-user if the user is not logged in.
  Then, inside
  Authentication, we'll get a
  history instance using
  useHistory from
  react-router:
  const history = useHistory()
  Then, whenever the
  userin the context changes, we'll check if the user is logged in and then we'll redirect to home if true.
  useEffect(() => { if (!isAnon(user)) { history.push('/') } }, [history, user])
  Our
  Authentication component is now done! Let's add
  src/App.js:
  <Route path="/signup" render={() => renderComponent(Authentication, {type: 'create'})} /> <Route path="/signin" render={() => renderComponent(Authentication)} /> <Route path="/" render={() => renderComponent(Home)} />
  We'll also need a
  LogOut page so create
  src/pages/Logout.js with the following content:
  import { useEffect } from "react" import Loading from "../components/Loading" import * as Realm from 'realm-web' import { useHistory } from "react-router" import { isAnon } from "../utils" function LogOut ({mongoContext: {app, setUser, setClient}}) { const history = useHistory() if (isAnon()) { history.push('/') } useEffect(() => { async function logout () { await app.currentUser.logOut() //login anon user setUser(await app.logIn(Realm.Credentials.anonymous())) //set new client setClient(app.currentUser.mongoClient('mongodb-atlas')) } logout() }, [app, setClient, setUser]) return ( <Loading /> ) } export default LogOut
  We're first checking if the user is already not logged in and we're redirecting them to homepage if that's the case. Then, we're displaying the loading component and inside
  useEffect we're logging the user out using:
  await app.currentUser.logOut()
  After that, we're setting the user as anonymous user again and reinitializing the MongoDB Client:
  //login anon user setUser(await app.logIn(Realm.Credentials.anonymous())) //set new client setClient(app.currentUser.mongoClient('mongodb-atlas'))
  And with that, we have our log out page. We just need to add it to the routes in
  src/App.js:
  <Route path="/signup" render={() => renderComponent(Authentication, {type: 'create'})} /> <Route path="/signin" render={() => renderComponent(Authentication)} /> <Route path="/logout" render={() => renderComponent(LogOut)} /> <Route path="/" render={() => renderComponent(Home)} />
  Lastly, we'll create a
  src/components/Navigation.js component to show a navigation bar with our links:
  import { Nav, Navbar } from "react-bootstrap" import { Link } from "react-router-dom" import { isAnon } from "../utils" function Navigation ({user}) { const loggedIn = !isAnon(user) return ( <Navbar bg="light" expand="lg"> <Navbar.BrandRestaurant Reviews</Navbar.Brand> <Navbar.Toggle <Navbar.Collapse <Nav className="mr-auto"> <Link to="/" className="mx-2">Home</Link> {!loggedIn && <Link to="/signup" className="mx-2">Sign Up</Link>} {!loggedIn && <Link to="/signin" className="mx-2">Sign In</Link>} {loggedIn && <Link to="/logout" className="mx-2">Log out</Link>} </Nav> </Navbar.Collapse> </Navbar> ) } export default Navigation
  We're passing it the user prop, then we're checking if the user is logged in, we'll show the log out link. If not, we'll show the sign in and sign up links.
  Add the
  Navigation component in
  src/App.js:
  return ( <Router> <Navigation user={user} /> <MongoContext.Provider value={{app, client, user, setClient, setUser, setApp}}> //... )
  We're done! Run the server if you aren't already:
  npm start
  You'll see we have a navigation bar that shows the sign in and sign up links when we're not logged in. Try to sign up, log out, sign it, do different things. To check if the users are actually being created, on the Realm platform, click on "App Users" in the sidebar. You'll see a list of users with the type of user either Anonymous or Email/Password.
  Conclusion
  In the next part, we'll add a form for users to create their own reviews. We'll be able to test the permissions we added earlier and see how the users are restricted based on the roles we create.
  If you would like to connect and talk more about this article or programming in general, you can find me on my twitter account @shahednasserr
  Discussion (1)
  A great post my man 💯💯💯
  Would love a second part to this.