Giter VIP home page Giter VIP logo

blue-chip's Introduction

BlueChip accepts payloads from GraphQL or JsonAPI servers, normalizes them into your store and provides an ORM like syntax for retrieving the data for use.

Table of Contents

The Basics

There are only two things that BlueChip does.

  1. Normalize data and organize it in a store.
  2. Retrieve the data from the store and prepare it for use.

What BlueChip Is

State API

BlueChip will take your data, normalize it and place it into a shared resources store. The API on this side is pretty minimal.

  • updateResources() Takes an entire payload of resources, normalize it and merge it into the store.
  • updateResource() Merges in a single resource into your store
  • removeResources() Removes a list of resource from the store
  • removeResource() Removes a single resource from the store
  • clearResources() Clears a resource store by types

And that is it for the State API.

Selector API

This is the meat of BlueChip. The selector API is how you prepare your data to be consumed by components. To select data, BlueChip offers a robust ORM-style syntax complete with models, relationships, filtering, includes and more. You only needs access to the resources store to be able to use the selector api.

Here is an example of using the ORM syntax to select from the store

Checklist.query(resources)
  .where({ active: true })
  .includes(['tasks'])
  .toObjects()

What BlueChip Is Not

Fetching

BlueChip is not interested in how you get your data. Fetch it, mock it, import it. However you get your data that is your business. The only requirement is that your data is formatted according to one of the adapters (JsonAPI, GraphQL). If it is not formatted you can write a custom adapter to normalize it.

Client Side Store

BlueChip is agnostic to which client-side state management library you choose to use (Redux, Mobx, Vuex, other). You only need access to a shared resources store for BlueChip to work.

Why BlueChip?

  1. You have multiple data sources (or multiple projects with different data sources) and want to consistently interact with all of them in the same way in your client-side state management systems. You can easily normalize and connect components from GraphQL, JsonAPI and custom formatted API's.

  2. You would like to keep resources unnessted in your stores for ease of updating, simplicity of mutation schema and ability to easily share resources across your application.

  3. You already have a state manager that you like or is a requirement of a project and do not want to adopt multiple to handle both GraphQL and JSON Rest data.

You are familiar with and prefer using ORM's when querying and working with data.

Demos

Redux Demo

Demo BlueChip/Redux Applicaiton

MobX Demo

Demo BlueChip/Mobx Application

React setState Demo

Demo BlueChip/React setState Application

Unstated Demo

Demo Unstated Application

Getting Started

To start, choose your state management flavor. This is an example using Redux.

$ npm i -S @blue-chip/core $ npm i -S @blue-chip/redux-adapter

Or

yarn add @blue-chip/core yarn add @blue-chip/redux-adapter

Adapters

To ensure that BlueChip is as flexible as possible, the state managment layer is implemented as adapters. These adapters are what do the work to mutate the state managment stores while BlueChip is in charge of delegating. To use the adapters you will need to setup a configuration file.

Configuration

The configuration file needs to be setup so that you can import and use the mutator actions.

import { Actions } from "@blue-chip/core";
import reduxAdapter from "@blue-chip/redux-adapter";
import store from "./store";

export const actions = Actions.config({
  adapter: reduxAdapter,
  mutator: store.dispatch
});

Redux

Actions

Batch update resources:

import { actions } from "../BlueChipConfig";

export const fetchChecklists = async (dispatch, state) => {
  dispatch({ type: "LOADING_DATA" });
  try {
    const response = await fetch("/checklists.json", {
      headers: {
        "content-type": "application/json"
      }
    });
    const payload = await response.json();

    actions.updateResources(payload);
    dispatch({ type: "LOADING_SUCCESS" });
  } catch (error) {
    console.log("error", error);
    dispatch({ type: "LOADING_ERROR" });
  }
};

Update a single resource

import { actions } from "../BlueChipConfig";

export const updateTask = ({ id, ...attributes }) => {
  actions.updateResource({ id, attributes, type: "tasks" });
};

Reducers

import { combineReducers } from "redux";
import reduxAdapter from "@blue-chip/redux-adapter";

export default combineReducers({
  resources: reduxAdapter.resourcesReducer
});

Models

Just like any other ORM you will be defining model classes:

import { BaseModel } from "@blue-chip/core";
import Task from "./Task";

export default class Checklist extends BaseModel {
  static get hasMany() {
    return [Task];
  }
}
import { BaseModel } from "@blue-chip/core";
import Checklist from "./Checklist";

export default class Task extends BaseModel {
  static get belongsTo() {
    return [Checklist];
  }
}

Containers

const mapStateToProps = state => { 
  const { resources } = state; 
  return { 
    checklists: Checklist.query(resources) 
                  .all() 
                  .includes(["tasks"]) 
                  .toObjects() 
  };
};

const mapDispatchToProps = dispatch => ({ 
  updateTask: task => updateTask(dispatch, task)
});

export default connect(mapStateToProps, mapDispatchToProps)(Container);

Store Structure

The resources store is structured as an object with top-level keys as pluralized resource names.

const store = {
  resources: {
    checklists: {},
    tasks: {}
  }
};

Each resource key points to an object that contains ids as keys and an JSON api object as a value.

const store = {
  checklists: {
    1: {
      id: 1,
      attributes: { name: "Oboarding" },
      links: { self: "http://example.com/checklists/1" },
      relationships: {
        tasks: {
         data: [{ id: 1, type: "tasks: }, { id: 2, type: "tasks: }],
       },
       type: "checklists"
    }
  },
  tasks: {
  ...
}  

Project Status

This project is currently in Alpha/Experimental phase. The APIs will almost assuredly change prior to 1.0. It is not ready for production yet, so use at your own risk.

RoadMap

  1. Increase Test Coverage
  2. Setup tests on CI
  3. More tests and examples with a diverse range of GraphQL and JsonAPI payloads.
  4. Refactor state managment to be an adapter as an external packages
  5. Add Vue and Vuex
  6. Allow for configurable and custom normailzers so you can use ANY api and spec.
  7. Bundle optimization
  8. belongsTo relationship
  9. Memoization

blue-chip's People

Contributors

bsmith83 avatar mfpiccolo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

blue-chip's Issues

Includes only work if relationship name matches resource type

The .includes() feature only works if the resource name is the same as the resource type (optionally pluralized), e.g.:

relationships: {
  user: {data: {id: 8, type: "users"}}
}

But there is nothing in the spec AFAIK that requires that, so this is also valid:

relationships: {
  reviewer: {data: {id: 8, type: "users"}}
}

But blue-chip will not find that relationship. I haven't dug into the code yet to determine what this will require, but this ought to be supported, right?

Build process fails with 'BABEL_ENV' is not recognized as an internal or external command

I'm encountering an issue when trying to install @blue-chip/core which has a dependency on graphql-normalizr. The build process for graphql-normalizr fails with the error 'BABEL_ENV' is not recognized as an internal or external command.

Steps to Reproduce:

  1. Run npm i -S @blue-chip/core in my project directory.

  2. Encounter the following error during the installation process:

    npm ERR! > [email protected] prepare
    npm ERR! > npm run clean && npm run build
    ...
    npm ERR! > [email protected] build:cjs
    npm ERR! > BABEL_ENV=cjs babel src --out-dir lib
    npm ERR! 'BABEL_ENV' is not recognized as an internal or external command,
    npm ERR! operable program or batch file.
    

Expected Behavior:
I expect the installation process to complete successfully without any errors.

Environment:

  • OS: Windows 10
  • Node.js version: 21.7.1
  • graphql-normalizr version: 1.0.2

Additional Context:
I've tried setting the BABEL_ENV environment variable in the package.json file using cross-env, but the issue persists. Any assistance in resolving this would be greatly appreciated.

Error in `whereRelated` for empty queries

Slightly similar to #28, if a query is made against a resource store that contains no matches, and that query has a .whereRelated, it will fail (v0.3.4, screenshot from Safari):

blue-chip-object-values

Prefixing a this.currentResource && check before checking Object.values() seems to do the trick, but I might be overlooking a better way to do it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.