{"id":15313298,"url":"https://github.com/weyoss/graphql-bookshelfjs","last_synced_at":"2025-04-24T00:18:22.037Z","repository":{"id":57158562,"uuid":"81825209","full_name":"weyoss/graphql-bookshelfjs","owner":"weyoss","description":"Using GraphQL with Bookshelf ORM","archived":false,"fork":false,"pushed_at":"2018-07-18T22:52:48.000Z","size":35,"stargazers_count":29,"open_issues_count":3,"forks_count":13,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-24T00:18:10.422Z","etag":null,"topics":["bookshelf","graphql"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/weyoss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-13T12:57:19.000Z","updated_at":"2024-07-06T20:46:32.000Z","dependencies_parsed_at":"2022-09-06T20:12:52.548Z","dependency_job_id":null,"html_url":"https://github.com/weyoss/graphql-bookshelfjs","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fgraphql-bookshelfjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fgraphql-bookshelfjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fgraphql-bookshelfjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fgraphql-bookshelfjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/weyoss","download_url":"https://codeload.github.com/weyoss/graphql-bookshelfjs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250535147,"owners_count":21446516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bookshelf","graphql"],"created_at":"2024-10-01T08:41:23.225Z","updated_at":"2025-04-24T00:18:22.014Z","avatar_url":"https://github.com/weyoss.png","language":"JavaScript","funding_links":[],"categories":["Libraries","\u003ca name=\"JavaScript\"\u003e\u003c/a\u003eJavaScript"],"sub_categories":["JavaScript Libraries"],"readme":"# graphql-bookshelfjs\n\n## SYNOPSIS\n\nA simple bridge between your graphql queries and your bookshelf models. This library resolves graphql queries to \n**batched** and **optimised** queries using bookshelf models.\n\n## INSTALL\n\n```text\n$ npm install --save graphql-bookshelfjs\n```\n\nPlease make sure you have graphql and bookshelf installed.\n\n## CONFIGURATION\n\nThere are 2 configuration steps:\n\n1. At the level of your GraphQL schema and resolve functions, you should provide the resolver which will be called by\nthe GraphQL engine for actually querying the data.\n\n```javascript\nconst graphQL = require('graphql');\nconst graphQLBookshelf = require('graphql-bookshelfjs');\n\nconst RootQuery =  new graphQL.GraphQLObjectType({\n    name: 'RootQuery',\n    fields: {\n        field: {\n            // ...\n\n            resolve: graphQLBookshelf.resolverFactory( SomeBookshelfModel ) // Provide the resolver\n        },\n    }\n});\nconst graphQLSchema = new graphQL.GraphQLSchema({query: RootQuery});\n```\n\n2. At the level of GraphQL query execution, include the data loaders in the context object for performing batch queries.\n\n```javascript\nconst someQueryString = '...';\ngraphQL.graphql(graphQLSchema, someQueryString, null, {\n        loaders: graphQLBookshelf.getLoaders() // include the loaders\n    }).then(function(result) {\n        // ...\n    });\n```\n\n## USAGE\n\n### EXAMPLE\n\nIf you're new to the GraphQL ecosystem and have troubles getting a project up and running or maybe you are confused\nabout how to use this library then check out the [EXAMPLE FOLDER](https://github.com/weyoss/graphql-bookshelfjs/blob/master/example)\nto get started.\n\n### HOW-TO — STEP BY STEP\n\nLet's assume: \n\n- We have 4 models: Article, User, Account and Note.\n- Article has a **one-to-one** (belongsTo) relationship with User.\n- User has a **many-to-many** (belongsToMany) relationship with Account.\n- User has a **one-to-one** (hasOne) relationship with Profile. \n- User has a **one-to-many** (hasMany) relationship with Note.\n- A user account has Administrator privileges when access attribute is set to 'admin'.\n\n#### BOOKSHELF MODELS\n\n```javascript\nconst knex = require('knex')(dbConfig);\nconst bookshelf = require('bookshelf')(knex);\n\nconst dbConfig = {\n    \"client\": \"mysql\",\n    \"connection\": {\n        \"host\": \"127.0.0.1\",\n        \"user\": \"root\",\n        \"password\": null,\n        \"database\": \"mydatabase\",\n        \"charset\": \"utf8\"\n    },\n    debug: true\n};\n\nconst Article = bookshelf.Model.extend({\n    tableName: 'articles',\n    user: function () {\n        return this.belongsTo(User);\n    }\n});\n\nconst User = bookshelf.Model.extend({\n    tableName: 'users',\n    notes: function () {\n       return this.hasMany(Note);\n    },\n    accounts: function () {\n       return this.belongsToMany(Account, 'users_accounts');\n    },\n    adminAccounts: function () {\n       return this.belongsToMany(Account, 'users_accounts').query({where: {access: 'admin'}});\n    },\n    profile: function () {\n       return this.hasOne(Profile);\n    }\n});\n\nconst Note = Bookshelf.Model.extend({\n    tableName: 'notes'\n});\n\nconst Account = bookshelf.Model.extend({\n    tableName: 'accounts'\n});\n\nconst Profile = bookshelf.Model.extend({\n    tableName: 'profiles'\n});\n```\n\n#### DEFINNING GRAPHQL TYPES\n\n```javascript\nconst graphQL = require('graphql');\nconst graphQLBookshelf = require('graphql-bookshelfjs');\n\nlet ArticleType = new graphQL.GraphQLObjectType({\n    name: 'Article',\n    fields: {\n        id: {\n            type: new graphQL.GraphQLNonNull(graphQL.GraphQLInt)\n        },\n        isPublished: {\n            type: graphQL.GraphQLBoolean\n        },\n        user: {\n            type: UserType,\n            resolve: graphQLBookshelf.resolverFactory(Article)\n        },\n        title: {\n            type: graphQL.GraphQLString\n        },\n        keywords: {\n            type: new graphQL.GraphQLList(graphQL.GraphQLString)\n        }\n    }\n});\n\nconst UserType = new graphQL.GraphQLObjectType({\n    name: 'User',\n    fields: {\n       id: {\n           type: graphQL.GraphQLString\n       },\n       name: {\n           type: graphQL.GraphQLString\n       },\n       profile: {\n           type: ProfileType,\n           resolve: graphQLBookshelf.resolverFactory(User)\n       },\n       notes: {\n           type: new graphQL.GraphQLList(NoteType),\n           resolve: graphQLBookshelf.resolverFactory(User)\n       },\n       accounts: {\n           type: new graphQL.GraphQLList(AccountType),\n           resolve: graphQLBookshelf.resolverFactory(User)\n       },\n       adminAccounts: {\n           type: new graphQL.GraphQLList(AccountType),\n           resolve: graphQLBookshelf.resolverFactory(User)\n       }\n    }\n});\nconst ProfileType = new graphQL.GraphQLObjectType({\n    name: 'Profile',\n    fields: {\n        id: {\n            type: graphQL.GraphQLInt\n        },\n        firstName: {\n            type: graphQL.GraphQLString\n        },\n        lastName: {\n            type: graphQL.GraphQLString\n        },\n        birthday: {\n            type: graphQL.GraphQLString\n        }\n    }\n});\n\nconst AccountType = new graphQL.GraphQLObjectType({\n    name: 'Account',\n    fields: {\n        id: {\n          type: graphQL.GraphQLInt\n        },\n        name: {\n          type: graphQL.GraphQLString\n        }\n    }\n});\n\nconst NoteType = new graphQL.GraphQLObjectType({\n    name: 'Note',\n    fields: {\n       id: {\n           type: graphQL.GraphQLInt\n       },\n       note: {\n           type: graphQL.GraphQLString\n       }\n    }\n});\n\nconst RootQuery = new graphQL.GraphQLObjectType({\n    name: 'RootQuery',\n    fields: {\n        articles: {\n            type: new graphQL.GraphQLList(ArticleType),\n            args: {\n                userId: {\n                    type: graphQL.GraphQLInt\n                },\n            },\n            resolve: graphQLBookshelf.resolverFactory( Article )\n        },\n        article: {\n            type: ArticleType,\n            args: {\n                id: {\n                    name: 'id',\n                    type: new graphQL.GraphQLNonNull(graphQL.GraphQLInt)\n                }\n            },\n            resolve: graphQLBookshelf.resolverFactory( Article )\n        }\n    }\n});\n```\n\n#### GRAPHQL SCHEMA\n\n```javascript\nconst graphQLSchema = new graphQL.GraphQLSchema({query: RootQuery});\n```\n\n#### GRAPHQL QUERY\n\n```javascript\nconst queryString =\n`{ \n    articles { \n        title, \n        user { \n            id,\n            name, \n            profile {\n                firstName,\n                lastName,\n                birthday\n            }\n            accounts {\n                id, \n                name\n            },  \n            adminAccounts {\n                id, \n                name\n            }, \n            notes {\n                id,\n                note \n            } \n        } \n    } \n}`;\n```\n\n#### GRAPHQL QUERY EXECUTION\n\n```javascript\ngraphQL.graphql( graphQLSchema, queryString, null, { loaders: graphQLBookshelf.getLoaders() }).then(function(result) {\n    console.log( JSON.stringify(result, null, 4) );\n});\n```\n\n#### OUTPUT SAMPLE\n\n```text\n{\n    \"data\": {\n       \"articles\": [\n           {\n               \"title\": \"qwerty2\",\n               \"user\": {\n                   \"id\": 66,\n                   \"name\": \"Tom Stevens\",\n                   \"profile\": {\n                       \"firstName\": Tom,\n                       \"lastName\": Stevens,\n                       \"birthday\": \"1970-02-24\"\n                   },\n                   \"accounts\": [\n                       {\n                           \"id\": 1,\n                           \"name\": \"Viewer account\"\n                       },\n                       {\n                           \"id\": 2,\n                           \"name\": \"Admin account\"\n                       }\n                   ],\n                   \"adminAccounts\": [\n                       {\n                           \"id\": 2,\n                           \"name\": \"Admin account\"\n                       }\n                   ],\n                   \"notes\": [\n                       {\n                           \"id\": 345,\n                           \"note\": \"qwerty\"\n                       },\n                       {\n                           \"id\": 346,\n                           \"note\": \"asdf\"\n                       },\n                       {\n                           id: 347,\n                           \"note\": \"zxcvb\"\n                       }\n                   ]\n               }\n           },\n           ...\n       ]\n   }\n}\n```\n\n#### DEBUG LOG\n\n```text\n{ ...\n  bindings: [],\n  sql: 'select `articles`.* from `articles`' }\n{ ...\n  bindings: [ 55, 66, 66 ],\n  sql: 'select `users`.* from `users` where `id` in (?, ?, ?)' }\n{ ...\n  bindings: [ 55, 66, 66 ],\n  sql: 'select `profiles`.* from `profiles` where `user_id` in (?, ?, ?)' }\n{ ...\n  bindings: [ 55, 66, 66 ],\n  sql: 'select `accounts`.*, `users_accounts`.* from `accounts` left join `users_accounts` on `accounts`.`id` = `users_accounts`.`account_id` where `users_accounts`.`user_id` in (?, ?, ?)' }\n{ ...\n  bindings: [ 'admin', 55, 66, 66 ],\n  sql: 'select `accounts`.*, `users_accounts`.* from `accounts` left join `users_accounts` on `accounts`.`id` = `users_accounts`.`account_id` where `access` = ? and `users_accounts`.`user_id` in (?, ?, ?)' }\n{ ...\n  bindings: [ 55, 66, 66 ],\n  sql: 'select `notes`.* from `notes` where `user_id` in (?, ?, ?)' }\n\n```\n## ADVANCED USAGE\n\n```javascript\nlet UserType = new graphQL.GraphQLObjectType({\n    name: 'User',\n    fields: {\n       id: {\n           type: graphQL.GraphQLString\n       },\n       \n       // ...\n       \n       adminAccounts: {\n           type: new graphQL.GraphQLList(AccountType),\n           resolve: function resolver(modelInstance, args, context, info) {\n               \n               // Before invoking graphQLBookshelf.resolverFactory() and returning a resolver, any actions or\n               // validation rules can be performed and if for some reason something went wrong we can just return a\n               // Promise.reject('something_went_wrong') for example.\n               \n               // ...\n               \n               // Everything is OK, so let's call the parent resolver\n               let parentResolver = graphQLBookshelf.resolverFactory(User);\n               return parentResolver(modelInstance, args, context, info);\n           }\n       }\n    }\n});\n```\n\n### USING 'extra' PARAMETER\n\nStarting from release 1.0.2, `extra` parameter was added to resolver.\n\nQuery parameters from client requests are automatically translated into where closes. Sometimes we need to have more \ncontrol of our models using complex queries (when dealing with pagination for example). With the help of 'extra' \nparameter we can apply any knex query builder method to our bookshelf model. \n\n`extra` parameter is optional. When provided, it should be either a function or an object. The listing bellow\ndemonstrates how it can be used:\n  \n```javascript\nlet RootQuery = new graphQL.GraphQLObjectType({\n    name: 'RootQuery',\n    fields: {\n        articles: {\n\n            // ...\n\n            resolve: function resolver(modelInstance, args, context, info) {\n                /*\n                // Defining extra using an object\n                const count = args.count || 5;\n                const extra = {\n                    query: [function (db) {\n                        db.limit(count);\n                    }],\n                    orderBy: ['id', 'DESC'],\n                };\n                !args.from || (extra.where = ['id', '\u003c', args.from]);\n                */\n\n                // Defining extra using a function\n                const { count = 5, from } = args;\n                const extra = (model) =\u003e {\n                    model.query((db) =\u003e {\n                        db.limit(count);\n                    });\n                    model.orderBy('id', 'DESC');\n                    if (from) model.where('id', '\u003c', from);\n                };\n\n                // 'from' and 'count' parameters are not model attributes, so let's get rid of them\n                const filteredArgs = _.omit(args, ['count', 'from']);\n\n                const resolverFn = graphQLBookshelf.resolverFactory(models.Article);\n                return resolverFn(modelInstance, filteredArgs, context, info, extra);\n            },\n        },\n    }\n});\n```\n\nTo make it more clear, the following:\n\n```javascript\nconst extra = {\n    query: [function(db) {\n        db.limit( 5 );\n    }],\n    orderBy: ['id', 'DESC'],\n    where: ['id', '\u003c', 10045]\n};\n```\n\nis the same as: \n\n```javascript\nconst knexQueryBuilder = model.query();\nknexQueryBuilder.query(function(db) {\n    db.limit( count );\n});\nknexQueryBuilder.orderBy('id', 'DESC');\nknexQueryBuilder.where('id', '\u003c', 10045);\n```\n\nFor the sake of simplicity defining `extra` using a function is recommended to be used instead of using an object. It\nis more clear and intuitive.\n\n## CONTRIBUTING\n\nSo you are interested in contributing to this project? Please see [CONTRIBUTING.md](https://github.com/weyoss/guidelines/blob/master/CONTRIBUTIONS.md).\n\n## SEE ALSO\n\n- [Graphql](http://graphql.org/graphql-js/)\n- [Bookshelf](http://bookshelfjs.org/)\n\n## LICENSE\n\n[MIT](https://github.com/weyoss/graphql-bookshelfjs/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweyoss%2Fgraphql-bookshelfjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fweyoss%2Fgraphql-bookshelfjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweyoss%2Fgraphql-bookshelfjs/lists"}