Working with directives for a GraphQL schema - Amazon Neptune
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Working with directives for a GraphQL schema

You can start from a GraphQL schema that already has directives, using a command like the following:

neptune-for-graphql \ --input-schema-file (your GraphQL schema file with directives) \ --create-update-aws-pipeline \ --create-update-aws-pipeline-name (name for your new GraphQL API) \ --create-update-aws-pipeline-neptune-endpoint (empty Neptune database endpoint):(port number) \ --output-resolver-query-https

You can modify directives that the utility has created or add your own directives to a GraphQL schema. Here are some of the ways to work with directives:

Running the utility so that it doesn't generate mutations

To prevent the utility from generating muatations in the GraphQL API, use the the --output-schema-no-mutations option in the neptune-for-graphql command.

The @alias directive

The @alias directive can be applied to GraphQL schema types or fields. It maps different names between the graph database and the GraphQL schema. The syntax is:

@alias(property: (property name))

In the example below airport is the graph database node label mapped to the Airport GraphQL type, and desc is the the graph node property mapped to the description field (see the Air Routes Example):

type Airport @alias(property: "airport") { city: String description: String @alias(property: "desc") }

Note that standard GraphQL formatting calls for Pascal-casing type namess and camel-casing field names.

The @relationship directive

The @relationship directive maps nested GraphQL types to graph database edges. The syntax is:

@relationship(edgeType: (edge name), direction: (IN or OUT))

Here is an example command:

type Airport @alias(property: "airport") { ... continentContainsIn: Continent @relationship(edgeType: "contains", direction: IN) countryContainsIn: Country @relationship(edgeType: "contains", direction: IN) airportRoutesOut(filter: AirportInput, options: Options): [Airport] @relationship(edgeType: "route", direction: OUT) airportRoutesIn(filter: AirportInput, options: Options): [Airport] @relationship(edgeType: "route", direction: IN) }

You can find @relationship directives in both the Todo example and the Air Routes Example.

The @graphQuery and @cypher directives

You can define openCypher queries to resolve a field value, add queries or add mutations. For example, this adds a new outboundRoutesCount field to the Airport type to count the outboud routes:

type Airport @alias(property: "airport") { ... outboundRoutesCount: Int @graphQuery(statement: "MATCH (this)-[r:route]->(a) RETURN count(r)") }

Here an example of new queries and mutations:

type Query { getAirportConnection(fromCode: String!, toCode: String!): Airport \ @cypher(statement: \ "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})") } type Mutation { createAirport(input: AirportInput!): Airport @graphQuery(statement: "CREATE (this:airport {$input}) RETURN this") addRoute(fromAirportCode:String, toAirportCode:String, dist:Int): Route \ @graphQuery(statement: \ "MATCH (from:airport{code:'$fromAirportCode'}), (to:airport{code:'$toAirportCode'}) \ CREATE (from)-[this:route{dist:$dist}]->(to) \ RETURN this") }

Note that if you omit the RETURN, the resolver assumes the keyword this is the returning scope.

You can also add a query or mututation using a Gremlin query:

type Query { getAirportWithGremlin(code:String): Airport \ @graphQuery(statement: "g.V().has('airport', 'code', '$code').elementMap()") # single node getAirportsWithGremlin: [Airport] \ @graphQuery(statement: "g.V().hasLabel('airport').elementMap().fold()") # list of nodes getCountriesCount: Int \ @graphQuery(statement: "g.V().hasLabel('country').count()") # scalar }

At this time Gremlin queries are limited to ones that return scalar values, or elementMap() for a single node, or elementMap().fold() for a list of nodes.

The @id directive

The @id directive identifies the field mapped to the id graph database entity. Graph databases like Amazon Neptune always have a unique id for nodes and edges that is assigned during bulk imports or that is autogenerated. For example:

type Airport { _id: ID! @id city: String code: String }

Reserved type, query and mutation names

The utility autogenerates queries and mutations to creeate a working GraphQL API. The pattern of these names is recognized by the resolver and is reserved. Here are examples for the type Airport and the connecting type Route:

The Options type is reserved.

input Options { limit: Int }

The filter and options function parameters are reserved.

type Query { getNodeAirports(filter: AirportInput, options: Options): [Airport] }

The getNode prefix of query names is reserved, and prefixes of mutations names like createNode, updateNode, deleteNode, connectNode, deleteNode, updateEdge, and deleteEdge are reserved.

type Query { getNodeAirport(id: ID, filter: AirportInput): Airport getNodeAirports(filter: AirportInput): [Airport] } type Mutation { createNodeAirport(input: AirportInput!): Airport updateNodeAirport(id: ID!, input: AirportInput!): Airport deleteNodeAirport(id: ID!): Boolean connectNodeAirportToNodeAirportEdgeRout(from: ID!, to: ID!, edge: RouteInput!): Route updateEdgeRouteFromAirportToAirport(from: ID!, to: ID!, edge: RouteInput!): Route deleteEdgeRouteFromAirportToAirport(from: ID!, to: ID!): Boolean }

Applying changes to the GraphQL schema

You can modify the GraphQL source schema and run the utility again, getting the latest schema from your Neptune database. Every time the utility discovers a new schema in the database, it generates a new GraphQL schema.

You can also manually edit the GraphQL source schema and run the utility again using the source schema as input instead of the Neptune database endpoint.

Finally, you can put your changes in a file using this JSON format:

[ { "type": "(GraphQL type name)", "field": "(GraphQL field name)", "action": "(remove or add)", "value": "(value)" } ]

For example:

[ { "type": "Airport", "field": "outboundRoutesCountAdd", "action": "add", "value":"outboundRoutesCountAdd: Int @graphQuery(statement: \"MATCH (this)-[r:route]->(a) RETURN count(r)\")" }, { "type": "Mutation", "field": "deleteNodeVersion", "action": "remove", "value": "" }, { "type": "Mutation", "field": "createNodeVersion", "action": "remove", "value": "" } ]

Then, as you run the utility on this file using the --input-schema-changes-file parameter in the command, the utility applies your changes at once.