A question that I’ve gotten from my last tutorial on Lambda functions “How To Use AWS AppSync in Lambda Functions” was: “How can you access the user so you can filter for his items in a query?” We are going to take a look at how you can access the user’s ID and the user object using the AWS SDK for JavaScript. Additionally, you are going to learn how to efficiently compose Express middleware to give access to the user in all requests in a DRY way. This tutorial should work for all Lambda functions where the caller is authenticated using Amazon Cognito User Pools, and we are going to generate our resources using the Amplify CLI.

Accessing the User

I assume you have already set up your Amplify project with the auth category and Cognito User Pools.

Create a new Lambda function using Express.

amplify add function

? Provide a friendly name for your resource to be used as a label for this category in the project:

LambdaUser

? Provide the AWS Lambda function name:

LambdaUser

? Choose the function template that you want to use:

Serverless express function (Integration with Amazon API Gateway)

? Do you want to access other resources created in this project from your Lambdafunction?

Yes

? Select the category (Press <space> to select, <a> to toggle all, <i> to invertselection)

auth

? Select the operations you want to permit for lambdauser<some_number>

read



You can access the following resource attributes as environment variables from your Lambda function

var environment = process.env.ENV

var region = process.env.REGION

var authLambdauser<some_number>UserPoolId = process.env.AUTH_LAMBDAUSER<some_number>_USERPOOLID



? Do you want to edit the local lambda function now?

No

cd into your Lambda function.

cd amplify/backend/function/LambdaUser/src/

Install the AWS SDK.

npm install --save aws-sdk

We are going to access the user through the API Gateway object that gets passed to the Lambda function by the AWS serverless express middleware. (Thank you to Troy who came up with this.) If you get an error about CORS when you invoke this function later, make your user is NOT in the state FORCE_CHANGE_PASSWORD .

Let’s go over this code. To make the example more concise, we deleted all routes, but the get route for /items and got rid of all comments. We extract the user's ID using a regex from them API gateway data that the AWS serverless express middleware injects. Afterwards, we configure the AWS SDK to communicate with our Cognito User Pool. Lastly, we call and pick our user.

We also need to add this function to our API category.

amplify add api

? Please select from one of the below mentioned services

REST

? Provide a friendly name for your resource to be used as a label for this category in the project:

LambdaUser

? Provide a path (e.g., /items)

/items

? Choose a Lambda source

Use a Lambda function already added in the current Amplify project

? Choose the Lambda function to invoke by this path

LambdaUser

? Restrict API access

Yes

? Who should have access?

Authenticated users only

? What kind of access do you want for Authenticated users? (Press <space> to select, <a> to toggle all, <i> to invert selection)

create, read, update, delete

? Do you want to add another path?

No

Push your changes.

amplify push

If you are using React you can use the following useEffect Hook to try out the function after you ran Amplify.configure(config); .

useEffect(() => {

async function fetch() {

try {

await Auth.signIn('user@tutorial.com', 'password');

const res = await API.get('LambdaUser', '/items');

console.log(res);

} catch (error) {

console.log(error);

}

}



fetch();

});

It works! 🎉 But, notice how much boilerplate it is to get and inject the user into our route. If you have many routes where you want to access the user, it would be cool if we could find a beautiful abstraction 🤔.

Middleware

Let’s clean up a bit. We are going to move the user injection logic into a separate middleware. Create middleware.js in your Lambda function's directory. If the following code intimidates you, relax. We'll go through it.

After importing the packages we need, we define pipe . pipe lets us functionally compose the middleware. Afterwards, we define tapRoute , which takes in a function and route (aka. app from app.js ), calls the function with it and returns route . We need tapRoute for the middleware that we didn't write ourselves so we can keep the data flowing through our pipe . Next, we define configureCors and injectUser . The former is the tapRoute logic bundled with the CORS settings that come when you create a Lambda function with Amplify. The ladder is a middleware that gets the user like we did in the previous section and puts it onto the req object. Lastly, we define applyMiddleware , which takes in route and optionally more middleware and composes all the middleware into one function.

Here is how app.js looks using applyMiddleware .

Wow, that looks a lot cleaner. Notice how we pass app to applyMiddleware . We call it route in the definition of applyMiddleware because you could also alter the function using the optional second ...middleware argument on a per route basis.

Push your changes to the cloud. The function should still work the same as before. However, app.js looks cleaner, we can easily add more middleware using applyMiddleware , and all middleware logic is bundled to one file.

To delete this tutorial’s code, you can run amplify delete to remove all resources in the cloud.

If you liked this tutorial, you might want to read my other articles about Amplify because they cover advanced use cases and make you an Amplify expert.

Summary

We learned how we can extract the user using the API gateway object that gets injected from the AWS serverless express middleware. Moreover, we refactored the user injection logic into a custom middleware, reducing the amount of boilerplate we have to write. And we learned how we can use function composition to tidy up our middleware and make our code more readable.