Skip to content

Importing the SDK

The SDK is one module for pages and for the worker, tools and run server. Install its package, @matter.work/extension-sdk:

Terminal window
npm install @matter.work/extension-sdk

and import it:

import matter from '@matter.work/extension-sdk'

The package holds the SDK’s types. The SDK itself comes from the Matter that runs the extension, so the extension always runs the SDK that matches each member’s Matter. The import hands over to it: in a page the SDK calls the extension’s own origin, and in Node it calls Matter with the token Matter gives the process. Outside Matter, the import throws.

matter extension create sets all this up for you.

Matter serves the SDK on the extension’s own origin, at /_matter/sdk.js, and the browser loads it from there. Have your bundler leave that import to the browser. With Vite:

import { defineConfig, type Plugin } from 'vite'
const matterSdk: Plugin = {
name: 'matter-sdk',
enforce: 'pre',
resolveId: (id) =>
id === '@matter.work/extension-sdk' || id === '/_matter/sdk.js'
? { id: '/_matter/sdk.js', external: true }
: null,
// In dev the page loads it from Matter, not from Vite.
load: (id) => (id === '/_matter/sdk.js' ? 'export {}' : null),
}
export default defineConfig({
plugins: [matterSdk],
optimizeDeps: { exclude: ['@matter.work/extension-sdk'] },
build: { rollupOptions: { external: ['/_matter/sdk.js'] } },
})

With another bundler, mark /_matter/sdk.js external: the package’s browser entry imports it.

The worker, tools and run server import the package too. It loads the module Matter names in MATTER_SDK_URL, which Matter sets for every process it starts. Bundle your code with the import, since a version doesn’t ship node_modules. The starter’s worker is bundled with esbuild.

Code you don’t bundle can import the module directly:

const { default: matter } = await import(process.env.MATTER_SDK_URL)

To read the module itself, matter extension sdk writes a copy of it.

In a page, the context is there before your code runs:

Property What it is
matter.user { id, memberId, name, role, timezone }
matter.role owner, admin, member or guest
matter.extension { id, name }
matter.space { id, name }
matter.workspace { id, name }

await matter.context() gives all of these, plus:

  • deployment: { id, preview, local }, the version showing. preview is true for a version that isn’t current.
  • setup: the member’s setup, { deviceId, thisDevice }, or null if they haven’t set the extension up.
  • device: { id }, this computer.

In Node, call await matter.context() first. Until then the properties are undefined.

matter.onContextChange(listener) hears renames, role changes and setup moves. It returns a function that stops listening.

const stop = matter.onContextChange((context) => renderHeader(context.user.name))

Calls that fail reject with a MatterError, which has a code, a message and the HTTP status. Codes include:

  • invalid: the request is wrong; the message says how.
  • not_found: no such table, routine or route.
  • precondition_failed: a conditional update’s condition didn’t hold.
  • conflict: a unique index already has a row with those values.
  • forbidden: the member isn’t allowed to do this.
  • unavailable: Matter can’t answer this here, or the SDK isn’t running inside Matter.
  • await matter.tables(): the tables of the extension’s space, with their fields ({ id, name, type, options? }).
  • await matter.members(): the workspace’s active members, { id, name, role }.