Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping a RESTapi with GraphQL using Apollo React

I need to do a project (currency exchange app) using Apollo client and React. I need to wrap an existing REST api (fixer.io) with graphql. So far no luck finding a solution online. Tried several tutorials but they don't seem to work. Anyone have experience with this?

Thanks.

like image 903
Patrick Molnár Avatar asked Oct 07 '17 12:10

Patrick Molnár


People also ask

Can you use REST API and GraphQL together?

It is possible to have a mix of APIs (GraphQL and REST, for example) in a project and even to share data stores (such as databases) between the various API schema.

Can I use Apollo client with REST API?

You can use apollo-link-rest to call REST API inside your GraphQL queries.


2 Answers

I assume you use Apollo client 2.0 and want everything to be client side.

First you need an apollo bridge link. It's used "When you don't have GraphQL server (yet) and want to use GraphQL on the client". Its source code is quite short, so you can inline it:

/*
  Copyright (c) 2017 David Cizek
  https://github.com/dacz/apollo-bridge-link
*/
import { GraphQLSchema, graphql, print } from 'graphql';
import { addMockFunctionsToSchema, makeExecutableSchema } from 'graphql-tools';

import { ApolloLink } from 'apollo-link';
import Observable from 'zen-observable';

export const createBridgeLink = ({ schema, resolvers, mock, context = {} }) => {
    let executableSchema;
    if (typeof schema === 'string') {
        executableSchema = makeExecutableSchema({ typeDefs: schema, resolvers });
    } else if (schema.kind === 'Document') {
        executableSchema = makeExecutableSchema({
            typeDefs: print(schema),
            resolvers,
        });
    } else if (schema instanceof GraphQLSchema) {
        executableSchema = schema;
    } else {
        throw new Error('schema should be plain text, parsed schema or executable schema.');
    }

    if (mock)
        {addMockFunctionsToSchema({
            schema: executableSchema,
            preserveResolvers: true,
        });}

    return new ApolloLink(
        operation =>
            new Observable(observer => {
                const { headers, credentials } = operation.getContext();
                const ctx = {
                    ...context,
                    headers,
                    credentials,
                };

                graphql(executableSchema, print(operation.query), undefined, ctx, operation.variables, operation.operationName)
                    .then(data => {
                        observer.next(data);
                        observer.complete();
                    })
                    .catch(err => {
                        /* istanbul ignore next */
                        observer.error(err);
                    });
            }),
    );
};

export class BridgeLink extends ApolloLink {
    requester;

    constructor(opts) {
        super();
        this.requester = createBridgeLink(opts).request;
    }

    request(op) {
        return this.requester(op);
    }
}

Next you create schema and resolvers:

// schema.js
export default `
type Rate {
  date: Date!
  rate: Float!
}

type Query {
  latestRate(from: String!, to: String!): Rate
}

schema {
  query: Query
}
`;


// resolvers.js
const resolvers = {
  Query: {
    latestRate(obj, args, context, info) {
      return fetch(`https://api.fixer.io/latest?base=${args.from}`).then(res => res.json())
      .then(res => { date: res.date, rate: res.rates[args.to] })
    }
  }
}

export default resolvers;

Finally, you create an apollo client factory:

// clientFactory.js
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';

import { BridgeLink } from './apollo-bridge-link';

import schema from './schema';
import resolvers from './resolvers';

export default () => {
    const mock = false;
    const context = {};

    const client = new ApolloClient({
        link: new BridgeLink({ schema, resolvers, mock, context }),
        cache: new InMemoryCache(),
    });
    return client;
};

Here's how you use it:

import gql from 'graphql-tag';
import clientFactory from './clientFactory'

const client = clientFactory();

client.query(gql`query {
  latestRate(from: "USD", to: "EUR") { date, rate }
}`).then(console.log)

If you want to use it in React:

import { ApolloProvider } from 'react-apollo';
const client = clientFactory();

const App = ({ data: { latestRate, refetch } }) => {
  return <div>
    <span>Today:</span><span>{latestRate.date}</span>
    <span>1 USD equals:</span><span>{latestRate.rate} EUR</span>
    <button onClick={() => refetch()}>
      Refresh
    </button>
  </div>
}

const AppWithQuery = graphql(gql`
  query {
    latestRate(from: "USD", to: "EUR") { date, rate }
  }
`)(App);

ReactDOM.render(
  <ApolloProvider client={client}>
    <AppWithQuery/>
  </ApolloProvider>,
  document.getElementById('root'),
);
like image 96
Vanuan Avatar answered Sep 30 '22 06:09

Vanuan


With the Graphcool Framework, you can define resolver functions, which allow you to easily wrap any REST API. You can define a function and connect it to a specific mutation or query in your GraphQL Schema.

I prepared a demo, wrapping the fixer API.

Try to run this query to get the exchange rates with USD as base, for example:

query {
  fixer(
    base: "USD"
  ) {
    base
    date
    eur
    usd
    rub
  }
}

You can build this demo yourself like this:

git clone [email protected]:graphcool/templates.git
cd templates/curated/misc/fixer-wrapper
npm install -g graphcool@next
graphcool init
graphcool deploy
graphcool playground

Please feel free to share any improvement you might have in mind, the example is open source. You can read more about resolvers here.

like image 43
marktani Avatar answered Sep 30 '22 06:09

marktani