​How To Use GraphQL with Angular 2 (with Example)

, , graphql, angular

​Using GraphQL with Angular 2

Hello. In this article I will tell you about the basics of working with GraphQL in Angular 2 environment.This article will be of interest to those who have played enough with REST resources in their apps and are looking for something more lightweight, versatile and easy. Or maybe you are facing the choice of technology stack for your next project.

How did it all begin?

No, I'm not talking about the history of the GraphQL creation. Guys from Facebook created it, and you can read about it in much more detail on the project's website. I mean, what should happen in the developer’s life that he would give up all the usual and well-established paradigms and begin to rebuild all of his services to work with GraphQL. And why should he want it, since REST satisfies the needs for building API. Or does it?

What's wrong with REST?

There's nothing wrong really. I do not even remember the cases when once the question “what should be used to build the API" has been arised, I haven’t thought about Django + Django Rest Framework. And it's true, it allows you to create an API quickly and easily and switch to more important matters.

- So is this an ideal?

Sort of. But very soon after the launch of the project we will want to "optimize" traffic and decide to get the items list while using limited serializer with limited number of fields, which we need just in this particular list view. It is a common practice in whole, and it is easy to configure it in DRF.

- Excellent, everyone is happy.

No, not everyone. It's like a snowball. And as soon as the first FooListSerializer appears, FooShortListSerializer appears too, and there are also BaseFooListSerializer + BaseFooDetailSerializer with them with a good quantity of mixins and subclasses to somehow streamline the code, and keep up with the DRY principle. In order to understand which specific serializer you need at every specific moment, you teach api to receive its name through query_param, and then create new API endpoint in order not to mix the person / client / user data, even though it is the same model.

Eventually you end up with 2-7 serializers and 1-2 endpoints for each of your 100 models, and it has to be maintained somehow. And also you have mobile applications on three platforms that already make you maintain multiple versions of the API at the same time.

Oh, and do not forget that it is running not just in one direction. Also, you have the data that comes from a client with POST / PUT / PATCH requests, and you need to know which serializer to use. There may be some options as well. Fun \o/

- Come on, everything's not always so difficult.

Yes, you're right, in my experience I have participated in only one project where there have been 150 django models to about 200+ API endpoints, 400+ serializers, and it was still fun. More often, everything is much easier. But this is not the reason to think about it in advance, unless your project becomes unmaintainable. At least you should be ready for it and have some tricks up your sleeve.

- Fine, sure. And what about the client app? We're here to talk about ​GraphQL with angular 2, right?

Yes, it seems to be simpler, but Angular2 forces us to use TypeScript, and to make things right, each time when sending queries to the API, we need to know what we will get (what fields and what types). This means that the entire set of serializers must somehow be repeated in the interfaces of angular models. Fun continues? I’m not sure :)

And what should I do with all this?

Do you remember how we decided to receive the serializer name from the client application as a parameter, so that the server could know what we need and in what form everything should be packed? So, this is a wake-up call. This is the moment when we allow the client application to manage the server, and specify how RESTful service should look. The guys from Facebook came up to this point as well, but went no further. They turned off the trail, took their own way. So in this way, they came to the GraphQL implementation.

How to try it?

Firstly, we need to set several libraries on our Angular2 application.

$ npm i --save graphql graphql-tools apollo-client angular2-apollo graphql-tag

Then we need to create a GraphQL client somewhere, through which we will communicate with our backend

// graphql.client.ts
import { ApolloClient, createNetworkInterface } from 'apollo-client';

const client = new ApolloClient({
  networkInterface: createNetworkInterface({
    uri: '/graphql',
    opts: {
      credentials: 'same-origin',
    },
  }),
});
export {
  client
}

After that, it is necessary to initialize it in your app.module

@NgModule({
  bootstrap: [ AppComponent ],
  declarations: [
  // …
  ],
  imports: [
    // ...
    ApolloModule.withClient(client),
  ],
  providers: [
// ... ] })

And that’s it, we can start.

What do we usually do to get the data from our API? That's right, we make a request, your Captain Obvious. In the case of REST, it looks like this:

    // Let's say we want to request the list of active projects.
    this.http.get(‘/api/projects’, {
        search: {
            status: ‘active’
        }
    })

As a result of this request, we will really get the list of active projects, what else is needed? Ah, yes. We also need a cunning list which contains only the fields that we need. As I wrote earlier, it is easy to reach, just by teaching our API to use any parameter on the basis of which it would decide which serializer should be used. Omitting implementation in the backend, on Angular2 it looks like this:

    // Let's say we want to request the list of active projects.
    this.http.get(‘/api/projects’, {
        search: {
            status: ‘active’,
            serializer: ‘extra’
        }
    })

And here is how it can be done with GraphQL and Angular 2. Firstly, you need to announce our query somewhere:

const ProjectsQuery = gqlquery getProjects($status: String!) {
projects (status: $status){
  edges {
    node {
      id
      name
    }
  }
}
};

That's all. One of the GraphQL features is that you get exactly what is requested. In this case, we have requested the list of projects, where each one contains only two fields (id, name), because more fields are not needed right now. Cool, isn’t it?

We execute the query:

    ngOnInit() {
    this.loadProjects()
  }

  loadProjects() {
    this.apollo.watchQuery({
      query: ProjectsQuery,
      variables: {
        status: this.selectedStatus,
      },
    })
      .map(result => result.data.projects.edges.map(item => item.node))
      .subscribe(projects => {
        this.projects = projects
      });
  }

It still should be noted that we send parameter status to our query. Currently this.selectedStatus is a string, but it can also be an Observable. Then apollo.watchQuery will wait for the new value in this Observable and update the query.

Yes, if we want to get more data, we can make queries at any time. So this is how the active project list query will look with the latest 5 tasks created in them.

query getProject {
  projects(status: "active") {
    edges {
      node {
        id
        name
        tasks(last:5){
          edges {
            node {
              id
              name
              status
            }
          }
        }
      }
    }
  }
}

Not bad, huh? Of course, this requires a preset on the backend, but this is done only once, and you can use it at any time and in any combination. It all depends on what data is needed for your component.

- Yes, but REST can do it as well.

Sure, it can. But then again, once introducing the field tasks = TaskListSerializer(many = True) to your ProjectListSerializer, you will be doomed to always get the list of all requests with this serializer. Moreover, if there's something you need to change in the composition of fields of TaskListSerializer, which is already used in many parts of your application, you get the idea, right? :) In the case of GraphQL at any time you are free to indicate which set of fields you need right now.

Do you want to see another trick? You can make 2 queries in one. You can make any number of requests in one. If you want to display Project -> Sprint -> Tasks tree, but your API does not allow getting structured data, your way out is to send 3 requests to the server.

let projectsSub = http.get(‘/projects’)
let sprintsSub = http.get(‘/sprints’)
let tasksSub = http.get(‘/tasks’)

Observable.zip(projectsSub, sprintsSub, tasksSub)
  .subscribe(subs => {
      // DO THE STUFF
 })

Instead of doing stuff you will have to build the desired structure on your own. And how are things in the case of GraphQL:

query getBulkData {
  projects(status: "active") {
    edges {
      node {
        id
        name
       }
    }
  }
   sprints (status: "active"){
    edges {
      node {
        id
        name
        status
      }
    }
  }
 tasks (status: "active"){
    edges {
      node {
        id
        name
        status
      }
    }
  }
}

That's all, everything in one request. Of course, this will not free you of the need to build the tree on your own.

CRUD

Yes, changing data is not a problem as well. Here it is called mutation, and it is simple enough. However, contrary to the general impression, it is not as easy as queries to get data. For example, let’s try to change the project status and write our mutation for this.

const ChangeStatusQuery = gqlmutation ChangeProjectStatus($projectId: ID, $newStatus: String!) {
    updateStatus(id: $projectId, status: $newStatus) {
      project {
        id
        name
        status
      }
    }
  };

  onStatusChange() {
    this.apollo.mutate({
      mutation: ChangeStatusQuery,
      variables: {
        projectId: this.project.id,
        newStatus: this.project.status
      }
    })
  }

We see that the syntax is very similar to the query syntax. We send projectId, newStatus, and mention response format. Removal mutations syntax, records creation in a database similar to the one described above.

This is what differs GraphQL from REST, where we got used to the fact that POST should be called to create an entry, PUT / PATCH - to change it, DELETE - to remove it. GraphQL also describes it a little differently. You can describe how to get the data - you do query for this, and if you need to change the data - do a mutation.

Pros and Cons

Even a half of opportunities that GraphQL can provide for Angular2 applications are not disclosed in this article, but it is clear that it is more fully meet the needs of WEB application in the integrated data access than REST. With angular 2 and GraphQL you can build fast and flexible projects, which are easy to kick-start, maintainable in long term and ready fot production. I am sure that if you use this new approach for your new projects, you won’t be disappointed.

contact us right now