このモジュールでは、デプロイしたアプリケーションを使用します。それを行う前に、アプリケーションで使用している AWS コンポーネントの概要を簡単におさらいしましょう。

  • データストレージ用の Amazon Neptune。これで、データをグラフとしてモデル化しました。
  • ユーザー登録および認証用の Amazon Cognito
  • コンピューティング用の AWS Lambda
  • Lambda 関数へ HTTP ベースでアクセスするための Amazon API Gateway

これらの各部分がどのように結び付いているかを見てみましょう。次の手順では、これらのコンポーネントを使用して、さまざまなアプリケーションエンドポイントを説明します。

まず、Registration エンドポイントから始めます。ここでは、新規ユーザーがサインアップしてアカウントを作成します。ユーザーを登録した後、FetchUser エンドポイントでユーザーの詳細を表示します。

次に、ユーザーがクライアント (ウェブアプリケーションやモバイルアプリなど) を使用して ID トークンを認証および受信できる Login エンドポイントを使用します。

3 番目に、FetchUserRecommendations エンドポイントを使用して、フォローすべきユーザーを見つけます。

最後に、FollowUser エンドポイントを使用して、別のユーザーのフォローを開始します。

モジュールの所要時間: 15 分


  • ステップ 1.新規ユーザーを登録する

    最初に確認するワークフローは、Registration エンドポイントです。このエンドポイントで、新規ユーザーがユーザー名やパスワードなどのログイン情報を提供してサインアップします。さらに、新規ユーザーは登録時にいくつかの興味対象を示すことができます。これは、ユーザーが新規である場合に、より関連性の高いレコメンデーションを提供するのに役立ちます。

    このエンドポイントを見ると、エンドポイントによって呼び出される関連するコードスニペットがこのガイドに示されています。

    登録エンドポイントを使用するには、POST リクエストを /users エンドポイントに送信します。このエンドポイントのロジックは、application/app.js ファイルの中間よりも下にあります。

    // 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)
    }))
    

    ハンドラーは、最初にリクエストの着信ペイロードの本文を検証します。それが成功した場合、ハンドラーは、Amazon Cognito にユーザーを作成し、次に createUser 関数を使用して Neptune データベースにユーザーを作成します。

    Amazon Cognito モジュールの createCognitoUser 関数はすでに確認しているため、ここではおさらいしません。application/data/createUser.js にある createUser 関数を見てみましょう。

    // 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
    

    createUser 関数は、ユーザー名と興味対象の配列を受け取ります。Neptune データベースにユーザーを作成し、興味対象を反復処理して、ユーザーから興味対象までの InterestIn エッジを追加します。

    Registration エンドポイントを呼び出して新しいユーザーを作成してみてください。ターミナルで次のコマンドを実行します。

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

    ターミナルに次の出力が表示されます。

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

    素晴らしい! ユーザーが正常に作成されました。

    次のコマンドを使用してユーザーの詳細を取得することにより、ユーザーが作成され、ユーザーの興味対象につながっていることを確認します。

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

    ターミナルに次の出力が表示されます。

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

    素晴らしい! ユーザーの興味対象が保存されていることがわかります。また、ユーザーにはまだ友人がいないこともわかります。これは、今後のステップで対応します。 

    それでは、ログインして ID トークンを受け取りましょう。

  • ステップ 2.ログインしてユーザー認証情報を取得します

    2 番目のエンドポイントは Login エンドポイントです。ユーザーは、ユーザー名とパスワードをこのエンドポイントに送信して ID トークンを受信します。この ID トークンは、後続のリクエストで認証するのに使います。

    この認証とトークンの分配を処理するために、アプリケーションには /login エンドポイントがあります。ハンドラーは、次のように application/app.js にあります。

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

    ペイロード本文には、usernamepassword プロパティが含まれていると想定しています。次に、auth.js ファイルからヘルパー login 関数を呼び出します。ログインに成功すると、ユーザーの ID トークンが返されます。

    作成したユーザーでテストしてみましょう。ターミナルで次のコマンドを実行します。

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

    ターミナルに次の出力が表示されます。

    {"idToken":"eyJraWQiO…."}

    この ID トークンは、後のリクエストでユーザーを認証するために使用します。引用符の間にトークンの値をコピーし、次のコマンドを実行して、ID トークンの値をシェルに保存します。

    export ID_TOKEN=<idToken>
  • ステップ 3:ユーザー向けのレコメンデーションを取得する

    現在、新規のユーザーには友人がいません。この状況はアプリケーションのユーザーにとって素晴らしいとは言えないので、これらのユーザーが新しい友人を見つけるのをサポートする必要があります。フォローする友人についてのレコメンデーションを生成することでサポートできます。

    アプリケーションには FetchUserRecommendations エンドポイントがあります。そのエンドポイントのコードは次のとおりです。

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

    ハンドラーは URL パスでユーザー名を取得し、それを fetchUserRecommendations 関数に渡してレコメンデーションを生成します。

    fetchUserRecommendations 関数は application/data/fetchUserRecommendations.js にあり、次のようになります。

    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
    

    fetchUserRecommendations 関数はユーザー名を受け取り、前のモジュールで見た両方のレコメンデーション戦略を実行します。友人の友人だけでなく、興味対象が同じ友達を探します。これらの両方の戦略からの結果を返します。

    このエンドポイントを使用してみましょう。ターミナルで次のコマンドを実行します。

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

    ターミナルに次の出力が表示されます。

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

    よくできました! ユーザーに対するレコメンデーションを生成できました。

    friendsWithInterests のレコメンデーションは受け取りましたが、friendsOfFriends のレコメンデーションは受け取っていないことに留意してください。これは、ユーザーにまだ友人がいないためです!

    次のステップでは、最初の友人をフォローします。

  • ステップ 4:友人をフォローする

    このステップでは、最初の友人をフォローします。

    ユーザーをフォローするエンドポイントは POST /users/:username/friends で、ハンドラーコードは次のように application/app.js に含まれています。

    // 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)
    }))
    

    ここではいくつかのことが起こっています。最初に、ペイロードの本体が検証され、username プロパティが存在することを確認します。次に、ハンドラーは渡された Authorization ヘッダーを検証し、URL パスのユーザー名の値と一致することを確認します。リクエストが検証され承認された場合、followUser 関数を呼び出してフォロー関係を追加します。

    前のステップでは、ユーザーに対する友人のレコメンデーションをいくつか見ました。最高スコアのものを使用して、そのユーザーをフォローしましょう。

    次のコマンドでユーザーをフォローできます。

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

    ターミナルに次の出力が表示されます。

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

    成功しました! 最初の友人をフォローしました!

    ユーザーの詳細を再度取得して、データベースに保存されていることを確認します。

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

    ターミナルに次の出力が表示されます。

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

    よくできました! ユーザーの友人の結果に thardy という友人が追加されました!

    最後に、レコメンデーションをチェックして、それがどのように更新されたかを確認しましょう。

    {"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]]}

    成功しました! 現時点ではユーザーには友人がいるので、friendsOfFriends のレコメンデーションがあることに留意してください。


このモジュールでは、作業エンドポイントを実行して、コンポーネントがどのように連携するかを確認しました。最初に、アプリケーションに新規ユーザーを登録し、すべての詳細が保存されたことを確認するためにユーザーを取得しました。次に、ログインエンドポイントを実行して、クライアントがユーザーの認証に使用できるユーザーの ID トークンを取得しました。3 番目に、ユーザーの友人関係についてのレコメンデーションを取得しました。そして、これらのレコメンデーションに基づき、最初のユーザーをフォローしました。ユーザーをフォローした後、友人関係が保存され、これに応じてレコメンデーションが更新されたことを確認しました。

次のモジュールでは、作成したリソースをクリーンアップします。