Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error 429 - Too many requests #59

Open
thebrianbug opened this issue Sep 29, 2023 · 4 comments
Open

Error 429 - Too many requests #59

thebrianbug opened this issue Sep 29, 2023 · 4 comments

Comments

@thebrianbug
Copy link

thebrianbug commented Sep 29, 2023

Thanks for working on this project!

When running this example project, I'm seeing an error code 429, "Too many requests" when I click the button to log in. I am using the code sent to my email as my sign-in strategy. I get 3 emails in my inbox before Clerk cuts off the requests (none of them work to sign in because it continues to send multiple sign-in requests). This happens both when I click the big button to sign up, as well as if I click "Didn't receive a code? Resend" buttons on the next page of the login form.

All of the actions are repeated more than once.

{
    "errors": [
        {
            "message": "Too many requests. Please try again in a bit.",
            "code": "too_many_requests"
        }
    ]
}

Any idea why this may be happening @BigAB? Thanks again for making such a comprehensive example!

@BigAB
Copy link
Contributor

BigAB commented Oct 2, 2023

You know, @honzikec did the updates after my initial contribution, @honzikec do you have any ideas?

@honzikec
Copy link
Contributor

honzikec commented Oct 2, 2023

@BigAB, I updated the dependencies (mainly Svelte 3 to Svelte 4), which may have caused the issue. I had a quick look and it seems that there's an infinite loop in the clerkui action's update function. Seems like removing this line fixes the issue, but I'm not a svelte expert, so I'll leave it up to you to verify...

@thebrianbug
Copy link
Author

thebrianbug commented Oct 2, 2023

I got things working with a lot of changes, but mostly in clerk.ts. I've included other files for reference so folks aren't guessing. My 'lil buddy ChatGPT helped by adding clarifying console log dumps etc. to get me to this point.

The main issue is that the version you guys have creates multiple instances of the clerk module. This (hopefully) ensures that only one instance is used.

clerk.ts

import { writable, type Writable } from 'svelte/store';
import Clerk from '@clerk/clerk-js';
import type ClerkInstance from '@clerk/clerk-js';
import { PUBLIC_CLERK_PUBLISHABLE_KEY } from '$env/static/public';

// Create a writable store for Clerk
export const clerk: Writable<ClerkInstance | null> = writable(null);

// Initialize Clerk once
let clerkInstance: ClerkInstance | null = null;

export async function initializeClerk(): Promise<void> {
  if (!clerkInstance && typeof window !== 'undefined') {
    clerkInstance = new Clerk(PUBLIC_CLERK_PUBLISHABLE_KEY);

    await clerkInstance
      .load({
        afterSignInUrl: '/',
        afterSignUpUrl: '/',
        signInUrl: '/sign-in',
        signUpUrl: '/sign-up'
      })
      .catch((error: Error) => {
        console.error('Failed to load Clerk:', error);
      });

    clerk.set(clerkInstance);
  }
}

clerk.subscribe((clerkInstance) => {
  // I'm setting window.Clerk so that I can access Clerk from my e2e tests to sign in programmatically without the UI
  if (clerkInstance) window.Clerk = clerkInstance;
});

// This ensures that Clerk is initialized as soon as this module is imported.
initializeClerk();

clerk-ui.ts

import type Clerk from '@clerk/clerk-js';

interface ClerkUIConfig {
  clerk?: Clerk | null;
  componentType: 'SignIn' | 'SignUp' | 'UserButton';
}

const COMPONENT_CONFIG: Record<ClerkUIConfig['componentType'], Record<string, unknown>> = {
  SignIn: {},
  SignUp: {},
  UserButton: {
    showName: false,
    afterSignOutUrl: '/sign-in',
    userProfileMode: 'modal'
  }
};

export const clerkUI = (node: HTMLDivElement, { clerk, componentType }: ClerkUIConfig) => {
  let currentComponentType = componentType;
  console.log(`[ClerkUI] Initial component type: ${currentComponentType}`);

  if (clerk) {
    console.log(`[ClerkUI] Mounting initial ${currentComponentType}`);
    clerk[`mount${currentComponentType}`](node, COMPONENT_CONFIG[currentComponentType]);
  }

  return {
    update: ({ clerk: newClerk, componentType: newComponentType }: ClerkUIConfig) => {
      console.log(`[ClerkUI] Update triggered. New component type: ${newComponentType}`);

      if (currentComponentType !== newComponentType || clerk !== newClerk) {
        console.log(`[ClerkUI] Unmounting previous ${currentComponentType}`);
        clerk?.[`unmount${currentComponentType}`](node);
        currentComponentType = newComponentType;
        clerk = newClerk;
      }

      if (clerk) {
        console.log(`[ClerkUI] Mounting updated ${currentComponentType}`);
        clerk[`mount${currentComponentType}`](node, COMPONENT_CONFIG[currentComponentType]);
      }
    },
    destroy: () => {
      console.log(`[ClerkUI] Destroy called. Unmounting ${currentComponentType}`);
      clerk?.[`unmount${currentComponentType}`](node);
    }
  };
};

hooks.server.ts

import type { Handle } from '@sveltejs/kit';
import { verifySession } from '$lib/clerk-svelte/server/session';

export const handle: Handle = async ({ event, resolve }) => {
  const sessionToken = event.cookies.get('__session');

  if (sessionToken) {
    console.log('[Server Hook] Found session token in cookies.');
    try {
      const session = await verifySession(sessionToken);
      if (session) {
        console.log('[Server Hook] Session verified successfully.');
        event.locals.session = session;
      } else {
        console.warn('[Server Hook] Session verification returned no session.');
      }
    } catch (reason) {
      console.warn('[Server Hook] Warning during session verification:', reason);
    }
  } else {
    console.log('[Server Hook] No session token found in cookies.');
  }

  return resolve(event);
};

session.ts (server side)

import { createClerkClient } from '@clerk/clerk-sdk-node';
import { CLERK_SECRET_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
import type { RequestHandler, RequestEvent } from '@sveltejs/kit';

const clerk = createClerkClient({ secretKey: CLERK_SECRET_KEY });

export const users = clerk.users;

export const verifySession = async (sessionToken?: string) => {
  if (sessionToken) {
    try {
      const claims = await clerk.verifyToken(sessionToken);
      return {
        userId: claims.sub,
        claims
      };
    } catch (err) {
      console.warn('ERROR', err);
    }
  }
};

export const requireSession = (handler: RequestHandler) => async (event: RequestEvent) => {
  if (!event.locals.session) {
    return json({ ok: false, error: 'Users Session not found' });
  }
  return handler(event);
};

+layout.server.ts

import type { LayoutServerLoad } from './$types';

// get `locals.user` and pass it to the `page` store
export const load: LayoutServerLoad = async ({ locals }) => {
  // Retrieve the session data from the locals object
  const session = locals.session;

  // If there's no session data, there's no need to pass anything to the client-side
  if (!session) {
    return { props: {} };
  }

  // Pass the session data to the client-side
  return {
    props: {
      session
    }
  };
};

SignUp.svelte

<script lang="ts">
  import { clerkUI } from '$lib/clerk-svelte';
  import { clerk } from '$lib/clerk-svelte/stores/clerk';
</script>

<div class="h-screen flex justify-center items-center">
  <div
    class="sign-up"
    class:loading={!$clerk}
    use:clerkUI={{ clerk: $clerk, componentType: 'SignUp' }}
  />
</div>

routes/sign-up/+page.svelte

<script lang="ts">
  import { SignUp } from '$lib/clerk-svelte';
</script>

<SignUp />

example clerk store usage

<script lang="ts">
  import { UserButton } from '$lib/clerk-svelte';
  import { clerk } from '$lib/clerk-svelte/stores/clerk';
</script>

{#if $clerk?.user}
  <div class="m-2">
    <UserButton />
  </div>
{:else}
  <a href="/sign-in" class="rounded bg-white text-black m-1 px-2 py-1">Sign in</a>
{/if}

@thebrianbug
Copy link
Author

FYI this is now part of a package, here: https://github.com/markjaquith/clerk-sveltekit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants