Creating Webhook between Clerk and DynamoDB
Tags: aws, nextjs, clerk, dynamodb
Published: 2024-03-13 18:11:48

The Whole Motive behind this blog is Auth is hard and a bit of pain to set up in AWS Cognito.With All Due AWS Cognito is a great service but has a learning curve.

So, lets learn how to integrate all this stack together

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1f7b46wxdehbp34zefy.png)

The common assumption you have a Nextjs Project clerk ready and a Users table in DynamoDB in your specific region

I have Users Table deployed with Partition key as ClerkID

So first let's get started with our nextjs Project

Folder Structure lib->actions->user.action.ts


```
"use server";
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
import { marshall } from "@aws-sdk/util-dynamodb";

export const createUser = async ({
  clerkId,
  name,
  username,
  email,
  picture,
}: {
  clerkId: string;
  name: string;
  username: string;
  email: string;
  picture: string;
}) => {
  const ddbClient = new DynamoDBClient({
    region: process.env.AWS_REGION as string,
    credentials: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
    },
  });
  const params = {
    TableName: "Users",
    Item: marshall({
      ClerkID: clerkId,
      Name: name,
      Username: username,
      Email: email,
      Picture: picture,
    }),
  };

  try {
    await ddbClient.send(new PutItemCommand(params));
  } catch (err) {
    throw err;
  }
};

```
As you see this code runs on server so lets update our next config too

next.config.mjs

```
const nextConfig = {
  experimental: {
    mdxRs: true,
    serverComponentsExternalPackages: [
      "@aws-sdk/client-dynamodb",
      "@aws-sdk/util-dynamodb",
    ],
  }
}
```

Now the API Post route in your app directory

This all is explained in the Documentation for clerk

[Webhook Clerk documentation](https://clerk.com/docs/integrations/webhooks/overview?_gl=1*2sp5w1*_gcl_au*Mjk3Mjc1MDIwLjE3MDkwNjYzMzI.*_ga*MTc2OTg3MDQ5OS4xNzA5MDY2MzMy*_ga_1WMF5X234K*MTcxMDM1MTYxNi4xMC4xLjE3MTAzNTE2MzYuMC4wLjA.)

Now in our App Directory
app->api->webhook->route.ts

```
/* eslint-disable camelcase */
import { Webhook } from "svix";
import { headers } from "next/headers";
import { WebhookEvent } from "@clerk/nextjs/server";
import { createUser } from "@/lib/actions/user.action";
import { NextResponse } from "next/server";

export async function POST(req: Request) {
  // You can find this in the Clerk Dashboard -> Webhooks -> choose the webhook
  //
  const WEBHOOK_SECRET = process.env.NEXT_CLERK_WEBHOOK_SECRET;

  if (!WEBHOOK_SECRET) {
    throw new Error(
      "Please add WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local"
    );
  }

  // Get the headers
  const headerPayload = headers();
  const svix_id = headerPayload.get("svix-id");
  const svix_timestamp = headerPayload.get("svix-timestamp");
  const svix_signature = headerPayload.get("svix-signature");

  // If there are no headers, error out
  if (!svix_id || !svix_timestamp || !svix_signature) {
    return new Response("Error occured -- no svix headers", {
      status: 400,
    });
  }

  // Get the body
  const payload = await req.json();
  const body = JSON.stringify(payload);

  // Create a new SVIX instance with your secret.
  const wh = new Webhook(WEBHOOK_SECRET);

  let evt: WebhookEvent;

  // Verify the payload with the headers
  try {
    evt = wh.verify(body, {
      "svix-id": svix_id,
      "svix-timestamp": svix_timestamp,
      "svix-signature": svix_signature,
    }) as WebhookEvent;
  } catch (err) {
    console.error("Error verifying webhook:", err);
    return new Response("Error occured", {
      status: 400,
    });
  }

  const eventType = evt.type;

  console.log({ eventType });
  // write this block by yourself
  try {
    // ... (existing code)

    if (eventType === "user.created") {
      const {
        id,
        email_addresses,
        image_url,
        username,
        first_name,
        last_name,
      } = evt.data;

      try {
        console.log("Creating user...");
        const user = await createUser({
          clerkId: id,
          name: `${first_name}${last_name ? ` ${last_name}` : ""}`,
          username: username!,
          email: email_addresses[0].email_address,
          picture: image_url,
        });
        return NextResponse.json({ message: "OK", user: user });
      } catch (err) {
        console.error("Error creating user:", err);
        return new Response("Error creating user", { status: 500 });
      }
    }

    return new Response("", { status: 201 });
  } catch (err) {
    console.error("Error in API route:", err);
    return new Response("Internal Server Error", { status: 500 });
  }
}
```

Deploy your website to Vercel or any other provider add your webhook endpoint of the deployed website to the environment variables here are images to do so
eg : www.example.com/api/webhook


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7d7r0kcp877vlykogcc.png)


Make sure you add NEXT_CLERK_WEBHOOK_SECRET in your .env.local

Here what .env.local should look like when deployed 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7l0p5rzzp0xx4pi8gnp.png)

Taddaa you are all set