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

iodio

v0.1.2

Published

Monadic query builder (Knex wrapper: Fluture powered)

Downloads

4

Readme

Table of Contents

About The Project

I really love Knex.js Query Builder. It is really easy to set up and use. It's Promise based and offers a clean, fluent way to interact with the Database engine of your choice (Postgres, MSSQL, MySQL, MariaDB, SQLite3, Oracle, and Amazon Redshift).

Some aspects of Knex I like less:

  • Mutable. Every time you specialize in your query calling some method (.where, .limit, .join, etc.) you are mutating the same instance. This makes it harder to compose your apps.

  • Not Lazy. Being based on Promise, the query is materially executed the moment you call .resolve on the builder or try to "await" for the result. You cannot composer your program in a Pure way leaving the actual "impure" query execution as the last step.

So, let's introduce you IODIO. It's a monadic wrapper of a Knex query builder and a Fluture Future that represents the result of the query. It's lazy and pure, so you can program in advance your computations (query composition and/or result transformation) and run all as the last step.

You can see it as a lazy Bifunctor, the left side represents the Knex QueryBuilder, and the right side the Fluture future value that wraps the results. Plus we have a simple object storing the query input parameters.

Built With

Iodio is implemented in a few lines of code that mix up some good libraries:

Getting Started

Installation

  1. Add Iodio to your project
yarn add iodio
  1. If not already in your project add peer dependencies
yarn add knex
yarn add fluture
  1. Do not forget your DB connection layer, ex. sqlite3
yarn add sqlite3

Usage

Node.js "Hello World".

import Knex from 'knex'
import Iodio from 'iodio'

// Init the Knex DB connection config
const db = Knex({
    client: 'sqlite3',
    connection: {
        filename: 'tests/chinook.sqlite'
    },
    useNullAsDefault: true
})

// Construct Iodio object. Lift take DB connection and a params list.
// Params are passed directly to knex: tableName, options={only: boolean}
const q = Iodio.lift(db, ['Track'])

// Then you can start working on your query
// (qb is knew QueryBuilder)
const q2 = q.qMap(qb => qb.limit(3))

// Lets transform the result
// res is the query result list
const q3 = q2.map(res => res.map(row => row.Name + ' by  ' + row.Composer))

// Let's filter by Composer
// qMap second param 'p' is the Iodio params object
const q4 = q3.qMap((qb, p) => qb.where({ Composer: p.Composer }))

// Get a Iodio binded to Composer 'Red Hot Chili Peppers'
const redHot = q4.pMap(() => ({
    Composer: 'Red Hot Chili Peppers'
}))

// Get a Iodio binded to Composer 'Kurt Cobain'
const cobain = q4.pMap(() => ({
    Composer: 'Kurt Cobain'
}))

// I want to work on the result of the two query
// we use async/await as DO Notation
const redHotCobain = Iodio.merge([redHot, cobain])(async (redHot, cobain) => [
    ...(await redHot),
    ...(await cobain)
])

// N.B. no real query is actually executed at this point, it's all lazy
//
// To run all the constructed computations and effects
// we call fork
redHotCobain.fork(console.error)(console.log)

Output:

[
  'Parallel Universe by  Red Hot Chili Peppers',
  'Scar Tissue by  Red Hot Chili Peppers',
  'Otherside by  Red Hot Chili Peppers',
  'Intro by  Kurt Cobain',
  'School by  Kurt Cobain',
  'Drain You by  Kurt Cobain'
]

API

Factories

Methods

Consuming / Collapsing Methods

Factories

of(pPred, qbR, fR)

Iodio base constructor. It's rare to call it directly, you can call other more confortable constructors: lift, resolve, merge. (pPred, qbR, fR)

Arguments:

  • pPred: Function Paramas Predicate: a function used to bind query params to Iodio monad. Take current params map as argument and return a new param map.

  • qbR: ParamsFunction, QueryBuilderT<T> QueryBuilder Reader: Monet Reader monad that wraps the knex query builder.

  • fR: QueryBuilderT<T>, FutureInstanceArrayT<T> Fluture Reader: Monet Reader monad that wraps the Fluture Future representing the query output.

Returns: IodioInstance


lift(db, args)

Contruct a new IodioInstance setting knex db connection object and an array of params to pass to knex to get the QueryBuilder.

Arguments:

  • db: knex The knex configured DB Connection.

  • args: Array<any> An array to pass to knex connection to init the QueryBuilder, usually is the tableName plus other options ['TableName'].

Returns: IodioInstance


resolve(db, args)

Contruct an IodioInstance resolved to a fixed value. Working on QueryBuilder of the resulting monad currently not supported.

Arguments:

  • v: any The resolution value.

Returns: IodioInstance


merge(iodio1, iodio2, ...)(doAsyncFunction)

Useful to merge the result of two or more iodio instance and work on the values. It's like a DO notation implemented with async/await. Take present that the merge it's lazy. So a iodio passed to merge is not resolved (collapsed) until we request it's value in the supplied async function.

Arguments:

  • iodio1, iodio2, ...: IodioInstance The IodioInstance wrapping the results we want to work with in the supplied async function

  • doAsyncFunction: (Promise<iodio1 res>, Promise<iodio2 res>, ...) => Promise<res> The async function that let us work with the requested iodio value.

Returns: IodioInstance


Methods

pMap(pred)

Params Map. Function to transform the query params object binded to the monad.

Arguments:

  • pred: (a: Object=> a: Object) Function that has the current binded params object as input and return the updated params object as output.

Returns: IodioInstance


qMap(pred)

Query Mapper. Function to transform the knex wrapped QueryBuilder object. You can use this method to compose your query (working on Left side of the Bifunctor) without altering the future result transformations (Right side of the Bifunctor).

Arguments:

  • pred: ((qb: QueryBuilder, p: Object) => QueryBuilder) For your confort the params object is passed as second argumet so you can reference binded params in the query.

Returns: IodioInstance


map(pred)

Fluture Future Mapper. Function to work on the knex wrapped Fluture future monad. You can use this method to compose computations on the future result (working on Right side of the Bifunctor) without altering the QueryBuilder (Left side of the Bifunctor).

Arguments:

  • pred: (f: FutureInstance => f: FutureInstance)

Returns: IodioInstance


bimap(qPred, fPred)

It's like calling qMap and Map at the same time. Let you work on the two side of the Bifunctor.

Arguments:

  • qPred: ((qb: QueryBuilder, p: Object) => QueryBuilder)
  • fPred: (f: FutureInstance => f: FutureInstance)

Returns: IodioInstance


chain(pred)

Like map but the supplied function must return an IodioInstance. The computation will continue with the new IodioInstance.

Arguments:

  • pred Function returning an IodioInstance

Returns: IodioInstance


ap(iodioFuntion)

Call the function wrapped as future value in the supplied IodioInstance passing the self future value as parameter.

Arguments:

  • iodioFuntion: IodioInstance An Iodio Instance that wraps a function as future value.

Returns: IodioInstance


future()

Transform Iodio to a Fluture Future wich wrapped value equals query result plus computation. No query is executed calling this method, query and computation will be done at time of forking returned future.

Returns: FutureInstance


Consuming / Collapsing Methods

fork(left)(right)

Make the query collapse and excute it's effects on the Database. The results will be computed by the Fluture Future object and result passed to the right predicate. In case of error the left predicate is called.

Arguments:

  • left Error predicate.
  • right Error predicate.

Returns: Cancel


toString()

Debug/Inspection method. Collapse the QueryBuilder (without running the actual query) and the Fluture Future, printing a string rapresentation of both and binded params map.

Returns: String


first()

Shortcut for testing purpouse. Make the IodioInstance collapse, exectuing the DB effects and retur the first result object.

Returns: String


License

Distributed under the MIT License. See LICENSE for more information.

Contact

Fabiano Taioli - [email protected]

Project Link: https://github.com/FbN/iodio