DEPRECATION NOTE:
This project has started a couple of years ago as we intended to create a way to allow developers to focus on building a structured domain based on well defined patterns instead of having the mess of loose uncoupled resolvers.
Unfortunately we haven’t had the proper time to develop this project further, so it has never gained enough momentum. Since then, as well, lots has changed in the ecosystem. A lot of the goals of this project have already been achieved by other projects, such as https://prisma.io.
Prisma is probably the way to go if you look for something mature and reliable. If you feel adventurous, though, you can take a look at our new experiment: https://github.com/zvictor/faugra.
Don't let your graph oriented project become a mess. Add structure to it!
This is an experimental project not ready for production use yet. Part of the features described in this document are still being implemented. The best way to check the features currently implemented is checking out our example app.
We are looking for feedback and are open for discussing the strategy adopted here. Do not hesitate opening an issue and sharing your thoughts with us, it will make us happy. :)
npm install graph-object --save
new MyModel({ field: value })
import { Model } from 'graph-object';
class Author extends Model {
get name() {
return `${this.firstName} ${this.lastName}`;
}
}
> const orwell = new Author({ firstName: 'George', lastName: 'Orwell' });
> orwell.firstName
'George'
> orwell.name
'George Orwell'
allow(object, objectCRUD, fields_RU_)
import { allow, controlAccess } from 'graph-object';
class Post extends Model {
...
}
class Author extends Model {
get posts() {
return Post.objects.find({ 'author._id': this._id });
}
get publicPosts() {
return controlAccess(
Post.objects.find({ 'author._id': this._id })
)
}
allowedPosts(context) {
return controlAccess(
Post.objects.find({ 'author._id': this._id }), context
)
}
}
allow(Post, {
read(context) { // Post's CRUD permissions could be changed
return this.author._id === context.userId || !this.private;
},
}, {
views: { // `view` field permissions.
// Only `read` and `update` are available for fields.
read(context) {
return this.author._id === context.userId;
},
update(context) {
return false;
}
}
});
> // someAuthor only published a private ({private: 1}) post.
> someAuthor.posts
Error: 'read' permission not granted by Post
> someAuthor.public
[]
> someAuthor.allowedPosts({ userId: 1 })
[Post]
Managers are inspired in the Django's manager concept. More docs to come.
In order to retrieve and persist the data from your objects you should connect your models to your prefered storage provider. Graph-object is agnostic about that, so you can use any database and storage engines, as many as you want, as long as there are adapters available for them.
Note: If no connector is specified for a given class, it will by default use a dumb memory store.
The example below will give your managers access to both mongodb and memory stores.
import models from './models';
import adapter from 'graph-object-adapter-waterline';
import memoryAdapter from 'sails-memory';
import mongoAdapter from 'sails-mongo';
import { injectConnectors } from 'graph-object';
const persistingModels = injectConnectors(models, adapter({
default: {
adapter: mongoAdapter,
host: 'localhost', // defaults to `localhost` if omitted
port: 27017, // defaults to 27017 if omitted
user: 'username_here', // or omit if not relevant
password: 'password_here', // or omit if not relevant
database: 'database_name_here' // or omit if not relevant
identifiers: {
Author: 'authors',
Posts: 'posts',
}
},
memory: {
adapter: memoryAdapter,
identifiers: {
Author: 'people',
Posts: 'posts',
}
},
});
export default persistingModels;
If these concepts make you confused, this issue could help clarifying it.
Graph-object models are plug & play to GraphQL servers. Just make sure you run generateResolvers(schema, models)
to have the resolvers generated.
import schema from '/domain/schemas';
import persistingModels from './models';
import { apolloExpress } from 'apollo-server';
const resolvers = generateResolvers(schema, persistingModels);
addResolveFunctionsToSchema(schema, resolvers);
...
app.use('/graphql', bodyParser.json(), apolloExpress({
schema,
resolvers,
context: {},
}));
Please check out the Perfect GraphQL Starter for a working example app.
If you like this project just give it a star :)