Does apollo-client work on node.js?
Asked Answered
O

7

53

I need a graphql client lib to run on node.js for some testing and some data mashup - not in a production capacity. I'm using apollo everywhere else (react-apollo, apollo's graphql-server-express). My needs are pretty simple.

Is apollo-client a viable choice? I can find no examples or docs on using it on node - if you're aware of any, please share.

Or maybe I should/can use the reference graphql client on node?

Oppressive answered 24/11, 2016 at 17:55 Comment(5)
The client is designed to run in the browser. With graphql-tools, and graphql-server-express (previous apollo server) you can do almost anything.Lagniappe
So, what if I want the server make graphql queries to some other server? Then, I need a graphql client library running on the server, yes?Tinishatinker
This was 8 months ago... if you had any insights since could you please share?Cavalier
@Cavalier See answer belowOppressive
Apollo Client should work just fine on Node. Check my answerKernan
K
55

Apollo Client should work just fine on Node. You only have to install cross-fetch.

Here is a complete TypeScript implementation of Apollo Client working on Node.js.

import { ApolloClient, gql, HttpLink, InMemoryCache } from "@apollo/client";

import { InsertJob } from "./graphql-types";
import fetch from "cross-fetch";

const client = new ApolloClient({
  link: new HttpLink({ uri: process.env.PRODUCTION_GRAPHQL_URL, fetch }),
  cache: new InMemoryCache(),
});


client.mutate<InsertJob.AddCompany, InsertJob.Variables>({
  mutation: gql`mutation insertJob($companyName: String!) {
      addCompany(input: { displayName: $companyName } ) {
          id
      }
  }`,
  variables: {
    companyName: "aaa"
  }
})
  .then(result => console.log(result));
Kernan answered 22/2, 2019 at 22:40 Comment(4)
Thanks for the help. If you don't want a global polyfill you can inject fetch into ApolloClient instead: import fetch from 'cross-fetch'; const client = new ApolloClient({ fetch, uri: ...Niphablepsia
do Apollo caching works without their React render props / hooks?Obeah
What tool did you use to generate types for the InsertJob method?Exothermic
@Exothermic graphql-code-generator.comKernan
M
16

Newer Apollo version provide a simpler approach to perform this, as described in Apollo docs, check the section "Standalone". Basically one can simply use ApolloLink in order to perform a query or mutation.

Below is copy of the example code from the docs as of writing this, with node-fetch usage as config to createHttpLink. Check the docs for more details on how to use these tools.

import { execute, makePromise } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import gql from 'graphql-tag';
import fetch from 'node-fetch';

const uri = 'http://localhost:4000/graphql';
const link = createHttpLink({ uri, fetch });

const operation = {
  query: gql`query { hello }`,
  variables: {} //optional
  operationName: {} //optional
  context: {} //optional
  extensions: {} //optional
};

// execute returns an Observable so it can be subscribed to
execute(link, operation).subscribe({
  next: data => console.log(`received data: ${JSON.stringify(data, null, 2)}`),
  error: error => console.log(`received error ${error}`),
  complete: () => console.log(`complete`),
})

// For single execution operations, a Promise can be used
makePromise(execute(link, operation))
  .then(data => console.log(`received data ${JSON.stringify(data, null, 2)}`))
  .catch(error => console.log(`received error ${error}`))
Mikkimiko answered 15/6, 2020 at 14:4 Comment(1)
I like this lightweight apollo-link solution much better. I had issues with node-fetch with Typescript, see #513, so I'm using cross-fetch instead.Marga
H
10

If someone is looking for a JavaScript version:

require('dotenv').config();
const gql = require('graphql-tag');
const ApolloClient = require('apollo-boost').ApolloClient;
const fetch = require('cross-fetch/polyfill').fetch;
const createHttpLink = require('apollo-link-http').createHttpLink;
const InMemoryCache = require('apollo-cache-inmemory').InMemoryCache;
const client = new ApolloClient({
    link: createHttpLink({
        uri: process.env.API,
        fetch: fetch
    }),
    cache: new InMemoryCache()
});

client.mutate({
    mutation: gql`
    mutation popJob {
        popJob {
            id
            type
            param
            status
            progress
            creation_date
            expiration_date
        }
    }
    `,
}).then(job => {
    console.log(job);
})
Heartwarming answered 4/3, 2019 at 19:6 Comment(0)
G
10

You can make apollo-client work, but it's not the best option for this use case.

Try graphql-request instead.

Minimal GraphQL client supporting Node and browsers for scripts or simple apps

Features per npmjs:

  • Most simple & lightweight GraphQL client
  • Promise-based API (works with async / await)
  • Typescript support
  • Isomorphic (works with Node / browsers)

example:

    import { request, gql } from 'graphql-request'
 
    const query = gql`
      {
        Movie(title: "Inception") {
          releaseDate
          actors {
            name
          }
        }
      }
`
 
request('https://api.graph.cool/simple/v1/movies', query).then((data) => console.log(data))

I have no affiliation with this package.

Garrulity answered 3/9, 2020 at 21:59 Comment(0)
C
2

Here is simple node js implementation.

'graphiql' client is good enough for development activities.

1. run npm install
2. start server with "node server.js"
3. hit "http://localhost:8080/graphiql"  for graphiql client

server.js

var graphql = require ('graphql').graphql  
var express = require('express')  
var graphQLHTTP = require('express-graphql')  

var Schema = require('./schema')  

// This is just an internal test
var query = 'query{starwar{name, gender,gender}}'  
graphql(Schema, query).then( function(result) {  
  console.log(JSON.stringify(result,null," "));
});

var app = express()  
  .use('/', graphQLHTTP({ schema: Schema, pretty: true, graphiql: true }))
  .listen(8080, function (err) {
    console.log('GraphQL Server is now running on localhost:8080');
  });

schema.js

//schema.js
var graphql = require ('graphql');  
var http = require('http');

var StarWar = [  
  { 
    "name": "default",
    "gender": "default",
    "mass": "default"
  }
];

var TodoType = new graphql.GraphQLObjectType({  
  name: 'starwar',
  fields: function () {
    return {
      name: {
        type: graphql.GraphQLString
      },
      gender: {
        type: graphql.GraphQLString
      },
      mass: {
        type: graphql.GraphQLString
      }
    }
  }
});



var QueryType = new graphql.GraphQLObjectType({  
  name: 'Query',
  fields: function () {
    return {
      starwar: {
        type: new graphql.GraphQLList(TodoType),
        resolve: function () {
          return new Promise(function (resolve, reject) {
            var request = http.get({
              hostname: 'swapi.co',
              path: '/api/people/1/',
              method: 'GET'
            }, function(res){
                    res.setEncoding('utf8');
                    res.on('data', function(response){
                    StarWar = [JSON.parse(response)];
                    resolve(StarWar)

                    console.log('On response success:' , StarWar);
                });
            });

            request.on('error', function(response){
                    console.log('On error' , response.message);
                });

            request.end();                      
          });
        }
      }
    }
  }
});

module.exports = new graphql.GraphQLSchema({  
  query: QueryType
});
Capitulum answered 24/11, 2016 at 17:56 Comment(0)
O
1

In response to @YakirNa 's comment:

I can't speak to the other needs I described, but I have done a fair amount of testing. I ended up doing all of my testing in-process.

Most testing ends up being resolver testing, which I do via a jig that invokes the graphql library's graphql function with a test query and then validates the response.

I also have an (almost) end-to-end test layer that works at the http-handling level of express. It creates a fake HTTP request and verifies the response in-process. This is all within the server process; nothing goes over the wire. I use this lightly, mostly for testing JWT authentication and other request-level behavior that's independent of the graphql request body.

Oppressive answered 27/7, 2017 at 22:41 Comment(0)
T
0

I was running into your same question, because I wanted to create a middleware service to prepare data from graphQL to a final frontend application, to have :

  • optimised data representation (and standard output data interface)
  • faster response time

assuming that graphQL server is provided by an external provider , so no ownership to data model, directly with GQL

So I didn't want to implement GraphQL Apolloclient directly in a frontend framework like React / Angular, Vuejs... but manage the queries via Nodejs at backend of a REST API.

So this is the class wrapper for Apolloclient I was able to assemble (using typescript):

import ApolloClient from "apollo-client";
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
import { onError } from 'apollo-link-error'
import fetch from 'node-fetch'
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import introspectionQueryResultData from '../../fragmentTypes.json';
import { AppConfig } from 'app-config';


const config: AppConfig = require('../../../appConfig.js');

export class GraphQLQueryClient {
    protected apolloClient: any;

    constructor(headers: { [name: string]: string }) {
        const api: any = {
            spaceId: config.app.spaceId,
            environmentId: config.app.environmentId,
            uri: config.app.uri,
            cdnApiPreviewToken: config.cdnApiPreviewToken,
        };
        // console.log(JSON.stringify(api));
        const ACCESS_TOKEN = api.cdnApiPreviewToken;
        const uri = api.uri;

        console.log(`Apollo client setup to query uri: ${uri}`);

        const fragmentMatcher = new IntrospectionFragmentMatcher({
            introspectionQueryResultData
        });

        this.apolloClient = new ApolloClient({
            link: ApolloLink.from([
                onError(({ graphQLErrors, networkError }:any) => {
                    if (graphQLErrors) {
                        graphQLErrors.map((el:any) =>
                            console.warn(
                                el.message || el
                            )
                        )
                        graphQLErrors.map(({ message, locations, path }:any) =>
                            console.warn(
                                `[GraphQL error - Env ${api.environmentId}]: Message: ${message}, Location: ${JSON.stringify(locations)}, Path: ${path}`
                            )
                        )
                    }
                    if (networkError) console.log(`[Network error]: ${networkError}`)
                }),
                new HttpLink({
                    uri,
                    credentials: 'same-origin',
                    headers: {
                        Authorization: `Bearer ${ACCESS_TOKEN}`
                    },
                    fetch
                })
            ]),
            cache: new InMemoryCache({ fragmentMatcher }),
            // fetchPolicy as network-only avoids using the cache.
            defaultOptions: {
                watchQuery: {
                    fetchPolicy: 'network-only',
                    errorPolicy: 'ignore',
                },
                query: {
                    fetchPolicy: 'network-only',
                    errorPolicy: 'all',
                },
            }
        });
    }
}   

After this constructor I run queries like :

let response = await this.apolloClient.query({ query: gql`${query}` });

As you might have noticed:

  • I needed to inject fetch on Httplink

  • I had to setup Authorization headers to access external provider graphQL endpoint

  • I used IntrospectionFragmentMatcher in order to use Fragments in my queries, along with building schema type ("fragmentTypes.json" with an init script)

Posting this to just add my experience and maybe more info for the question. Also looking forward for comments and points of improvement for this wrapper.

Tracheostomy answered 14/3, 2020 at 11:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.