Any chance for relay integration with apollo...?

first of all congratulation with alpha apollo… i can’t believe you make it alpha so fast… this is so great…

something wondering me though… why redux…?? why not relay…??

i don’t really know redux though, when i started to learn react, people was debating redux with relay… not sure what they were debating but the point i got is that most people prefer relay, so should i… that’s the reason i learn react with graphql and relay… but relay is truly fantastic…

for example with relay we don’t need to deal so much with flux store state mounting unmounting and event handlers related stuff… a lot react api would be useless because relay take care of them… i feel react with relay is as easy as blaze with tracker reactivevar session and mongo/minimongo but with more power and weapon…

for example here i made simple bear app with react relay and graphql…

without relay…

import API from "../API";
import BearHouse from "../BearHouse";

let _getBearAppState = () => {
  return {bears: BearHouse.getAll()};
}

class Karina extends React.Component{

  static propTypes = {limit: React.PropTypes.number}
  
  static defaultProps = {limit: 5}

  state = _getBearAppState();

  constructor(props){
    super(props);
    this.state = _getBearAppState();
    this.onChange = this.onChange.bind(this);
  }

  onChange(){
    this.setState(_getBearAppState());
  }

  componentDidMount(){
    API.fetchLinks();
    BearHouse.on("change", this.onChange);
  }

  componentWillUnmount(){
    BearHouse.removeListener("change", this.onChange);
  }

  var h = React.createElement;

  render(){
    let getBear = this.state.slice(0, this.props.limit).bears.map(bear => {
      return 
        h('li', {key: bear._id}, 
          h('strong', null, bear.name),
          h('a', {href: bear.url}, 
            h('img', {src: bear.picture})
          )
        );
    });
    
    return h('div', null, 
              h('h3', null, "I Want My Bear!!"),
              h('ul', {href: bear.url}, 
                h(getBear)
              )
            );
  }
}

with relay…

class Karina extends React.Component{

  static propTypes = {limit: React.PropTypes.number}
  
  static defaultProps = {limit: 5}

  var h = React.createElement;

  render(){
    let getBear = this.props.bearHouse.slice(0, this.props.limit).bears.map(bear => {
      return h('li', {key: bear._id}, 
                h('strong', null, bear.name),
                h('a', {href: bear.url}, 
                  h('img', {src: bear.picture})
                )
              );
    });
    
    return h('div', null, 
              h('h3', null, "I Want My Bear!!"),
              h('ul', {href: bear.url}, 
                h(getBear)
              )
            );
  }
}

# after that,,, we only need to define data requirement then relay and graphql do the rest... 
# Relay.createContainer is similar to onCreated in blaze template and createContainer in meteor react 
# which will guarantee to supply data before rendering begin...

Karina = Relay.createContainer(Karina, { 
  fragments: {
    bearHouse: () => Relay.QL`
      fragment on Bear {
        bears {
          _id,
          name,
          url,
          picture
        }
      }
    `
  }
});

relay also come with routing stuff with similar usage like flow router params query params, if u previously use flow router then routing with relay would be dead easy…

# route for single bear...
class BearRoute extends Relay.Route {
  static routeName = 'HouseOfBear';
  static queries = {
    bearHouse: (Component) => Relay.QL`
      query KarinaQuery {
        bearHouse: {${Component.getFragment('bearHouse')}}  
        // 'bearHouse' is a fragment that we defined above.. look look... it's similar with flow router query params...!! 
        // getFragment of course like it's name it is relay function to call fragment...
      }
    `
  }
}

ReactDOM.render(Relay.RootContainer, {Component:Karina route:new BearRoute()}, document.getElementById('myBigBear'));

relay makes pagination easier…very easy in offset/limit with after/first rule data management… for example if i have bearsy berry bearny bernard bearnea and berthold (they’re all my bear if u ask) and i limit into first 3… if user delete bearsy then second fetching will be bernard bearnea and berthold (correct) instead of bearny bernard and bearnea (wrong)… also with edges node connection we have pageInfo providing many usefull connection info, for example it provide hasNextPages… you can use for something like this…

if (hasNextPages) {
  # do loading state for infinite scroll and then query new bear list without refetch what we already have in cleint
}

no need publish count or whatsoever…

also optimistic mutation and optimistic update… for example…

# pagination with 5 bears for initial limit,,, find bear,, 
# and adding new bear with optimistic mutation and optimistic update..

class Karina extends React.Component{
  constructor(props) {
    super(props);
    this.find = _.debounce(this.find, 300);
  }
  find = (e) => {
    let query = e.target.value;
    this.props.relay.setVariables({ query });
  }
  pageLimit = (e) => {
    let newLimit = Number(e.target.value);
    this.props.relay.setVariables({limit: newLimit});
  }
  insert = (e) => {
    e.preventDefault();
    Relay.Store.update(
      new InsertBearMutation({ // mutationWithClientMutationId
        name: this.refs.newName.value,
        url: this.refs.newUrl.value,
        picture: this.refs.newPicture.value,
        bearHouse: this.props.bearHouse // data reactivity with relay
      })
    );
    this.refs.newName.value = "";
    this.refs.newUrl.value = "";
    this.refs.newPicture.value = "";
  }

  saving = () => {
    let {bear, relay} = this.props;
    if (relay.hasOptimisticUpdate(bear)) {
      return "Storing your cute bear to House of Bear..."; //  very easy to do client to server optimistic update..!! i can't find something like this in meteor world
    }
  }

  var h = React.createElement;

  render(){
    let getBear = this.props.bearHouse.bearConnection.edges.map(edge => {
      return h(Bear, {key:edge.node.id, bear:edge.node}); // tracking single bear state changing similar like tracker
    });

    return h('div', null, 
        h('h3', null, "Submit Your Cute Bear..."),

        h('form', {onSubmit:this.insert}, 
          h('input', {type:"text", placeholder:"Your Bear Name" ref:"newName"}),
          h('input', {type:"text", placeholder:"Your Bear Url" ref:"newUrl"}),
          h('input', {type:"text", placeholder:"Your Bear Picture" ref:"newPicture"}),
          h('button', {type:"submit"}, "Submit")
        ),
        
        h('p', null, this.saving()),
        h('h3', null, "Show My Bear..."),
        h('p', null, "Showing:   bears"),

        h('select', {onChange:this.pageLimit, defaultValue:this.props.relay.variables.limit}, // data state change with relay
          h('option', {value:5}, "5"),
          h('option', {value:10}, "10")
        ),
        
        h('input', {type:"text", placeholder:"Find Your Cute Bear" onChange:this.find}),
        h('ul', null, h(getBear))
      );
    
    let getBear = this.props.bearHouse.bearConnection.edges.map(edge => {
      return <Bear key={edge.node.id} bear={edge.node} />;
    });
  }
}

Karina = Relay.createContainer(Karina, {
  initialVariables: {
    limit:5, // setup limit similar to ReactiveVar self.limit.set(5) put on onCreated template..
    query: ''
  },
  fragments: {
    bearHouse: () => Relay.QL`
      fragment on Bear {
        id,
        bearConnection(first: $limit, query: $query) {
          edges {
            node {
              id,
              ${Bear.getFragment('bear')}
            }
          }
        }
      }
    `
  }
});

the other powerful features that come with relay out of the box:

  • relay uses batching request that will drastically optimize server resources… hence scaling would be easy because it’s already efficient at the core…
  • error handling for request if request somewhat fail…
  • retry and rollback strategies when request failed…
  • relay implement edges node connection and will fetch the difference between what we need and what we have in the client thus will bring huge efficiency…
  • and data caching to reduce server round trips…

there are a lot of room that apollo or else could provide that will bring better value,

1. make it more easier…

for example… this is how i muatate bear…

/*schemas.js*/

let insertBearMutation = mutationWithClientMutationId({
  name: 'InsertBear',

  inputFields: {
    name: { type: new GraphQLNonNull(GraphQLString) },
    url: { type: new GraphQLNonNull(GraphQLString) },
    picture: { type: new GraphQLNonNull(GraphQLString) }
  },

  outputFields: {
    bearEdge: {
      type: bearConnection.edgeType,
      resolve: (obj) => ({ node: obj.ops[0], cursor: obj.insertedId })
    },
    bearHouse: {
      type: bearType,
      resolve: () => bearHouse
    }
  },

  mutateAndGetPayload: ({title, url, picture}) => {
    return db.collection("bears").insert({
      title,
      url,
      picture,
      createdAt: Date.now()
    });
  }
});

let schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      node: nodeDefs.nodeField,
      bearHouse: {
        type: bearType,
        resolve: () => bearHouse
      }
    })
  }),

  mutation: new GraphQLObjectType({
    name: 'Mutation',
    fields: () => ({
      insertBear: insertBearMutation
    })
  })
});

/*InsertBearMutation.js*/

class InsertBearMutation extends Relay.Mutation {
  getMutation() {
    return Relay.QL`
      mutation { insertBear }
    `;
  }

  getVariables() {
    return {
      name: this.props.name,
      url: this.props.url,
      picture: this.props.picture
    }
  }

  getFatQuery() {
    return Relay.QL`
      fragment on CreateLinkPayload {
        bearEdge,
        bearHouse { bearConnection }
      }
    `;
  }

  getConfigs() {
    return [{
      type: 'RANGE_ADD',
      parentName: 'bearHouse',
      parentID: this.props.bearHouse.id,
      connectionName: 'bearConnection',
      edgeName: 'bearEdge',
      rangeBehaviors: {
        '': 'prepend',
      },
    }]
  }

  getOptimisticResponse() {
    return {
      bearEdge: {
        node: {
          title: this.props.title,
          url: this.props.url,
          picture: this.props.picture
        }
      }
    }
  }
}

understand…?? no…?? same goes with me… actually i don’t really understand… that’s why this would be great if i just write something like this…

Bears.insert({name:"Bearsy",url:"/bearsy",picture:"bearsy.jpg"})

2. scaling real time app…

what make meteor soo coool because it easy to create real time app… unfortunately i found a lot thread questioning scalability… and guess what, big enterprise is taking this point seriously… that’s why, although meteor gain a lot popularity this time, when i see meteor homepage site i don’t see big enterprise using meteor for now… i guess big enterprise still at the state of watching meteor stack rather than trying… but this state is great, i guess they know scaling real time app is really really hard… all you need to do is to break the pain point…

graphql-relay have good architecture and in theory this will scale app… as for now graphql-relay is not real time… why don’t mdg take it as a starting point and combining with real time meteor to create a scalable real time app stack…?? big enterprise will start trying i guess…

3. no clear guide…

when you play with graphql-relay for now, you should deal with very hard to understand documentation… little useful tutorial… little useful q&a… no clear where to start and how to start… something like meteor guide would be awesome…!!

4. setup is really really hard…

it takes me a week to figure out how to setup react-graphql-relay… you should deal with webpack react setup db setup babel relay plugin graphql setup relay setup etc etc… i guess this is barrier for adoption… i miss something like…

meteor create iMissMyBear 
# or maybe
apollo create iMissMyBear 

after that just focus writing app without setup headache…

5. giving router to the core…

relay have router api inside it, though it’s not so great like react router… then make it great based on current router api and put to the core… then this will make apollo un-opinionated as your users don’t need to choose which router should they use…

6. integrate with react-native…

providing a relay integration would be a good start if meteor/apollo would start giving first class support for react-native… why…?? because relay integrate with react-native out of the box…

i see facebook guy doing a lot pull request onto react native in order to get better and better integration with relay…

7. apollo-native: real time native mobile app creation with react-native…

since relay is integrated with react-native out of the box, the hard part is already taken care i guess, no more hacking i assume… now what missing…?? real time…?? yappp… isn’t that meteor positioning…?? if possible, would be great if mdg create apollo-native which giving real-time support for react-native, if that’s the case, i guess apollo-native would be the most amazing mobile app creation on the planet…!!!

also the need for mobile backend that support real time thing would be huge i guess… parse will shutdown in 2017… where the users should go…?? firebase…?? what if google shutdown firebase later with similar reason why facebook shutdown parse since they both play on the same league…?? how about galaxy which have concept BYOD (bring your own db)…?? i like BYOD… BYOD would be preferable especially for enterprise who have sensitive data and need ultimate privacy like financial service… financial service have their own hardware to store data… they can’t rent db for whatever reason…

and also easy setup is beautiful… something like this…

# for native mobile 
apollo-native create iMissMyBear 

8. apollo-native integration with meteor web app…

i don’t have production meteor web app, i just built app with meteor for my college project, but if i do, i don’t have idea how to integrate with react-native-relay with graphql server… for simple example, how do i authenticate current user with react-native-relay-graphql app that previously signed up with meteor web app…?? that’s simple thing… but a lot of things to do i guess… thus would be great if apollo-native could also support integration with meteor web-app without a lot code rewriting…

summary, if possible, would be great if apollo could also support relay integration… by leveraging relay advantage, apollo would possibly become the most beautiful app creation with the title: real-time x BYOD x scale x easy x mobile nativethis is the dream of every developer i guess

thank Youuuu…
@karina
whaaa i can mention myself… this is so great…

7 Likes

Why not using Relay?

This is a good question. Relay is Facebook’s application framework for React. It’s also a very efficient GraphQL cache. However, it’s not as easy to use as working with Meteor and it’s only for React.

Again, before you work on a Relay app, you need to build some logistics on your own. So, it won’t be an experience like Meteor.

source

Apollo Client is just one option

I think the first thing to mention here is that Apollo Client is just one option to fetch data from your graphql server. While we think people will get the best developer experience with the whole apollo stack together, you can use Apollo Server with Relay, or Apollo Client with Graphene, etc. So if you really like Relay, you can use it just like you did before! Probably all of the important tools we build for the server will be compatible with Relay, since it’s such a popular choice for the frontend.

However, we think there should be more than one option for the GraphQL client, and we aren’t the only ones. There have been many proposed alternatives to Relay, and we think we can make something that has some very appealing tradeoffs.

I’ve opened an issue to make a clearer comparison table between Apollo and Relay:

https://github.com/apollostack/docs/issues/110

Thanks!

Apollo client is much more than Redux

We’re actually building an alternative to Relay on top of Redux. So we aren’t suggesting that people should use Redux off the shelf. In fact our main goal is to combine the things people love about Redux - great dev tools, simple mental model, easy server-side rendering, and more - with what people love about Relay - declarative data fetching, colocating data with components, etc.

I don’t know if you have read much about Apollo yet, but you get the exact same things with Apollo Client.

Code snippet with Apollo

I agree that it’s great that Relay lets you declaratively fetch data! The Apollo example will look almost identical to your Relay code sample:

// With apollo client
Karina = connect({
  mapQueriesToProps: () => {
    bearHouse: {
      query: gql`
        {
          bears {
            _id,
            name,
            url,
            picture
          }
        }
      `
    }
  }
})(Karina);

I think this is actually one of the downsides of Relay - that it requires you to use a specific Router. With Apollo Client, you can actually use Flow Router, or React Router, or anything you want.

This is true - pagination in Relay is relatively simple once you implement connections on your server.

This is one of the things we hope to get to soon in Apollo Client. I think Relay’s optimistic updates and mutation results could be a lot simpler. Meteor probably has the simplest optimistic UI model of any framework ever, and we want to bring that to Apollo.

Features already included in Apollo client:

  1. Batching
  2. Error handling
  3. Caching

Not yet included:

  1. Pagination based on Relay connections, but I think we have some much simpler ideas that will be easier for people to use.

Bringing even more value

I completely agree - making mutations simpler is one of our primary goals. For example, here is how you make a mutation in Apollo Client:

https://github.com/apollostack/GitHunt/blob/e8907d9a7150f14e5fba4917dd4627d200144fac/ui/Feed.js#L149-L167

We hope to get to this soon, but we are deprioritizing reactivity for now because there is so much else to do that we think will be more valuable. Right now, we have a built-in pollInterval and refetch feature so that you can have the client refetch automatically to simulate reactivity.

I agree! The Apollo docs aren’t too great either right now, but I think that’s one of the defining features of a technical preview. The docs are coming soon.

We’re attaching this head on! In fact we already have a few tutorials, and setting up apollo client is just as simple as npm install apollo-client. You don’t need to set up babel, any build plugins, etc.

Our approach to this is to make Apollo much more agnostic to this than Relay. Right now you can use Apollo with any router at all, and even with any UI framework. For example we have a first-class Angular 2 integration.

Apollo client already supports React Native out of the box with no configuration needed. In fact we have heard of many people using it this way.

Conclusion

I think there is currently a mindset that Relay gives you a lot of features that you can only get with it. I think we are doing a good job of proving that alternatives to Relay can exist with a favorable list of tradeoffs. At the end of the day, you should pick whichever is the best for you, and we are doing our best to make sure that choice is up to the developer, so that you can use Relay if that is your favorite.

I’d suggest you give Apollo Client another try, since it looks like you haven’t really looked into it. Take a look at the example app here: https://github.com/apollostack/GitHunt/tree/e8907d9a7150f14e5fba4917dd4627d200144fac/ui

I think it’s really simple.

5 Likes

like this…
there is no such one perfect solution for everything… everything has things they are good at and things they are not… and difference is what makes this world beautiful…
more option is always better…

oooo i see i see i see… actually i like this… competition will push creators to increase their product quality better and better and this will push human race forward yielding prettier world… good job mdg…!! i love what you guys doing…

i like meteor guide style… it’s easy to read and easy to understand… that’s non-technical strategy that matter much in adoption of new technology…

whaaaaa can’t wait to play it…

indeed hahha… i just knowing u released alpha the day i wrote this topic… and haven’t read the docs… will play with apollo after i finish my exam…

thank Youu for ur explanation…
@karina

3 Likes

If apollo can provide a common real-time database solutions in the upper layer, Using multiple databases, but still be able to have real-time without the brain,it would be very cool thing, horizon project will not have much meaning; meteor now it seems very front, if apollo can freely combine various technologies, with meteor features, I think it is perfect

1 Like

That’s the goal! Horizon seems very focused on being specifically for RethinkDB, but we’ve seen that no one database technology can work for all apps. That’s why we’re committed to building things that are not coupled to any specific backend, as long as you can write a simple GraphQL adapter for it.

2 Likes