const { graphql, buildSchema } = require('graphql');
const schema = buildSchema(`
type Query {
}
type SchemalessDoc {
_id: ID!
json: String!
get(k:String!): SchemalessDoc!
}
type Mutation {
# Authentication
auth(name: String!, phrase: String!): SchemalessDoc!
deauth: Boolean!
# CRUD for all models
create(type:String!, json:String!): SchemalessDoc!
update(type:String!, _id:ID!, json:String!): SchemalessDoc!
delete(type:String!, _id:ID!): SchemalessDoc!
}
`);
// NOTICE: the only difference between Query and Mutation is Parallel vs. Serial execution;
// the labels are more presumptive than prescriptive.
class SchemalessDoc {
constructor(o) {
this.o = o;
}
_id() {
return this.o._id;
}
json() {
// TODO: could return 'null' but that isn't precise. 'undefined' is invalid JSON, but its no biggie,
// just check first: return 'undefined' === s ? undefined : JSON.parse(s);
if (undefined === this.o) return 'undefined';
return JSON.stringify(this.o);
}
get({k}, args, context, info) {
// NOTICE: this would be perfect if an error didn't abort the query
// returning zero results, instead of partial results
// if (undefined === this.o[k]) {
// throw Error(`key ${k} was undefined`);
// }
return new SchemalessDoc(this.o[k]);
}
}
const root = {
// authenticate
auth: function ({ name, phrase }, args, context, info) {
// throw Error(`whats up?`);
return new SchemalessDoc({ _id: 'abcd-efgh-hij', a: { b: { c: 3 }}});
},
deauth(obj, args, context, info) {
debugger;
},
// crud for all models
create(obj, args, context, info) {
debugger;
},
update(obj, args, context, info) {
debugger;
},
delete(obj, args, context, info) {
debugger;
},
};
const query = async s =>
graphql(schema, s, root);
describe('graphql', () => {
it('works', async () => {
const output = await query(`
mutation {
auth(
name: "mike",
phrase: "turkey"
) {
_id,
a: get(k:"a") {
b: get(k:"b") {
x: get(k:"x") {
json
},
c: get(k:"c") {
json
}
}
}
}
}
`);
console.log(JSON.stringify(output)); // =>
// {"data":{"auth":{"_id":"abcd-efgh-hij","a":{"b":{"x":{"json":"undefined"},"c":{"json":"\"hamster\""}}}}}}
debugger;
});
});
// obviously this method requires you to both
// insert data as a JSON string,
// and parse JSON at the very end
// but you can still reduce keys server-side using your get() query
// honestly this is how they should have made it
// and support should be first-class
// no type validation just run it and see
// and stop recursing once you resolve the first null
// and for parallel resolves with some null just return the partial results you have
// and include errors[] for the keys which were undefined
// one downside without that first-class support is
// the json comes back double-escaped as json string inside json.
// the other downside is that it removes the ability to
// do dynamic lookups on foreign key references
// unless you define a specific string or data type for that.
// ie. if it finds a string 'TypeName#abcd-defgh' then it
// could go lookup and attach that document
см. Также (другие способы сделать это):
(мне нравится этот, потому что он предоставляет типизированные геттеры вместо всего как строка)
http://blog.riand.com/2017/03/schemaless-graphql.html
https://blog.hasura.io/working-with-schemaless-data-with-graphql-on-postgres-574a1ee2e87f/
https://github.com/hasura/graphql-engine/issues/403
UPDATE
Я в конечном итоге пошел с очень упрощенной реализацией с нуля. Делимся в надежде, что другие могут получить бесплатное вдохновение:
https://gist.github.com/ancmikesmullin/526b64b262561fb4ca1be824c2faec7f#file-test-sgql-alpha2-js-L63-L67