AngularT2_Light_1200x303

Apollo Angular allows you to fetch data from your GraphQL server and use it in building reactive UIs using the Angular framework. In this article, I will show you how to make a GraphQL query in Angular using Apollo Angular.

GraphQL is a specification that defines a type system, query language, and schema language for building web APIs. The specification is language agnostic, but in this article you will use a GraphQL API built in JavaScript to build an Angular app that will communicate with the API. We will be working with Apollo Angular, which is an Apollo client integration for Angular. It allows you to query any GraphQL server and build reactive UI using the Angular framework.

What We'll Build

We will build an Angular app that can query for and create books. We will be using an already-built GraphQL server which you can download on GitHub, then follow the setup instructions to set it up and start it.

Prerequisite

This article assumes some knowledge of GraphQL, Angular, and how to work with the Angular CLI. If you're not familiar with those, I've got you covered! I have recently written about the fundamental GraphQL concepts and how to build a GraphQL API. It'll work you through the specification and the query language. I've also written about Angular and how to use the CLI. If you're comfortable with those, you can continue reading.

Adding Angular Apollo Client

We will begin now with the Angular app. We will be working with a bootstrapped Angular app. The app already has bootstrap set up with a navigation header. Follow the instructions below to set it up.

  1. Open your command-line application and switch to the directory you'd like to keep the application.
  2. Run git clone https://github.com/pmbanugo/graphql-angular-intro.git to clone the repo.
  3. Run cd graphql-angular-intro to switch to the project's directory.
  4. Run git checkout base to switch to the base directory which has bootstrap, a home component, and routing enabled.
  5. Run npm install to install the dependencies.

To add Angular Apollo to the project, we'll use the ng add command. While you're still on the command line, run the command ng add apollo-angular to add the library to the project. This will install the necessary dependencies and also modify the project by adding a module named graphql.module.ts.

We need to set the URL of the GraphQL server in the GraphQLModule module. Open src/app/graphql.module.ts and change the value of the uri variable as follows:

const uri = "http://localhost:4000";

Fetching Data

You've installed the Angular Apollo client and set it up with how it should initialize the client and what the endpoint for the server is. The next step is for you to query the server to retrieve data. You will do this using the Apollo service. This service is a regular Angular service that is used to query the API, with the returned data streamed through Observables.

We'll use the Apollo service to retrieve data and display in the Home component. Open src/app/home/home.component.html and add the import statements below in it.

import { Apollo } from "apollo-angular";
import gql from "graphql-tag";

Replace the code on line 11 with the following statement:

books: any[];
loading = true;

constructor(private apollo: Apollo) {}

We updated the constructor so the Apollo service can be injected, and created a variable to hold the returned data.

We will now make use of the Apollo service to fetch data. Copy and paste the code statement below to the ngOnInit function.

ngOnInit() {
    this.apollo
      .query<any>({
        query: gql`
          {
            books {
              title
              authors {
                name
              }
            }
          }
        `
      })
      .subscribe(
        ({ data, loading }) => {
          this.books = data && data.books;
          this.loading = loading;
        }
      );
}

We use apollo.query() to request for data, with the query specified as a parameter to the function. We use the gql function from the graphql-tag library to parse the query as a GraphQL document to Apollo client. The function returns an Observable which we call subscribe on, in order to retrieve the data.

Add the following function which will be used to trim the array of authors into a string.

getAuthorNames(authors) {
    if (authors.length > 1)
        return authors.reduce((acc, cur) => acc.name + ", " + cur.name);
    else return authors[0].name;
}

Update the component's template in home.component.html with the following:

<p>Books</p>

<div *ngIf="loading">
  Loading...
</div>

<table class="table">
  <thead>
    <tr>
      <th scope="col">Title</th>
      <th scope="col">Authors</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let book of books">
      <th scope="row">{{ book.title }}</th>
      <td>{{ getAuthorNames(book.authors) }}</td>
    </tr>
  </tbody>
</table>

With this, when the component is loaded, the query is sent to the server and the page will display the Loading... message. When the data is ready, it is then displayed in the table.

Querying For a Single Book

The GraphQL API we're using has a query that allows you to request a book by its ID. The GraphQL query for this takes an argument which specifies the ID of the book to retrieve. We'll implement another page which will allow a user to enter the book ID in an input field, with a button which when clicked will make the request to the server and the result displayed on the page.

We're going to add a new component for this functionality. Open the command line and and run ng g c find --module command. Open src/app.module.ts and update the routes to include a path to /find.

const routes: Routes = [
  { path: "find", component: FindComponent },
  { path: "", component: HomeComponent },
  { path: "**", redirectTo: "/", pathMatch: "full" }
];

Don't forget to add the import statement for FindComponent.

Open find.component.html and put the code below in it.

<h3>find book</h3>

<form class="form-inline">
  <div class="form-group mx-sm-3 mb-2">
    <label for="bookId" class="sr-only">Enter Book ID</label>
    <input
      [(ngModel)]="bookId"
      type="text"
      class="form-control"
      name="bookId"
      placeholder="Enter Book ID"
    />
  </div>
  <button (click)="findBook()" type="submit" class="btn btn-primary mb-2">
    Find
  </button>
</form>

<br />
<div *ngIf="loading">
  Loading...
</div>

<div>{{ error }}</div>

<hr />
<p><b>Title:</b><span> {{ book.title }}</span></p>
<p><b>Pages:</b><span> {{ book.pages }}</span></p>
<p><b>Chapters:</b><span> {{ book.chapters }}</span></p>
<p><b>Authors:</b><span> {{ getAuthorNames(book.authors) }}</span></p>

The markup above provides an input field and a button that is bound to a function to fetch the book. When we get the book, it is then displayed on the page. We used NgModel directive here, so you should import the FormsModule module so the app will find that directive.

Open the component's class find.component.ts and update it with the following code:

import { Component, OnInit } from "@angular/core";
import { Apollo } from "apollo-angular";
import gql from "graphql-tag";

@Component({
  selector: "app-find",
  templateUrl: "./find.component.html",
  styleUrls: ["./find.component.css"]
})
export class FindComponent implements OnInit {
  bookId: string;
  book: any = {};
  loading = false;
  error: string;
  constructor(private apollo: Apollo) {}

  getAuthorNames(authors) {
    if (authors && authors.length > 1)
      return authors.reduce((acc, cur) => acc.name + ", " + cur.name);
    else if (authors && authors.length == 0) return authors[0].name;
  }

  findBook() {
    this.error = "";
    this.loading = true;
    this.apollo
      .query<any>({
        query: gql`
          query($id: ID!) {
            book(id: $id) {
              title
              pages
              chapters
              authors {
                name
              }
            }
          }
        `,
        variables: {
          id: this.bookId
        }
      })
      .subscribe(({ data, loading }) => {
        if (data.book) this.book = data.book;
        else this.error = "Book does not exits";
        this.loading = loading;
      });
  }
}

You should notice that the variables property included in the object passed to the query function and that the query declares a $id variable. The $id variable will be evaluated and the value will be used to query the API. It is in the variables property that you specify the variables the query needs, in our case, we set id to the value of bookId. That is how you can specify variables and use them in queries that the value for the argument is dynamic.

We've written the code, now let's test the application. If you’re followed along with my introduction series to building GraphQL API, you should already have some data to test with. Otherwise, check the screen record below to see how the app works and stay tuned for the next article in which you'll build a page to create books.

Open the command line and run ng serve -o to start the application.

angular-graphql-query

Wrap-up

Apollo Angular allows you to fetch data from your GraphQL server and use it in building reactive UIs using the Angular framework. You learned how to use the query function in the Apollo service to query a Graphql API.

There's another function named watchQuery which can be used for the same purpose. The watchQuery method returns a QueryRef object which has the valueChanges property that is an Observable, and can be subscribed to just like we did in this post. The difference is that watchQuery will update the UI with updated data if another component in the application makes a query or mutation operation that changes the data already retrieved when the query was first run. You can read more about that in the documentation.

You can get the source code on GitHub and the query branch has the code for this article.


Peter Mbanugo
About the Author

Peter Mbanugo

Peter Mbanugo is a software developer, tech writer, and maker of Hamoni Sync. He currently works with Field Intelligence, where he helps build logistic and supply chain apps. He's also a contributor to Hoodie and a member of the Offline-First community. You can follow him on Twitter.

Related Posts

Comments

Comments are disabled in preview mode.