Skip to content

Instantly share code, notes, and snippets.

@andyjessop
Last active December 31, 2021 14:10
Show Gist options
  • Save andyjessop/adcf5c3fd185eb6dc48a2b31e543c007 to your computer and use it in GitHub Desktop.
Save andyjessop/adcf5c3fd185eb6dc48a2b31e543c007 to your computer and use it in GitHub Desktop.
import * as express from 'express';
import { Server } from 'http';
import { mathService } from './math-service';
import { CreateExpressApp } from './express-service';
export function createApp({ math }: { math: MathService }): CreateExpressApp<{ MathService }> {
const app = express();
let server: Server | null = null;
app.get('/api', async (req, res) => {
const added = await math.add({ a: 1, b: 2 });
res.json({added});
});
return {
start,
stop,
};
function start() {
return new Promise<void>(resolve => {
server = app.listen(Number(process.env.PORT), () => {
console.log(`Listening at http://localhost:${process.env.PORT}${process.env.API_PREFIX}`);
resolve();
});
});
}
function stop() {
await new Promise<string>((resolve, reject) => {
if (server?.listening) {
server.close(err => {
if (err) {
reject(`Server close error: ${err}`);
}
resolve('Server stopped successfully.');
});
}
});
server = null;
}
}
import 'express';
import Moleculer = require('moleculer');
export type CreateExpressApp<T> = (deps: T) => {
start: () => Promise<unknown>;
stop: () => Promise<unknown>;
};
export function moleculerExpress<T = unknown>({
createExpressApp, dependencies, ...rest, name,
}: {
createExpressApp: CreateExpressApp<T>;
dependencies?: string | Moleculer.ServiceDependency | (string | Moleculer.ServiceDependency)[];
name: string;
rest?: Moleculer.ServiceSchema
}) {
return function createService(broker: Moleculer.ServiceBroker) {
return broker.createService({
name,
dependencies,
created() {
// Add individual dependencies as empty objects to allow for deconstructing in express app
this.deps = this.schema.dependencies
.map((dep: string | Moleculer.ServiceDependency | (string | Moleculer.ServiceDependency)) => typeof dep === 'string' ? dep : dep.name)
.reduce((acc, cur) => {
acc[cur] = {};
return acc;
}, <Record<string, unknown>>{});
this.app = createExpressApp(this.deps);
},
async started(): Promise<void> {
const servicesList: Moleculer.Service[] = await broker.call("$node.services", { withActions: true });
servicesList
.filter(service => this.schema.dependencies
.map(dep => dep.name).includes(service.name)
)
.forEach(service => {
if (!service.name) {
return;
}
Object.keys(service.actions)
.forEach(fullActionName => {
const actionName = fullActionName.split('.')[1];
this.deps[service.name] = this.deps[service.name] || {};
this.deps[service.name][actionName] = async function(args: Record<string, unknown>) {
return broker.call(fullActionName, args);
}
});
});
this.app.start();
},
async stopped(): Promise<void> {
this.app.stop();
},
...rest,
});
}
}
import { ServiceBroker } from 'moleculer';
import { moleculerExpress } from './moleculer-express';
import { createApp } from './express-app';
import { mathService } from './math-service';
const broker = new ServiceBroker();
broker.createService(mathService);
// Create the express Gateway Factory
const createExpressGateway = moleculerExpress({
createExpressApp: createApp,
dependencies: [mathService.name],
name: 'api',
});
createExpressGateway(broker);
broker.start();
export interface MathService {
add: ({ a, b }: { a: number, b: number }) => number;
}
export const mathService = {
name: 'math',
actions: {
add(ctx) {
return Number(ctx.params.a) + Number(ctx.params.b);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment