DEV Community

Cover image for Creando una API con GraphQL y Apollo | Parte III
gugadev
gugadev

Posted on

Creando una API con GraphQL y Apollo | Parte III

En el tutorial anterior creamos una aplicación Angular y creamos un formulario. Vimos cómo por medio de directivas modularizamos nuestro código y le dimos un aspecto elegante mediante un poco de CSS. Sin embargo, como eso no es suficiente, en esta tercera y última parte de la serie, veremos cómo hacer que nuestra app se comunique con nuestra API GraphQL.

Bien, empecemos. Ya tenemos nuestro formulario, ahora, necesitamos darle alguna funcionalidad. En concreto, nuestra tarea para hoy serán dos cosas:

  • Añadir validación al email para asegurarnos que no esté en uso.
  • Registrar al nuevo usuario si el punto anterior fue pasado con éxito.

Antes de empezar: ve hacia src/app/graphql.module.ts y asigna a la variable uri la dirección en donde corre nuestra API, la cual es: http://localhost:3000.

Validando que el email no esté registrado.

Si recuerdas mi tutorial pasado acerca de Reactive Forms, recordarás que hablamos de validadores asíncronos. Estos, tienen la particularidad de ser validaciones que retornan una promesa o una instancia de Observable. Por medio de este tipo de validadores podemos realizar validaciones personalizadas. En este tutorial, veremos cómo realizar una validación personalizada con nuestra API GraphQL.

Creación del servicio

Nuestro primer paso será generar un servicio. Le llamaremos signup:

ng g s signup/
Enter fullscreen mode Exit fullscreen mode

Y agregamos el siguiente método checkForExists:

import { Injectable } from '@angular/core'
import { Apollo } from 'apollo-angular'
import { Observable, Subscriber, Observer } from 'rxjs'
import gql from 'graphql-tag'
import { ApolloQueryResult } from 'apollo-client'
import { User } from '../models/user'

@Injectable({
  providedIn: 'root'
})
export class SignupService {

  constructor(private apollo: Apollo) { }

  /**
   * Search an user by his email address
   * @param email | string user's email who's looking for
   * @returns boolean if the user exists or not
   */
  public checkForExists(email: string): Observable<boolean> {
    return Observable.create((sub: Subscriber<boolean>) => {
      this.apollo.query({
        query: gql`
          query Find($email: String!) {
            user(email: $email) {
              id
            }
          }
        `,
        variables: { email }
      })
      .subscribe((value: ApolloQueryResult<any>) => {
        const found: User | null = value.data.user
        sub.next(found !== null)
        sub.complete()
      })
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Veámoslo un poco en detalle. Lo primero es inyectar la dependencia de Apollo en nuestro constructor. Esta dependencia nos permitirá hacer las consultas a nuestra API.

Segundo, nuestro método checkForExists recibe un parámetro que es el email. Este método devuelve un Observable que almacenará un booleano. Dentro del Observable.create hacemos uso del método query de Apollo. Este método recibe una propiedad query y una variables opcional. En la propiedad query procedemos a realizar nuestra consulta. Vemos que declaramos una variable en GraphQL llamada $email, a esta variable le vamos a dar un valor en la propiedad variables:

variables: { email }
Enter fullscreen mode Exit fullscreen mode

variable cuyo valor no es nada más que el email recibido por parámetro. Esta consulta devolverá un Observable al cual nos subscribimos para obtener data.user que es en donde estará la respuesta que obtengamos.

Esta consulta buscará un usuario por su email. Si lo encuentra, retornará al usuario, caso contrario, retornará null.

Importación del servicio

Ahora procedemos a importar el servicio en SignupModule:

@NgModule({
  declarations: [
    SignupComponent
  ],
  imports: [
    CommonModule,
    ReactiveFormsModule,
    InputModule,
    ButtonModule
  ],
  exports: [
    SignupComponent
  ],
  providers: [SignupService] // <--- aquí
})
export class SignupModule { }
Enter fullscreen mode Exit fullscreen mode

Y por último, lo inyectamos en nuestro SignupComponent:

constructor(
  private fb: FormBuilder,
  private ss: SignupService // nuevo parámetro
) {}
Enter fullscreen mode Exit fullscreen mode

Y eso es todo. Ahora estamos listo para usar el servicio. 😉

Consumiendo nuestra API

Una vez que tenemos nuestro servicio listo, procedemos a usar su método checkForExists para la validación. Para esto, creamos un método llamado validateEmailNotTaken, el cual será nuestro validador.

validateEmailNotTaken(ctrl: AbstractControl) {
    return (
      this
        .ss
        .checkForExists(ctrl.value)
        .pipe(map(taken => taken ? { taken: true } : null))
    )
  }
Enter fullscreen mode Exit fullscreen mode

Este método, como toda función validadora, acepta un argumento de tipo AbstractControl, el cual hace referencia al control que controla, en este caso email. Ejecutamos el método checkForExists pasándole el email que se ha ingresado en el textbox. Una vez que lo ejecutamos, hacemos un map del Observable, con el fin de transformar la respuesta por una personalizada. En este punto ya tenemos la respuesta booleana, es decir, si existe el usuario o no. Finalmente, si existe retornamos un objeto { taken: true }, el cual será añadido al objeto errors del FormControl y que podrá ser accedido por el template. En caso contrario, retorna simplemente null.

Finalmente, agregamos el validador al array de validadores asíncronos del control email:

ngOnInit() {
    this.suForm = this.fb.group({
      email: new FormControl('', [
        Validators.required,
        Validators.email
      ], [ // lo agregamos aquí
        this.validateEmailNotTaken.bind(this)
      ]),
      password: new FormControl('', [
        Validators.required,
        Validators.pattern('^(?=.*[0-9])(?=.*[a-zA-Z])([a-zA-Z0-9]+)$')
      ])
    })
  }
Enter fullscreen mode Exit fullscreen mode

Si ejecutamos la aplicación e ingresamos un email que previamente hemos guardado, veremos el siguiente mensaje de error:

¡Genial! Ya tenemos nuestro formulario completamente validado. 😎

Registrando el usuario

Ya tenemos las validaciones, ahora nos falta registrar al usuario si las ha pasado todas. Para esto, vayamos a signup.service.ts y añadamos el siguiente método:

 /**
   * 
   * @param data | User information of the user
   * @returns User the recently created user
   */
  public register(data: User): Observable<User> {
    return Observable.create((sub: Subscriber<User>) => {
      this.apollo.mutate({
        mutation: gql`
          mutation Register($data: UserInput!) {
            createUser(data: $data) {
              id,
              email
            }
          }
        `,
        variables: { data }
      })
      .subscribe((value: ApolloQueryResult<any>) => {
        const created: User = value.data.createUser
        sub.next(created)
        sub.complete()
      })
    })
  }
Enter fullscreen mode Exit fullscreen mode

Este método es parecido a nuestra consulta anterior. Recibe un argumento de tipo User y retornamos un Observable<User>. Dentro del Observable.create ejecutamos el método mutate de Apollo para ejecutar una mutation y le pasamos como variable $data, el objeto User que hemos recibido. Finalmente, nos subscribimos al Observable, obtenemos la información del usuario creado y la despachamos.

Este método lo llamamos desde el método signup de SignupComponent, el cual se disparará en el evento submit del formulario. El nuevo método signup queda así:

public signup() {
    const user = new User
    user.email = this.email.value
    user.password = this.password.value
    // agregamos esto
    this.ss.register(user).subscribe((created: User) => {
      alert('Registro exitoso')
      this.suForm.reset()
    })
  }
Enter fullscreen mode Exit fullscreen mode

Una vez que hemos establecido los datos del usuario, se lo pasamos a register, este usará Apollo para ejecutar la mutación createUser, retornará la respuesta, la guardamos en un objeto User y la retornamos al Observer. Si nos subscribimos, tendremos a disposición al usuario recientemente creado. Para terminar, hacemos uso del método FormGroup#reset para restablecer los valores de los controles.

Y eso es todo. Ahora ya tenemos nuestro formulario completamente funcional. 😉

Recuerda que el código está disponible en Github. ¡Nos vemos!

Top comments (0)