Introduction
Embarking on the journey of web development with Nuxt 3 and Recaptcha v3 can be both thrilling and challenging. Today, I tried unveil a solution that simplifies the integration process, especially when dealing with the mysterious g-recaptcha-token that being needed by the API. We need to provide it as a field when submitting a post request to the API for backend purpose: to make sure that the recaptcha token received is matching with the setup of the recaptcha token saved from the backend. If you've ever struggled with the token being undefined in production even though we already trying to use the tutorial from the vue-recaptcha-v3, me too!
In this article, I will unravel the intricacies and provide a step-by-step guide to empower developers with javascript only (no typescript) in their quest for a flawless integration. Hope it will helps you.
Part 1: Plugins - The Gateway to Recaptcha Power
Before started, please make sure that you already install the vue-recaptcha-v3.
Plugins serve as the gateway to injecting external functionalities into your Nuxt 3 project. To kickstart the integration process, create a dedicated plugin for Recaptcha v3 on plugins/recaptcha.js
.
// plugins/recaptcha.js
import { VueReCaptcha } from 'vue-recaptcha-v3'
// The plugin enables the usage of Google reCAPTCHA in a Nuxt.js application
// by registering the VueReCaptcha plugin with the necessary configuration options.
export default defineNuxtPlugin(nuxtApp => {
const config = useRuntimeConfig()
const options = {
siteKey: config.public.RECAPTCHA_SITE_KEY,
loaderOptions: {
autoHideBadge: true,
useRecaptchaNet: true,
renderParameters: {
hl: 'id'
}
}
}
nuxtApp.vueApp.use(VueReCaptcha, options)
})
Part 2: Composable - The Art of Reusable Logic
Create a composable to encapsulate the logic for handling Recaptcha interactions. This composable will be the engine that drives the token generation process.
Please make sure that you save it on the composables
folder.
// composables/useGoogleRecaptcha.js
import { useReCaptcha } from 'vue-recaptcha-v3'
/**
* The exported executeRecaptcha function allows
* you to execute reCAPTCHA actions
* and retrieve the reCAPTCHA token along with the header options
* to be used in subsequent requests.
*/
export default () => {
const recaptchaInstance = useReCaptcha()
const executeRecaptcha = async action => {
/**
* Wait for the recaptchaInstance to be loaded
* by calling the recaptchaLoaded method.
* This ensures that the reCAPTCHA library is fully loaded
* and ready to execute reCAPTCHA actions.
*/
await recaptchaInstance?.recaptchaLoaded()
const token = await recaptchaInstance?.executeRecaptcha(action)
const headerOptions = {
headers: {
'google-recaptcha-token': token
}
}
return { token, headerOptions }
}
return { executeRecaptcha }
}
Part 3: Calling the Function - Bridging the Frontend and Backend
Now that we have our Recaptcha composable ready, let's integrate it into our component and call the function whenever needed.
In this example, we use NuxtApiParty for handling the api that they will generate the useApiData
based on our config. You can learn more about NuxtApiParty here: https://nuxt-api-party.byjohann.dev/
<template>
<!-- insert code for your component's template -->
</template>
<script setup>
// other functions
const { executeRecaptcha } = useGoogleRecaptcha()
// the function where you submit the form
const submitForm = async values => {
isSubmitting.value = true
const { token } = await executeRecaptcha('submit')
// console.log(token) -> to make sure there is the token
// combine the form value with g-recaptcha-response that we need to send via api
const bodyData = {
...formData.value,
'g-recaptcha-response': token
}
const { pending, error } = await useApiData(
`/api/form_submissions`,
{
method: 'post',
cache: false,
body: bodyData
}
)
if (error.value) {
isSubmitting.value = pending.value
showFail.value = true
} else {
isSubmitting.value = pending.value
clearForm()
showSuccess.value = true
}
}
// other functions
</script>
Additional step (if your plugins are not being called)
All plugins inside are auto-registered, you don't need to add them to your nuxt.config separately.
source: Thanks to
Thiru for sending this source https://nuxt.com/docs/guide/directory-structure/plugins
If you have already registered but are not being called (special case), you can try this additional step:
Nuxt Config - Configuring the Nexus of Nuxt 3 and Recaptcha
Now, let's configure Nuxt to incorporate the Recaptcha plugin seamlessly. Open your nuxt.config.ts file and include the plugin in the plugins array.
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
// {insert CODE for other Nuxt config options}
// Include the Recaptcha plugin
plugins: [{src: '~/plugins/recaptcha.js'}],
// {insert CODE for other Nuxt config options}
};
Conclusion:
Congratulations! You've embarked on a journey to seamlessly integrate Nuxt 3 with Recaptcha v3, overcoming the challenges associated with g-recaptcha-token (getting the token sent to the API). This solution ensures a smooth transition from development to production, empowering developers to create secure and reliable web applications. Happy coding!
Top comments (7)
Thanks for the article!
Suggestion: As mentioned here
All plugins inside are auto-registered, you don't need to add them to your nuxt.config separately.
Part 2 is not needed.Thank you for your suggestion. I revised my article and create an additional steps in case the auto-registered is not working 😊
Thanks for the article, it helps me. But i have a correction here
that
should be
because when you
await executeRecaptcha('submit')
the action will always undefinedThank you for your correction! I didn't know that the action will always be undefined because using
action.name
always works for me. I revised my article already 😆Nice post! Thank you.
Now it loads google recaptcha script from the beginning, which has its downside in terms of google core vitals.
How can I load the script, only when I need to show a form?
Nice article.
Do You push it into GitHub?
Can I see it?
Thanks
I'm getting token undefined... I'm not sure what's wrong