GraphQL and Flutter step by step guide


Photo by Isaac Smith on Unsplash

GraphQL: get exactly what you need and nothing more

Let’s look at two scenarios:

  • getUser API returns a bunch of irrelevant data where you just need an email address
  • You must retrieve 4–5 APIs to build a screen

This is where GraphQL can be useful, in this article, I will demo how to create a GraphQL server and a Flutter Client step by step.

As an App developer, you might wonder why would I care about the server? Can’t I just download the repo? In fact, by the nature of GraphQL, it is highly likely that front-end engineers will be maintaining and updating the server-side code, so understand the basics helps.

Apollo GraphQL Server

package.json

Create a package.json file under the project root, this file contains the project configuration data and dependencies, it looks like this:

{
  "name": "graphql-server",
  "version": "1.0.0",
  "main": "app.js",
  "scripts": {...},
  "dependencies": {
    "apollo-datasource-http": "^0.10.0",...
  }
}

Datasource

There are few ways to fetch data in GraphQL: fetch from a REST API, fetch from a GraphQL api, fetch from a database, and that is done via the data source. This is where you can reduce a good amount of api calls and combine them into one. Let’s say we need to make one api call to get user and one to get a list of items, then we need to create two separate data course file:

const userdata = new (class UserAPI extends HTTPDataSource {
  constructor() {
       const pool = new Pool("http://localhost:8080/");
       super(baseURL, {pool})
  }
  async getUser(userID) {
       return this.get(`/company/${userID}`, {});
  }})
module.exports = userdata

Second data source file to fetch a list of items

//second data source file to fetch list of items
const listdata = new (class UserAPI extends HTTPDataSource {
  constructor() {
       const pool = new Pool("http://localhost:8080/");
       super(baseURL, {pool})
  }
  async getList(itemID) {
       return this.get(`/list`, {query: {itemID: itemID}
  });
}})
module.exports = listdata

Last, use an index file to merge these two APIs into one:

const datasources = () => {
   return {
       userAPI: UserAPI,
       listAPI: ListAPI,
   }
};
module.exports = datasources;

Schema and Resolver

Now that we have functions to merge APIs, another important feature of GraphQL is to query exactly what you need, and this is handled using Schema and Resolver. Schema is to define the data while the resolver is responsible to populate it.

//Schema
const { gql } = require('apollo-server');
const baseSchema = gql`
   type Query {_: Boolean}
   type Mutation {_: Boolean}
   type User {
      userID: String!
      userName: String!
   }`
module.exports = baseSchema;
//Resolver
const userResolvers = {
   Query: {user: (id) => {}}};
module.exports = userResolvers;

GraphQL server

First, we need to install the Apollo server:

npm install apollo-server graphql 

Now let’s create a GraphQL server file — project/src/app.js.

const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const datasources = require('./datasources');
const server = new ApolloServer({typeDefs,resolvers,dataSources});
server.listen({port: 8080}, () => {
   console.log('Server is running');
});

Kick-off start

npm install
npm start

Target architecture

Shall you use GraphQL to replace REST? Probably not. It is better to use GraphQL as an extra layer in front of REST API services. In this way, the backend team can focus on building the core services, while the frontend team can optimize the server response in such a way that works best for its performance.


Flutter GraphQL Client

The transition mobile app layer contains: presentation, domain, and data, and usually the data layer are responsible for fetching, mapping, and storing data. Is there a better way? What if we can ditch the data layer? With GraphQL it is possible.

Using GraphQL in Flutter is fairly easy, unfortunately, at the time of writing this article, there isn’t an Apollo Flutter lib, therefore we will be using the GraphQL lib only. Let’s see the dependencies:

name: graph_ql
...
dependencies:
    graphql_flutter: ^4.0.0-beta

Init and create GraphQL server in main()

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
     ValueNotifier<GraphQLClient> client =    ValueNotifier(GraphQLClient(link:HttpLink('http://localhost:8080/graphql')));
     
     return GraphQLProvider(
          client: client,
          child: MaterialApp(home: UserScreen()));
     }
}

Create a GraphQL scheme

final String getItem = """
query {
   item(id: "1") {user(id: "2") {name}}
}""";

Ues the data inside a Widget

class ItemScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
       body: Container(
          child: Query(
             options: WatchQueryOptions(document: gql(getItem), fetchPolicy: FetchPolicy.networkOnly),
             builder: (QueryResult result, {VoidCallback refetch,    FetchMore fetchMore}) {
               if (result.data == null) return Text('Error')
               if (result.isLoading) return Text('Loading');
               return _buildUserWidget(result.data);
               })));
  }}

🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉