Server Hooks

Server Hooks allow you to

  • Influence the publication process
  • Influence the document rendering
  • Get notified about list updates

Use Cases of a Server Hook

  • Modify a document
  • Abort the publish process and return an error to the server
  • Notify other systems

Alternatives to Server Hooks

2 Types of Server Hooks

  • Project specific hooks: register as many as you need, executed in the order they got registered
  • Server wide hooks: run on every project, but are only allowed for publication hooks

Anchor Link to SectionPublication Hooks

With publication hooks you can influence the Document Publication Lifecycle. E.g. manipulate the content before a publish happens.

Anchor Link to SectionOverview

NameSupported inEditor Feedback
preparePublishHookAsyncInstant Publish
postPublishHookAsyncInstant Publish
preUnpublishHookAsyncInstant Publish
postUnpublishHookAsyncInstant Publish

Anchor Link to SectionRegister a Publication Hook

Two ways of registering a Publication Hook

  • liServer.registerPublicationHooks(): Register publication hooks per project.
  • liServer.registerGlobalPublicationHooks(): Register publication hooks that are executed on all projects (these hooks run before project specific ones).

API of Publication Hooks

  • preparePublishHookAsync: ({documentVersion}) {return}
  • postPublishHookAsync: ({documentVersion}) {return}
  • preUnpublishHookAsync: ({documentVersion}) {return}
  • postUnpublishHookAsync: ({documentVersion}) {return}

Example

const appConfig = require('./conf')
const liServer = require('@livingdocs/server')(appConfig)

liServer.registerInitializedHook(async () => {
  // Global publish hooks
  liServer.registerGlobalPublicationHooks({
    async preparePublishHookAsync({documentVersion}) {
      return
    }
  })

  // Project-specific publish hooks
  liServer.registerPublicationHooks({
    projectHandle: 'daily-planet',
    async preparePublishHookAsync({documentVersion}) {
      return
    },
    async postPublishHookAsync({documentVersion}) {
      liServer.log.info(
        `postPublishHookAsync called for documentType: ${documentVersion.documentType}!`
      )
      liServer.log.debug({documentVersion: documentVersion})
      return
    },
    async preUnpublishHookAsync({documentVersion}) {
      liServer.log.info(
        `preUnpublishHookAsync called for documentType: ${documentVersion.documentType}!`
      )
      liServer.log.debug({documentVersion})
      return
    },
    async postUnpublishHookAsync({documentVersion}) {
      liServer.log.info(
        `postUnpublishHookAsync called for documentType: ${documentVersion.documentType}!`
      )
      liServer.log.debug({documentVersion})
      return
    }
  })
})
Copy

Anchor Link to SectionpreparePublishHookAsync

The preparePublishHookAsync hook allows modifications of the DocumentVersion before a document will be published.

Use Cases

  • Modify document (DocumentVersion)
  • Error feedback with throwing an error (document will not be published)

Example

const {validationError} = require('@livingdocs/server').errors

async preparePublishHookAsync ({documentVersion}) {
  if (documentVersion.metadata.title === 'Let me pass') {
    // modify the document here
    return
  } else {
    // Example Validation Error for a metadata property
    throw validationError({
      message: 'Invalid Title',
      metadataProperty: 'title'
    })
  }
}
Copy

If a single content type has multiple hooks throwing validation errors, you’ll need to throw all the errors at once. To do so, you can collect each error into an array as they occur. Then, once all hooks have completed, use a global function to handle all the accumulated errors simultaneously.

In case you need to change metadata properties, please always clone the origal value before mutating it. If you mutate an object directly, it can’t be tracked and saved.

Therefore always assign metadata properties on a root-level:

const value = _.cloneDeep(documentVersion.metadata.category)
documentVersion.metadata.category = Object.assign(value, {id: '123', name: 'new name'})`
Copy

Anchor Link to SectionpostPublishHookAsync

The postPublishHookAsync hook will be called after a document has been published. Any change to the DocumentVersion has no effect. A use case for this hook is to inform remote systems about the publication of a document.

Use Cases

  • Notify other systems

Example

async postPublishHookAsync ({documentVersion}) {
 axios.post(`https://my-remote-service.com/publish`,
  {
    projectId: documentVersion.projectId,
    documentId: documentVersion.id,
    publicationId: documentVersion.getLastPublicationId(),
  })
}
Copy

Anchor Link to SectionpreUnpublishHookAsync

The preUnpublishHookAsync hook allows modifications of the DocumentVersion before a document will be unpublished.

Use Cases

  • Modify document (DocumentVersion)
  • Error feedback with throwing an error (document will not be unpublished)

Example

const {validationError} = require('@livingdocs/server').errors

async preUnpublishHookAsync ({documentVersion}) {
  if (documentVersion.metadata.title === 'Let me pass') {
    // modify the document here
    return
  } else {
    // Example Validation Error for a metadata property
    throw validationError({
      message: 'Document cannot be unpublished because ...',
      metadataProperty: 'title'
    })
  }
}
Copy

Anchor Link to SectionpostUnpublishHookAsync

The postUnpublishHookAsync hook will be called after a document has been unpublished or a published document gets deleted. Any change to the DocumentVersion has no effect. A use case for this hook is to inform remote systems about the unpublish of a document.

Use Cases

  • Notify other systems

Example

async postUnpublishHookAsync ({documentVersion}) {
 axios.post(`https://my-remote-service.com/unpublish`,
  {
    projectId: documentVersion.projectId,
    documentId: documentVersion.id
  })
}
Copy

Anchor Link to SectionList Hooks

Anchor Link to SectionRegister a List Hook

There is one hook for the document-lists feature. The hook can be registered through liServer.registerListHooks().

Example:

const appConfig = require('./conf')
const liServer = require('@livingdocs/server')(appConfig)

liServer.registerInitializedHook(async () => {
  liServer.registerListHooks({
    projectHandle: 'daily-planet',
    listUpdateHookAsync({projectId, listId, remove, add}) {
      console.info(
        `The list with id '${listId}' in the project '${projectId}' has changes.`,
        `removing ${remove.length} things, adding ${add.length} things.`
      )
      return
    }
  })
})
Copy

Anchor Link to SectionlistUpdateHookAsync

The payload described here has a custom format where it gives the added and removed documentIds. An example how to use that hook would be to have Elasticsearch reindex the documents which got added/removed from a list.

listUpdateHookAsync: ({
  trx,
  eventSource, // which api method triggers the hook 'updateList' or `removeDocumentFromList`
  projectId,
  channelId,
  listId,
  remove: [30, 199],
  add: [{id: 77, order: 12}]
})
Copy