Introducing Harmony: Unified JavaScript Platform for Building Composable Frontends and Backends | by Mike Chen | Feb, 2025
We can start by creating a Harmony platform for the application. Then we can register the required runtimes (e.g BrowserRuntime , NodeJSRuntime ) which are directly supported by the platform.
export const MyApp = HarmonyPlatform.from({
name: ‘my-app’,
platform: [
SymphonyPlatformAspect,
{
name: ‘MyApp’,
slogan: ‘Platform’,
logo: ‘https://static.bit.dev/extensions-icons/wayne.svg’,
},
],
runtimes: [new BrowserRuntime(), new NodeJSRuntime()],
aspects: [MyAppAspect, KubernetesAspect],
});
Choosing runtimes is optional. For example, if you are building purely an API or a CLI application, you can opt-out the BrowserRuntime.
Symphony Platform Aspect: A ready-made aspect built by the Bit team. It acts as the platform’s foundation and gateway.
Then we can start building functional aspects and register them into the Harmony Platform.
Example: The “People” Aspect for Authentication
Let’s consider an authentication service built as a Harmony Aspect. Instead of having separate frontend and backend services, the People Aspect provides both:
- Frontend UI for login and user management
export class PeopleBrowser {
// …
// Declare this aspect’s dependencies (Harmony uses this to initialize the aspects at the right order)
static dependencies = [MyPlatformAspect];
// List the aspects to be injected into this aspect
static async provider([myPlatform]: [MyPlatformBrowser]) {
const people = new PeopleBrowser(config, menuItemSlot);
myPlatform.registerRoute([
{
path: ‘people’,
component: () => {
return ;
},
},
]);
// Registration via other aspects can be optional (depending on whether the aspect is part of the platform or not)
// Add a widget to the platform’s homepage
myPlatform?.registerPanel([
{
category: ‘People’,
component: () => {
return ;
},
},
]);
// Register a top-level component to wrap the platform with the authentication context
myPlatform?.registerTopLevelComponents({
component: ({ children }) => {
return {children};
},
});
// …
return people;
}
}
- Backend API for authentication and user data storage
// …
export class PeopleNode {
constructor(
private myPlatform: MyPlatformNode,
private userRepo: UserRepository
) {}
// …
async getCurrentUser(req: any): Promise {
// …
return User.from(user);
}
static dependencies = [SymphonyPlatformAspect, MyPlatformAspect];
static async provider([symphonyPlatform, myPlatform]: [
SymphonyPlatformNode,
MyPlatformNode
]) {
const userRepo = new UserRepository();
const people = new PeopleNode(myPlatform, userRepo);
const gqlSchema = createPeopleGqlSchema(people);
/**
* Register a GraphQL schema to the platform’s backend server.
*/
myPlatform.registerBackendServer([
{
routes: [],
gql: gqlSchema,
},
]);
/**
* Register to the platform’s middleware to intercept requests to the backend
* and attach the user to the request.
*/
symphonyPlatform.registerMiddlewares([
(req, _, next) => {
const user = people.getCurrentUser(req);
req.user = user;
next();
},
]);
return people;
}
}
export default PeopleNode;
You can see how we can create the Browser-related functionality of the People aspects and Node-related functionality of the people aspects in unification and inject them into the Harmony platform using dependency injection.