0.6.0 • Published 3 years ago

data-bits-express v0.6.0

Weekly downloads
-
License
ISC
Repository
github
Last release
3 years ago

Data-Bits-Express

Databits express extends the core data-bits module by providing an extension for the methods execute, executeAll, and executeProps to be used as middlewares with the express server framework.

When using data-bits as a middleware with express the request and response context will be added as part of the data-bits context implicitly. We can even have the middleware respond on our behalf by setting a flag in the options.

execute

execute signature (id: string, options?: {})

The execute method builds the dependency tree based on the needs of the parent and recursively resolves bits until all the bits are resolved, a bit fails to resolve and throws an error, or no more bits can be resolved and an error is thrown.

If using the execute middleware the resolved result will be added to the request context of express as a bits object where all resolved bits can be found. All bits are stored by their parent bits id.

fieldsdescriptiontypedefault
sendwill send the resolved execution of the bits to the clientbooleanfalse
inOrderwill execute all bits in order based off the parent bit and its needs. Will not optimize based on what bits are resolvablebooleanfalse
optimizewill skip unnecessary bits not needed from the tree structure once parent is resolvedbooleanfalse
parentOnlyforces execute to only return the parents resolved data not all resolved databooleanfalse
forceArgResolverwill force each arg resolver to be called regardless of value existing in the params list and will override paramsbooleanfalse

exeucteAll

execute signature (ids: Array, options?: {})

If there is a requirement to execute multiple bits in parallel execuateAll takes a list of bit id's. When using executeAll the ctx and params will be used between all parallel executions. This is done on purpose because data-bits tries to increase performance by sharing context between parallel executions. If the use case calls for a different context it is suggested to wrap multiple executions in a Promise.all. ExecuteAll will return a list of resolved bits in the same order which was provided to the method.

If execuateAll is used as middleware an array will not be added the bit object on the request context but it will behave like executeProps. This is done to keep the bit object on the request context constant as multiple bit middlewares can be used for a single request.

fieldsdescriptiontypedefault
sendwill send the resolved execution of the bits to the clientbooleanfalse
inOrderwill execute all bits in order based off the parent bit and its needs. Will not optimize based on what bits are resolvablebooleanfalse
optimizewill skip unnecessary bits not needed from the tree structure once parent is resolvedbooleanfalse
collapsewill collapse all resolvers and not duplicate request if bit has been resolvedbooleanfalse
parentOnlyforces execute to only return the parents resolved data not all resolved databooleanfalse
forceArgResolverwill force each arg resolver to be called regardless of value existing in the params list and will override paramsbooleanfalse

executeProps

execute signature (ids: { id: string: string }, options?: {})

ExecuteProp uses executeAll under the hood and provides a more descriptive interface to work with using custom key value pair instead of an array.

If using the executeProps middleware you are able to define the exact key which will be added to the bit object on the request context.

fieldsdescriptiontypedefault
sendwill send the resolved execution of the bits to the clientbooleanfalse
inOrderwill execute all bits in order based off the parent bit and its needs. Will not optimize based on what bits are resolvablebooleanfalse
optimizewill skip unnecessary bits not needed from the tree structure once parent is resolvedbooleanfalse
collapsewill collapse all resolvers and not duplicate request if bit has been resolvedbooleanfalse
parentOnlyforces execute to only return the parents resolved data not all resolved databooleanfalse
forceArgResolverwill force each arg resolver to be called regardless of value existing in the params list and will override paramsbooleanfalse

createLoader

BitLoader is an instance which is created through your DataBits instance and leverages your bits which you have created. By using the createLoader middleware the loader will be inject into the request context provided by express as req.loader

fieldsdescriptiontypedefault
shouldBatchSet to false to disable batching. This is equivalent to setting maxBatchSize to 1booleantrue
maxBatchSizeLimits the number of items that get passed in to the batchScheduler. May be set to 1 to disable batchingnumberInfinity
cacheDisable cache my setting to falsebooleantrue
batchSchedulerA function to schedule the later execution of a batch. This function will call a callback in the future to start the batchfunctionevent loop tick

Example

To use data-bits with an express middleware we use the asMiddlware property on the instance to indicate to data-bits we are going to use a method as a middleware.

The below is an example of a simple express app with four routes, user, friends, userAndFriends, and photo. Each route shows a different way to use data-bits middleware. User and friend routes use the send and parentOnly flags to implicitly send the result of the resolved method call and only the data from the resolved parent.

The UserAndFriends route is the exact same as the friends route but does not have the parentOnly and send flag. Because if this the result will be added to the express request context and we move to the next middleware in the flow. Here we pull the USER and FRIEND_LIST results and construct the response we want to be returned to the client.

Finally we have the photo route. Photos can be large and we do not want to store the full photo in memory for obvious reasons so we pipe the photo back to the client one chunk at a time. The send flag will not work for the desired behavior so we have two options. We can handle it similar to userAndFriends and create another middleware to respond to the client or we can add this logic in the parents resolver. If we do this there is not need for creating an additional middleware just to handle sending the response. Data-bits handles this behavior internally and don't have to worry about sending headers twice.

It does not however stop the execution of bits if the headers have been sent, that continues until completely resolved. If rejected and headers have been sent data-bits assumes the request has been handled and prevents the error from being propagated and the error that may proceed by sending headers twice. To prevent behavior like this it is recommend to only send data in the parent bit and non of the children.

const express = require('express')
const request = require('./request')
const { Bit, Types, DataBits } = require('data-bits-express')
const app = express()
const bits = [{
  id: 'USER',
  type: Types.Object,
  args: [{
      field: 'cookie',
      type: Types.String,
      required: true,
      resolver: (bitCtx) => {
          const { ctx } = bitCtx
          const { req } = req
          return req.get('Cookie')
      }
  }],
  config: async (bitCtx) => { ... },
  resolver: async (bitCtx) => { ... }
}, {
  id: 'FRIENDS_LIST',
  type: Types.Object,
  needs: ['USER'],
  args: [{
      field: 'userID',
      type: Types.String,
      required: true
  }],
  config: async (bitCtx) => { ... },
  resolver: async (bitCtx) => { ... }
},{
  id: 'USER_PHOTO',
  type: Types.Any,
  needs: ['USER'],
  args: [{
      field: 'userID',
      type: Types.String,
      required: true
  }, {
      field: 'photoID',
      type: Types.String,
      required: true,
      resolver: (bitCtx) => {
          const { ctx } = bitCtx
          const { req } = req
          const { query } = query
          const { photoID } = query

          return photoID
      }
  }],
  resolver: async (bitCtx) => {
      const { ctx, args } = bitCtx
      const { photoID, userID } = args
      const { res } = ctx
      const url = `https://data-bits.com/user/${userID}/photo/${photoID}`
      const photo = await request({ url })

      res.set('content-type', 'application/pdf')

      photo.pipe(res)
  }
}]
const bits = new DataBits(bits)
app.get('/user', bits.asMiddleware.execute('USER', { parentOnly: true, send: true }))
app.get('/friends', bits.asMiddleware.execute('FRIENDS_LIST', { parentOnly: true, send: true }))
app.get('/userAndFriends', bits.asMiddleware.execute('FRIENDS_LIST'))
app.get('/userAndFriends', (req, res) => {
  const { bits } = req
  const { FRIENDS_LIST } = bits
  const { USER: user, FRIENDS_LIST: friends } = FRIENDS_LIST
  res.json({ user, friends })
})
app.use('/photo', bits.asMiddleware.execute('USER_PHOTO'))
app.listen(8080)
0.6.0

3 years ago

0.5.0

4 years ago

0.4.0

4 years ago

0.3.0

4 years ago

0.2.1

4 years ago

0.2.0

4 years ago

0.1.2

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago

0.0.2

4 years ago

0.0.1

4 years ago