NativeScript and GraphQL — Best way to handle data

NativeScript is an awesome framework to build truly mobile native app using javascript or Angular2.

But in order to deliver good and fast experiences for your users, you should also use the latest client-server best practices.

GraphQL is emerging a new standard for client-server communication.

You can find out more about the benefits of using GraphQL in your Angular apps in this talk and this blog post.

In this post I will show you how to use GraphQL and NativeScript with the great angular2-apollo library.

Let’s start by creating our Angular Native app using the telerik’s native-script CLI and the Angular template:

tns create angular-native-apollo-sw --ng
Project angular-native-apollo-sw was successfully created.

You can use your preferred IDE or Text Editor(WebStorm, visual studio code, sublime, atom…) to edit files of this project, My choice is WebStorm using the NativeScript support Plugin.

The data we will be querying is the SWAPI GraphQL API which retrieves a list of star-wars films and all of their related data:

GraphiQL and the Mobile app App side by side

Now in order to add GraphQL support, we just need to add Apollo.

Modify your package.json with the following dependencies:

{
"description": "NativeScript Application",
"license": "SEE LICENSE IN <your-license-filename>",
"readme": "NativeScript Application",
"repository": "<fill-your-repository-here>",
"nativescript": {
"id": "org.nativescript.angularnativeapollosw",
"tns-ios": {
"version": "2.3.0"
}
},
"dependencies": {
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"@angular/platform-server": "2.0.0",
"@angular/router": "3.0.0",
"angular2-apollo": "0.4.6",
"apollo-client": "0.4.19",
"graphql-tag": "^0.1.14",
"nativescript-angular": "1.0.0",
"reflect-metadata": "^0.1.8",
"tns-core-modules": "2.3.0",
"rxjs": "5.0.0-beta.12"
},
"devDependencies": {
"nativescript-dev-typescript": "^0.3.2",
"typescript": "^1.8.10",
"zone.js": "^0.6.21",
"babel-traverse": "6.9.0",
"babel-types": "6.10.0",
"babylon": "6.8.1",
"filewalker": "0.1.2",
"lazy": "1.0.11"
}
}

Make sure your tsconfig.json is configured with the folowing options“noEmitHelpers”: false, and “noEmitOnError”: false

{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": false,
"noEmitOnError": false
},
"exclude": [
"node_modules",
"platforms"
]
}

Add the following file client.ts on your app directory

import ApolloClient, { createNetworkInterface } from 'apollo-client';

export const client = new ApolloClient({
networkInterface: createNetworkInterface('https://graphql-swapi.parseapp.com', {}),
shouldBatch: true,
});

On your main.ts make sure to make the following modifications:

// this import should be first in order to load some required settings (like globals and reflect-metadata)
import { platformNativeScriptDynamic, NativeScriptModule } from "nativescript-angular/platform";

//"process" doesn't exist as part of NativeScript.
//You can pollyfill it by adding to your our main.ts `global.process = {env: {}};`
//and that will make process and its env work globally in the app as a empty variable.
global.process = {env: {}};

import { NgModule } from "@angular/core";
import { AppComponent } from "./app.component";
import { ApolloModule } from "angular2-apollo";
import { client } from "./client";

@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
imports: [
NativeScriptModule,
// Define the default ApolloClient
ApolloModule.withClient(client),
],
})
class AppComponentModule {}

platformNativeScriptDynamic().bootstrapModule(AppComponentModule);

You can find below an example of component retrieving and displaying data from the graphQL endpoint using apollo

app.component.ts

import {Component, OnInit} from "@angular/core";
import {Angular2Apollo} from "angular2-apollo";
import {client} from "./client";
import gql from "graphql-tag";

@Component({
selector: "my-app",
templateUrl: "app.component.html",
})
export class AppComponent implements OnInit {
// Observable with GraphQL result
public films: any;
// Observable variable of the graphql query
private apollo: Angular2Apollo;
private title: string;

// Inject Angular2Apollo service
constructor() {
//Angular2Apollo service is null when using inject, init manually
this.apollo = new Angular2Apollo(client);
}

public ngOnInit() {
// Query allFilms data with observable variables
this.apollo.watchQuery({
query: gql`
query getAllFilms{
allFilms{
films{
title
releaseDate
}
}
}
`
})
// Return only users, not the whole ApolloQueryResult
.subscribe(({data}) => {
this.films = data.allFilms.films;
});
this.title = 'SW Films'
}
}

app.component.html

<ActionBar [title]="title"></ActionBar>
<GridLayout rows="*">
<ListView [items]="films">
<template let-item="item" let-i="index">
<StackLayout>
<Label textWrap="true" [text]='item.title'></Label>
<Label textWrap="true" [text]='item.releaseDate'> </Label>
</StackLayout>
</template>
</ListView>
</GridLayout>

You can now run your project on ios|android simulator with the following command line.

cd angular-native-apollo-sw
tns livesync ios --emulator --watch
tns livesync android --emulator --watch
An Angular Native app getting data from SWAPI GraphQL demo

Clone this repo to run the above app yourself.

Would love to hear your feedback and thoughts here below and in the example repo.

Hope you’ll find this useful!