In this module, you use your application that you’ve deployed. Before you do that, let’s do a quick recap of the AWS components you’re using in your application:

Let’s see how each of these pieces tie together. In the following steps, you walk through your different application endpoints using these components.

First, you start with a Registration endpoint, where a new user signs up and creates their account. After you register the user, you view the user's details with the FetchUser endpoint.

Second, you use a Login endpoint where a user can use a client (such as a web application or a mobile app) to authenticate and receive an ID token.

Third, you use a FetchUserRecommendations endpoint to find users you should follow.

Finally, you use the FollowUser endpoint to start following another user.

Time to Complete Module: 15 Minutes


  • Step 1. Register a new user

    The first workflow you review is the Registration endpoint. At this endpoint, a new user signs up by providing login information, like username and password. Additionally, the new user can indicate some interests when they register. This helps provide more relevant recommendations when the user is new.

    As you look at these endpoints, the relevant snippets of code that are invoked by the endpoint are shown in this guide.

    Your registration endpoint is available by making a POST request to the /users endpoint. You can see the logic for this endpoint in the application/app.js file about halfway down:

    // Create user
    app.post('/users', wrapAsync(async (req, res) => {
      const validated = validateCreateUser(req.body)
      if (!validated.valid) {
        throw new Error(validated.message)
      }
      await createCognitoUser(req.body.username, req.body.password, req.body.email)
      const user = await createUser(req.body.username, req.body.interests)
      res.json(user)
    }))
    

    Your handler first validates the incoming payload body on the request. If that succeeds, the handler creates a user in Amazon Cognito and then creates a user in your Neptune database using the createUser function.

    You already reviewed the createCognitoUser function in the Amazon Cognito module, so we won’t recap it here. Let’s look at the createUser function, which can be found in application/data/createUser.js.

    // application/data/createUser.js
    const { g } = require('./utils')
    
    const createUser = async (username, interests) => {
      const user = await g.addV('User').property('username', username).next()
      const edgePromises = interests.map((interest) => {
        return g.V()
          .has('Interest', 'interest', interest)
          .as('interest')
          .V(user.value.id)
          .addE('InterestedIn').to('interest')
          .next()
      })
      await Promise.all(edgePromises)
      return {
        username,
        interests
      }
    }
    
    module.exports = createUser
    

    The createUser function takes a username and an array of interests. It creates the user in your Neptune database, then iterates over the interests to add InterestedIn edges from the user to the interest.

    Try invoking your Registration endpoint to create a new user. Run the following command in your terminal:

    curl -X POST ${BASE_URL}/users \
      -H 'Content-Type: application/json' \
      -d '{
    	"username": "thefriendlyuser",
    	"password": "Password1",
    	"email": "test@email.com",
    	"interests": ["Sports", "Nature", "Cooking"]
    }'
    

    You should see the following output in your terminal:

    {"username":"thefriendlyuser","interests":["Sports","Nature","Cooking"]}

    Great! You’ve successfully created your user.

    Check to see that your user was created and that the connections were made to the user's interests by fetching the user's details with the following command:

    curl -X GET ${BASE_URL}/users/thefriendlyuser

    You should see the following output in your terminal:

    {"username":"thefriendlyuser","friends":[],"interests":["Cooking","Nature","Sports"]}

    Great! You can see that the user's interests were saved. You can also see that your user has no friends yet. This is something we'll fix in the upcoming steps. 

    Now, let’s log in and receive an ID token.

  • Step 2. Login to fetch user credentials

    Your second endpoint is the Login endpoint. Users will submit their username and password to this endpoint to receive an ID token, which will be used to authenticate them on subsequent requests.

    To handle this authentication and token dispensing, your application has a /login endpoint. The handler is in application/app.js as follows:

    // Login
    app.post('/login', wrapAsync(async (req, res) => {
      const idToken = await login(req.body.username, req.body.password)
      res.json({ idToken })
    }))

    It expects the payload body to have username and password properties. It then calls your helper login function from your auth.js file. If the login is successful, it returns the ID token for the user.

    Let’s test it out with your created user. Run the following command in your terminal:

    curl -X POST ${BASE_URL}/login \
      -H 'Content-Type: application/json' \
      -d '{
    	"username": "thefriendlyuser",
    	"password": "Password1"
    }'
    

    You should see the following output in your terminal:

    {"idToken":"eyJraWQiO…."}

    This ID Token is used in subsequent requests to authorize a user. Save the value of the ID token in your shell by copying the value of the token between the quotations, then running the following command:

    export ID_TOKEN=<idToken>
  • Step 3. Fetch recommendations for a user

    Currently, your new user has no friends. This isn't a great experience for people in your application, so you want to help them find new friends. You can do this by generating recommendations of friends to follow.

    Your application has a FetchUserRecommendations endpoint. The code for that endpoint is as follows:

    // Fetch user recommendations
    app.get('/users/:username/recommendations', wrapAsync(async (req, res) => {
      const recommendations = await fetchUserRecommendations(req.params.username)
      res.json(recommendations)
    }))
    

    The handler takes the username in the URL path and passes it to the fetchUserRecommendations function to generate recommendations.

    The fetchUserRecommendations function is in application/data/fetchUserRecommendations.js and looks as follows:

    const { g, local, values, desc, without, neq } = require('./utils')
    
    const fetchUserRecommendations = async (username) => {
      const friendsOfFriends = await g.V()
        .has('User', 'username', username).as('user')
        .out('Follows').aggregate('friends')
        .in_('Follows')
        .out('Follows').where(without('friends'))
        .where(neq('user'))
        .values('username')
        .groupCount()
        .order(local)
        .by(values, desc)
        .limit(local, 10)
        .next()
      const friendsWithInterests = await g.V()
        .has('User', 'username', username).as('user')
        .out('InterestedIn')
        .in_('InterestedIn')
        .out('Follows')
        .where(neq('user'))
        .values('username')
        .groupCount()
        .order(local)
        .by(values, desc)
        .limit(local, 10)
        .next()
      return {
        username,
        friendsOfFriends,
        friendsWithInterests
      }
    }
    
    module.exports = fetchUserRecommendations
    

    The fetchUserRecommendations function takes a username and then runs both of the recommendation strategies you saw in the previous modules. It looks for friends of friends as well as friends with the same interests. It returns the results from both of these strategies.

    Let’s try using this endpoint. Run the following command in your terminal:

    curl -X GET ${BASE_URL}/users/thefriendlyuser/recommendations

    You should see the following output in your terminal:

    {"username":"thefriendlyuser","friendsOfFriends":[],"friendsWithInterests":[["thardy",21],["michaelunderwood",20],["paullaurie",19],["ocarrillo",16],["paulacruz",16],["bergjames",16],["petersonchristina",15],["annette32",15],["evanewing",14],["hortonamy",14]]}
    

    Nice! You were able to generate recommendations for your user.

    Notice that you received recommendations for friendsWithInterests, but you didn't receive any for friendsOfFriends. This is because your user doesn't have any friends yet!

    In the next step, you follow your first friend.

  • Step 4. Follow a friend

    In this step, you follow your first friend.

    The endpoint to follow a user is POST /users/:username/friends, and the handler code is contained in application/app.js as follows:

    // Follow user
    app.post('/users/:username/friends', wrapAsync(async (req, res) => {
      const validated = validateFollowUser(req.body)
      if (!validated.valid) {
        throw new Error(validated.message)
      }
      const token = await verifyToken(req.header('Authorization'))
      if (token['cognito:username'] != req.params.username) {
        throw new Error('Unauthorized')
      }
      const friendship = await followUser({ follower: req.params.username, friend: req.body.username })
      res.json(friendship)
    }))
    

    There are a few things going on here. First, the payload body is validated to ensure that the username property is present. Second, the handler verifies the Authorization header passed and ensures that it matches the username value in the URL path. If the request is both validated and authorized, then it calls the followUser function to add a follow relationship.

    In the previous step, you saw some friend recommendations for your user. Let's use the one with the highest score and follow that user.

    You can follow the user with the following command:

    curl -X POST ${BASE_URL}/users/thefriendlyuser/friends \
     -H 'Content-Type: application/json' \
      -H "Authorization: ${ID_TOKEN}" \
      -d '{
    	"username": "thardy"
    }'
    

    You should see the following output in your terminal:

    {"follower":"thefriendlyuser","friend":"thardy"}

    Success! You followed your first friend!

    Let's make sure it was stored in the database by retrieving your user details again:

    curl -X GET ${BASE_URL}/users/thefriendlyuser

    You should see the following output in your terminal:

    {"username":"thefriendlyuser","friends":["thardy"],"interests":["Sports","Cooking","Nature"]}

    Nice! Your user now has the friend thardy in their friends result!

    Finally, let's check the recommendations to see how it has been updated:

    {"username":"thefriendlyuser","friendsOfFriends":[["michaelunderwood",8],["paullaurie",7],["morenojason",6],["paulacruz",6],["warrenpaul",5],["pricelucas",5],["ocarrillo",5],["hlowe",5],["clynch",5],["douglasrichard",5]],"friendsWithInterests":[["thardy",24],["michaelunderwood",20],["paullaurie",19],["ocarrillo",16],["paulacruz",16],["bergjames",16],["petersonchristina",15],["annette32",15],["evanewing",14],["hortonamy",14]]}

    Success! Notice that your user now has friendsOfFriends recommendations since they have some friends.


In this module, you exercised your working endpoints to see how your components worked together. First, you registered a new user in your application and retrieved your user to ensure all details were saved. Second, you exercised the login endpoint to fetch an ID token for your user that can be used by the client to authenticate the user. Third, you retrieved friendship recommendations for your user. You then acted on those recommendations by following your first user. After following a user, you verified that the friendship was saved and that your recommendations were updated accordingly.

In the next module, you clean up the resources you created.