Skip to content

Implement public method to get cypher from graphql query - #7215

Draft
a-alle wants to merge 2 commits into
neo4j:devfrom
a-alle:dry-run-translation
Draft

Implement public method to get cypher from graphql query#7215
a-alle wants to merge 2 commits into
neo4j:devfrom
a-alle:dry-run-translation

Conversation

@a-alle

@a-alle a-alle commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Description

This PR exposes a method to get the translation of a given GraphQL query.
Adds a dryRun param to the execution context to change some behaviour:

  • authentication is skipped
  • populatedBy callbacks are skipped
  • custom resolvers are skipped

Also calls the dry run method on every execute GraphQL query call of the testHelper.

Complexity

Complexity: Medium (maybe)

Issue

Note

Please link to the GitHub issue(s) in which the proposal for this work was discussed

To link to multiple issues, use full syntax for each, for example Closes #1, closes #2, closes #3

Closes

Checklist

The following requirements should have been met (depending on the changes in the branch):

  • Documentation has been updated
  • TCK tests have been updated
  • Integration tests have been updated
  • Example applications have been updated
  • New files have copyright header
  • CLA (https://neo4j.com/developer/cla/) has been signed

@a-alle
a-alle requested a review from angrykoala April 2, 2026 17:01
@a-alle
a-alle requested a review from a team as a code owner April 2, 2026 17:01
@changeset-bot

changeset-bot Bot commented Apr 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6b1d088

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Comment on lines +171 to +172
const translationResult = await this.dryRun(query, args.variableValues || {}, schema);
console.log(translationResult?.cypher);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just like a debug thing?

variableValues?: Record<string, unknown>,
schema?: GraphQLSchema
): Promise<{ cypher: string; params: Record<string, unknown> } | undefined> {
return this.neo4jGraphQL?.giveMeCypher(query, schema, variableValues);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a chance of using this new methods instead of the hack for our tck tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, could do

this.complexityEstimatorHelper = new ComplexityEstimatorHelper(!!this.features.complexityEstimators);
}

public async giveMeCypher(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this name a lot, but I guess we need to think of a different name for the public interface 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you said it was ok!


public async giveMeCypher(
queryString: string,
executableSchema?: GraphQLSchema,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand why do we allow to pass a different executableSchema here? 🤔


if (!this.translator) {
this.translator = new GraphQL2CypherTranslator({
executableSchema,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being stored, so if the executableSchema is different when calling this method, it will still use the old one

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can it be different? executableSchema is being stored in the Neo4jGraphQL class

Comment on lines +1 to +18
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*/

variableValues?: Record<string, unknown>
): Promise<{ cypher: string; params: Record<string, unknown> }> {
const parsed = await this.parseQuery(queryString, variableValues);
if (!parsed) return EMPTY_RESULT;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very small issue, but consider either creating the EMPTY_RESULT inside this method, or returning the object directly without a variable (return { query ....})

As it is now, the following behavior could happen in the user's code:

const res1 = neoGraphql.giveMeCypher(); // EMPTY_RESULT
res1.potato="foo" // This modifies the original reference
const res2 = graphql2cypher(); // { cypher, params, potato: "foo"}

const resolveTree = getNeo4jResolveTree(info, { args });
return { info, args, fieldName, resolveTree };
} catch (_err) {
console.error("Error during query parsing");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not throw here. instead of returning undefined?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bc this just logs a cypher query, do we want it to throw?

if (mapping) return mapping;

if (this.userResolvedFields.has(fieldName)) {
console.log(`Skipping translation for "${fieldName}": field uses a user-provided JS resolver`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about all this logging

Comment on lines +379 to +391
(res, [key, value]) => {
if (key === "Node") return res;
if (key === typeName) {
return {
...res,
[key]: {
...value,
id: { name: "id", alias: "id", args: {}, fieldsByTypeName: {} },
},
};
}
return { ...res, [key]: value };
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm, I'm not sure how performance critical is this bit, but this is cloning objects for every item in fieldsByTypename.

Consider instead just modifying res

@angrykoala angrykoala linked an issue May 28, 2026 that may be closed by this pull request
@MacondoExpress
MacondoExpress marked this pull request as draft July 14, 2026 12:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Get translation result in cypher

2 participants