pronto-data-service v0.0.8-somata-peer
data-service
A base Data Service with multiple DB backends and a GraphQL layer
Installation
$ npm install git+ssh://git@github.com/prontotype-us/data-service.gitSchema
Create a file schema.sch
TODO: Document schema syntax
Usage
A DataService is used like a regular Somata Service, with a slightly different set of arguments:
new DataService(service_name, db_options, custom_methods)
DataService = require 'data-service'
# Create a MongoDB data service with only generic methods
new DataService 'myproject:data', {
type: 'mongo'
config: db: 'myprojectdb'
}
# Create a LocalDB with a specific method
new DataService 'myproject:data', {}, {doSpecificThing}Options
type: Supported types aremongofor MongoDB, andpgfor Postgres. Leave blank for an in-memory DB.config:id_key: Default is alwaysid(for MongoDB, IDs are coerced from_id).- Specific DB types might use extra config options:
db: Required for MongoDB and Postgres.host: Optional for MongoDB and Postgres.user: Optional for MongoDB and Postgres.
strict_auth: Optionally set authorization to default toUnauthorizedwithout access rules
Database Types
- MongoDb
- PostgresDb
- LocalDb: In memory database for quick testing
- ServiceDb: Forwards methods to a service that follows the data service protocol
- MultiDb: Combines multiple Dbs into one, each Db handling its own set of collections
Methods
The base database exposes the following functions, all requiring a type argument (the collection name, e.g. "users"):
get(type, query)→Itemfind(type, query, search, options)→{items: [Item], total: Int, pages: Int}create(type, new_item, options)→Itemupdate(type, id, item_update)→Itemremove(type, id)→Bool
GraphQL methods
query(query, context...)
The base query method takes a GraphQL query that may represent a get, find, create, or update, and one or more objects of arguments to pass to the query.
Get
Get a single item with either id or query argument:
query($id: ID) {
user(id: $id) {id, name, email}
}query($email: String) {
user(query: {email: $email}) {id, name, email}
}Find
Find multiple items, paginated, with a query. Note that the items are wrapped in another object with the shape {items, total, pages}. You can pass an options with {page} for pagination:
query($city: String, $page: Int) {
users(query: {city: $city}, options: {page: $page}) {items {id, name, email}, total}
}Create
Mutation query with argument create, which returns the created item:
mutation($name: String, $email: String) {
create_user(create: {name: $name, email: $email}) {id, name, email}
}Update
Mutation query with arguments id and update, which returns the updated item:
mutation($id: ID, $name: String) {
update_user(id: $id, update: {name: $name}) {id, name}
}Subscriptions
By passing a GraphQL query as second subscription argument you can specify the shape of the event to be returned. The query needs an id argument which will use the created or updated item's id.
task_query = '''
query($id: ID){
task(id: $id){id, name}
}
'''
client.subscribe 'sconce:data', 'tasks:5:updated', task_query, ({task}) ->
console.log '[specific task updated, graphql]', taskWithout a query, you'll get the object as it is returned from the Db class.
client.subscribe 'sconce:data', 'tasks:created', (task) ->
console.log '[any task created, raw]', taskTriggers
Attach extra actions before and after creates and updates using triggers, passed as a config argument in the shape {collection: {postCreate: fn, ...}, ...}. Available actions are preCreate, postCreate, and postUpdate.
triggers = {
tasks: {
postCreate: (created_task) ->
console.log 'Look at this task', created_task
}
}Authorization
Optionally configure authorization for each type with three dictionaries. By default users will be authorized to perform all actions.
canUpdate = {
'[type]': (user_id, item_id, cb) ->
authorized = true # Bool
cb err, authorized
}
canCreate = {
'[type]': (user_id, new_item, cb) ->
cb err, true
}
canRead = {
'[type]': (user_id, item, cb) ->
cb err, true
}You can set the authorization to default to Unauthorized unless a successful access rule is specified with config.strict_auth = true.
The service will then offer an authorized version of each generic method, with a user_id as an additional first argument.
createAs = (user_id, args...) ->
# optional type-wise canCreate
@create args...
getAs = (user_id, args...) ->
# optional type-wise canRead
@get args...
findAs = (user_id, args...) ->
# optional type-wise canRead
@find args...
updateAs = (user_id, args...) ->
# optional type-wise canUpdate
@update args...
queryAs = (user_id, query, context) ->
# substitute getAs, findAs for get, find...
query query, context
# Create a LocalDB with some triggers and access rules
new DataService 'myproject:data', {
config: {
triggers
canRead:
tasks: (user_id, item, cb) ->
get 'users', {id: user_id}, (err, user) ->
cb err, user.god == true ||
user_id in item.assigned_user_ids
canUpdate:
tasks: (user_id, item_id, cb) ->
get 'users', {id: user_id}, (err, user) ->
cb err, user.god
}
}Db Subclasses
Implement these methods:
Db = require './db'
class CustomDb extends Db
_get: (type, query, cb) ->
_find: (type, query, search, options, cb) ->
_findWithArray: (type, queries, cb) ->
_create: (type, new_item, cb) ->
_update: (type, id, item_update, cb) ->
_remove: (type, id, cb) ->7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago