2/18/19

What Is GraphQl

GraphQl - is query language.
A way you can quickly and clear explain what kind of data you want
Graphql can be compared to REST API:

In REST:

By sending request to "/users" url you telling server that you asking for users
By sending request to "/users/123" url you telling server that you asking for user with id 123

In GraphQl

the ways to "explain" to server what exactly you want are much wider:
Here is example how we can achieve the total repos count of user, only instead of using v3 GitHub api (like in previous posts)
we using GitHub grpahQL v4 api:

function getReposCount(user) {
  // graphQL query
  const query = `query {
     user(login: ${user}) {
       repositories {
         totalCount
       }
     }
  }`;

  const body = JSON.stringify({ query });

  const uri = `https://api.github.com/graphql`;
  return fetch(uri, {
    method: "POST",
    headers: { Authorization: `bearer ${token}` },
    body
  })
  .then(d => d.json())
  .then(data => {
    // extract only count field
    const {
      data: {
        user: {
          repositories: { totalCount: total }
        }
      }
    } = data;
    return total;
  });
}

2/11/19

Handle nested requests with rxjs

In the previous post i manage to get data using nested requests - by vanilla javascript.
In this post I will try to explain how to do the same thing, but using angular + rxjs library.
Firstly - lets write here the usual angular service which has "getUsers" and "getRepos" methods:

import { HttpClient,  HttpErrorResponse } from '@angular/common/http';
import { Observable} from 'rxjs';
import { from } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  token = 'PUT-YOUR-TOKEN-HERE';
  constructor(private http: HttpClient) { }
  
  getUsers(since=0) : Observable {
    return this.http.get(`https://api.github.com/users?per_page=10&since=${since}&access_token=${this.token}`)
      .pipe(
        catchError(this.handleError('getUsers'))
      ) as Observable;
  }

  getReposCount(user) : Observable {   
    return this.http.get(`https://api.github.com/users/${user}/repos?per_page=10000&access_token=${this.token}`)
      .pipe(
        map(repos => repos.length || 0 ),
        catchError(this.handleError('getReposCount'))
      ) as Observable;
  }
  ...
  ...
}

But how to combine those two requests to work together as nested requests?
After googling around I came up with following code:

  getData(since) {
     return this.getUsers(since)
       .pipe(
         concatMap(users => from(users)),
         mergeMap((user) => this.getReposCount(user.login)
            .pipe(
              map(count => ({login: user.login, avatar: user.avatar_url, count}))
            )         
         ),
         toArray()
       )
  }
What a mess! - What is "form"? What are those "concatMap" and "mergeMap"? and why this "toArray" needed?
I will try to understand myself

Big Picture (as i understood so far)

The big picture of what is need to be done here - is:
* We need to convert the stream which brings as a collection to stream which brings us single user (one by one)
* For each user we need to send the request to get repos count and update the user object
* Stream of single users need to be converted back to stream of arrays


concatMap

according to the docs: Map values to inner observable, subscribe and emit in order (same as mergeMap but preserves the order)

from

according to this article - converts an array to an observable. - need for converting the observable of users array to observable of single users

mergeMap

Map to observable, emit values

why not switchMap?

In many examples, demonstrating how to do some operation on the stream - the "switchMap" method is widely used:


  getQuote(): Observable {
    return Observable.create(observer => observer.next(this.nextId++)).pipe(
      // here
      switchMap((id: number) => this.http.get(`api/quotes/${id}`)),
      map((q: Quote) => q.quote),
      ...
    )
SwitchMap is also maps the observable to different one , however - the main feature of switchMap is that it "kills" the previous observable, thus it cannot fit for our case...

2/8/19

Fetch the data using nested requests

Recently I was challenged with relatively common task - to fetch the data using nested requests.
More into the details:
the task was "to fetch GitHub users using GitHub API"(which s easy, but here is caveat:) each user must have a repoCount field (where total count of his owned repos should display):
The GitHub api ave the endpoint which brings you GitHub users, but each user has only "repos_url" field - a link to fetching repos

const token = 'your-github-access-token-here';

function getUsers(since=0) {
 const gitUri =`https://api.github.com`;
 const usersUri = `${gitUri}/users?per_page=10&since=${since}&access_token=${token}`;
 fetch(usersUri)
   .then(d => d.json())
   .then(data => {
      //  must get only those relevant two fields
      const modified_users = data.map(({login, repos_url}) => ({login, repos_url}));
      console.log(modified_users[0]);
   });
}

getUsers(0);
Outcome:
To bring the repos-count you will need to make one more request for each user to fill the missing data:

function getReposCount(repos_url) {
   // lets set "per_page" param to 10000
   // assuming no one can have so much repos
   // this way we will get the count using "length" property
   const reposUri = `${repos_url}?per_page=10000&access_token=${token}`;
   return fetch(reposUri)
   .then(d => d.json())
   .then(repos => {
     return repos.length || 0;
   })
}

Now we must change our "getUsers" so it will loop through all users and modify each user`s data properly:

 fetch(usersUri)
   .then(d => d.json())
   .then(data => {
      // loop through collection of users
      let promises = data.map(({login, repos_url}) => getReposCount(repos_url)
        .then(count => ({login, count}))); 
   
      Promise.all(promises).then((users) => {
         // lets display the data of ninth user
         console.log(users[9]); 
      })
   });

Now you can see the output:
The "count" field is added to user object!

What Is GraphQl

GraphQl - is query language. A way you can quickly and clear explain what kind of data you want Graphql can be compared to REST API: In ...