├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── demo
├── custom.d.ts
├── index.html
├── index.tsx
├── queries.ts
├── tsconfig.json
└── webpack.demo.js
├── docs
├── gender_stats.png
├── gqlodash-logo.png
├── lodash.gif
├── people_to_films.png
├── planet_with_max_population.png
└── relay-architecture.png
├── package.json
├── src
├── index.ts
├── lodash_idl.ts
└── transformations.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 |
40 | # output
41 | /lib
42 | /demo/bundle*
43 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *
2 | !/lib/**
3 | !/package.json
4 | !LICENSE
5 | !README.md
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # GraphQL Lodash
4 | [](https://www.npmjs.com/package/graphql-lodash) [](https://david-dm.org/APIs-guru/graphql-lodash)
5 | [](https://david-dm.org/APIs-guru/graphql-lodash?type=dev)
6 | [](https://github.com/APIs-guru/graphql-lodash/blob/master/LICENSE)
7 |
8 | Unleash the power of [lodash](https://lodash.com/) inside your GraphQL queries
9 |
10 | #### Table of contents
11 | - [Why?](#why)
12 | - [Example queries](#example-queries)
13 | - [API](#api)
14 | - [Usage Examples](#usage-examples)
15 | - [`fetch`](#fetch-example)
16 | - [Caching clients](#caching-clients)
17 | - [**Usage with react-apollo**](#usage-with-react-apollo)
18 | - [Usage on server-side](#usage-on-server-side) (tl;dr **don't**)
19 |
20 | ## Why?
21 | GraphQL allows to ask for what you need and get exactly that. But what about the shape?
22 | GraphQL Lodash gives you the power of `lodash` right inside your GraphQL Query using `@_` directive.
23 |
24 | [](https://apis.guru/graphql-lodash/)
25 |
26 | **Note**: This is an **experimental** project created to explore the concept of **Query and transformation collocation**.
27 |
28 | We encourage you to try it inside our [demo](https://apis.guru/graphql-lodash/) or check detailed [walkthrough](https://docs.google.com/presentation/d/1aBXjC98hfYrbjUKlWGFMWgAMh9FcxeW_w97uatNYXls/pub?start=false&loop=false&delayms=3000).
29 |
30 | ## Example queries
31 | Here are a few query examples you can run against StartWars API:
32 |
33 | #### Find the planet with the biggest population
34 | 
35 | #### Get gender statistics
36 | 
37 | #### Map characters to films they are featured in
38 | 
39 |
40 | ## Install
41 |
42 | npm install --save graphql-lodash
43 | or
44 |
45 | yarn add graphql-lodash
46 |
47 | ## API
48 |
49 | ### `graphqlLodash(query, [operationName])`
50 |
51 | - **query** (_required_) - query string or query AST
52 | - **operationName** (_optional_) - required only if the query contains multiple operations
53 |
54 | ### Returns
55 | ```
56 | {
57 | query: string|object,
58 | transform: Function
59 | }
60 | ```
61 | - **query** - the original query with stripped `@_` directives
62 | - **transform** - function that receives `response.data` as a single argument and returns
63 | the same data in the intended shape.
64 |
65 |
66 |
67 | ## Usage Examples
68 |
69 | The simplest way to integrate `graphql-lodash` is to write wrapper function for graphql client of you choice:
70 | ```js
71 | import { graphqlLodash } from 'graphql-lodash';
72 |
73 | function lodashQuery(queryWithLodash) {
74 | let { query, transform } = graphqlLodash(queryWithLodash);
75 | // Make a GraphQL call using 'query' variable as a query
76 | // And place result in 'result' variable
77 | ...
78 | result.data = transform(result.data);
79 | return result;
80 | }
81 | ```
82 |
83 | ### Fetch example
84 | An example of a simple client based on [fetch API](https://developer.mozilla.org/en/docs/Web/API/Fetch_API):
85 | ```js
86 | function executeGraphQLQuery(url, query) {
87 | return fetch(url, {
88 | method: 'POST',
89 | headers: new Headers({"content-type": 'application/json'}),
90 | body: JSON.stringify({ query: query })
91 | }).then(response => {
92 | if (response.ok)
93 | return response.json();
94 | return response.text().then(body => {
95 | throw Error(response.status + ' ' + response.statusText + '\n' + body);
96 | });
97 | });
98 | }
99 |
100 | function lodashQuery(url, queryWithLodash) {
101 | let { query, transform } = window.GQLLodash.graphqlLodash(queryWithLodash);
102 | return executeGraphQLQuery(url, query).then(result => {
103 | result.data = transform(result.data);
104 | return result;
105 | });
106 | }
107 |
108 | // then use as bellow
109 | lodashQuery('https://swapi.apis.guru', `{
110 | planetWithMaxPopulation: allPlanets @_(get: "planets") {
111 | planets @_(maxBy: "population") {
112 | name
113 | population
114 | }
115 | }
116 | }`).then(result => console.log(result.data));
117 | ```
118 |
119 | ### Caching clients
120 | For caching clients like Relay and Apollo we recommend to apply the transformation after the caching layer.
121 | Here is proposed solution for Relay:
122 |
123 | 
124 |
125 | We are still figuring out how to do this and any [feedback](https://github.com/APIs-guru/graphql-lodash/issues/new) is welcome.
126 |
127 | #### Usage with [react-apollo](https://github.com/apollographql/react-apollo)
128 |
129 | When using with Apollo you can use `props` option to apply transformations:
130 |
131 | ```js
132 | const rawQuery = gql`
133 | # query with @_ directives
134 | `;
135 |
136 | const {query, transform} = graphqlLodash(rawQuery);
137 | export default graphql(query, {
138 | props: (props) => ({...props, rawData: props.data, data: transform(props.data)})
139 | })(Component);
140 | ```
141 |
142 | You can write a simple wrapper for simplicity:
143 |
144 | ```js
145 | import { graphql } from 'react-apollo';
146 | import { graphqlLodash } from 'graphql-lodash';
147 |
148 | export function gqlLodash(rawQuery, config) {
149 | const {query, transform} = graphqlLodash(rawQuery);
150 | let origProps = (config && config.props) || ((props) => props);
151 |
152 | return (comp) => graphql(query, {...config,
153 | props: (props) => origProps({
154 | ...props,
155 | rawData: props.data,
156 | data: transform(props.data)
157 | })
158 | })(comp);
159 | }
160 | // then use as bellow
161 | export default gqlLodash(query)(Component);
162 | ```
163 |
164 | Just replace `graphql` with `gqlLodash` and you are ready to use lodash in your queries.
165 | Check out the [react-apollo-lodash-demo](https://github.com/APIs-guru/react-apollo-lodash-demo) repo.
166 |
167 | You can also do the transformation inside an [Apollo
168 | Link](https://www.apollographql.com/docs/link/) by rewriting the
169 | parsed GraphQL `Document`:
170 |
171 | ```js
172 | new ApolloLink((operation, forward) => {
173 | const { query, transform } = graphqlLodash(operation.query);
174 | operation.query = query;
175 | return forward(operation)
176 | .map(response => ({
177 | ...response,
178 | data: transform(response.data),
179 | }));
180 | });
181 | ```
182 |
183 | Chaining this link with the other links passed to your `ApolloClient`
184 | will apply the transformation to every query that
185 | Apollo runs, such as those from the `` component or
186 | subscriptions.
187 |
188 | ### Introspection queries
189 |
190 | If your application uses introspection queries (like GraphiQL does to
191 | get documentation and autocomplete information), you will also need to
192 | extend the introspection query result with the directives from
193 | graphql-lodash. One way you could do this is:
194 |
195 | ```js
196 | import {
197 | buildClientSchema,
198 | extendSchema,
199 | graphqlSync,
200 | introspectionQuery,
201 | } from 'graphql';
202 |
203 | // inside the above ApolloLink function
204 | if (response.data && response.data.__schema) {
205 | const schema = extendSchema(
206 | buildClientSchema(response.data),
207 | lodashDirectiveAST,
208 | );
209 | return graphqlSync(schema, introspectionQuery);
210 | }
211 | ```
212 |
213 | See the `demo/` source in this repo for another example of modifying
214 | the introspection query result.
215 |
216 | ## Usage on server side
217 |
218 | In theory, this tool can be used on the server. But this will break the contract and, most likely,
219 | will break all the GraphQL tooling you use. Use it on server-side only if you know what you do.
220 |
--------------------------------------------------------------------------------
/demo/custom.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css' {
2 | var content: any;
3 | export = content;
4 | }
5 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |