DEV Community

Cover image for Automatically Send User Data to Database with Clerk OAuth
Satomi Nagano / Akikaze1119
Satomi Nagano / Akikaze1119

Posted on

1

Automatically Send User Data to Database with Clerk OAuth

Key Points

  • Learn how to use Clerk webhooks.
  • Understand how to verify Clerk webhooks.
  • Use ngrok to expose a local endpoint for external access.

Background

When using Clerk OAuth for user authentication, detecting new user registrations from the web app can be challenging. To address this, we use Clerk webhooks to automatically send user data to a database upon signup. This article walks through the implementation steps.

Prerequisites

Implementation Steps

1. Set Up ngrok

Clerk’s documentation recommends using ngrok to allow webhook requests to reach the local app.

1.1 Install and Set Up ngrok

Sign up on ngrok's official website and follow the installation steps provided in the Setup & Installation section.

1.2 Obtain an External URL

Start your local development server (npm run dev), then run

ngrok http [Port]

Example: In the case of http://localhost:3000

ngrok http 3000
Enter fullscreen mode Exit fullscreen mode

Copy the Forwarding URL displayed.
Console screen

What is ngrok?

Ngrok allows a locally running web app to be temporarily accessible over the internet. In this case, it enables Clerk webhooks to send requests to a local development server.

2. Configure Clerk Webhooks

2.1 Access Clerk Dashboard

Go to the Clerk dashboard.
Clerk dashboard button

2.2 Add a Webhook Endpoint

  1. Navigate to the Webhooks tab and click Add Endpoint. Clerk side menu End point
  2. Set the Endpoint URL to the ngrok forwarding URL + /api/webhooks/user (e.g., https://example.ngrok-free.app/api/webhooks/user).
  3. Add a description if needed.
  4. Subscribe to the user.created event.
  5. Click Create. Setup endpoint

3. Add Signing Secret to .env.local

On the endpoint's settings page, copy the Signing Secret from the webhook settings.
Copy the Signing Secret
Add Signing Secret, like SIGNING_SECRET=whsec_123, to your .env.local file, which should already include your Clerk API keys and DB URL.
The file should resemble:

DATABASE_URL=postgresql://database_url
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_YXdha2Utb3gtODUuY2xlcmsuYWNjb3VudHMuZGV2JA
CLERK_SECRET_KEY=sk_test_5DbmoNJeli74tMRgtzZLBY4SC6gpTLbNGnvAPeWJkE
SIGNING_SECRET=whsec_123
Enter fullscreen mode Exit fullscreen mode

4. Make the Webhook Route Public

If using clerkMiddleware(), ensure that /api/webhooks(.*) is public. For information on configuring routes, see the clerkMiddleware() guide.

5. Install svix

Clerk uses svix to send signed webhook requests. Install it:

npm install svix
Enter fullscreen mode Exit fullscreen mode

6. Create the Webhook API Route

Create /src/app/api/webhooks/user/route.ts to receive webhook requests:

import { NextResponse } from 'next/server';
import { Pool } from 'pg';

import { Webhook } from 'svix';
import { headers } from 'next/headers';
import { WebhookEvent } from '@clerk/nextjs/server';

// Initialize Neon DB connection
const pool = new Pool({
  connectionString: process.env.DATABASE_URL, // Add to .env
});

export async function POST(req: Request) {
  const SIGNING_SECRET = process.env.SIGNING_SECRET;

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

  // Create new Svix instance with secret
  const wh = new Webhook(SIGNING_SECRET);

  // Get headers
  const headerPayload = await 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: Missing Svix headers', {
      status: 400,
    });
  }

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

  let evt: WebhookEvent;

  // Verify payload with 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: Could not verify webhook:', err);
    return new Response('Error: Verification error', {
      status: 400,
    });
  }

  // Handling the user.created event (modify as needed)
  try {
    if (evt.type === 'user.created') {
      const { id } = evt.data;
      // Insert user into Neon DB
      await pool.query(
        `INSERT INTO users (user_id) VALUES ($1) 
        ON CONFLICT (user_id) DO NOTHING`,
        [id]
      );
      return NextResponse.json({ message: 'User saved to DB' }, { status: 200 });
    }

    return NextResponse.json({ message: 'Unhandled event' }, { status: 200 });
  } catch (error) {
    console.error('Webhook Error:', error);
    return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
  }
}
Enter fullscreen mode Exit fullscreen mode

7. Test the Webhook

  1. In the Clerk Webhook settings, go to Testing.
  2. Select user.created from Send event. Testing
  3. Click Send Example.
  4. If the request succeeds, it will be marked as Succeeded in Message Attempts Message Attempts

Therefore your app can automatically register new user information in the database even when using Clerk's OAuth. Please try registering a new user within the app to confirm that it works correctly.

Debugging Tips

If you encounter issues:

  • Check your Middleware configuration
  • Check your configuration in the Clerk Dashboard [For example] Directory: /src/app*/api/webhooks/user/route.ts* Endpoint in Clerk : http://.../api/webhooks/user
  • Test the Route Handler or API Route(following the guide) 1.Create a test route in your application:

Test route
▽app/webhooks/test/route.ts

export async function POST() {
  return Response.json({ message: 'The route is working' });
}
Enter fullscreen mode Exit fullscreen mode

2.Run your application.
3.Use curl to test:

curl -H 'Content-Type: application/json' -X POST
http://localhost:3000/api/webhooks/test
Enter fullscreen mode Exit fullscreen mode

4.If you see the {"message":"The route is working"}, then the basic Route Handler is working and ready to build on.

User deletion in Clerk

If you want to delete user information on Clerk, you can do so from the "User" tab.
User Tab

Deploying

  1. Add the webhook URL in Clerk with the production domain.
  2. Add the Signing Secret to production environment variables.
  3. Deploy your app.

Conclusion

  • Clerk webhooks enable real-time detection of new users.
  • Ngrok allows local webhook testing by exposing an accessible endpoint.
  • Implementing svix ensures request verification and security.

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay