apollographql / persistgraphql Goto Github PK
View Code? Open in Web Editor NEWA build tool for GraphQL projects.
License: MIT License
A build tool for GraphQL projects.
License: MIT License
The README says "middleware for Express servers in persistgraphql/lib/server", but there's no middleware to be found.
It also says "These will likely be moved to their own packages in the future", which I assume already has happened. Where did they go?
Does this work with the subscriptions interface? I see no mention in the docs.
persistgraphql "/(!node_modules)/*.graphql" output.json
It feels like the above should work. Otherwise persistgraphql needs to be run multiple times for each desired directory, with the results being composed later.
Would this also work with createBatchingNetworkInterface or just createNetworkInterface?
It would be really nice if this tool would be available as a webpack plugin as well.
Graphql-tag has a loader module that it offers which I use extensively. I would love to have as an automatic part of my build a similar function with persisted queries.
I have a couple of theories about how this might function, they start out pretty much the same.
Option 1 ends here. Option 2 goes one step further and does something like DefinePlugin to replace some kind of defined import with a json loader which has the json inlined.
Hello,
I installed this package today, but when I tried to run it, I got the following error:
at Function.Module._resolveFilename (module.js:470:15)
at Function.Module._load (module.js:418:25)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/home/ex0ns/.nvm/versions/node/v7.5.0/lib/node_modules/persistgraphql/bin/persistgraphql:4:1)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
Indeed, lib/src/index.js
is missing, it seems that it should be binary.js
, could you please take a look.
ex0ns
Hi, would it be possible for you to add a license file to this repo?
I saw that in the package.json it's described as MIT.
Thanks for the lib!
Currently there is no automatic testing for PRs and commits. We should add Trabis CI to test against the different Node.js releases and catch breaking builds.
persistgraphql's serialization always puts fragments at the bottom, whereas apollo-client matches the source. So if you have a query like
fragment Y on Z {
# etc
}
query x {
# etc, uses fragment Y
}
then whitelisting is broken 😬😬😬 because persistgraphql rearranges the text.
You can work around it by putting the fragment after in the source, but it's a weird inconsistency that just cost me a couple hours' work trying to debug.
Current behavior
When defining two queries with the same operation name, only the last one is kept. All other versions are removed.
Expected behavior
Two options:
Minimal reproduction
Run persistgraphql on
// bar.graphql
query foo {
foo
{
bar
foobar
}
}
// baz.graphql
query foo {
faz
{
baz
fazbaz
}
}
// result {"query foo {\n faz {\n baz\n fazbaz\n }\n}\n":1}
Minimal demo
git clone -b duplicate-operation-names https://github.com/
Aides359/persistgraphql
cd persistgraphql
npm install
npm run compile
npm start
I get an error if I use apollo-client v0.7.3.
import 'whatwg-fetch';
SyntaxError: Unexpected token import
With apollo-client v0.6.0 it is ok.
Just a thought, so we already have query -> id mapping. Would it then not be possible to extend that concept to the JSON payload property names?
I.e.
{
orders: [
id: "",
customer: {...}
]
}
to
{
1: [
2: "",
3: {...}
]
}
and then have apollo restore the property names?
First off, let me say thanks for the awesome codebase and toolkit! I am using it with Apollo 2.x now and am really enjoying the lighter network footprint!
To help others work with apollo-link-state and persistgraphql, I have a small queryTransformer which strips any fields with the @client
directive, and I'd like to share it for others to use. Is there a repo naming pattern I should aim for, or anything like that?
ex: persistgraphql-query-transform-trim-client-fields
Thanks again!
I’m getting an error I’m not sure how to start debugging when trying to run persistgraphql
on gql
-tagged queries in a project:
$ persistgraphql src/client/containers/Test.js --add_typename
Using the add-typename query transformer.
(node:26286) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): GraphQLError: Syntax Error GraphQL (1:1) Unexpected <EOF>
1:
^
(node:26286) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I stripped down the file to make sure it wasn’t something else in my code:
import { gql } from 'react-apollo';
const QueryOne = gql`
query TestQuery($id: String) {
test(id: $id) {
id
name
}
}
`;
export default {
QueryOne,
};
After looking through the tests, I believe this should work, so I’m not sure how or where this is failing. If this is a bug and not an issue with how I'm using it, I'm happy to contribute — just need a pointer to how I might start debugging.
Thanks!
I'm having this issue when trying to execute:
(node:28632) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1):
GraphQLError: Syntax Error GraphQL (1:1) Unexpected <EOF>
1:
^
I'm using extractgql": "^0.2.1" with [email protected] and graphql 0.9.0 on Mac OS
Hi, thanks for sharing this repo! Looks like lots of good work.
I've been doing some thinking about persisted queries as well (but not much doing). I'm curious how you'd handle this situation:
For example, let's say you start with a .graphql
file like
query getThing($id: ID!) {
get(id: $id) {
name
}
}
And you run persistgraphql
which generates a query map:
{"query getThing($id: ID!) {\n get(id: $id) {\n name\n }\n}\n":1}
Then, later, you refactor the query:
query getThing($id: ID!) {
get(id: $id) {
- name
+ title
}
}
And again, persistgraphql
to get a map:
{"query getThing($id: ID!) {\n get(id: $id) {\n title\n }\n}\n":1}
But now, ID 1
points at a new query, which may cause some clients to break! How should we avoid that?
The only thing I can think of is: never change or remove a persisted query until you're sure no outstanding clients depend on it. Instead of changing queries, always add new ones. Do we have any other tools to help in this case?
We are having build issues in our project due to downloading @types/mocha
by this library as it conflicts with @types\jest
. The following PR should fix it, but we don't see newer package version after that fix. Can you please release a new version ASAP?
Support for the .gql
file extension would be nice in addition to the .graphql
extension. Some projects like to keep short extensions where possible.
Hi there
I've run into a maximum call stack error and could trace it back to this cloneDeep function:
https://github.com/apollographql/persistgraphql/blob/master/src/queryTransformers.ts#L63
Any way around it?
Best
Dimitri
Hi,
I'd like to know if it is possible to extract the queries in a human readable form, instead of minified AST that is supposed to be shared between client and server.
My usecase is that I have graphql fragments everywhere and I use fragments composition, so it would be useful to be able to output the composed version of my queries so that I can play with the queries in a sandbox like Graphiql. Otherwise I have to do this composition manually and maintain it over time.
Here's the kind of output I'd like to have
query ReportEditionSynthesisShowQuery($reportEditionId: ID!) {
reportEdition: ReportEdition(id: $reportEditionId) {
id
startDate
endDate
report {
id
name
}
contributions: answers {
id
...SynthesisItemContribution
}
importantTopicContributions: topicAnswers(filter: {important: true}) {
...ImportantItemsTopicContribution
}
recipients {
email
}
}
}
fragment ImportantItemsTopicContribution on TopicAnswer {
id
content
contribution: answer {
id
user {
id
firstName
lastName
avatar
}
}
}
fragment SynthesisItemContribution on Answer {
answerType
user {
...SynthesisItemHeaderByUser
}
topicContributions: topicAnswers {
...SynthesisSectionsTopicContribution
}
}
fragment SynthesisItemHeaderByUser on User {
...UserAlt
}
fragment UserAlt on User {
id
firstName
lastName
pictureUrl: avatar
job
color
email
}
fragment SynthesisSectionsTopicContribution on TopicAnswer {
topic {
id,
name
}
content
}
Here is my query defined in HOC (I did not include subcomponents but I can if needed)
const injectSynthesisData = Comp => {
return graphql(gql`
query ReportEditionSynthesisShowQuery($reportEditionId: ID!) {
reportEdition: ReportEdition(id: $reportEditionId) {
id
startDate
endDate
report {
id
name
}
contributions: answers {
id
...SynthesisItemContribution
}
importantTopicContributions: topicAnswers(filter: {important: true}) {
...ImportantItemsTopicContribution
}
recipients {
email
}
}
}
${ImportantItems.fragments.topicContribution}
${SynthesisItem.fragments.contribution}
`,{
options: (props) => ({
variables: {
reportEditionId: props.match.params.reportEditionId
}
}),
})(Comp)
};
Queries from both external graphql files and JS extraction are not compiled in the same output file
I'm not getting a .json file pointing it to a directory, like so:
$ persistgraphql client/src
When I point it to a file it seams to work fine. Am I missing something here?
Thanks for the all the amazing work contributed to the graphql community!
Hey All-
Has anyone moved to Hapi17 and used this library?
Im getting a wonky error with sending GETs....even though I do have a query string that looks like -
http://localhost:8000/graphql?hash=1
The error is-
{
"statusCode": 400,
"error": "Bad Request",
"message": "Must provide query string."
}
An example of the code I converted to looks like -
server.ext('onPreHandler', (request, h) => {
console.log(request.url.path)
if (request.url.path.indexOf('/graphql') >= 0) {
const { hash = '' } = request.query
if (!hash) {
return h.continue
}
request.query = `query events {
events{
eventuuid
storyId
}
}
`
return h.continue
}
return h.continue
});
Working on a project that uses primarily the gql
tagged template literal for defining queries, but .graphql|.gql
files for defining fragments.
Currently persistgraphql seems to be an either-or proposition. If you pass in the --js --extension=js
arguments, the fragments aren't in the docMap, and as a result the following throws
export function isOperationDefinition(defn: DefinitionNode): defn is OperationDefinitionNode {
return (defn.kind === 'OperationDefinition');
}
Trace:
TypeError: Cannot read property 'kind' of undefined
at isOperationDefinition (/Users/mergebandit/dev/super-top-secret-but-totes-badass-project/node_modules/persistgraphql/lib/src/extractFromAST.js:5:17)
at Array.filter (<anonymous>)
at Object.getOperationDefinitions (/Users/mergebandit/dev/super-top-secret-but-totes-badass-project/node_modules/persistgraphql/lib/src/extractFromAST.js:46:28)
at ExtractGQL.createMapFromDocument (/Users/mergebandit/dev/super-top-secret-but-totes-badass-project/node_modules/persistgraphql/lib/src/ExtractGQL.js:70:49)
at /Users/mergebandit/dev/super-top-secret-but-totes-badass-project/node_modules/persistgraphql/lib/src/ExtractGQL.js:99:26
at Array.map (<anonymous>)
at ExtractGQL.createOutputMapFromString (/Users/mergebandit/dev/super-top-secret-but-totes-badass-project/node_modules/persistgraphql/lib/src/ExtractGQL.js:95:46)
at /Users/mergebandit/dev/super-top-secret-but-totes-badass-project/node_modules/persistgraphql/lib/src/ExtractGQL.js:132:31
at <anonymous>
Seems to me that, if you choose the .js
argument, it should still be able to extract .graphql
files and merge them into the output document mapping.
I am having issues with multiple, nested fragments where the query string produced by getQueryDocumentKey doesn't match the run-time query. The mismatch lies in the sorting of the fragments.
This is the query that is sent:
query routeQuery($path: String!) {
routeByPath(path: $path) {
object {
...BasicPageFragment
__typename
}
__typename
}
}
fragment BasicPageFragment on NodeBasicPage {
lead: fieldPageLead
title: entityLabel
content: fieldPageContent {
...PageContentFragment
__typename
}
__typename
}
fragment PageContentFragment on Paragraph {
id
...CopyTextFragment
...AccordionFragment
__typename
}
fragment CopyTextFragment on ParagraphCopytext {
text: fieldParaText
__typename
}
fragment AccordionFragment on ParagraphAccordion {
container: fieldAccordionContainer {
... on ParagraphAccordionItem {
id
title: fieldParaTitle
subtitle: fieldParaSubtitle
content: fieldParaContent {
id
...CopyTextFragment
__typename
}
__typename
}
__typename
}
__typename
}
And this is the query from the queryMap:
query routeQuery($path: String!) {
routeByPath(path: $path) {
object {
...BasicPageFragment
__typename
}
__typename
}
}
fragment AccordionFragment on ParagraphAccordion {
container: fieldAccordionContainer {
... on ParagraphAccordionItem {
id
title: fieldParaTitle
subtitle: fieldParaSubtitle
content: fieldParaContent {
id
...CopyTextFragment
__typename
}
__typename
}
__typename
}
__typename
}
fragment CopyTextFragment on ParagraphCopytext {
text: fieldParaText
__typename
}
fragment PageContentFragment on Paragraph {
id
...CopyTextFragment
...AccordionFragment
__typename
}
fragment BasicPageFragment on NodeBasicPage {
lead: fieldPageLead
title: entityLabel
content: fieldPageContent {
...PageContentFragment
__typename
}
__typename
}
Hi there
It took me a while to figure out that using nodemon this nice library/utility throws an 'Maximum call stack size exceeded' error, if you give it a change to do so :-)
I'll attach a PR to this.
Thx
Best
Dimitri
Not read it deeply, just wonder can or cannot be used in Relay Modern. Thanks!
One of the things keeping us from using this in our implementation is that existing published client builds would not be supported if a query goes away, or something reorders them. One thought as to how to handle this:
Big questions I see are how to handle deprecation data. A separate counts file is one option which could be committed along side the persisted json but stripped automatically from production builds by any tool which does any kind of dead code removal (as it's build tool metadata, not used code). Any query which is not in the current code gets checked/added to that counts file and once it is past x number of builds (which is a command line option) it gets removed.
Another option I see is to add deprecation data in place of the raw id number in the json. This won't affect a client consuming it, though in development perhaps the connection could understand that and emit a warning if a deprecated query is hit. Server side is where this changes the most, as the reversal isn't alone enough as the deprecation information put into the json would alter the key map.
Benefits to the two ways I see of storing deprecation information: Separate file:
Same JSON:
Installing this package appears to have broken in 50970f7
mdeltito@abc $ mkdir test && cd test && npm init
mdeltito@abc $ npm install --save persistgraphql
npm WARN [email protected] No description
npm WARN [email protected] No repository field.
npm ERR! path /Users/mdeltito/code/test/node_modules/persistgraphql/bin/persistgraphql
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall chmod
npm ERR! enoent ENOENT: no such file or directory, chmod '/Users/mdeltito/code/test/node_modules/persistgraphql/bin/persistgraphql'
npm ERR! enoent This is most likely not a problem with npm itself
npm ERR! enoent and is related to npm not being able to find a file.
npm ERR! enoent
When using with subscription
if fails with the following error.
"Error: Must provide a query.
at SubscriptionClient.vendor../node_modules/subscriptions-transport-ws/dist/client.js.SubscriptionClient.checkOperationOptions
May be related to #33
In a recent PR to apollo-codegen, a mechanism for generating unique operation IDs was introduced. In addition, it introduced a generated output file that maps between these IDs and the corresponding operation text, for use in registering operations with a server for persistence and whitelisting. The rationale for not simply using persistgraphql
is contained in the PR description.
This proposed output file differs from persistgraphql
's output in a few important ways:
As pointed out by @martijnwalraven, there are good reasons to align the approaches taken by apollo-codegen
and persistgraphql
:
One area in particular where I think standardization would be helpful is in the generated mapping file. Ideally, these mapping files would be supported by different servers and tools in the same way the extracted_queries.json file that persistgraphql generates is currently supported by Scaphold for example.
So I'm not saying we should stick to what persistgraphql currently does, but I'd at least like us to synchronize the changes (like switching to hashes or changing the mapping format).
There is an existing issue on persistgraphql
that addresses difference point 1. I think switching to operation hashes is the right approach.
What I’d like to propose here is that, to address difference points 2 and 3, persistgraphql
adopt the approach proposed in the apollo-codegen
PR. The PR lays out an argument for this approach, which I’ll recap here:
Using persisted queries throws and error when using the @connection directive. See apollographql/apollo-client#1801 (comment)
The GQL extraction provided in persistgraphql is pretty great. I'm hoping to use it to add support for javascript source files within apollo-codegen (apollographql/apollo-tooling#25) or similar projects and having the gql extraction stuff available as a separate package would make that much easier. It that something that sounds reasonable?
I have several ".graphql" files in 1 folder, and use persistgraphql ./src/graphql/
to generate the map json file, but the result is not correct.
P.S. They are valid ".graphql" files with correct syntax and can be used with webpack-graphql-loader
.
The graphql files:
The json output:
{"mutation ($songID: Int, $playlistID: Int) {\n removeSongFromPlaylist(SongID: $songID, PlaylistID: $playlistID) {\n ID\n ClassName\n LastEdited\n Created\n Title\n Description\n NumberOfSongs\n }\n}\n":1}
Extending on #3 when using other NetworkInterface
s this type information is lost after calling this function.
As the function expects a parameter of type NetworkInterface
and returns this parameter after Object.assign
the inferred type is NetworkInterface & { query: Promise<ExecutionResult> }
.
Thus all type information of the networkInterface extending NetworkInterface
is lost.
const networkInterface = addPersistedQueries(
createNetworkInterface({
uri: '/graphql',
}), // creates HTTPNetworkInterface
persistedQueries,
);
networkInterface.use(/* some middleware */);
Above code should work, since .use()
is defined on HTTPNetworkInterface
.
Throws with Property 'use' does not exist on type 'NetworkInterface & { query: (request: Request) => Promise<ExecutionResult>; }'.
The solution could look somewhat like this:
export function addPersistedQueries<T extends NetworkInterface>
(networkInterface: T, queryMap: OutputMap) {
let __interfaceType: T; // I think this is necessary for Generic to work correctly
// [...]
}
I'm using this tool just to extract queries from JS files, and it does that great, but the strict dependcy requirements cause it to load a version of graphql
that doesn't support the Interface1 & Interface2
SDL syntax, and also it brings in a dated version of Apollo too. I'm using Yarn's resolutions
to push the requirements of:
"apollo-client": "^1.1"
"graphql": ">=0.9.4 <0.11"
up to:
"apollo-client": "2.3.5",
"graphql": "0.13.2"
and for my use case, it seems to work fine.
Is there a reason why tslint
is a dependency
instead of a devDependency
?
I use babel-plugin-graphql-tag to compile my queries, this is used mainly so I can use aliases when importing GraphQL files. However this doesn't work with the build tool as it appears fragments are not being imported.
It would be helpful to be able to crawl precompiled AST JS so that crawling can be done on resolved documents (prevent having to bootstrap babel).
For instance, something like:
# Build persisted queries across babel compiled GraphQL.
persist-grapqhl ./lib/grapqhl --ast --extension=js
Why just not register them manually?
Client team can make a request to backend team to register a new query, in this case backend team will be able to improve performance of SQL queries.
chmod?
npm install --save persistgraphql
npm ERR! path E:\apollo-universal-starter-kit\node_modules\persistgraphql\bin\persistgraphql
npm ERR! code ENOENT
npm ERR! errno -4058
npm ERR! syscall chmod
npm ERR! enoent ENOENT: no such file or directory, chmod 'E:\apollo-universal-starter-kit\node_modules\persistgraphql\bin\persistgraphql'
npm ERR! enoent This is most likely not a problem with npm itself
npm ERR! enoent and is related to npm not being able to find a file.
npm ERR! enoent
Hi
example query:
// works via variables
query MoviesByName($name : String!) {
movies(name: $name) { ... }
}
// works with static value
query MoviesByName() {
movies(name: 'StarWars') { ... }
}
// does not work via template string
const name: string = 'StarWars';
query MoviesByName() {
movies(name: ${name}) { ... }
}
all queries work on default POST
request using apollo. after running persistgraphql you'll receive an error, that no value was passed to the name attribute on the 3rd example.
currently you can "workaround" it by using variables, but in some cases it would be great to just pass a variable via template string literals.
package | version |
---|---|
apollo client | 2.2.2 |
persistgraphql | 0.3.11 |
thanks for the great tool 👍
It then assigns these queries ID values/hashes and produces a JSON file which maps from queries to hashes/IDs. This map can then be used by the client and server to perform query whitelisting
How server and client sync this map when on client was added new query?
It is not clear in Readme, please add more information.
If you don't have tags, that's okay too, feel free to close this issue. Thanks :)
When running a js extraction, it throws a syntax error when there is a commented out query in the code. Ideally it would simply skip over it.
I would like to be able to use persistgraphql
with Apollo link. Is there a way to currently do this? Can it be a feature?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.