@slothworks/switchboard
v0.1.0
Published
TypeScript client for the Switchboard feature flag service
Readme
@slothworks/switchboard
TypeScript client for the Switchboard feature flag service.
Two entry points:
@slothworks/switchboard— framework-agnostic core (fetch + SSE)@slothworks/switchboard/vue— Vue 3 composables backed by@tanstack/vue-query
Install
npm install @slothworks/switchboard
# Vue users
npm install @slothworks/switchboard @tanstack/vue-queryVanilla TS / JS
import { createSwitchboard } from '@slothworks/switchboard'
const sb = createSwitchboard({
url: 'https://switchboard.example.com',
// Optional: when running on the server (SSR), hit the backend internally
// instead of going through the public hostname.
ssrUrl: process.env.SWITCHBOARD_INTERNAL_URL, // e.g. http://switchboard:80
envKey: 'sw_<your-environment-key>',
})
const flags = await sb.getFlags()
const userFlags = await sb.identify('user_123', { plan: 'pro' })
const unsubscribe = sb.subscribe((event) => {
console.log('flags changed at', new Date(event.updatedAt))
})Vue 3
// main.ts
import { createApp } from 'vue'
import { VueQueryPlugin } from '@tanstack/vue-query'
import { switchboardPlugin } from '@slothworks/switchboard/vue'
import App from './App.vue'
createApp(App)
.use(VueQueryPlugin)
.use(switchboardPlugin, {
url: import.meta.env.VITE_SWITCHBOARD_URL,
envKey: import.meta.env.VITE_SWITCHBOARD_ENV_KEY,
// Opt in to SSE so flag toggles in the admin propagate live.
// Off by default — flags load once and stay until you call refresh().
subscribe: true,
})
.mount('#app')<script setup lang="ts">
import { useSwitchboard } from '@slothworks/switchboard/vue'
const sb = useSwitchboard()
async function onLogin(userId: string) {
await sb.identify(userId, { plan: 'pro' })
}
</script>
<template>
<div v-if="sb.isEnabled('new_checkout_flow')">New checkout!</div>
<span>Welcome banner: {{ sb.getValue('welcome_banner', 'Hi there') }}</span>
<button v-if="sb.isEnabled('beta_export', false)" @click="exportData">
Export
</button>
</template>SSE is off by default — pass subscribe: true to the plugin to open a single EventSource per app and have flag toggles in the admin propagate live. Without it, useSwitchboard() loads flags once and stays until you call refresh() or remount.
Disabled flags
When a flag's enabled is false, the server omits the value field from the response entirely. getValue(name, fallback) then returns your fallback (or undefined), so a hardcoded default in your consumer is always what you get back from a disabled flag — isEnabled and getValue can't drift apart by accident. If you need to "preview" a value before flipping a flag on, use a separate environment for it.
API
Core (@slothworks/switchboard)
| Symbol | Description |
| --- | --- |
| createSwitchboard({ url, envKey }) | Create a client instance |
| client.getFlags() | Promise<ResolvedFlagDto[]> — anonymous flags |
| client.identify(id, traits?) | Promise<IdentityFlagsResponseDto> — identified flags |
| client.subscribe(handler) | SSE subscription; returns unsubscribe function |
Vue (@slothworks/switchboard/vue)
Primary composable — everything you usually need:
const sb = useSwitchboard()
// sb.flags ComputedRef<ResolvedFlagDto[] | undefined>
// sb.isLoading ComputedRef<boolean>
// sb.isEnabled(name, default?) boolean
// sb.getValue(name, default?) T | undefined
// sb.getFlag(name) ResolvedFlagDto | undefined
// sb.identify(id, traits?) Promise<IdentityFlagsResponseDto>
// sb.refresh() Promise<void>Lower-level composables for advanced use:
| Composable | Description |
| --- | --- |
| useFlags() | Raw useQuery result for anonymous flags |
| useIdentityFlags(id, traits?) | Raw useQuery result for personalized flags |
| useIdentify() | Raw useMutation for imperative identify |
