Cómo crear una aplicación de encuestas Vue utilizando la base de datos y la autenticación de Firebase

Publicado: 2022-03-10
Resumen rápido ↬ Este tutorial lo guiará paso a paso para crear una aplicación de encuesta funcional usando Vue.js y Firebase. Desde la validación de los datos del usuario a través de Vuelidate, hasta la autenticación, el almacenamiento de los datos del usuario, la protección de rutas y el envío de datos a los servidores de Firebase. Todos los pasos utilizados en el tutorial son prácticos y se pueden reproducir en cualquier proyecto de la vida real, incluso con un backend personalizado.

En este tutorial, creará una aplicación de encuestas, donde aprenderemos a validar los datos de los formularios de nuestros usuarios, implementar la autenticación en Vue y poder recibir datos de encuestas mediante Vue y Firebase (una plataforma BaaS).

A medida que construimos esta aplicación, aprenderemos cómo manejar la validación de formularios para diferentes tipos de datos, incluido llegar al backend para verificar si ya se recibió un correo electrónico, incluso antes de que el usuario envíe el formulario durante el registro.

Además, la aplicación manejaría el inicio de sesión del usuario con API tranquilas. Hará uso de Authguard en el enrutador Vue para evitar que los usuarios que no hayan iniciado sesión obtengan acceso al formulario de la encuesta y envíe con éxito los datos de la encuesta de los usuarios registrados a una base de datos segura.

Solo para que estemos en la misma página, aclaremos qué es Firebase y qué hará en este tutorial. Firebase es un conjunto de herramientas para "crear, mejorar y hacer crecer su aplicación". Le da acceso a una gran parte de los servicios que los desarrolladores normalmente tendrían que construir por sí mismos, pero que en realidad no quieren construir, porque prefieren centrarse en la experiencia de la aplicación en sí. Esto incluye cosas como análisis, autenticación, bases de datos, almacenamiento de archivos y la lista continúa.

Esto es diferente al desarrollo de aplicaciones tradicional, que generalmente implica escribir software tanto de frontend como de backend. El código del frontend simplemente invoca los puntos finales de la API expuestos por el backend, y el código del backend realmente hace el trabajo. Sin embargo, con los productos de Firebase, se pasa por alto el backend tradicional y se pone el trabajo en el cliente. Técnicamente, esto permite que los ingenieros front-end como yo construyamos aplicaciones full-stack escribiendo solo código front-end.

¡Más después del salto! Continúe leyendo a continuación ↓

La conclusión es que Firebase actuaría como nuestro backend en este proyecto brindándonos los puntos finales de API necesarios para manejar nuestras necesidades de autenticación y base de datos. Al final, habrá creado una aplicación de encuestas funcional con Vue+ Firebase. Después de eso, puede continuar y crear cualquier aplicación web de su elección utilizando estos mismos procesos, incluso con un backend personalizado.

Para continuar, debe tener Node y npm/yarn instalados en su máquina. Si aún no lo ha hecho, siga estas guías rápidas para instalar yarn o npm en su máquina. También debe tener una comprensión básica de la sintaxis del enrutador Vue, Vuex y Vue para este tutorial.

Los archivos de inicio para este tutorial están aquí, que contienen los archivos base para este proyecto, y aquí está el repositorio para la demostración completa. Puede clonar o descargar los repositorios y ejecutar npm install en su terminal.

Después de instalar el archivo de inicio, verá una página de bienvenida, que tiene las opciones para registrarse e iniciar sesión. Después de iniciar sesión, podrá acceder a la encuesta.

Arquitectura de la aplicación de encuestas
Esto describe cómo funcionará nuestra aplicación de encuestas. (Vista previa grande)

Siéntase libre de crear un nuevo proyecto si desea construir este proyecto completamente por su cuenta, solo asegúrese de instalar Vuex, el enrutador Vue, Vuelidate y axios en su proyecto Vue. Así que saltemos directamente:

Primero, necesitaremos una cuenta de Firebase para configurar este proyecto, que es muy parecido a crear un contenedor para nuestra aplicación, lo que nos da acceso a la base de datos, varios medios de autenticación, alojamiento, etc. Es sencillo de configurar una vez que Estás en el sitio de Firebase.

Página de inicio de base de fuego
La página de inicio donde puede registrarse y comenzar su viaje de Firebase. (Vista previa grande)
Crear nuevos proyectos de Firebase
Creación de proyectos de Firebase (vista previa grande)

Ahora que tenemos nuestro proyecto, lo siguiente es configurar tanto nuestro sistema de autenticación como nuestra base de datos (base de datos en tiempo real) en Firebase.

  • Haga clic en la opción "autenticación";
  • Configure el "método de inicio de sesión" que queremos (en este caso, correo electrónico/contraseña).
Configurar el método de inicio de sesión
Configure el método de autenticación de correo electrónico/contraseña para el proyecto. (Vista previa grande)
  • Haga clic en "base de datos".
  • Elija "Base de datos en tiempo real" y copie este enlace que está justo en la parte superior.

Será muy útil como punto final de la API cuando queramos enviar los datos a nuestra base de datos de firebase.

Nos referiremos a esta API como la API de la base de datos. Para usarlo, deberás agregar el nombre de la base de datos de tu elección al enviarlo. Por ejemplo, para enviar a una base de datos llamada usuario. Simplemente agrega user.json al final:

 {databaseAPI}/user.json
base de datos en tiempo real
Use la API sobre la base de datos para enviar datos a la base de datos. (Vista previa grande)

Después de esto, iremos a la documentación de la API de descanso de autenticación de Firebase para obtener nuestros puntos finales de API de registro e inicio de sesión. Dentro de estos puntos finales, se necesitará la clave API de nuestro proyecto, que se puede encontrar en la configuración de nuestro proyecto.

Validación

Volviendo a nuestro código, habrá una validación de los datos de registro antes de que se envíen al servidor, solo para asegurarse de que el usuario envíe la información adecuada. Usaremos Vuelidate, que es una biblioteca genial que facilita la validación en Vue. En primer lugar, instala Vuelidate en el proyecto:

 npm i vuelidate

Vaya a src/components/auth/signup.vue y, dentro de la etiqueta del script, importe vuelidate y todos los eventos necesarios que necesitaremos de la biblioteca, como se ve a continuación.

Nota : puede consultar los documentos para obtener una descripción general completa de la biblioteca y todos los eventos disponibles.

 import { required, email, numeric, minValue, minLength, sameAs } from 'vuelidate/lib/validators'

Una explicación rápida:

Descripción
Valor
required El valor es obligatorio
email El valor debe ser un correo electrónico
numeric Tiene que ser un número
minValue Valor numérico mínimo que el usuario puede ingresar.
sameAs Se utiliza para comparar entre dos valores para asegurarse de que son iguales
Importa también [`axios`](https://github.com/axios/axios) para poder enviar una solicitud HTTP al servidor:
 import axios from 'axios'
Antes de continuar, necesitaremos agregar algunas reglas a la base de datos para poder validar el correo electrónico como deberíamos, tal como se ve a continuación:
Reglas de base de fuego
Las reglas de la base de datos ayudan a decidir si puede o no acceder a la base de datos en cualquier momento. (Vista previa grande)
 "read" = "true"
Lo que significa que la base de datos se puede leer sin ningún obstáculo desde el lado del cliente.
 "write" = "auth" !== null
No puede escribir en la base de datos a menos que sea un usuario autenticado.
 "Users" = { "onIndex" : ["email"] }
Esto nos permite consultar el documento `users` con un índice de `email`. Es decir, literalmente puede filtrar la base de datos para un correo electrónico único. Luego agregue una propiedad calculada personalizada con el nombre `validaciones` al igual que tenemos métodos, computados, etc. En `validaciones` tendremos métodos para validar los datos necesarios a partir de `correo electrónico` donde se requiere y obviamente debe ser un correo electrónico . Además, queremos poder decirle a un usuario cuándo otra persona ya tomó un correo electrónico, revisando la base de datos después de que el usuario lo haya escrito usando algo llamado validadores asíncronos, todo dentro de un validador personalizado y todo es compatible con [vuelidate. ](https://vuelidate.js.org/#sub-asynchronous-validation)
 validations : { email: { required, email, unique: val => { if (val === '') return true return axios.get('https://vue-journal.firebaseio.com/users.json?orderBy="email"&equalTo="' + val + '"') .then(res => { return Object.keys(res.data).length === 0 }) } } }
Luego, en único, consulte la base de datos usando axios y use las teclas Object.keys predeterminadas para devolver la respuesta solo si su longitud es 0. Para la edad, agregará el valor requerido, numérico y mínimo de 18 que está asignado a `minVal ` como sus propiedades.
 age: { required, numeric, minVal: minValue(18) }
Las propiedades de la contraseña son obligatorias, con una longitud mínima de 6 asignada a `minLen`.
 password: { required, minLen: minLength(6) }
Las propiedades de `confirmPassword` son básicamente las mismas que la contraseña.
 confirmPassword: { sameAs: sameAs(vm => { return vm.password }) }
Para decirle al usuario que se tomó el correo electrónico, use `v-if` para verificar si `unique` es verdadero o falso. Si es verdadero, significa que la longitud del objeto devuelto es 0, y el correo electrónico aún se puede usar y viceversa. De la misma manera, puede verificar si la entrada del usuario es un correo electrónico real usando `v-if`. Y para todos los divs circundantes en la entrada individual, agregaremos una clase no válida que se activa una vez que hay un error en esa entrada. Para vincular los eventos de validación a cada una de las entradas en el HTML, usamos [`$touch()`](https://vuelidate.js.org/#sub-without-v-model) como se ve con el `email ` a continuación.
 <div class="input" :class="{invalid: $v.email.$error}"> <h6 v-if="!$v.email.email">Please provide a valid email address.</h6> <h6 v-if="!$v.email.unique">This email address has been taken.</h6> <input type="email" placeholder="Email" @blur="$v.email.$touch()" v-model="email"> </div>
`Age`, `password` y `confirmPassword` se vincularán a su entrada HTML de manera similar al `correo electrónico`. Y desactivaremos el botón 'Enviar' si hay un error en alguna de las entradas.
 <button type="submit" :disabled="$v.$invalid">create</button>
Aquí hay un [ejemplo de CodePen] completo (https://codepen.io/atanda1/pen/Yzyqrjv) para esta sección de revisión.
Implementación de Vuelida
Vuelidate se utiliza aquí para determinar el tipo de datos que se envían a la base de datos. (Vista previa grande)
## Autenticación Esta aplicación es un SPA y no se recarga como los sitios tradicionales, por lo que usaremos Vuex, como nuestra única "fuente de verdad" para permitir que cada componente de nuestra aplicación conozca el estado general de autenticación. Vamos a nuestro archivo de tienda y creamos ambos métodos de inicio de sesión/registro dentro de las acciones. La respuesta (`token` y `userId`) recibida cuando enviamos los datos de los usuarios, se almacenará dentro de nuestro estado. Esto es importante porque el token se utilizará para saber si todavía estamos conectados o no en algún momento dentro de nuestra aplicación. El `token`, el `userId` y el usuario se crean en el estado con un valor inicial nulo. Llegaremos al usuario mucho más tarde, pero por ahora, nos centraremos en los dos primeros.
 state: { idToken: null, userId: null, user: null }
Luego se crean mutaciones para cambiar el estado cuando sea necesario.
authUser Guarda el token y el userId de usuario
storeUser Almacena la información del usuario
clearAuthData Borra los datos de vuelta al estado inicial
 mutations: { authUser (state, userData) { state.idToken = userData.token state.userId = userData.userId }, storeUser (state, user) { state.user = user }, clearAuthData (state) { state.idToken = null state.userId = null state.user = null } }
Para registrarse/iniciar sesión, tendremos que crear acciones individuales para ambos, donde enviamos nuestras solicitudes de autenticación al servidor. Después de lo cual, nuestra respuesta (token e Id. de usuario) de registro/inicio de sesión se compromete a authUser y se guarda en el almacenamiento local.
 signup ({commit, dispatch}, authData) { axios.post('https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyCFr-OMMzDGp4Mmr0t66w2cTGfNazYjptQ', { email: authData.email, password: authData.password, returnSecureToken: true }) .then(res => { console.log(res) commit('authUser', { token: res.data.idToken, userId: res.data.localId }) localStorage.setItem('token', res.data.idToken) localStorage.setItem('userId', res.data.localId) localStorage.setItem('email', res.data.email) dispatch('storeUser', authData) setTimeout(function () { router.push('/dashboard') }, 3000) }) .catch(error => console.log(error)) }
 login ({commit}, authData) { axios.post('https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=AIzaSyCFr-OMMzDGp4Mmr0t66w2cTGfNazYjptQ', { email: authData.email, password: authData.password, returnSecureToken: true }) .then(res => { console.log(res) localStorage.setItem('token', res.data.idToken) localStorage.setItem('userId', res.data.localId) localStorage.setItem('email', res.data.email) commit('authUser', { token: res.data.idToken, userId: res.data.localId }) router.push('/dashboard') }) .catch(error => console.log(error.message)) }
Pero aquí está la parte difícil, lo que haremos con la acción de registro en particular es enviar solo el correo electrónico y la contraseña para registrarse en la base de datos de autenticación. En el sentido real, no tenemos acceso para usar los datos en esta base de datos de autenticación, y no enviamos ninguno de nuestros datos de registro además del correo electrónico/contraseña. Entonces, lo que haremos es crear otra acción para enviar los datos completos de registro a otra base de datos. En este documento de base de datos separado, tendremos acceso completo a toda la información que elijamos guardar allí. Llamaremos a esta nueva acción `storeUser` Luego vamos a nuestra acción de registro y enviamos todo el objeto que contiene nuestros datos de registro a una base de datos a la que ahora tenemos acceso a través de `storeUser`. **Nota:** Es posible que no desee enviar la contraseña de su usuario con `storeUser` a la base de datos por razones de seguridad.
 storeUser ({ state}, userData) { if (!state.idToken) { return } axios.post('https://vue-journal.firebaseio.com/users.json' + '?auth=' + state.idToken, userData) .then(res => console.log(res)) .catch(error => console.log(error)) } }
`storeUser` agrega una consulta utilizando nuestro token recién obtenido y la API de la base de datos mientras se publica en la base de datos. Esto se debe a que no podemos escribir en nuestra base de datos, excepto que estemos autenticados con nuestra prueba (el token). Esa es la regla que le dimos a Firebase al principio, ¿recuerdas?
 “write” = “auth” !== null
El código completo para las acciones de registro/inicio de sesión está justo [aquí] (https://codepen.io/atanda1/pen/mdePKqj). Luego envíe tanto el registro como el inicio de sesión desde sus componentes dentro del método `onSubmit` a las acciones respectivas en la tienda.
 methods : { onSubmit () { const signupData = { email : this.email, name : this.name, age : this.age, password : this.password, confirmPassword : this.co nfirmPassword } this.$store.dispatch('signup', signupData) } } }
**Nota:** `signupData` contiene los datos del formulario.
 methods : { onSubmit = { const formData = { email : this.email, password : this.password } this.$store.dispatch('login', {email: formData.email, password: formData.password}) } }
## AuthGuard Es necesario que AuthGuard evite que los usuarios que no hayan iniciado sesión obtengan acceso al tablero donde enviarán la encuesta. Vaya al archivo de ruta e importe nuestra tienda.
 import store from './store'
Dentro de la ruta, vaya a la ruta del tablero y agregue lo siguiente:
 const routes = [ { path: '/', component: WelcomePage }, { path: '/signup', component: SignupPage }, { path: '/signin', component: SigninPage }, { path: '/dashboard', component: DashboardPage, beforeEnter (to, from, next) { if (store.state.idToken) { next() } else { next('/signin') } } } ]
Todo esto hace es comprobar si hay un token en el estado, si es así, damos acceso al tablero y viceversa. ## LogOut Para crear nuestra opción de cierre de sesión, haremos uso de `clearAuth` que creamos anteriormente en `mutations`, que simplemente establece tanto el `token` como el `userId` en `null`. Ahora creamos una nueva `acción` `logout`, que se compromete a `clearAuth`, elimina el almacenamiento local y agrega `router.replace('/')` para redirigir al usuario por completo.
 actions: { logout ({commit}) { commit('clearAuth') localStorage.removeItem('token') localStorage.removeItem('userId') router.replace('/') } }
En el componente de encabezado, tenemos un método `onLogout` que envía nuestra acción de cierre de sesión en la tienda.
 methods: { onLogout() { this.$store.dispatch('logout') } }
Luego agregamos un `@click` al botón que activa el método `onLogout` como podemos ver [aquí](https://codepen.io/atanda1/pen/jObqKNd).
 <ul @click="onLogout">Log Out</ul>
## UI_State Ahora que hemos otorgado acceso condicional al tablero, el siguiente paso es eliminarlo de la barra de navegación, para que solo los usuarios autenticados puedan verlo. Para hacer eso, añadiríamos un nuevo método bajo los `getters` llamado `ifAuthenticated` que verifica si el token dentro de nuestro estado es nulo. Cuando hay un token, muestra que el usuario está autenticado y queremos que vea la opción del panel de la encuesta en la barra de navegación.
 getters: { isAuthenticated (state) { return state.idToken !== null } }
Después de lo cual, regresa al componente de encabezado y crea un método `auth` bajo computado, que envía a nuestro `isAuthenticated` dentro de los `getters` que acabamos de crear en la tienda. Lo que esto hace es que `isAuthenticated` devolvería false si no hay token, lo que significa que `auth` también sería nulo y viceversa.
 computed: { auth () { return this.$store.getters.ifAuthenticated } }
Después de esto, agregamos un `v-if` a nuestro HTML para verificar si `auth` es nulo o no, determinando si esa opción se mostraría en la barra de navegación.
 <li v-if='auth'> <router-link to="/dashboard">Dashboard</router-link> </li> <li v-if='!auth'> <router-link to="/signup">Register</router-link> </li> <li v-if='!auth'> <router-link to="/signin">Log In</router-link> </li>
- Encontrarás el código completo de la sección Estado de UI [aquí](https://codepen.io/atanda1/pen/QWjNxyo).
Estado de la interfaz de usuario
Hay un cambio en el encabezado basado en el estado de autenticación del usuario. (Vista previa grande)

Ingreso automático

Cuando recargamos nuestra aplicación perdemos los datos y nos desconectamos, teniendo que empezar de nuevo. Esto se debe a que nuestro token e identificación se almacenan en Vuex, que es javascript, y esto significa que nuestra aplicación se vuelve a cargar con el navegador cuando se actualiza.

Y finalmente, lo que haremos será recuperar el token dentro de nuestro almacenamiento local. Al hacerlo, podemos tener el token del usuario en el navegador, independientemente de cuándo actualicemos la ventana, y tener un método de inicio de sesión automático de nuestro usuario siempre que el token siga siendo válido.

Se crea un nuevo método de actions llamado AutoLogin , donde obtendremos el token y el userId de usuario del almacenamiento local y enviaremos nuestros datos al método authUser en las mutaciones.

 actions : { AutoLogin ({commit}) { const token = localStorage.getItem('token') if (!token) { return } const userId = localStorage.getItem('userId') const token = localStorage.getItem('token') commit('authUser', { idToken: token, userId: userId }) } }

Luego vamos a nuestro App.vue y escribimos un método created , que enviará el autoLogin de sesión automático desde nuestra tienda cada vez que se cargue la aplicación.

 created () { this.$store.dispatch('AutoLogin') }

Obtener_datos_de_usuario

Queremos dar la bienvenida al usuario en el tablero mostrando el nombre del usuario. Y así, se crea otra acción llamada fetchUser que primero verifica si hay un token como de costumbre. Luego, obtiene el correo electrónico del almacenamiento local y consulta la base de datos como se hizo anteriormente con la validación del correo electrónico.

Esto devuelve un objeto que contiene los datos del usuario enviados inicialmente durante el registro. Luego convertimos este objeto en una matriz y lo asignamos a la mutación storeUser creada inicialmente.

 fetchUser ({ commit, state}) { if (!state.idToken) { return } const email = localStorage.getItem('email') axios.get('https://vue-journal.firebaseio.com/users.json?orderBy="email"&equalTo="' + email + '"') .then(res => { console.log(res) // const users = [] console.log(res.data) const data = res.data const users = [] for (let key in data) { const user = data[key] user.id = key users.push(user) console.log(users) } commit('storeUser', users[0]) }) .catch(error => console.log(error)) }

Después de lo cual creamos otro captador llamado user que devuelve el storeUser state.user

 getters: { user (state) { return state.user }, isAuthenticated (state) { return state.idToken !== null } }

Volviendo al tablero, creamos un nuevo método computado llamado name que devuelve state.user.name solo si el usuario existe.

 computed: { name () { return !this.$store.getters.user ? false : this.$store.getters.user.name } }, created () { this.$store.dispatch('fetchUser') } }

Y también agregaremos la propiedad calculada created para enviar la acción fetchUser una vez que se cargue la página. Luego usamos el v-if en nuestro HTML para mostrar el nombre si existe.

 <p v-if="name">Welcome, {{ name }} </p>

Enviar_encuesta

Para enviar la encuesta, crearemos una acción postData que envíe los datos a la base de datos utilizando la API de la base de datos, con el token para mostrarle al servidor que el usuario ha iniciado sesión.

 postData ({state}, surveyData) { if (!state.idToken) { return } axios.post('https://vue-journal.firebaseio.com/survey.json' + '?auth=' + state.idToken , surveyData) .then(res => { console.log(res) }) .catch(error => console.log(error)) }

Volvemos al componente del tablero y enviamos los datos a nuestra acción postData en la tienda.

 methods : { onSubmit () { const postData = { price: this.price, long: this.long, comment: this.comment } console.log(postData) this.$store.dispatch('postData', postData) } }

Ahí lo tenemos, tenemos muchas funciones útiles implementadas en nuestra aplicación de demostración mientras nos comunicamos con nuestro servidor Firebase. Con suerte, utilizará estas potentes funciones en su próximo proyecto, ya que son muy importantes para crear aplicaciones web modernas en la actualidad.

Si tienes alguna pregunta, puedes dejarla en la sección de comentarios y estaré encantado de responder a cada una de ellas.

  • La demostración del tutorial está disponible aquí.
aplicación de encuesta vue
La aplicación de encuesta completada (vista previa grande)

Otros recursos que pueden resultar útiles incluyen:

  • Para obtener más información sobre Firebase y los demás servicios que ofrece, consulte el artículo de Chris Esplin, "¿Qué es Firebase?"
  • Vuelidate es una biblioteca realmente agradable en la que realmente deberías profundizar. Debe leer su documentación para obtener una visión completa. https://vuelidate.js.org/.
  • También puede explorar axios por sí solo, especialmente si desea usarlo en proyectos más grandes.