Skip to content

CRUD data access library with a functional approach

License

Notifications You must be signed in to change notification settings

serafin-labs/pipeline

Repository files navigation

Serafin Pipeline is a Typescript CRUD data access library with a functional approach.

Concepts

Serafin Pipeline is a library that allow access to data through the CRUD methods (create, read, patch, replace and delete) of a data pipeline.

A pipeline is an element that performs operations over a data source through these 5 methods. Its behavior can be extended in a functional way, by adding pipes, elements that perform data processing or transformation over any of these 5 methods.

The Serafin Pipeline library is designed to take advantage of the Typescript language, thus providing compilation-time checks and auto-completion.

Methods

Serafin Pipeline provides data access through 4 methods: create, read, patch and delete.

Entry parameters

These methods take different entry parameters, according to their nature.

They almost all accept query and context parameters. query (or options) represent model-related parameters, that filter the result set or alter the action behavior, while context represent pipeline related parameters that influence the behavior of any action.

The parameters are:

  • create:
    • values: an array of resources to create
    • options: parameters affecting the create behavior
    • context: parameters affecting the pipeline behavior
  • read:
    • query: filtering parameters
    • context: parameters affecting the pipeline behavior
  • patch:
    • query: filtering parameters
    • values: the resources values to set
    • context: parameters affecting the pipeline behavior
  • delete:
    • query: filtering parameters
    • context: parameters affecting the pipeline behavior

Return value

All methods return a Results object with

  • meta: an object containing metadata fields
  • data: an array containing the returned resources
{
    data: [{
            id: "1",
            firstName: "Nico"
            lastName: "Degardin"
        }, {
            id: "2",
            firstName: "Seb"
            lastName: "De Saint-Florent"
        }],
    meta: {
        count: 2
    }
}

Pipelines and Pipes

A pipeline is an object that handles these four methods, operates over a data source, and whose behavior and model can be extended by plugging pipes to it.

Pipeline

At instantiation, the pipeline constructor takes a model argument.

The model is defined by passing a SchemaBuilder object. Thanks to this library, the model can be defined by providing a JSON Schema representation object, or by building the schema using the SchemaBuilder functional syntax.

let myPipeline = new PipelineInMemory(
    SchemaBuilder.objectSchema({
        id: SchemaBuilder.stringSchema({ description: "id" }),
        firstName: SchemaBuilder.stringSchema({ description: "user first name" }),
        lastName: SchemaBuilder.stringSchema({ description: "user last name" })
    })
)

Pipe

A pipe is a generic element that can be plugged to a pipeline by using the .pipe method, to extend its behavior.

A pipe can alter the pipeline model and do virtually anything as long as it returns the expected result structure (or an error): it can filter or transform data, add parameters to the query, perform checks, etc...

let userSchema = ...
let myPipeline = new PipelineInMemory(userSchema)
    .pipe(new PipeUpdateTime())
    .pipe(new PipeMemcached())
    .pipe(new PipeSomeSecurity())

Conventions

Inside pipes and pipelines, the context properties that begin by a _ are considered as private: any remote API that rely on Serafin Pipeline must trim them from the *user-provided values*. These context properties correspond to security parameters or other *internal options* that would be supplied internally by the transport.

Data access

A pipeline, wether it has been extended or not, can be called by using one of the CRUD operations it owns.

All CRUD operations are asynchronous (and return Promises). Also, all operations return an array of results.

let userSchema = ...
let myPipeline = new PipelineInMemory(userSchema)
    .pipe(new PipeUpdateTime())
    .pipe(new PipeMemcached())
    .pipe(new PipeSomeSecurity())
    ;

let user = await myPipeline.read({"id":"1"});
console.log(user.data[0] ? `User ${user.data[0].firstName} ${user.data[0].lastName}` : "User with id '1' not found");

Relations

Relations between pipelines can be defined using the pipeline .addRelation method.

let addressPipeline = new PipelineInMemory(addressSchema);
let userPipeline = new PipelineInMemory(userSchema);
    .addRelationWithOne("address", addressPipeline, { "id": ":addressId" });

Relations are simply one-direction links resolved at runtime.

The .addRelationWithOne and .addRelationWithMany arguments are the relation name, a pipeline, and an optional query.

The provided query can be a templated query: if a query value begins by :, its value will be replaced at runtime by the resource value correspond to this model field.

About

CRUD data access library with a functional approach

Resources

License

Stars

Watchers

Forks

Packages

No packages published