databless

databless

Usage no npm install needed!

<script type="module">
  import databless from 'https://cdn.skypack.dev/databless';
</script>

README

Databless

Build Status Coverage Maintainability Vulnerabilities Dependency Status Dev Dependency Status

TL;DR manual

  • .create(data, options)

    • creates new record with given data
    • for optimized batch create use bulkCreate(data[], options)
  • .detail(filters, options)

    • gets a single record based on filters
  • .list(filters, options)

    • get a list of records based on filters
  • .update(filters, data, options)

    • updates a record based on filters with given data
  • .delete(filters, options)

    • deletes records based on filters (if empty, deletes all)
  • 🧙 Databless uses Bookshelf for underlying models, and even though Databless should be enough for most of the times, refer Bookshelf documentation for options if necessary, as they are passed to save/fetch/fetchAll/destroy metods as options.

Model

  • creating a model

    const userModel = createModel({
        adapter: () => knex     // Knex getter
        collectionName: 'users' // Table name
        attributes: {           // Record properties
            id: { type: 'number' },
            name: { type: 'string' }
        }
    })
    
  • attributes

    • define model shape and propety types
    • not defined attributes are filtered before inserting/updating into a database
  • custom serialization

    • via attribute.serialize
    objectStoredAsJson: {
        type: 'string',
        // serialize before inserting into a database
        serialize: x => JSON.stringify(x || null),
        // deserialize from database shape
        // deserialize can also be used to define attribute TS type, e.g. (x: string): MyEnum => x
        deserialize: x => JSON.parse(x),
    }
    
  • bulk create

Repository

  • via createRepository(model)
  • helper to create CRUD methods bound to a model

Filtering

  • via filters (except custom queries)

  • exact match

    { foo: 'bar' }
    // SELECT ... WHERE foo='bar'
    
  • where-in

    { foo: ['bar', 'baz'] }
    // SELECT ... WHERE foo IN ('bar', 'baz')
    
  • inequality

    • prefix value with one of >, <, >=, <=
    { foo: '>2' }
    // SELECT ... WHERE foo > 2
    
  • searching

    • only left and right wildcards are supported
    { foo: '*abc*' }
    // SELECT ... WHERE foo LIKE '%abc%'
    { foo: 'abc*' }
    // SELECT ... WHERE foo LIKE 'abc%'
    { foo: '*abc' }
    // SELECT ... WHERE foo LIKE '%abc'
    
  • custom queries

    • via options.qb parametr
    • options.qb handler is passed to Bookshelf model.query(arguments), see docs
    • ⚠️ Use with care - any SQL you make is processed, means you can group, join, add columns and change the logic output of completely and the return value and types dont have to match.
    { qb: (qb: Knex.QueryBuilder) => qb.whereRaw('...') }
    // SELECT WHERE ...
    
  • custom model filters

    • via Model.filters and filters
    • allows you to use additional filters in filters (apart from Model.attributes)
    • define in Model.filters first, use in filters second
    • ⚠️ Don't use this to overwrite any of the default Model.attribute filters, only add new filters
    // Simplified example for brevity
    Model({
        attributes: { /* */ },
        filters: { myAwesomeFilter: (value, options) => {/* */} }
    })
    
    detail({ myAwesomeFilter: awesomeValue })
    

Counting

  • via options.count
  • use count: true to get number of filtered records
  • filtering applies

Ordering

  • via options.order

  • default ordering

    { order: 'foo' }
    // SELECT ... ORDER BY foo ASC
    
  • asc ordering

    { order: '+foo' }
    // SELECT ... ORDER BY foo ASC
    
  • desc ordering

    { order: '-foo' }
    // SELECT ... ORDER BY foo DESC
    
  • order by multiple

    { order: ['+foo', '-baz'] }
    // SELECT ... ORDER BY foo ASC, baz DESC 
    

Pagination

  • via options.limit and options.offset

  • if either of limit or offset is defined, the other is filled with defaults (defaults: limit=10, offset=0)

    { limit: 10, offset: 0 }
    // SELECT ... LIMIT 10 OFFSET 0
    

Relations

Define a relation

  • via attribute of type=relation

    books: {
        type: 'relation',
        targetModel: () => bookModel, // Databless model getter
        relation: bookshelfRelation.createHasMany(/* ... */),
        // Resolves to Bookshelf relation
        // books() {
        //     return this.hasMany('Book')
        // }
    }
    
  • refer to Bookshelf docs if you want to take full advantage of configuration createBelongsTo/createHasMany/createBelongsToMany/createHasOne

Populating relation properties

  • via options.withRelated on .list, .detail

    // Simplified example for brevity
    
    // Author
    { id, name }
    // Book
    { id, author: { relation: { targetModel: Author /*... */} } }
    
    const book = Books.detail({ id }, { withRelated: ['author' ] })
    // { id, author: Author }
    
  • invokes original withRelated on underlying Boookshelf model

Reflexive relation

  • via targetModel='self'

TODO

  • Range queries
  • Like queries
  • Option typing
  • Custom relation queries (e.g. in Bookshelf this.hasMany().where(...))
  • Custom queries (via options.qb)
  • Pagination (limit/offset)
  • Cursor streaming
  • Model serialization/deserialization
  • (docs) knex-stringgify doesnt work on sqlite in memory
  • withRelated should be optional
  • withRelated shouldn't be available for update/create

Discussion

  • Fetch all (fetchAll option)
  • Default pagination
  • Bug: Knex connection reuse (if a adapter getter fn value changes, its never used)

Test

You can create database via docker-compose

sudo docker-compose -f docker-compose/docker-compose.yml up

and run the tests

npm t

License

This project is licensed under MIT.