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
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:
eventStore
- a configured eventStore instance.projection
- a map of redux-like reducer functions (one function for each event type).
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 specifiedgqlSchema
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 thegraphQLQuery
contains variables;jwtToken
- non-verified actual JWT token provided from client.
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;jwtToken
- non-verified actual JWT token provided from client.
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({})
}
}
}