DEMO: Captcha Integration
This example uses HCaptcha to inject a captcha token into the request body.
Normally it only makes sense to have only one captcha per page. So, the captcha element is here encapsulated in a reusable component that sets the captcha token in a global model that is consumed by the factory's conditional building when the API request being created has the useCaptcha
meta property set.
Note
In real life it would probably be simpler just to have the conditional captcha injector straight in a request interceptor defined right in the API config or have a reusable adapter that does the same. The purpose of this example is to show a comprehensive use of the factory capabilities.
Code
ts
import { APIConfig, HTTPRequestFactory } from '../../../src';
//NOTE: you will import from @apihive/apihive-core in your own code
import captchaModel from './captcha-model';
const myAPI: APIConfig = {
name: 'default',
baseURL: 'https://httpbin.org',
endpoints: {
'register-user': {
target: '/post',
method: 'POST',
meta: {
useCaptcha: true
}
},
'some-other-endpoint': {
target: '/post',
method: 'POST'
}
}
};
export default new HTTPRequestFactory()
.withAPIConfig(myAPI)
.when((config) => config.meta.useCaptcha)
.withRequestInterceptors(({ controls }) => {
controls.replaceBody((body) =>
JSON.stringify(
Object.assign({}, JSON.parse(body), {
captcha: captchaModel.token //your function to retrieve the local captcha token
})
)
);
});
ts
import { reactive } from 'vue';
import requestFactory from './requestFactory';
type User = {
name: string;
email: string;
};
type CaptchaResponseModel = {
user: User;
somethingElse: {
foo: string;
};
results: {
registerUser: (User & { captcha: string }) | null;
doSomethingElse: { foo: string; captcha?: string } | null;
};
};
class CaptchaRequestsController {
private _model = reactive<CaptchaResponseModel>({
user: {
name: 'John Doe',
email: 'john.doe@example.com'
},
somethingElse: {
foo: 'bar'
},
results: {
registerUser: null,
doSomethingElse: null
}
});
get model() {
return this._model;
}
async registerUser() {
const response = await requestFactory
.createAPIRequest('register-user')
.withJSONBody(this._model.user)
.execute();
this._model.results.registerUser = response.json;
}
async doSomethingElse() {
const response = await requestFactory
.createAPIRequest('some-other-endpoint')
.withJSONBody(this._model.somethingElse)
.execute();
this._model.results.doSomethingElse = response.json;
}
}
export default new CaptchaRequestsController();
vue
<template>
<article class="captcha-request-demo demo pico">
<form @submit.prevent>
<h3>Register User</h3>
<input type="text" v-model="controller.model.user.name" placeholder="Name" />
<input type="email" v-model="controller.model.user.email" placeholder="Email" />
<div class="flex-cols">
<button :disabled="!captchaModel.token" @click="controller.registerUser">Register</button
><CaptchaElement />
</div>
</form>
<br />
<br />
<InlineMessage ref="userInlineMessage" />
<transition>
<pre v-if="controller.model.results.registerUser">PAYLOAD: {{ controller.model.results.registerUser }}</pre>
</transition>
<hr />
<div>
<h3>Do Something Else</h3>
<form @submit.prevent>
<input type="text" v-model="controller.model.somethingElse.foo" placeholder="foo" />
<button @click="controller.doSomethingElse">Do Something Else</button>
</form>
</div>
<br />
<br />
<InlineMessage ref="somethingElseInlineMessage" />
<transition>
<pre v-if="controller.model.results.doSomethingElse">PAYLOAD: {{ controller.model.results.doSomethingElse }}</pre>
</transition>
</article>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import CaptchaElement from './CaptchaElement.vue';
import captchaModel from './captcha-model';
import controller from './captcha-requests-controller';
import InlineMessage from 'apihive-common-docs-assets/components/InlineMessage.vue';
const userInlineMessage = ref<typeof InlineMessage | null>(null);
const somethingElseInlineMessage = ref<typeof InlineMessage | null>(null);
watch(() => controller.model.results.registerUser, () => {
if(controller.model.results.registerUser?.captcha) {
userInlineMessage.value!.show('success', 'Yeyy! Captcha was injected');
}
})
watch(() => controller.model.results.doSomethingElse, () => {
if(!controller.model.results.doSomethingElse?.captcha) {
somethingElseInlineMessage.value!.show('success', 'Yeyy! Captcha NOT injected');
}
})
</script>
<style lang="scss" scoped>
.captcha-request-demo {
pre {
background-color: var(--pico-color-azure-100);
padding: 1rem;
&:empty {
display: none;
}
}
.flex-cols {
display: flex;
gap: 1rem;
align-items: flex-start;
justify-content: space-between;
@media (max-width: 600px) {
flex-direction: column;
}
}
}
</style>
vue
<template>
<vue-hcaptcha
sitekey="11aa5322-6cfe-417c-9844-03d8657048ea"
@verify="onVerified"
@expired="onExpired">
</vue-hcaptcha>
</template>
<script setup lang="ts">
import VueHcaptcha from '@hcaptcha/vue3-hcaptcha';
import captchaModel from './captcha-model';
const onVerified = (token: string) => {
captchaModel.token = token;
};
const onExpired = () => {
captchaModel.token = '';
};
</script>
ts
import { reactive } from 'vue';
export default reactive({
token: '',
})