Skip to content

Data Persistence ​

Apps can store data in different ways. The choice of storage depends on the use case and the requirements of the app.

Canvas context ​

For storing data that is specific to a given canvas, the context object can be used. Context is a JSON object that can be manipulated by the app components. The context object is persisted in the canvas and is publicly available to all components and apps to read and write.

Since context is part of the canvas, any changes to it must be persisted on the server through a request. This means it can take a while to persist.

Summary: From an app, read the context from the canvas payload (for example useUi().onAppSetData() in an embeddable) and write it with useUi().updateCanvas({ context }) or usePitcherApi().updateCanvas({ id, context }). Inside Canvas UI itself, the useCanvas composable provides the activeCanvas property and saveCanvasContext method. Apps can read and modify specific sections of the context object using their own namespaces.

Apps DB ​

Apps DB is a simple key-value store that is either instance specific or user-instance specific. Each Apps DB entry is scoped to:

  • instance_id: the id of CatalogIQ instance the entry belongs to
  • store_name: arbitrary name of the store that can be used to group entries
  • key: the key of the entry
  • user_id: the id of the user the entry belongs to (only used for user-instance specific entries)

For the full specification, check out the Apps DB REST API documentation.

From an installable app, use the JS API AppsDB methods instead: appsDbGetEntries({ type }), appsDbUpsertEntry({ type, data, user_id? }), appsDbDeleteEntry({ id }) and appsDbPsql({ query }). They store entries by type (not store_name/key) under /core/api/protected/appsdb on the web, and go through the native bridge (with its offline mirror) in the native apps.

Summary: Installable apps use the appsDb* JS API methods above. Inside Canvas UI itself, the useAppsDb composable wraps the store/key REST API with upsertEntry, getEntry, and deleteEntry.

Browser storage ​

For storing data that is specific to the user and the browser, the localStorage can be used. localStorage is a key-value store that is persisted in the browser and is available to the app even after the page is reloaded.

Due to its nature you can use the localStorage object to instantly share data between different apps that are running on the page without burdening the server.

Keep in mind that localStorage can be removed at any time, has a size limit that depends on the browser and that it is not shared between different browsers or devices.

Other browser storage mechanisms like sessionStorage or IndexedDB can also be used.

Summary: The Canvas UI system primarily uses VueUse's useLocalStorage and useSessionStorage composables for reactive browser storage. For larger datasets, IndexedDB implementation is available in some components.

External storage ​

Since apps are web pages, they can use any external server as a storage mechanism. Please note that in this case the app is responsible for the security and availability of the data.

Summary: Implement HTTP requests to external servers using the Fetch API or Axios. Ensure proper authentication and error handling when connecting to external services.


Implementation Examples ​

Canvas Context ​

typescript
// Access canvas context
import { useCanvas } from '@canvas-builder/composables/useCanvas'

const { saveCanvasContext, activeCanvas } = useCanvas()

// Read order data from context
const submittedOrders = computed(() => 
  activeCanvas.value?.context?.omAppData?.submittedOrders || []
)
const orderReference = activeCanvas.value?.context?.omAppData?.draftOrder?.custRef || ''

// Save data to context
async function saveOrderData(draftOrder) {
  await saveCanvasContext({
    omAppData: {
      draftOrder,
      lastUpdated: new Date().toISOString()
    }
  })
}

Apps DB ​

typescript
// From an installable app (JS API)
import { usePitcherApi } from '@pitcher/js-api'

const api = usePitcherApi()
await api.appsDbUpsertEntry({ type: 'my-app-settings', data: { theme: 'dark' } })
const { entries } = await api.appsDbGetEntries({ type: 'my-app-settings' })
typescript
// Inside Canvas UI (internal composable, store/key API)
import { useAppsDb } from '@lib/composables/appsDb.use'
import { APPS_DB } from '@lib/constants/appsDb.const'

const appsDb = useAppsDb({ pitcherInfo, restApiAxios })

// Save block overrides
async function saveBlockOverrides(blockId, overrides) {
  await appsDb.upsertEntry({
    store_name: APPS_DB.STORES.BLOCK_OVERRIDES,
    user_id: myUser.value.id,
    entry_key: `${APPS_DB.ENTRIES.BLOCK_OVERRIDES}_${blockId.toLowerCase()}`,
    data: overrides,
  })
}

// Get agenda items
async function getAgenda() {
  const result = await appsDb.getEntry({
    store_name: APPS_DB.STORES.PITCH_MASTER,
    user_id: myUser.value.id,
    entry_key: APPS_DB.ENTRIES.AGENDA,
  })
  return result?.data
}

Browser Storage ​

typescript
// Using VueUse for reactive localStorage
import { useLocalStorage } from '@vueuse/core'

// Smart folders path storage
const currentPath = useLocalStorage('canvas_smart-folders_current_path', [], {
  serializer: {
    read: (v) => JSON.parse(v || '[]'),
    write: (v) => JSON.stringify(v),
  },
})

// Using sessionStorage for pagination
import { useSessionStorage } from '@vueuse/core'

const pageSize = useSessionStorage('canvases-pageSize', 10)
const currentFilters = useSessionStorage('canvases-filters', {})

External Storage ​

typescript
// Store data on your own server
async function saveExternalData(data) {
  const response = await fetch('https://your-server.example.com/api/data', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  })

  return response.json()
}