Integration Libraries
Integration packages for Productify framework - frontend and backend.
GitHub Packages (Public)
ProductifyFW packages are published as public packages on GitHub Packages. Add to .npmrc:
@productifyfw:registry=https://npm.pkg.github.comNo authentication required. See Frontend Integration or Backend Integration for details.
Frontend Integration
@productifyfw/core
TypeScript client library for accessing Productify configuration injected by the proxy.
Features:
- Type-safe access to
window.__PRODUCTIFY__ - User, tenant, and application context
- Configuration management with defaults
- Pilet loading system - Dynamic ES module loading from feed service
- Framework packages
@productifyfw/vueand@productifyfw/reactare planned but do not exist yet — use@productifyfw/coredirectly (Known Issues)
Installation:
npm install @productifyfw/coreQuick Start:
import { ProductifyClient, loadPilets } from "@productifyfw/core";
import { reactive } from "vue";
const pfy = new ProductifyClient();
// User and tenant context
const user = pfy.getUser();
const tenant = pfy.getTenant();
// Configuration
const theme = pfy.getConfig<string>("theme", { defaultValue: "light" });
// Modules (Pilets)
const enabledModules = pfy.getEnabledModules();
if (pfy.isModuleEnabled("dashboard")) {
// Load dashboard module
}
// Load pilets dynamically
const piletRegistry = reactive({ extensions: {}, pages: {} });
loadPilets("/pilet-feed", piletRegistry);
// Themes
const enabledThemes = pfy.getEnabledThemes();
const defaultTheme = pfy.getDefaultTheme();
// Locales
const enabledLocales = pfy.getEnabledLocales();
const defaultLocale = pfy.getDefaultLocale();Vue 3 + Pilet Loading Example
Complete example application demonstrating microfrontend architecture with Vue 3, ES module pilets, and @productifyfw/core.
Features:
- Vue 3 shell application with Composition API
- Dynamic ES module pilet loading from feed service
- Custom pilet API (Piral-compatible without framework dependency)
- Example pilet implementations with dashboard widgets
- Productify proxy integration
- CLI pilet upload support with PAT authentication
See the Vue 3 + Pilet Loading Example Guide for detailed documentation.
Backend Integration
A server library for Node.js/Express, plus a Go library that is still only a design (see below). The wire protocol (identity headers, registration + heartbeat, trigger callbacks) is shared, and a parity test suite plus fake-Manager test doubles (@productifyfw/testing, Go managertest) are planned — none of those exist yet, so today the Node library is the only shipped option.
@productifyfw/node-express
Express.js middleware for accessing Productify authentication headers.
Features:
- Extract user/tenant from HTTP headers
- Type-safe request context via
req.productify - Authentication middleware (note: it does not verify that the request came from the proxy — see Known Issues)
- Backend registration with heartbeat, and trigger handling
Installation:
npm install @productifyfw/node-expressQuick Start:
import express from "express";
import {
productifyMiddleware,
requireProductify,
} from "@productifyfw/node-express";
const app = express();
app.use(productifyMiddleware({ applicationId: "my-app" }));
app.get("/api/profile", requireProductify, (req, res) => {
const { user, tenant } = req.productify;
res.json({ user, tenant });
});Go library (be-integrations/go) — planned
Not yet implemented
The Go library does not exist — be-integrations/ contains no Go module, so the go get commands below will fail. See Known Issues. The design is described in Backend Integration - Go.
It would provide a dependency-free net/http core with a Gin adapter module: the same header contract, registration lifecycle (heartbeat, re-register on 404, graceful unregister), timing-safe trigger handling, and per-tenant config client as the Node library.
Installation:
go get github.com/ProductifyFW/be-integrations/go # core (net/http)
go get github.com/ProductifyFW/be-integrations/go/gin # Gin adapterQuick Start:
import (
"net/http"
productify "github.com/ProductifyFW/be-integrations/go"
)
func main() {
mw := productify.Middleware(productify.MiddlewareConfig{
ProxySharedSecret: os.Getenv("PROXY_SHARED_SECRET"), // planned; strict would auto-enable when set
})
mux := http.NewServeMux()
mux.HandleFunc("/api/profile", func(w http.ResponseWriter, r *http.Request) {
ctx, _ := productify.FromContext(r.Context())
json.NewEncoder(w).Encode(map[string]any{
"user": ctx.User, "tenant": ctx.Tenant,
})
})
backend, err := productify.Register(context.Background(), productify.RegisterConfig{
ManagerURL: os.Getenv("MANAGER_URL"),
MachineUserToken: os.Getenv("MACHINE_USER_TOKEN"),
ApplicationID: os.Getenv("APPLICATION_ID"),
CallbackURL: os.Getenv("CALLBACK_URL"),
})
if err == nil {
defer backend.Close(context.Background())
} // registration failure is non-fatal: serve traffic, retry in background
http.ListenAndServe(":8080", mw(mux))
}Testing against a fake Manager:
import "github.com/ProductifyFW/be-integrations/go/managertest"
m := managertest.New(t) // register/heartbeat/config endpoints, scriptable failuresCommunications
Notifications and live data updates, shared by the backend and frontend SDKs.
Add Notifications to Your App
Durable, per-user notifications: your backend emits, a bell lights up over the live socket, and an unread notification falls back to email once the user's delivery window is open.
await comms.notifications.emit(
{ userIds: [order.customerId], tenantId: order.tenantId },
{ kind: "order.shipped", title: "Your order shipped" },
);Live Data Updates
A lossy, at-most-once hint channel for "something you are looking at changed". Read the D7 contract before publishing anything — payloads are visible to every tenant member with access to the app.
comms.subscribe("orders", () => refetchOrders());Architecture
Data Flow
┌─────────┐ ┌───────┐ ┌─────────┐ ┌─────────┐
│ Client │─────▶│ Proxy │─────▶│ Backend │─────▶│ Manager │
└─────────┘ └───────┘ └─────────┘ └─────────┘
│ │ │ │
│ Auth Token X-Headers Config
│ │ │ API
│ ▼ │ │
│ Validate Extract Fetch
│ │ │ │
│ ▼ ▼ ▼
│ User/Tenant req.productify /frontend-config
│ │ │
│ ▼ │
└──────▶ window.__PRODUCTIFY__ │
│ │
▼ ▼
@productifyfw/core @productifyfw/node-expressFrontend (Browser)
- Proxy injects
window.__PRODUCTIFY__into HTML - Application loads and accesses via
@productifyfw/core - Type-safe access to user, tenant, and configuration
Backend (Server)
- Proxy forwards requests with authentication headers
- Express middleware extracts headers into
req.productify - Route handlers access authenticated context
Type Consistency
Both packages use types that map to the same proxy structures:
| Proxy Source | Frontend Type | Backend Type |
|---|---|---|
| X-User-ID | user.id | user.id |
| X-User-Email | user.email | user.email |
| X-User-Name | user.name | user.name |
| X-User-Username | user.username | user.username |
| X-User-Picture ¹ | user.picture | user.picture |
| X-Tenant-ID | tenant.id | tenant.id |
| X-Tenant-Name | tenant.name | tenant.name |
| X-User-Tenant-Role ¹ | tenantRole | tenantRole |
| X-User-Tenant-Permissions ¹ | tenantPermissions | tenantPermissions |
| X-Auth-Type | authType | authType |
| Manager API | config | - |
¹ Opt-in per application (forward_role setting, default off). X-User-Tenant-Role is the membership role name (project-configurable — not a stable contract); gate on X-User-Tenant-Permissions (fixed vocabulary) instead. window.__PRODUCTIFY__ also exposes available_tenants: [{id, name}] for building an in-app tenant switcher. All three opt-in headers are on the proxy's sanitizer strip list. See Access Model and Multi-tenancy.
Security Considerations
Frontend
- Configuration is injected by trusted proxy
- No sensitive data should be in frontend config
- Use for UI preferences and feature flags only
Backend
- Headers are injected after authentication
- Headers can be trusted - they come from validated tokens
- Always validate permissions for actions
- Never rely on client-side checks alone
- Implement tenant isolation in database queries
Development Workflow
Setting Up Local Development
- Start Proxy with authentication enabled
- Configure Application ID in proxy
- Add Middleware to backend
- Access Context in frontend and backend
Testing
Both packages include comprehensive test suites:
Frontend:
cd fe-integrations/packages/core
pnpm testBackend:
cd be-integrations/packages/node-express
pnpm testCommon Patterns
Full-Stack Type Safety
// Shared types (optional)
interface AppConfig {
theme: "light" | "dark";
maxItems: number;
features: string[];
}
// Frontend
const config = pfy.getConfig<AppConfig>("ui");
// Backend
const { user, tenant } = req.productify;Permission Checking
// Frontend (UI only)
if (pfy.getConfig<boolean>("features.admin")) {
// Show admin UI
}
// Backend (actual permission check)
app.get("/api/admin", requireProductify, async (req, res) => {
const hasPermission = await checkPermission(req.productify.user.id, "admin");
if (!hasPermission) {
return res.status(403).json({ error: "Forbidden" });
}
// Process admin request
});Monorepo Structure
Both integration libraries use pnpm workspaces for monorepo management:
What exists today:
fe-integrations/
└── packages/
├── core/ # @productifyfw/core (framework-agnostic)
└── vue3-example/ # example app, not a published binding
be-integrations/
└── packages/
├── node-express/ # @productifyfw/node-express
└── node-express-example/Planned, but not implemented: @productifyfw/vue, @productifyfw/react, @productifyfw/testing, and the Go library (go/, go/gin/, go/managertest/). This structure allows for additional platform-specific packages while sharing common configurations.