Skip to content

Commit

Permalink
[persisted queries] Revert file extension to .queryMap.json
Browse files Browse the repository at this point in the history
Having `foo.graphql.json` and `foo.graphql.other-ext` next to each other
can lead to undefined results depending on packager and presumably the
order supported file extensions have been defined. For instance, using
TypeScript and the React Native packager (Metro), the `.json` file would
get loaded at runtime by Relay rather than the expected `.ts` file.
  • Loading branch information
alloy committed Mar 24, 2018
1 parent f9428bb commit c08f2c9
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 41 deletions.
8 changes: 4 additions & 4 deletions docs/Modern-GraphQLInRelay.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Will cause a generated file to appear in `./__generated__/MyComponent.graphql`,
with both runtime artifacts (which help to read and write from the Relay Store)
and [Flow types](https://flow.org/) to help you write type-safe code.

The Relay Compiler is responsible for generating code as part of a build step which, at runtime, can be used statically. By building the query ahead of time, the client's JS runtime is not responsible for generating a query string, and fields that are duplicated in the query can be merged during the build step, to improve parsing efficiency.
The Relay Compiler is responsible for generating code as part of a build step which, at runtime, can be used statically. By building the query ahead of time, the client's JS runtime is not responsible for generating a query string, and fields that are duplicated in the query can be merged during the build step, to improve parsing efficiency.

### Persisting queries
The Relay Compiler can convert a query or mutation's text into a unique identifier during compilation. This can greatly reduce the upload bytes required in some applications.
Expand All @@ -164,8 +164,8 @@ The Relay Compiler can persist your queries with the `--persist` flag:
}
```

This will create a matching `./__generated__/MyComponent.graphql.json` containing the query id and the operation text of the query in the same directory.
The Relay Compiler aggregates all the generated `*.graphql.json` files into a single complete query map file at `./src/queryMap.graphql.json`. You can then use this complete
This will create a matching `./__generated__/MyComponent.queryMap.json` containing the query id and the operation text of the query in the same directory.
The Relay Compiler aggregates all the generated `*.queryMap.json` files into a single complete query map file at `./src/queryMap.json`. You can then use this complete
json file in your server side to map query ids to operation text.

For more details, refer to the [Persisted Queries section](./persisted-queries.html).
Expand Down Expand Up @@ -248,7 +248,7 @@ This would produce three generated files, and two `__generated__` directories:

If you use `--persist`, then an extra query map json file will also be generated:

* `src/Queries/__generated__/DictionaryQuery.graphql.json`
* `src/Queries/__generated__/DictionaryQuery.queryMap.json`

Only one query map json file is generated in this instance because only concrete queries can be persisted. Fragments are not persisted.

Expand Down
50 changes: 25 additions & 25 deletions docs/Modern-PersistedQueries.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ title: Persisted Queries

The relay compiler supports persisted queries which is useful because:

* the client operation text becomes just an md5 hash which is usually shorter than the real
* the client operation text becomes just an md5 hash which is usually shorter than the real
query string. This saves upload bytes from the client to the server.

* the server can now whitelist queries which improves security by restricting the operations
* the server can now whitelist queries which improves security by restricting the operations
that can be run from the client.

## Usage on the client
Expand Down Expand Up @@ -41,9 +41,9 @@ The `--persist` flag does 3 things:
};
})();
```

With `--persist` this becomes:

```js
const node/*: ConcreteRequest*/ = (function(){
//... excluded for brevity
Expand All @@ -58,31 +58,31 @@ The `--persist` flag does 3 things:
})();
```

2. It generates a matching `.graphql.json` file containing a map of the id and the operation text in the same `__generated__`
2. It generates a matching `.queryMap.json` file containing a map of the id and the operation text in the same `__generated__`
directory as the `.graphql.js` file. In the example above, the `__generated__` directory will have these files:

* `./__generated__/TodoItemRefetchQuery.graphql.js`
* `./__generated__/TodoItemRefetchQuery.graphql.json`
The `.graphql.json` file looks something like this:
* `./__generated__/TodoItemRefetchQuery.queryMap.json`

The `.queryMap.json` file looks something like this:

```json
{
"3be4abb81fa595e25eb725b2c6a87508": "query TodoItemRefetchQuery(\n $itemID: ID!\n) {\n node(id: $itemID) {\n ...TodoItem_item_2FOrhs\n }\n}\n\nfragment TodoItem_item_2FOrhs on Todo {\n text\n isComplete\n}\n"
}
```

3. It also generates a complete query map file at `[your_src_dir]/queryMap.graphql.json`. This file contains all the query ids
3. It also generates a complete query map file at `[your_src_dir]/queryMap.json`. This file contains all the query ids
and their operation texts. You can specify a custom file path for this file by using the `--persist-output` option:

```js
"scripts": {
"relay": "relay-compiler --src ./src --schema ./schema.graphql --persist --persist-output ./src/queryMaps/queryMap.desktop.graphql.json"
"relay": "relay-compiler --src ./src --schema ./schema.graphql --persist --persist-output ./src/queryMaps/queryMap.desktop.queryMap.json"
}
```

The example above writes the complete query map file to `./src/queryMaps/queryMap.desktop.graphql.json`. You need to ensure all the directories
leading to the `graphql.json` file exist. Also note that the file extension has to be `.json`.
The example above writes the complete query map file to `./src/queryMaps/queryMap.desktop.queryMap.json`. You need to ensure all the directories
leading to the `queryMap.json` file exist. Also note that the file extension has to be `.json`.

### Network layer changes
You'll need to modify your network layer fetch implementation to pass a queryId parameter in the POST body instead of a query parameter:
Expand All @@ -96,7 +96,7 @@ function fetchQuery(operation, variables,) {
},
body: JSON.stringify({
queryId: operation.id, // NOTE: pass md5 hash to the server
// query: operation.text, // this is now obsolete because text is null
// query: operation.text, // this is now obsolete because text is null
variables,
}),
}).then(response => {
Expand All @@ -106,10 +106,10 @@ function fetchQuery(operation, variables,) {
```
## Usage on the server
On the server, you'll need to map the query id in the POST body to the real operation text. You can utilise the complete
`queryMap.graphql.json` file to do this so you'll need a copy of it on your server.
On the server, you'll need to map the query id in the POST body to the real operation text. You can utilise the complete
`queryMap.json` file to do this so you'll need a copy of it on your server.
For universal applications where the client and server code are in one project, this is not an issue since you can place
For universal applications where the client and server code are in one project, this is not an issue since you can place
the query map file in a common location accessible to both the client and the server.
### Compile time push
Expand Down Expand Up @@ -138,15 +138,15 @@ requiring the client and server to interact to exchange the query maps.
### Simple server example
Once your server has access to the query map, you can perform the mapping. The solution varies depending on the server and
database technologies you use, so we'll just cover the most common and basic example here.
If you use `express-graphql` and have access to the query map file, you can import the `queryMap.graphql.json` file directly and

If you use `express-graphql` and have access to the query map file, you can import the `queryMap.json` file directly and
perform the matching using the `matchQueryMiddleware` from [relay-compiler-plus](https://github.com/yusinto/relay-compiler-plus).

```js
import Express from 'express';
import expressGraphl from 'express-graphql';
import {matchQueryMiddleware} from 'relay-compiler-plus';
import queryMapJson from './queryMap.graphql.json';
import queryMapJson from './queryMap.json';
const app = Express();
Expand All @@ -156,9 +156,9 @@ app.use('/graphql',
```

## Using `--persist` and `--watch`
It is possible to continuously generate the query map files by using the `--persist` and `--watch` options simultaneously.
This only makes sense for universal applications i.e. if your client and server code are in a single project
and you run them both together on localhost during development. Furthermore, in order for the server to pick up changes
to the `queryMap.graphql.json`, you'll need to have server side hot-reloading set up. The details on how to set this up
It is possible to continuously generate the query map files by using the `--persist` and `--watch` options simultaneously.
This only makes sense for universal applications i.e. if your client and server code are in a single project
and you run them both together on localhost during development. Furthermore, in order for the server to pick up changes
to the `queryMap.json`, you'll need to have server side hot-reloading set up. The details on how to set this up
is out of the scope of this document.
2 changes: 1 addition & 1 deletion packages/relay-compiler/bin/RelayCompilerBin.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ Ensure that one such file exists in ${srcDir} or its parents.
),
isGeneratedFile: (filePath: string) =>
(filePath.endsWith('.graphql.' + outputExtension) ||
filePath.endsWith('.graphql.json')) &&
filePath.endsWith('.queryMap.json')) &&
filePath.includes(generatedDirectoryName),
parser: sourceParserName,
baseParsers: ['graphql'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('RelayCompilerBin', () => {
});

test('should throw error when persist-output path does not exist', async () => {
mockCliArguments('./', './', 'some/path/queryMap.graphql.json');
mockCliArguments('./', './', 'some/path/queryMap.json');

await require('../RelayCompilerBin');

Expand Down
6 changes: 3 additions & 3 deletions packages/relay-compiler/codegen/RelayFileWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,20 +368,20 @@ class RelayFileWriter implements FileWriterInterface {
}

/**
* Find all *.graphql.json and write it into a single file.
* Find all *.queryMap.json and write it into a single file.
* @param allOutputDirectories
*/
writeCompleteQueryMap(
allOutputDirectories: Map<string, CodegenDirectory>,
): void {
const queryMapFilePath =
this._config.persistOutput ||
`${this._config.baseDir}/queryMap.graphql.json`;
`${this._config.baseDir}/queryMap.json`;
try {
let queryMapJson = {};
allOutputDirectories.forEach(d => {
fs.readdirSync(d._dir).forEach(f => {
if (f.endsWith('.graphql.json')) {
if (f.endsWith('.queryMap.json')) {
const singleQueryMap = JSON.parse(
fs.readFileSync(path.join(d._dir, f), 'utf8'),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('writeRelayGeneratedFile', () => {
expect(codeGenDir.writeFile.mock.calls.length).toEqual(2);
expect(codeGenDir.writeFile.mock.calls[0][0]).toBe('summaryBar_refetch_Query.graphql.js');
expect(codeGenDir.writeFile.mock.calls[0][1]).toBe('mockFormatModuleOuput');
expect(codeGenDir.writeFile).lastCalledWith('summaryBar_refetch_Query.graphql.json', `{
expect(codeGenDir.writeFile).lastCalledWith('summaryBar_refetch_Query.queryMap.json', `{
\"${expectedQueryId}\": \"${node.text}\"
}`);
});
Expand Down Expand Up @@ -85,7 +85,7 @@ describe('writeRelayGeneratedFile', () => {
expect(codeGenDir.writeFile.mock.calls.length).toEqual(2);
expect(codeGenDir.writeFile.mock.calls[0][0]).toBe('summaryBar_refetch_Query.graphql.js');
expect(codeGenDir.writeFile.mock.calls[0][1]).toBe('mockFormatModuleOuput');
expect(codeGenDir.writeFile).lastCalledWith('summaryBar_refetch_Query.graphql.json', `{
expect(codeGenDir.writeFile).lastCalledWith('summaryBar_refetch_Query.queryMap.json', `{
\"${expectedQueryId}\": \"${node.requests[0].text}\"
}`);
});
Expand Down Expand Up @@ -116,7 +116,7 @@ describe('writeRelayGeneratedFile', () => {
expect(codeGenDir.writeFile).lastCalledWith('summaryBar_refetch_Query.graphql.js', 'mockFormatModuleOuput');
});

test('should mark graphql.json as unchanged if hash is unchanged', async () => {
test('should mark queryMap.json as unchanged if hash is unchanged', async () => {
jest.doMock('crypto', () => ({createHash: () => ({update: () => '', digest: () => null})}));

const node = {
Expand All @@ -141,10 +141,10 @@ describe('writeRelayGeneratedFile', () => {
expect(codeGenDir.markUnchanged.mock.calls.length).toEqual(2);
expect(codeGenDir.markUpdated).not.toBeCalled();
expect(codeGenDir.markUnchanged.mock.calls[0][0]).toBe('summaryBar_refetch_Query.graphql.js');
expect(codeGenDir.markUnchanged).lastCalledWith('summaryBar_refetch_Query.graphql.json');
expect(codeGenDir.markUnchanged).lastCalledWith('summaryBar_refetch_Query.queryMap.json');
});

test('should mark graphql.json as updated when only validating', async () => {
test('should mark queryMap.json as updated when only validating', async () => {
jest.unmock('crypto');
codeGenDir.onlyValidate = true;

Expand All @@ -170,7 +170,7 @@ describe('writeRelayGeneratedFile', () => {
expect(codeGenDir.markUnchanged).not.toBeCalled();
expect(codeGenDir.markUpdated.mock.calls.length).toEqual(2);
expect(codeGenDir.markUpdated.mock.calls[0][0]).toBe('summaryBar_refetch_Query.graphql.js');
expect(codeGenDir.markUpdated).lastCalledWith('summaryBar_refetch_Query.graphql.json');
expect(codeGenDir.markUpdated).lastCalledWith('summaryBar_refetch_Query.queryMap.json');
});
});
});
2 changes: 1 addition & 1 deletion packages/relay-compiler/codegen/writeRelayGeneratedFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function writeRelayGeneratedFile(
const moduleName = generatedNode.name + '.graphql';
const platformName = platform ? moduleName + '.' + platform : moduleName;
const filename = platformName + '.' + extension;
const queryMapFilename = `${generatedNode.name}.graphql.json`;
const queryMapFilename = `${generatedNode.name}.queryMap.json`;
const typeName =
generatedNode.kind === RelayConcreteNode.FRAGMENT
? 'ConcreteFragment'
Expand Down

0 comments on commit c08f2c9

Please sign in to comment.