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 character.

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 an AddItem endpoint to add a new item to your user’s inventory.

Finally, you use the GetUser endpoint to retrieve your user’s information, including all of the items held in the user’s inventory.

Time to Complete Module: 20 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, as well as attributes for their character, such as height and weight.

    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.height, req.body.weight)
      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 database.

    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.

    const { executeWriteSql } = require('./utils')
    
    const createUser = async (username, height, weight) => {
      sql = `INSERT INTO users (user_id, username, height, weight) \
    VALUES (DEFAULT, :username, :height, :weight) \
    RETURNING user_id, username, height, weight`
      parameters = [
        {
          name: 'username',
          value: { stringValue: username }
        },
        {
          name: 'height',
          value: { longValue: height}
        },
        {
          name: 'weight',
          value: { longValue: weight}
        }
      ]
      const result = await executeWriteSql(sql, parameters)
      return result[0]
    }
    
    module.exports = createUser

    In this method, you write up some parameterized SQL and your parameters and pass it to the executeWriteSql helper function.

    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": "bonecrusher",
    	"password": "Mypassword1",
    	"email": "test@hello.com",
    	"height": 75,
    	"weight": 350
    }'

    You should see the following output in your terminal:

    {"user_id":51,"username":"bonecrusher","height":75,"weight":350}

    Great! You’ve successfully created your user. 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": "bonecrusher",
    	"password": "Mypassword1"
    }'

    You should see the following output in your terminal:

    {"idToken":"eyJraWQiOiJGRWJmUUdVbHYyUmE0bEg5STFQV2V4Z3lxVDdNSGMrRkJJajRDQVNjdWZVPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2YzNjNDc3ZS0xMzU0LTRlMzktYjNmZS04OTM3ODEwODhhMTciLCJhdWQiOiI2dWMwMW5hZ2p0M3BpZWF0c2tudDBwcnZlZCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZXZlbnRfaWQiOiJkZjRhZTgwMi03MDY1LTQzMmQtYmI1ZC0xNjkyODM2ZTRkYWYiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTU3MTc0Mzc5NSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfajBsSUJXam9qIiwiY29nbml0bzp1c2VybmFtZSI6ImJvbmVjcnVzaGVyIiwiZXhwIjoxNTcxNzQ3Mzk1LCJpYXQiOjE1NzE3NDM3OTUsImVtYWlsIjoidGVzdEBoZWxsby5jb20ifQ.TzAmg3JF1pYH25f9oZHeR7ZN4ELPnvzmX08JWFm3tMc5b7J4Y5KDU8ybKzcmb2gUdwRFrDPz46oGF0huTSwfxXt4oMU9fKOAntdGwCXu0oZmBY7Gixt63Fg9Grfku924XpbpYjwrbYL8j4QqY66Mt_1sLCKRwbuZHEQf5A2miT7_FWjTbLi7PT4ftU7eZW19dgjq3miLA6r9JeQTfYU06_cgap8hAyULK-28rh_PD1ChWDwYlxKAXy5Zcv14Ja0dRCLJiufK4aSEcweVoipPanZrDzXuB8UjLmswvq4paJq0oq6OEnpx1w6k2ru4H9rYQEtXVIbRSqfHOqprl4XNLw"}

    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. Add items to your user

    Now that your user has a way to authenticate, let’s add an item to a user’s inventory. You can imagine this would happen when a user finds an item on the ground or in a treasure chest in your game. In our use case, you can imagine that the application client doesn’t know about the type or properties of the item added -- that is generated in the backend application and returned to the user.

    The endpoint for adding an item is POST /users/:username/items, and the handler code in application/app.js is as follows:

    // Add item
    app.post('/users/:username/items', wrapAsync(async (req, res) => {
      const token = await verifyToken(req.header('Authorization'))
      if (token['cognito:username'] != req.params.username) {
        throw new Error('Unauthorized')
      }
      const item = await addItemToUser(req.params.username)
      res.json(item)
    }))

    This code is doing two things. First, it is verifying the ID token that is sent over in the Authorization header. If this token is valid and the username from the token matches the username in the URL path, then the username is passed into the addItemToUser function in your data package.

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

    const { executeWriteSql } = require('./utils')
    
    const types = [
      'WEAPON',
      'ARMOR',
      'CLOTHING',
      'POTION'
    ]
    
    const addItemToUser = async (username) => {
      const type = types[Math.floor(Math.random()*types.length)]
      let properties
      if (type == 'WEAPON') {
        properties = { attack: Math.floor(Math.random() * 10) + 1  }
      } else if (type == 'ARMOR') {
        properties = { defense: Math.floor(Math.random() * 10) + 1  }
      } else if (type == 'CLOTHING') {
        properties = { charisma: Math.floor(Math.random() * 10) + 1  }
      } else if (type == 'POTION') {
        properties = { healing: Math.floor(Math.random() * 10) + 1  }
      }
      writeSql = `INSERT INTO items (item_id, owner_id, type, properties) \
    VALUES (DEFAULT, (SELECT user_id from users where username = :username), :type, :properties) \
    RETURNING item_id, owner_id, type, properties`
      writeParameters = [
        {
          name: 'username',
          value: { stringValue: username }
        },
        {
          name: 'type',
          value: { stringValue: itemType}
        },
        {
          name: 'properties',
          value: { stringValue: JSON.stringify(properties)}
        }
    
      ]
      const writeResult = await executeWriteSql(writeSql, writeParameters)
      return writeResult[0]
    }
    
    module.exports = addItemToUser

    The addItemToUser function generates a random item type and properties then updates the items table to add the given item to the user’s inventory.

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

    curl -X POST ${BASE_URL}/users/bonecrusher/items \
      -H "Authorization: ${ID_TOKEN}"
    

    Note that you’re passing your ID token in the Authorization header with your request.

    You should see the following output in your terminal:

    {"item_id":107,"owner_id":51,"type":"CLOTHING","properties":"{\"charisma\":8}"}

    This new item has been added to your user! Note that the properties printed out for you will be slightly different as the item type and properties are random.

    Try running this endpoint again but without an ID token. Run the following command in your terminal:

    curl -X POST ${BASE_URL}/users/bonecrusher/items

    Because your request does not include a token, you should see the following output:

    {"message":"jwt must be provided"}

    You have used your Amazon Cognito tokens to protect an endpoint.

  • Step 4. Fetch your user

    In your final endpoint, let’s fetch your user, including all the items in this user’s inventory. This way you can confirm that your item was successfully added to your user in the previous step.

    The endpoint to fetch a user is GET /users/:userId, and the handler code is contained in application/app.js as follows:

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

    This endpoint is not an authenticated endpoint as each user can see details about other users if desired.

    Invoke the endpoint and fetch your user by entering the following command in your terminal:

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

    You should see the following output in your terminal:

    {"user_id":51,"username":"bonecrusher","height":75,"weight":350,"items":[{"item_id":107,"owner_id":51,"type":"CLOTHING","properties":"{\"charisma\":8}"}]}

    Success! You retrieved your user, and it has the new item that you added in the previous step.


In this module, you exercised your working endpoints to see how your components worked together. First, you registered a new user, which involved creating a new user in your Amazon Cognito user pool as well as adding that user to your Amazon Aurora Serverless database. Second, you exercised the login endpoint to fetch an ID token that can be used by the client to authenticate the user. Third, you used this ID token to authorize the user when adding a new item to the user’s inventory. Finally, you fetched your user details to see the new item you added.

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