Documentação
Comece a construirQuickstarts por frameworkVue

Vue

Um composable compartilhado por todos os componentes e guards que perguntam por permissão.

Este é o formato que o próprio console da nulvless usa — Vue 3 com o mesmo SDK, rodando inteiramente no navegador. Os guards de rota decidem o que é renderizado; quem decide o que é permitido continua sendo a sua API.

Instalação

npm install @nulvless/nulvless-js

Crie o client

src/lib/nulvless.ts
import { createClient, Storage } from '@nulvless/nulvless-js';

export const nulvless = createClient(
  import.meta.env.VITE_NULVLESS_URL,
  import.meta.env.VITE_NULVLESS_KEY,
  {
    storage: Storage.Local,
    trackUser: true,
    language: () => navigator.language,
  },
);

language define em qual idioma a plataforma renderiza qualquer mensagem disparada por este client — um e-mail de verificação, por exemplo.

Um composable de sessão

src/composables/useSession.ts
import { onUnmounted, ref } from 'vue';
import type { Session } from '@nulvless/nulvless-js';
import { nulvless } from '@/lib/nulvless';

const session = ref<Session | null>(null);
const loading = ref(true);

nulvless.auth.getSession().then(({ data }) => {
  session.value = data;
  loading.value = false;
});

export function useSession() {
  const offIn = nulvless.auth.on('signed-in', async () => {
    const { data } = await nulvless.auth.getSession();
    session.value = data;
  });
  const offOut = nulvless.auth.on('signed-out', () => (session.value = null));
  const offDead = nulvless.auth.on('unauthorized', () => (session.value = null));

  onUnmounted(() => {
    offIn();
    offOut();
    offDead();
  });

  return { session, loading };
}

As refs vivem no escopo do módulo, então todos os componentes compartilham uma única sessão em vez de cada um buscar a sua.

Entrada

src/views/SignIn.vue
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { nulvless } from '@/lib/nulvless';

const router = useRouter();
const email = ref('');
const password = ref('');
const error = ref<string | null>(null);

async function signIn() {
  error.value = null;

  const { data, error: signInError } = await nulvless.auth.signIn({
    email: email.value,
    password: password.value,
  });

  if (signInError) {
    error.value = 'Confira o e-mail e a senha.';
    return;
  }

  if (data.challenge) {
    router.push({ name: 'verify', state: { challenge: data.challenge } });
    return;
  }

  router.push('/');
}
</script>

<template>
  <form @submit.prevent="signIn">
    <input v-model="email" type="email" />
    <input v-model="password" type="password" />
    <p v-if="error" role="alert">{{ error }}</p>
    <button type="submit">Entrar</button>
  </form>
</template>

Cadastro

O cadastro se ramifica no desafio, porque se a confirmação é por link ou por código é uma configuração do ambiente.

const { data, error } = await nulvless.auth.signUp({
  email: email.value,
  password: password.value,
  profile: { name: name.value },
});

if (error) return;

if (data.challenge) {
  if (data.challenge.preferred === 'link') {
    checkYourEmail.value = true;
    return;
  }
  router.push({ name: 'verify', state: { challenge: data.challenge } });
  return;
}

router.push('/');

Proteja as rotas

src/router/guards.ts
import type { NavigationGuard } from 'vue-router';
import { nulvless } from '@/lib/nulvless';

export const requireAuth: NavigationGuard = async (to) => {
  const { data } = await nulvless.auth.getSession();
  if (!data) return { name: 'sign-in', query: { next: to.fullPath } };
  return true;
};

Para uma rota que também exige permissão, pergunte na mesma chamada:

export const requireOwner: NavigationGuard = async (to) => {
  const { error } = await nulvless.auth.verify({
    resource: String(to.params.org),
    roles: ['owner', 'admin'],
  });

  return error ? { name: 'forbidden' } : true;
};

Próximos

Nesta página