Skip to content

Commit acaf7c0

Browse files
authored
Merge pull request #34 from marmelab/ajax_interceptor
[WIP] Ajax interception
2 parents 9613388 + 1f09b56 commit acaf7c0

File tree

8 files changed

+244
-608
lines changed

8 files changed

+244
-608
lines changed

README.md

Lines changed: 70 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -407,42 +407,36 @@ app.use('/graphql', jsonGraphqlExpress(data));
407407
app.listen(PORT);
408408
```
409409

410-
## Usage on client
410+
## Usage in browser with XMLHttpRequest
411411

412-
Install the module locally:
413-
414-
```sh
415-
npm install --save json-graphql-server
416-
```
412+
Useful when using XMLHttpRequest directly or libaries such as [axios](https://www.npmjs.com/package/axios).
417413

418-
Then use the `graphQLClientServer` function:
414+
### Install with a script tag
419415

420-
```js
421-
import { graphQLClientServer } from 'json-graphql-server';
416+
Add a `script` tag referencing the library:
422417

423-
const data = [...];
418+
```html
419+
<script src="../lib/json-graphql-server.min.js"></script>
420+
```
424421

425-
const server = GraphQLClientServer({
426-
data,
427-
url: 'http://localhost:3000/graphql'
428-
});
422+
It will expose the `GraphQLClientServer` as a global object:
429423

430-
server.start();
431-
```
424+
```html
425+
<script type="text/javascript">
426+
window.addEventListener('load', function() {
427+
const data = [...];
432428
433-
This will intercepts all XMLHttpRequests and make them responds like a GraphQL server when the url matches the one specified.
429+
const server = GraphQLClientServer({
430+
data,
431+
url: 'http://localhost:3000/graphql'
432+
});
434433
435-
For example:
434+
server.start();
436435
437-
```js
438-
window.document
439-
.getElementById('btnLoadPosts')
440-
.addEventListener('click', function () {
441-
var xhr = new XMLHttpRequest();
442-
xhr.responseType = 'json';
443-
xhr.open("POST", "http://localhost:3000/graphql", true);
444-
xhr.setRequestHeader("Content-Type", "application/json");
445-
xhr.setRequestHeader("Accept", "application/json");
436+
const xhr = new XMLHttpRequest();
437+
xhr.open('POST', 'http://localhost:3000/graphql', true);
438+
xhr.setRequestHeader('Content-Type', 'application/json');
439+
xhr.setRequestHeader('Accept', 'application/json');
446440
xhr.onerror = function(error) {
447441
console.error(error);
448442
}
@@ -454,31 +448,63 @@ window.document
454448
const body = JSON.stringify({ query: 'query allPosts { allPosts { id } }' });
455449
xhr.send(body);
456450
});
451+
</script>
457452
```
458453

459-
## Usage in browser through script
454+
### Use with a bundler (webpack)
460455

461-
Add a `script` tag referencing the library:
462-
463-
```html
464-
<script src="../lib/json-graphql-server.min.js"></script>
456+
```sh
457+
npm install json-graphql-server
465458
```
466459

467-
It will expose the `GraphQLClientServer` as a global object:
460+
```js
461+
import GraphQLClientServer from 'json-graphql-server';
468462

469-
```html
470-
<script type="text/javascript">
471-
window.addEventListener('load', function() {
472-
const data = [...];
463+
const data = [...];
473464

474-
const server = GraphQLClientServer({
475-
data,
476-
url: 'http://localhost:3000/graphql'
477-
});
465+
const server = GraphQLClientServer({
466+
data,
467+
url: 'http://localhost:3000/graphql'
468+
});
478469

479-
server.start();
480-
});
481-
</script>
470+
server.start();
471+
472+
const xhr = new XMLHttpRequest();
473+
xhr.open('POST', 'http://localhost:3000/graphql', true);
474+
xhr.setRequestHeader('Content-Type', 'application/json');
475+
xhr.setRequestHeader('Accept', 'application/json');
476+
xhr.onerror = function(error) {
477+
console.error(error);
478+
}
479+
xhr.onload = function() {
480+
const result = JSON.parse(xhr.responseText);
481+
console.log('data returned:', result);
482+
alert('Found ' + result.data.allPosts.length + ' posts');
483+
}
484+
const body = JSON.stringify({ query: 'query allPosts { allPosts { id } }' });
485+
xhr.send(body);
486+
```
487+
488+
## Usage in browser with fetch
489+
490+
```js
491+
import fetchMock from 'fetch-mock';
492+
import GraphQLClientServer from 'json-graphql-server';
493+
494+
const data = [...];
495+
const server = GraphQLClientServer({ data });
496+
497+
fetchMock.post('http://localhost:3000/graphql', server.getHandler());
498+
499+
fetch({
500+
url: 'http://localhost:3000/graphql',
501+
method: 'POST',
502+
body: JSON.stringify({ query: 'query allPosts { allPosts { id } }' })
503+
})
504+
.then(response => response.json())
505+
.then(json => {
506+
alert('Found ' + result.data.allPosts.length + ' posts');
507+
})
482508
```
483509

484510
## Adding Authentication, Custom Routes, etc.

example/index.html

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<!doctype html>
22
<html lang="en">
3-
<head></head>
3+
<head>
4+
<meta charset="UTF-8">
5+
</head>
46
<body>
57
<button id="button">Load posts</button>
68
<script src="../lib/json-graphql-server.min.js"></script>
@@ -33,29 +35,29 @@
3335
],
3436
};
3537

36-
const server = GraphQLClientServer({
37-
data,
38-
url: 'http://localhost:3000/graphql'
39-
});
40-
41-
server.start();
42-
});
43-
window.document.getElementById('button').addEventListener('click', function () {
44-
const xhr = new XMLHttpRequest();
45-
xhr.responseType = 'json';
46-
xhr.open("POST", "http://localhost:3000/graphql", true);
47-
xhr.setRequestHeader("Content-Type", "application/json");
48-
xhr.setRequestHeader("Accept", "application/json");
49-
xhr.onerror = function(error) {
50-
console.error(error);
38+
try {
39+
const server = GraphQLClientServer({ data, url: "http://localhost:3000/graphql" });
40+
server.start();
41+
} catch(error) {
42+
console.error({error});
5143
}
52-
xhr.onload = function() {
53-
const result = JSON.parse(xhr.responseText);
54-
console.log('data returned:', result);
55-
alert('Found ' + result.data.allPosts.length + ' posts');
56-
}
57-
const body = JSON.stringify({ query: 'query allPosts { allPosts { id } }' });
58-
xhr.send(body);
44+
45+
window.document.getElementById('button').addEventListener('click', function () {
46+
const xhr = new XMLHttpRequest();
47+
xhr.open("POST", "http://localhost:3000/graphql", true);
48+
xhr.setRequestHeader("Content-Type", "application/json");
49+
xhr.setRequestHeader("Accept", "application/json");
50+
xhr.onerror = function(error) {
51+
console.error(error);
52+
}
53+
xhr.onload = function() {
54+
const result = JSON.parse(xhr.responseText);
55+
console.log('data returned:', result);
56+
alert('Found ' + result.data.allPosts.length + ' posts');
57+
}
58+
const body = JSON.stringify({ query: 'query allPosts { allPosts { id } }' });
59+
xhr.send(body);
60+
});
5961
});
6062
</script>
6163
</body>

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"graphql-type-json": "~0.1.4",
6363
"inflection": "~1.12.0",
6464
"lodash.merge": "~4.6.0",
65-
"reify": "~0.12.0"
65+
"reify": "~0.12.0",
66+
"xhr-mock": "2.0.2"
6667
},
6768
"bin": {
6869
"json-graphql-server": "bin/json-graphql-server.js"

src/graphQLClientServer.js

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { MockHttpServer } from './mockHttpRequest';
2-
import { graphql } from 'graphql';
3-
import schemaBuilder from './schemaBuilder';
1+
import mock, { proxy } from 'xhr-mock';
2+
import handleRequestFactory from './handleRequest';
43

54
/**
65
* Starts a GraphQL Server in your browser: intercepts every call to http://localhost:3000/graphql
@@ -42,40 +41,39 @@ import schemaBuilder from './schemaBuilder';
4241
* GraphQLClientServer(data, 'http://localhost:8080/api/graphql');
4342
*/
4443
export default function({ data, url }) {
45-
const schema = schemaBuilder(data);
44+
const handleRequest = handleRequestFactory(data);
4645

47-
const server = new MockHttpServer(req => {
48-
if (!req.url.startsWith(url)) {
49-
// FIXME: if req.url does not match url for endpoint, handle it with window.OriginalHttpRequest
50-
}
46+
return {
47+
start() {
48+
// Intercept all XmlHttpRequest
49+
mock.setup();
5150

52-
const query = JSON.parse(req.requestText);
51+
// Only handle POST request to the specified url
52+
mock.post(
53+
url,
54+
(req, res) =>
55+
new Promise(resolve => {
56+
handleRequest({
57+
requestBody: req.body(),
58+
respond(status, headers, body) {
59+
res.status(status);
60+
res.headers(headers);
61+
res.body(body);
5362

54-
graphql(
55-
schema,
56-
query.query,
57-
undefined,
58-
undefined,
59-
query.variables
60-
).then(
61-
result => {
62-
const body = JSON.stringify(result);
63-
req.setResponseHeader('Content-Type', 'application/json');
64-
req.receive(200, body);
65-
},
66-
error => {
67-
req.setResponseHeader('Content-Type', 'application/json');
68-
req.receive(500, JSON.stringify(error));
69-
}
70-
);
71-
});
63+
resolve(res);
64+
},
65+
});
66+
})
67+
);
7268

73-
return {
74-
start: () => {
75-
server.start();
69+
// Ensure all other requests are handled by the default XmlHttpRequest
70+
mock.use(proxy);
71+
},
72+
stop() {
73+
mock.teardown();
7674
},
77-
stop: () => {
78-
server.stop();
75+
getHandler() {
76+
return handleRequest;
7977
},
8078
};
8179
}

src/handleRequest.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { graphql } from 'graphql';
2+
import schemaBuilder from './schemaBuilder';
3+
4+
/**
5+
* Starts a GraphQL Server in your browser: intercepts every call to http://localhost:3000/graphql
6+
* and returns a response from the supplied data.
7+
*
8+
* @export A sinon.js FakeServer (http://sinonjs.org/releases/v2.3.6/fake-xhr-and-server/#fake-server)
9+
* @param {any} data
10+
* @param {any} url Specifies the endpoint to intercept (Default is 'http://localhost:3000/graphql').
11+
*
12+
* @example
13+
* const data = {
14+
* "posts": [
15+
* {
16+
* "id": 1,
17+
* "title": "Lorem Ipsum",
18+
* "views": 254,
19+
* "user_id": 123,
20+
* },
21+
* {
22+
* "id": 2,
23+
* "title": "Sic Dolor amet",
24+
* "views": 65,
25+
* "user_id": 456,
26+
* },
27+
* ],
28+
* "users": [
29+
* {
30+
* "id": 123,
31+
* "name": "John Doe"
32+
* },
33+
* {
34+
* "id": 456,
35+
* "name": "Jane Doe"
36+
* }
37+
* ],
38+
* };
39+
*
40+
* GraphQLClientServer(data);
41+
* GraphQLClientServer(data, 'http://localhost:8080/api/graphql');
42+
*/
43+
export default function(data) {
44+
const schema = schemaBuilder(data);
45+
return request => {
46+
const query = JSON.parse(request.requestBody);
47+
48+
return graphql(
49+
schema,
50+
query.query,
51+
undefined,
52+
undefined,
53+
query.variables
54+
).then(
55+
result => {
56+
request.respond(
57+
200,
58+
{ 'Content-Type': 'application/json' },
59+
JSON.stringify(result)
60+
);
61+
},
62+
error => {
63+
request.respond(
64+
500,
65+
{ 'Content-Type': 'application/json' },
66+
JSON.stringify(error)
67+
);
68+
}
69+
);
70+
};
71+
}

0 commit comments

Comments
 (0)