Skip to content

Instantly share code, notes, and snippets.

@nickhudkins
Last active July 30, 2021 16:18
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nickhudkins/5802e3303d14fdfc1031d54615db486b to your computer and use it in GitHub Desktop.
Save nickhudkins/5802e3303d14fdfc1031d54615db486b to your computer and use it in GitHub Desktop.
Data Masking with Apollo / GraphQL Anywhere
import React from 'react';
import { filter } from 'graphql-anywhere';
import hoistNonReactStatic from 'hoist-non-react-statics';
/*
* createFragmentContainer returns a component which expects props that match
* WrappedComponent's fragment names, and provides data masking
*/
export default function createFragmentContainer(WrappedComponent) {
const Enhanced = props => {
const fragmentKeys = Object.keys(WrappedComponent.fragments);
const fragmentDataProps = fragmentKeys.reduce((accProps, fragmentKey) => {
const fragment = WrappedComponent.fragments[fragmentKey];
return {
...accProps,
// `filter` from graphql-anywhere is doing the heavy lifting here of masking
[fragmentKey]: filter(fragment, props[fragmentKey] || {}),
};
}, {});
return <WrappedComponent {...props} {...fragmentDataProps} />;
};
// retain fragments defined statically on WrappedComponent
hoistNonReactStatic(Enhanced, WrappedComponent);
return Enhanced;
}
import React from 'react'
import gql from 'graphql-tag';
import { Query } from 'react-apollo'
import { UserOverview } from './user-overview';
// The fragment is composed here, and
const QUERY = gql`
query getData {
currentUser {
id /* These fields, will not flow down to child components ...*
emailAddress unless otherwise specified by the child's fragment.
phoneNumber */
...UserOverView_fragment // This data will be the only data that `UserOverview` has access to.
...OtherComponent_fragment // This data will be the only data that `OtherComponent` has access to.
}
}
${UserOverview.fragments.user}
${OtherComponent.fragments.user}
`
export default function ParentComponent() {
return (
<Query query={QUERY}>
{({ data, }) => (
<React.Fragment>
<UserOverview user={data.currentUser} />
<OtherComponent user={data.currentUser} />
</React.Fragment>
)}
</Query>
)
}
import React from 'react'
import gql from 'graphql-tag';
import withMaskedData from './createFragmentContainer';
const OtherComponentUI = ({ user }) => (
<div>
<span style={{ backgroundColor: user.favoriteColor.hex } className='swatch' />
<span>{user.favoriteColor.name}</span>
</div>
)
// Define your fragments to match your expected props.
OtherComponentUI.fragments = {
user: gql`
fragment OtherComponent_fragment on User {
id
profile {
favoriteColor {
name
hex
}
}
}
`
}
// This wrapped component will have its data masked.
export const OtherComponent = withMaskedData(OtherComponent);
import React from 'react';
import gql from 'graphql-tag';
import withMaskedData from './createFragmentContainer';
const UserOverviewComponent = ({ user }) => (
<div>
<h3>{user.fullName}</h3>
<h5>{user.profile.birthDate}</h5>
</div>
);
// Define your fragments to match your expected props.
UserOverviewComponent.fragments = {
user: gql`
fragment UserOverView_fragment on User {
id
fullName
profile {
birthDate
}
}
`
}
// This wrapped component will have its data masked.
export const UserOverview = withMaskedData(UserOverviewComponent);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment