Automatically Create a DynamoDB Record from a Cognito User Pool
Published: 2023-07-07 12:33:31

To successfully authenticate a user, it means that you have securely stored their credentials on your server, and you verify their accuracy every time they try to sign in.

However, authentication is one of many reasons we store user data. In addition to verifying user credentials, creating a user often involves capturing and storing their profile information. You might want to store user data so you can use the profile data and connect it to the user data, for example, in social media use cases, such as displaying comments from all users related to a specific post.

[AWS Amplify Auth](https://docs.amplify.aws/lib/auth/getting-started/q/platform/js/) uses [Amazon Cognito](https://aws.amazon.com/cognito/) behind the scene which means that users are stored in an AWS Cognito User Pool. This pool is different from your GraphQL API data which is stored in your [DynamoDB](https://aws.amazon.com/dynamodb/#:~:text=Amazon%20DynamoDB%20is%20a%20fully,data%20import%20and%20export%20tools.). If you are using GraphQL API with [AppSync](https://aws.amazon.com/pm/appsync/?trk=4301a4e1-3af3-45f9-8fdc-1400729d3f5e&sc_channel=ps&ef_id=Cj0KCQjw756lBhDMARIsAEI0AglNx66mhK4fjVztqbJtqaOAJA320bAbHTWi1X7rvJPia7M3jSK3rvcaAv3cEALw_wcB:G:s&s_kwcid=AL!4422!3!656437113988!e!!g!!aws%20appsync!20039309735!148673400219) then your user data and your app data are in two different locations.

To connect your user data from Cognito User Pool to your Amplify GraphQL API, you need to setup a Lambda function that Cognito will call with the user data. This Lambda function is a lambda trigger that is automatically invoked right after the sign-up process is completed.

In this article, we will walk through how to set up this Lambda function and connect and write to our database after Cognito has called it.

## Prerequisites

To follow this tutorial, you'll need:

1. An AWS account. If you don't have one, you can create an account [here](https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start/email).
2. [Node.js](https://nodejs.org/) v16.x or later and [npm](https://www.npmjs.com/) v6.14.4 or later

This tutorial also assumes that you're familiar with the basics of both [JavaScript/ES6](https://developer.mozilla.org/en-US/docs/Web/JavaScript) and [React](https://react.dev/).

## Set up Your Development Environment

You need to configure the Amplify CLI locally, and connect it to your AWS account. Run the following command in your terminal:

```sh
npm install -g @aws-amplify/cli
```

Next, run the `amplify configure` command to configure your AWS Amplify environment. Follow the prompts to create a new IAM user that AWS Amplify will use to manage resources for your app. Here’s a [link](https://docs.amplify.aws/start/getting-started/installation/q/integration/react/#configure-the-amplify-cli) with step-by-step details on how to configure the Amplify CLI.

## Create an AWS Amplify Project

The Amplify Console is where you build, manage, and host your Amplify applications. Let's set up an AWS Amplify app. Log into your AWS account and search for **AWS Amplify** in the console. Select **AWS Amplify** to open the Amplify Console.

If this is your first app, scroll to the bottom of the page in the Amplify Studio section, select [**Get started**](https://console.aws.amazon.com/amplify/home?#/deploy-backend). Name your app, and select **Confirm Deployment**.

If you’ve created an Amplify App in the past, follow the steps below:

- Select **New app** in the upper right-hand corner, and select **Build an app** from the dropdown menu.

![](https://paper-attachments.dropboxusercontent.com/s_7F3CF14D615733D9DF192FA7B5D2A45648AD4D8DC11889AC89C249A61C40BC9E_1686653904474_CleanShot+2023-06-08+at+11.42.282x.png)

- Name your app, and select **Confirm Deployment.**

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688397336610_CleanShot+2023-07-03+at+16.15.002x.png)

This would take a few seconds as Amplify need some time to set up a new project. Once that is completed, click the **Launch Studio** button.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688397569357_CleanShot+2023-07-03+at+16.18.542x.png)

AWS Amplify Studio is a visual development environment tailored for building fullstack web and mobile apps. One of its standout features is the ease with which you can set up backend resources for various tasks, such as authentication and managing customer data.

It also has a Content Management System (CMS), which is incredibly useful for viewing and managing user data.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688457184161_CleanShot+2023-07-04+at+08.52.422x.png)

You should now see the **Home** menu for your application. To learn more, see the [Amplify Studio introduction](https://docs.amplify.aws/console/).

## Set up Authentication

We need to set up authentication for our app so that users can create accounts and sign in. We want only authenticated users to access the App’s UI.

Amplify simplifies authentication and authorization. You can setup a complete auth flow and auth UI for your app in minutes.

1. Select the **Authentication** option in the setup menu on the screen's left side. Leave Email as the log in option.
2. Scroll down to the **Configure Signup settings** section. You should see the **Password protection settings** in this section. Click on this to see a variety of security options for user passwords.
3. Click the **Deploy** button, acknowledge the warning, and select **Confirm Deployment**.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688457374008_CleanShot+2023-07-04+at+08.54.34.gif)

You should see the progress of this deployment process — a visual representation of how your setup is being deployed in the AWS environment.

Amplify will provision AWS Cognito behind the scene. Once it’s done, you should get a confirmation message.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688457676824_CleanShot+2023-07-04+at+09.00.392x.png)

## Create a Data Model in Amplify Studio

Next, we need to define a data model to store the user details. Amplify provides the ability to create data models, which represent the structure of the data your application will work with. The Studio data model designer provides a visual way to achieve this.

When you define your data models, AWS Amplify creates corresponding AWS AppSync GraphQL APIs to allow your application to interact with the data and Amazon DynamoDB tables based on those models.

Follow the steps below to create a data model for your app:

- On the **Setup** menu on the left, select **Data,** and select **Add model**.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688457832264_CleanShot+2023-07-04+at+09.02.442x.png)

- As seen in the image below, we call our data model **User** for this example. Click the **Add a field** link, and in the field that appears, type in "email".

- For this **email** field, you will notice an option labeled 'is required' to the right. Check this box to make this a mandatory field.

- Select the **Save and Deploy** button. Acknowledge the warning, and select **Deploy.**

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688458480097_CleanShot+2023-07-04+at+09.13.552x.png)

The deployment should take a few minutes, but once it is completed, you should see a message saying you've successfully deployed the data model.

Additionally, you should also see a "pull" command that should be executed in your local terminal, precisely from your project's root directory to pull the project into your React project. You can either copy the command or ignore it for now; we'll use it soon. Let’s go ahead and set up our React project.

## Set up React Project

Run the following command from your preferred directory to set up a React project:

```sh
npx create-react-app dyneapp
```

Run the following command to navigate into the **dyneapp** directory, and start up your development server:

```sh
cd dyneapp
npm start
```

You should see the running app by navigating to [http://localhost:3000](http://localhost:3000/).

## Install Amplify Libraries

The first step to using Amplify in the client is to install the necessary dependencies.

Run the following command to install the Amplify Libraries:

```sh
npm install aws-amplify @aws-amplify/ui-react
```

This command installs two libraries. The `aws-amplify` package is the library that enables you to connect to your backend resources. The `@aws-amplify/ui-react` package includes React-specific UI components that you can use to build your app UI.

The `@aws-amplify/ui-react` library exports a connected component that handles Auth UI flow for us.

## Pull Amplify Backend Project

Navigate to your Amplify Studio application, and copy the pull command displayed. This command is important as it allows you to pull the newly created Amplify backend project from the cloud to your local environment (React project in our case).

> Dependency: Make sure you have AmplifyCLI configured before executing the pull command.

From your projects directory, paste the copied command into the terminal and then execute it. A typical instance of this command would appear as follows:

```sh
amplify pull --appId d2act21onzf92o --envName staging
```

When you execute the command, you will be automatically redirected to your web browser to grant authorization. Once there, click 'Yes' to authenticate with Amplify Studio.

After successful login, return to the CLI. Here, you will be asked a series of questions to gather essential details about your project's configuration. Your answers will help Amplify understand your project's structure and needs.

Accept the **default values** highlighted below:

![](https://paper-attachments.dropboxusercontent.com/s_F44586F38B16010B303EA332A735EE3A53124A971A632F52CD30930FEA058F1C_1688565625950_CleanShot+2023-07-05+at+14.59.322x.png)

The Amplify CLI will automatically carry out the following steps for you:

1. Amplify will create a new project in the current local directory. This is indicated by the creation of an `amplify` folder in your project's root directory, which contains all of your project's backend infrastructure settings.
2. It creates a file called `aws-exports.js` in the `src` directory of your React application. This file holds all the configuration for the services you create with Amplify, allowing the Amplify client to access necessary information about your backend services, such as API endpoints, authentication configuration, etc.

## Configure Amplify

For your application to talk to AWS, you need to configure AWS Amplify for use throughout the project. In your app's entry point `index.js`, import and load the configuration file:

```js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

// Add these lines
import { Amplify } from "aws-amplify";
import awsconfig from "./aws-exports";
Amplify.configure(awsconfig);

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

In the code above, we import the Amplify library and configuration details from the `aws-exports.js` file.

We then configure AWS Amplify with the details we imported. This setup ensures that the Amplify library knows about your specific backend setup and can interact with it correctly.

## Protect Your App UI with Amplify Auth

With the configuration out of the way, we can go ahead and protect our app with the Authenticator UI component. Open your `src/App.js` file and import the `withAuthenticator` component below the last import.

```js
import "@aws-amplify/ui-react/styles.css";
import { withAuthenticator } from "@aws-amplify/ui-react";
```

Next, update the code in your `**src/App.js**` file to match the following:

```js
import React from "react";
//...

function App({ user, signOut }) {
  return (
    <div>
      <div className="flex justify-end px-4 py-2">
        <button
          type="button"
          className="relative inline-flex items-center gap-x-1.5 rounded-md bg-indigo-500 px-3 py-2 text-sm font-semibold text-white"
          onClick={() => signOut()}
        >
          Sign Out
        </button>
      </div>
      <div className="flex justify-center items-center h-screen w-full">
        Hello World
      </div>
    </div>
  );
}

export default withAuthenticator(App);
```

The `App` component is a simple React component that displays a greeting message and a sign-out button. It receives `signOut` and `user` props because we wrapped the App component with a higher order compnent. The `signOut` function is used to log the user out of the application, and `user` contains the details of the currently logged-in user.

Head to the browser and you should see a sign in and create account form.

![](https://paper-attachments.dropboxusercontent.com/s_77E45FA941771A34FF42E563A6A8E71DEA51CCC13033AD6D063DDE0395D560E2_1686224810241_CleanShot+2023-06-08+at+12.46.292x.png)

No need to create an account at the moment as we are yet to configure Lambda triggers for the authentication events.

## Create a Pre-Signup Lambda Trigger

We can use Lambda triggers to intercept the auth flow and run custom code before a user is created. We need to configure Amplify Auth module with Lambda functions to extend its functionality.

Run the following command from your terminal:

```sh
amplify update auth
```

This command allows us to modify our existing authentication configurations. Select **Walkthrough all the auth configurations** and accept the configuration for the rest of the CLI prompts as as highlighted in the image below:

![](https://paper-attachments.dropboxusercontent.com/s_F44586F38B16010B303EA332A735EE3A53124A971A632F52CD30930FEA058F1C_1688565679640_CleanShot+2023-07-05+at+15.01.082x.png)

Let’s go through some of the important values we selected above and why:

- **Walkthrough all the auth configurations:**This option allows us to go through and modify all the configurations for your authentication resource step by step. It is helpful for changing multiple aspects or reviewing all settings.
- **Selecting to Configure Lambda Triggers for Cognito:** This allows us to specify that we want to run custom code during various stages of the authentication process. Lambda triggers can be used for custom validation, adding custom logic, etc.
- **Selecting "pre-signup"**: With this, we are choosing to create a Lambda function that is triggered before Amazon Cognito signs up a new user.
- **Selecting "Create your own module"**: allows us to write our custom Lambda function to implement specific logic rather than using pre-built templates provided by Amplify.

After these steps, Amplify will create a new directory called `amplify/backend/function` containing the source code for your new Lambda function. The file for the custom function — `custom.js` will be automatically opened in VS Code for you. We will modify this code to implement our custom logic for the pre-signup.

Next, we need to `cd` into the `amplify/backend/function/DyneappPreSignup/src` folder and run the following command to install version 2 of the `node-fetch` module as a dependency to just the Lambda function.

```sh
npm install node-fetch@2
```

Because we will be writing functions with CommonJS, we are installing version 2 of [node-fetch](https://www.npmjs.com/package/node-fetch). We need this to be able to access the [**Fetch API**](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) in our Node.js project to make a HTTP request to our GraphQL API.

Next, we need to add our GraphQL API credentials as environmental variables to our existing function.
Run the following command at the root of your project:

```sh
amplify update function
```

Select the Lambda function you want to update and follow the CLI prompts by selecting the values displayed in the image below:

![](https://paper-attachments.dropboxusercontent.com/s_F44586F38B16010B303EA332A735EE3A53124A971A632F52CD30930FEA058F1C_1688565928085_CleanShot+2023-07-05+at+15.04.322x.png)

At the end of this command you can take note of your new API endpoint and API key.

Finally, we can use this GraphQL API access credentials to store the user signing up to our Amplify Datastore.

Replace the code in your `custom.js` file withe the following:

```js
/**
 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
 */

const fetch = require("node-fetch");

exports.handler = async (event, context) => {
  const GRAPHQL_ENDPOINT = process.env.ADD_YOUR_API_ENDPOINT;
  const GRAPHQL_API_KEY = process.env.ADD_YOUR_API_KEY;

  const query = /* GraphQL */ `
    mutation CREATE_USER($input: CreateUserInput!) {
      createUser(input: $input) {
        email
      }
    }
  `;

  const variables = {
    input: {
      email: event.request.userAttributes.email,
    },
  };

  const options = {
    method: "POST",
    headers: {
      "x-api-key": GRAPHQL_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  };

  const response = {};

  try {
    const res = await fetch(GRAPHQL_ENDPOINT, options);
    response.data = await res.json();
    if (response.data.errors) response.statusCode = 400;
  } catch (error) {
    response.statusCode = 400;
    response.body = {
      errors: [
        {
          message: error.message,
          stack: error.stack,
        },
      ],
    };
  }

  return {
    ...response,
    body: JSON.stringify(response.body),
  };
};
```

The code defines an AWS Lambda function handler. The function is an async function that receives a parameter — `event`, which contains information about the event that triggered the function. We defined two constants, `GRAPHQL_ENDPOINT`, and `GRAPHQL_API_KEY`. Replace their values with your API endpoint and API key credentials

Next, we defined a `query` constant that contains a GraphQL mutation that creates a user. It accepts an input of type `CreateUserInput` and returns the `email` field. We also created an object called `variables` that holds the input values for the GraphQL mutation. In this case, it sets the `email` field to the email value extracted from the `userAttributes` property of the `event.request` object. This property is set when Cognito calls this Lambda function.

The options object defines the options for the HTTP request. It specifies the request method as POST, sets the headers (including the API key), and converts the `query` and `variables` objects into a JSON string for the request body. We also initialized a response object as an empty object which will store the response from the GraphQL server.

We then try to make an HTTP request to the GraphQL endpoint. The request is sent to the `GRAPHQL_ENDPOINT` with the defined `options`. If the request is successful, the response is parsed as JSON and assigned to `response.data`. If the response contains errors, the status code is set to 400.

## Deploy and Test the Signup Process

Run the following command at the root of your project to deploy the project and apply the changes to your backend infrastructure.

```sh
amplify push
```

Amplify will display the detected changes and request your confirmation to proceed with the deployment. You can review the changes and enter "Y" to confirm. It will go ahead to create or update the necessary AWS Lambda resources and configurations based on your code.

Let’s go ahead and test the signup process by creating sample user accounts to ensure the Lambda function is triggered when a user signs up, and records are successfully created in DynamoDB. Head over to your [browser](http://localhost:3000/), and you should see a default sign-in/sign-up UI for users that are not authenticated. Use this form to create a new account.

![](https://paper-attachments.dropboxusercontent.com/s_F44586F38B16010B303EA332A735EE3A53124A971A632F52CD30930FEA058F1C_1688566110813_CleanShot+2023-07-05+at+15.07.52.gif)

This function should be triggered before verification since it is pre-signup. If you head back to the Amplify Studio Console for you app, on the **Setup** menu on the left, select **Content**. You should see the user's email as shown below.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688477622908_CleanShot+2023-07-04+at+14.33.112x.png)

## Monitor CloudWatch Logs

Debugging is crucial to understanding what is happening to your Lambda function. You can use CloudWatch Logs to inspect what is going on.

Open your [Amplify Management Console](https://us-east-1.console.aws.amazon.com/amplify/home?region=us-east-1#/), search for and select Lambda as shown below.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688462368587_CleanShot+2023-07-04+at+10.15.092x.png)

Search for the name of your app, and select the preSignup option. I called mine **Dyneapp**.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688462504062_CleanShot+2023-07-04+at+10.21.072x.png)

Click on the function.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688475596158_CleanShot+2023-07-04+at+13.59.262x.png)

Click on **Monitor** and then click **View cloudWatch logs**.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688475710131_CleanShot+2023-07-04+at+14.00.542x.png)

You should see a log stream, representing the invocation of the Lambda function or trigger event.

![](https://paper-attachments.dropboxusercontent.com/s_C63574B323B8E921AE7A391BD8DAA7B148BF5E8BAC2C1B6B90D96AA475522127_1688475868445_CleanShot+2023-07-04+at+14.04.032x.png)

Click on the stream to view the detailed log messages for that particular function invocation.

## Clean Up

To ensure that you don’t have any unused resources in you AWS account, run the following command to delete all the resources that were created in this project if you don’t intend to keep them.

```sh
amplify delete
```

## Conclusion

In this article, we leveraged the power of AWS Cognito Pre Sign Up Lambda Trigger to connect the AWS Cognito User Pool with AWS Amplify database. You now have the knowledge and tools to extend the capabilities of your application beyond simple authentication.