Yep sure thing.
Http request header:
{
"authorization" :"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCb2JMb2JsYXc3Njg3Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.f2GKIu31gz39fMJwj5_byFCMDPDy3ncdWOIhhqcwBxk"
}
GraphQL mutation:
mutation {
createCustomers(
input: [
{
username: "BookWorm"
reviews: {
create: [
{
rating: 10
text: "Why not"
book: { connect: { where: { isbn: "190962151X" } } }
}
]
}
orders: {
create: {
books: {
connect: [
{ where: { title: "Ross Poldark" } }
{ where: { isbn: "1119387507" } }
{ where: { isbn: "1492047686" } }
]
}
shipTo: {
create: {
address: "Nowhere"
location: { latitude: 55.6122270502, longitude: 12.99481772774 }
}
}
}
}
}
]
)
}
Here is my GraphQL Schema for good measures:
type Order {
orderID: ID! @id
placedAt: DateTime @timestamp
shippingCost: Float
shipTo: Address @relationship(type: "SHIPS_TO", direction: OUT)
customer: Customer @relationship(type: "PLACED", direction: IN)
books: [Book] @relationship(type: "CONTAINS", direction: OUT)
}
extend type Order {
subTotal: Float @cypher(statement:"MATCH (this)-[:CONTAINS]->(b:Book) RETURN sum(b.price)")
shippingCost: Float @cypher(statement:"MATCH (this)-[:SHIPS_TO]->(a:Address) RETURN round(0.01 * distance(a.location, Point({latitude: 40.7128, longitude: -74.0060})) / 1000, 2)")
estimatedDelivery: DateTime @ignore
}
extend type Order @auth(rules: [{allow: {customer: {username: "$jwt.sub"}}}, {roles: ["admin"]}])
type Customer {
username: String
orders: [Order] @relationship(type: "PLACED", direction: OUT)
reviews: [Review] @relationship(type: "WROTE", direction: OUT)
recommended(limit: Int = 3): [Book] @cypher(statement: "MATCH (this)-[:PLACED]->(:Order)-[:CONTAINS]->(:Book)<-[:CONTAINS]-(:Order)<-[:PLACED]-(c:Customer) MATCH (c)-[:PLACED]->(:Order)-[:CONTAINS]->(rec:Book) WHERE NOT EXISTS((this)-[:PLACED]->(:Order)-[:CONTAINS]->(rec)) RETURN rec LIMIT toInteger($limit)")
}
extend type Customer @auth(rules: [{where: {username: "$jwt.sub"}}])
extend type Customer @auth(rules: [{operations: [CREATE, UPDATE, DELETE], roles: ["admin"]}])
type Address {
address: String
location: Point
order: Order @relationship(type: "SHIPS_TO", direction: IN)
}
extend type Address {
currentWeather: Weather @cypher(statement:"CALL apoc.load.json('https://www.7timer.info/bin/civil.php?lon=' + this.location.longitude + '&lat=' + this.location.latitude + '&ac=0&unit=metric&output=json&tzshift=0') YIELD value WITH value.dataseries[0] as weather RETURN {temperature: toFloat(weather.temp2m), windSpeed: toFloat(weather.wind10m.speed), windDirection: weather.wind10m.direction, precipitation: weather.prec_type, summary: weather.weather} AS conditions")
}
type Weather {
temperature: Float
windSpeed: Float
windDirection: String
precipitation: String
summary: String
}
type Book {
isbn: ID!
title: String
price: Float
description: String
authors: [Author] @relationship(type: "AUTHOR_OF", direction: IN)
subjects: [Subject] @relationship(type: "ABOUT", direction: OUT)
reviews: [Review] @relationship(type: "REVIEWS", direction: IN)
}
extend type Book @auth(rules: [{operations: [CREATE, UPDATE, DELETE], roles: ["admin"]}])
extend type Book {
similar: [Book] @cypher(statement: """
MATCH (this)-[:ABOUT]->(s:Subject)<-[:ABOUT]-(rec:Book)
WITH rec, COUNT(*) AS num
RETURN rec ORDER BY num DESC
""")
}
type Review {
rating: Int
text: String
createdAt: DateTime @timestamp
book: Book @relationship(type: "REVIEWS", direction: OUT)
author: Customer @relationship(type: "WROTE", direction: IN)
}
extend type Review @auth(rules: [{operations: [CREATE,UPDATE], bind: {author: {username: "$jwt.sub"} }}])
type Author {
name: String!
books: [Book] @relationship(type: "AUTHOR_OF", direction: OUT)
}
type Subject {
name: String!
books: [Book] @relationship(type: "ABOUT", direction: IN)
}
extend type Subject @auth(rules: [{isAuthenticated: true}])
type Mutation {
mergeBookSubjects(subject: String!, bookTitles: [String!]!): Subject @cypher(statement: """
MERGE (s:Subject {name: $subject})
WITH s
UNWIND $bookTitles AS bookTitle
MATCH (t:Book {title: bookTitle})
MERGE (t)-[:ABOUT]->(s)
RETURN s
""")
}
type Query {
bookSearch(searchString: String!): [Book] @cypher(statement: """
CALL db.index.fulltext.queryNodes('bookIndex', $searchString+'~')
YIELD node RETURN node
""")
}
extend type Query {
booksForCurrentUser: [Book] @auth(rules: [{ isAuthenticated: true }]) @cypher(statement: """
MATCH (c:Customer {username: $auth.jwt.sub})-[:PLACED]->(:Order)-[:CONTAINS]->(b:Book)
MATCH (b)-[:ABOUT]->(s:Subject)<-[:ABOUT]-(rec:Book)
WITH rec, COUNT(*) AS score ORDER BY score DESC
RETURN rec
""")
}
error message:
{
"error": {
"errors": [
{
"message": "Field \"createCustomers\" of type \"CreateCustomersMutationResponse!\" must have a selection of subfields. Did you mean \"createCustomers { ... }\"?",
"locations": [
{
"line": 2,
"column": 3
}
],
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED",
"exception": {
"stacktrace": [
"GraphQLError: Field \"createCustomers\" of type \"CreateCustomersMutationResponse!\" must have a selection of subfields. Did you mean \"createCustomers { ... }\"?",
" at Object.Field (/sandbox/node_modules/graphql/validation/rules/ScalarLeafsRule.js:40:31)",
" at Object.enter (/sandbox/node_modules/graphql/language/visitor.js:323:29)",
" at Object.enter (/sandbox/node_modules/graphql/utilities/TypeInfo.js:370:25)",
" at visit (/sandbox/node_modules/graphql/language/visitor.js:243:26)",
" at Object.validate (/sandbox/node_modules/graphql/validation/validate.js:69:24)",
" at validate (/sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:233:34)",
" at Object.<anonymous> (/sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:119:42)",
" at Generator.next (<anonymous>)",
" at fulfilled (/sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)",
" at processTicksAndRejections (internal/process/task_queues.js:93:5)"
]
}
}
}
]
}
}