npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

pronto-data-service

v0.0.8-somata-peer

Published

A base Data Service with multiple DB backends and a GraphQL layer

Downloads

28

Readme

data-service

A base Data Service with multiple DB backends and a GraphQL layer

Installation

$ npm install git+ssh://[email protected]/prontotype-us/data-service.git

Schema

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 are mongo for MongoDB, and pg for Postgres. Leave blank for an in-memory DB.
  • config:
    • id_key: Default is always id (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 to Unauthorized without 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)Item
  • find(type, query, search, options){items: [Item], total: Int, pages: Int}
  • create(type, new_item, options)Item
  • update(type, id, item_update)Item
  • remove(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]', task

Without 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]', task

Triggers

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) ->