Creando una API con GraphQL y Apollo | Parte III
Tags: angular, javascript, graphql
Published: 2019-01-28 20:35:50

En [el tutorial anterior](http://bit.ly/2sRioQo) 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`:

```bash
ng g s signup/
```

Y agregamos el siguiente método `checkForExists`:

```typescript
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()
      })
    })
  }
}
```
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`:

```javascript
variables: { email }
```

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`:

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

Y por último, lo inyectamos en nuestro `SignupComponent`:

```typescript
constructor(
  private fb: FormBuilder,
  private ss: SignupService // nuevo parámetro
) {}
```

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.

```typescript
validateEmailNotTaken(ctrl: AbstractControl) {
    return (
      this
        .ss
        .checkForExists(ctrl.value)
        .pipe(map(taken => taken ? { taken: true } : null))
    )
  }
```

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`:

```typescript
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]+)$')
      ])
    })
  }
```

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

<p align="center">
<img src="https://i.imgur.com/qTrd7Xi.png" width="420"/>
</p>

¡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:

```typescript
 /**
   * 
   * @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()
      })
    })
  }
```

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í:

```typescript
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()
    })
  }
```

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](https://github.com/gugadev/graphql-ng-signup). ¡Nos vemos!