JSPM

resolve-query

0.2.0-alpha.e6d2805d
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 43
  • Score
    100M100P100Q81988F
  • License MIT

This package creates a function to execute a query.

Package Exports

  • resolve-query

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (resolve-query) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

🔍 resolve-query npm version

Provides an interface for creating read and view models and query facade for them.

Queries are used to observe a system 's state. Read Models answer Queries and are built using Projection functions. All events from the beginning of time are applied to a Read Model to build its current state (or a state at a particular moment in time if necessary). Some Read Models, called View Models, are sent to the client UI to be a part of a Redux app state. They are small enough to fit into memory and can be kept up to date in the browser.

import { createReadModel, createViewModel, createFacade } from 'resolve-query'

Usage

To create a read model, pass the following arguments to the createReadModel factory function:

  • eventStore - a configured eventStore instance.
  • projection - functions converting an event stream into a read model storage. A projection form is dependent on the used adapter. When the default adapter is used, a projection is a map of functions (one function for each event type) which manipulate data in the provided MongoDB-like store.
  • adapter - a read model adapter instance. A memory adapter supporting the MongoDB-like query language is used by default.

To create a view model, pass the following arguments to the createViewModel factory function:

To create a query facade for a read/view model, pass the following arguments to the createFacade factory function:

  • model - a read/view model resource a factory function created.
  • gqlSchema - a read model data schema description in terms of the GraphQL schema language.
  • gqlResolvers - a map of resolvers for replying to a GraphQL query depending on the specified gqlSchema and read model storage data.
  • customResolvers - optional resolvers for specific read/view models.

A facade supports the following functions to send queries to a read/view model:

  • executeQueryGraphql - gets data from read/view models using a GraphQL request;
  • executeQueryCustom - executes a custom resolver function;
  • dispose - removes a facade and releases resources.

The executeQueryGraphql function receives the following arguments:

  • qraphQLQuery (required) - the GraphQL query to get data;
  • graphQLVariables - specify it if the graphQLQuery contains variables;
  • getJwt - a callback to retrieve the actual client state stored in a verified JWT token.

The executeQueryCustom function receives the following arguments:

  • name (required) - a custom resolver name to handle a request;
  • customParams - custom parameters passed to a resolver function;
  • getJwt - a callback to retrieve the actual client state stored in a verified JWT token.

A custom query can be helpful in the following cases:

  • if the selected read model storage provides a n API for retrieving data, like Elasticsearch or Searchify;
  • to pass the actual view model state as a client-side redux initial state;
  • to use the selected adapter's internal features which are not accessible via a regular graphql query.

Example

Implement a read model for building a News state with custom GraphQL resolvers and use the resolve-query library to get the first news page. It handles events an aggregate produces ( see the resolve-command documentation).

import { createReadModel, createFacade } from 'resolve-query'
import createMemoryAdapter from 'resolve-readmodel-memory'
import createEventStore from 'resolve-es'
import createStorageAdapter from 'resolve-storage-lite'
import createBusAdapter from 'resolve-bus-memory'

import newsReadModel from './news-read-model.js'

const eventStore = createEventStore({ 
    storage: createStorageAdapter(), 
    bus: createBusAdapter()
})

const executeQueryGraphql = createFacade({
  model: createReadModel({
    eventStore,
    projection: newsReadModel.projection,
    adapter: createMemoryAdapter()
  }),
  gqlSchema: newsReadModel.gqlSchema,
  gqlResolvers: newsReadModel.gqlResolvers
})

// Request by GraphQL query with paramaters
executeQueryGraphql(
  'query ($page: ID!) { news(page: $page) { title, text, link } }',
  { page: 1 }
).then((result) => {
  console.log(result)
})
news-read-model.js
const NUMBER_OF_ITEMS_PER_PAGE = 10

export default {
  projection: {
    NewsCreated: async (store, { aggregateId,  timestamp, payload: { title, link, text } }) => {
      const news = await store.collection('News')
      await news.insert({ id: aggregateId, title, text, link })
    },

    NewsDeleted: (store, { aggregateId }) => {
      const news = await store.collection('News')
      await news.remove({ id: aggregateId })
    }
  },

  gqlSchema: `
    type News {
      id: ID!
      title: String!
      text: String
      link: String
    }
    type Query {
      news(page: Int, aggregateId: ID): [News]
    }
  `,

  gqlResolvers: {
    news: async (store, { page }) => {
      const news = await store.collection('News')

      if(Number.isInteger(+page) && (+page > 0)) {
        return await news.find({})
          .skip((page - 1) * NUMBER_OF_ITEMS_PER_PAGE)
          .limit(NUMBER_OF_ITEMS_PER_PAGE)
      }
        
      return await news.find({})
    }
  }
}