Back
Jan 10, 2017

How To Use GraphQL with Angular 2 (with Example)

​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 -> Taskstree, 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 for production. I am sure that if you use this new approach for your new projects, you won’t be disappointed.

Subscribe for the news and updates

More thoughts
May 10, 2018Technology
How to Build a Cloud-Based Leads Management System for Universities

Lead management is an important part of the marketing strategy of every company of any size. Besides automating various business processes, privately-held organizations should consider implementing an IT solution that would help them manage their leads. So, how should you make a web-based leads management system for a University in order to significantly increase sales?

Jun 14, 2017Technology
How to Deploy a Django Application on Heroku?

In this article I'll show you how to deploy Django with Celery and Postgres to Heroku.

May 18, 2017Technology
Angular2: Development Tips and Trick

In this article we'll discuss some tricks you can use with Angular to make routing cleaner and improve SEO of your application.

Mar 4, 2011Technology
Css sprite generation

I've created this small sprite to create css sprites. It glues images from directory directory into single file and generates corresponding css.

Sep 23, 2010Technology
Dynamic class generation, QuerySetManager and use_for_related_fields

It appears that not everyone knows that in python you can create classes dynamically without metaclasses. I'll show an example of how to do it.So we've learned how to use custom QuerySet to chain requests:Article.objects.old().public()Now we need to make it work for related objects:user.articles.old().public()This is done using use_for_related_fields, but it needs a little trick.

Feb 18, 2010Technology
Business logic in models

In my recent project there was a lot of data business logic, so I had to organize this code somehow. In this article I'll describe a few hints on how to it.