Skip to content

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:

ini
@productifyfw:registry=https://npm.pkg.github.com

No 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/vue and @productifyfw/react are planned but do not exist yet — use @productifyfw/core directly (Known Issues)

Installation:

bash
npm install @productifyfw/core

Quick Start:

typescript
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:

bash
npm install @productifyfw/node-express

Quick Start:

typescript
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:

bash
go get github.com/ProductifyFW/be-integrations/go        # core (net/http)
go get github.com/ProductifyFW/be-integrations/go/gin    # Gin adapter

Quick Start:

go
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:

go
import "github.com/ProductifyFW/be-integrations/go/managertest"

m := managertest.New(t) // register/heartbeat/config endpoints, scriptable failures

Communications

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.

ts
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.

ts
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-express

Frontend (Browser)

  1. Proxy injects window.__PRODUCTIFY__ into HTML
  2. Application loads and accesses via @productifyfw/core
  3. Type-safe access to user, tenant, and configuration

Backend (Server)

  1. Proxy forwards requests with authentication headers
  2. Express middleware extracts headers into req.productify
  3. Route handlers access authenticated context

Type Consistency

Both packages use types that map to the same proxy structures:

Proxy SourceFrontend TypeBackend Type
X-User-IDuser.iduser.id
X-User-Emailuser.emailuser.email
X-User-Nameuser.nameuser.name
X-User-Usernameuser.usernameuser.username
X-User-Picture ¹user.pictureuser.picture
X-Tenant-IDtenant.idtenant.id
X-Tenant-Nametenant.nametenant.name
X-User-Tenant-Role ¹tenantRoletenantRole
X-User-Tenant-Permissions ¹tenantPermissionstenantPermissions
X-Auth-TypeauthTypeauthType
Manager APIconfig-

¹ 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

  1. Start Proxy with authentication enabled
  2. Configure Application ID in proxy
  3. Add Middleware to backend
  4. Access Context in frontend and backend

Testing

Both packages include comprehensive test suites:

Frontend:

bash
cd fe-integrations/packages/core
pnpm test

Backend:

bash
cd be-integrations/packages/node-express
pnpm test

Common Patterns

Full-Stack Type Safety

typescript
// 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

typescript
// 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.

Further Reading