UNPKG

3.88 kBPlain TextView Raw
1import express, { Express } from 'express';
2import { Application as FeathersApplication, defaultServiceMethods } from '@feathersjs/feathers';
3import { routing } from '@feathersjs/transport-commons';
4import { createDebug } from '@feathersjs/commons';
5
6import { Application } from './declarations';
7
8export { default as original, static, static as serveStatic, json, raw, text, urlencoded, query } from 'express';
9
10export * from './authentication';
11export * from './declarations';
12export * from './handlers';
13export * from './rest';
14
15const debug = createDebug('@feathersjs/express');
16
17export default function feathersExpress<S = any, C = any> (feathersApp?: FeathersApplication<S, C>, expressApp: Express = express()): Application<S, C> {
18 if (!feathersApp) {
19 return expressApp as any;
20 }
21
22 if (typeof feathersApp.setup !== 'function') {
23 throw new Error('@feathersjs/express requires a valid Feathers application instance');
24 }
25
26 const app = expressApp as any as Application<S, C>;
27 const { use: expressUse, listen: expressListen } = expressApp as any;
28 const { use: feathersUse, teardown: feathersTeardown } = feathersApp;
29
30 Object.assign(app, {
31 use (location: string & keyof S, ...rest: any[]) {
32 let service: any;
33 let options = {};
34
35 const middleware = rest.reduce(function (middleware, arg) {
36 if (typeof arg === 'function' || Array.isArray(arg)) {
37 middleware[service ? 'after' : 'before'].push(arg);
38 } else if (!service) {
39 service = arg;
40 } else if (arg.methods || arg.events) {
41 options = arg;
42 } else {
43 throw new Error('Invalid options passed to app.use');
44 }
45 return middleware;
46 }, {
47 before: [],
48 after: []
49 });
50
51 const hasMethod = (methods: string[]) => methods.some(name =>
52 (service && typeof service[name] === 'function')
53 );
54
55 // Check for service (any object with at least one service method)
56 if (hasMethod(['handle', 'set']) || !hasMethod(defaultServiceMethods)) {
57 debug('Passing app.use call to Express app');
58 return expressUse.call(this, location, ...rest);
59 }
60
61 debug('Registering service with middleware', middleware);
62 // Since this is a service, call Feathers `.use`
63 feathersUse.call(this, location, service, {
64 ...options,
65 express: middleware
66 });
67
68 return this;
69 },
70
71 async listen (...args: any[]) {
72 const server = expressListen.call(this, ...args);
73
74 this.server = server;
75 await this.setup(server);
76 debug('Feathers application listening');
77
78 return server;
79 },
80
81 async teardown (server?: any) {
82 return feathersTeardown.call(this, server).then(() =>
83 new Promise((resolve, reject) => this.server.close(e => e ? reject(e) : resolve(this)))
84 );
85 }
86 } as Application<S, C>);
87
88 const appDescriptors = {
89 ...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(app)),
90 ...Object.getOwnPropertyDescriptors(app)
91 };
92 const newDescriptors = {
93 ...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(feathersApp)),
94 ...Object.getOwnPropertyDescriptors(feathersApp)
95 };
96
97 // Copy all non-existing properties (including non-enumerables)
98 // that don't already exist on the Express app
99 Object.keys(newDescriptors).forEach(prop => {
100 const appProp = appDescriptors[prop];
101 const newProp = newDescriptors[prop];
102
103 if (appProp === undefined && newProp !== undefined) {
104 Object.defineProperty(expressApp, prop, newProp);
105 }
106 });
107
108 app.configure(routing() as any);
109 app.use((req, _res, next) => {
110 req.feathers = { ...req.feathers, provider: 'rest' };
111 return next();
112 });
113
114 return app;
115}
116
117if (typeof module !== 'undefined') {
118 module.exports = Object.assign(feathersExpress, module.exports);
119}