UNPKG

4.63 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'
5import cors from 'cors'
6import compression from 'compression'
7
8import { rest, RestOptions, formatter } from './rest'
9import { errorHandler, notFound, ErrorHandlerOptions } from './handlers'
10import { Application, ExpressOverrides } from './declarations'
11import { AuthenticationSettings, authenticate, parseAuthentication } from './authentication'
12import { default as original, static as serveStatic, json, raw, text, urlencoded, query } from 'express'
13
14export {
15 original,
16 serveStatic,
17 serveStatic as static,
18 json,
19 raw,
20 text,
21 urlencoded,
22 query,
23 rest,
24 RestOptions,
25 formatter,
26 errorHandler,
27 notFound,
28 Application,
29 ErrorHandlerOptions,
30 ExpressOverrides,
31 AuthenticationSettings,
32 parseAuthentication,
33 authenticate,
34 cors,
35 compression
36}
37
38const debug = createDebug('@feathersjs/express')
39
40export default function feathersExpress<S = any, C = any>(
41 feathersApp?: FeathersApplication<S, C>,
42 expressApp: Express = express()
43): Application<S, C> {
44 if (!feathersApp) {
45 return expressApp as any
46 }
47
48 if (typeof feathersApp.setup !== 'function') {
49 throw new Error('@feathersjs/express requires a valid Feathers application instance')
50 }
51
52 const app = expressApp as any as Application<S, C>
53 const { use: expressUse, listen: expressListen } = expressApp as any
54 const { use: feathersUse, teardown: feathersTeardown } = feathersApp
55
56 Object.assign(app, {
57 use(location: string & keyof S, ...rest: any[]) {
58 let service: any
59 let options = {}
60
61 const middleware = rest.reduce(
62 function (middleware, arg) {
63 if (typeof arg === 'function' || Array.isArray(arg)) {
64 middleware[service ? 'after' : 'before'].push(arg)
65 } else if (!service) {
66 service = arg
67 } else if (arg.methods || arg.events || arg.express || arg.koa) {
68 options = arg
69 } else {
70 throw new Error('Invalid options passed to app.use')
71 }
72 return middleware
73 },
74 {
75 before: [],
76 after: []
77 }
78 )
79
80 const hasMethod = (methods: string[]) =>
81 methods.some((name) => service && typeof service[name] === 'function')
82
83 // Check for service (any object with at least one service method)
84 if (hasMethod(['handle', 'set']) || !hasMethod(defaultServiceMethods)) {
85 debug('Passing app.use call to Express app')
86 return expressUse.call(this, location, ...rest)
87 }
88
89 debug('Registering service with middleware', middleware)
90 // Since this is a service, call Feathers `.use`
91 feathersUse.call(this, location, service, {
92 express: middleware,
93 ...options
94 })
95
96 return this
97 },
98
99 async listen(...args: any[]) {
100 const server = expressListen.call(this, ...args)
101
102 this.server = server
103 await this.setup(server)
104 debug('Feathers application listening')
105
106 return server
107 }
108 } as Application<S, C>)
109
110 const appDescriptors = {
111 ...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(app)),
112 ...Object.getOwnPropertyDescriptors(app)
113 }
114 const newDescriptors = {
115 ...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(feathersApp)),
116 ...Object.getOwnPropertyDescriptors(feathersApp)
117 }
118
119 // Copy all non-existing properties (including non-enumerables)
120 // that don't already exist on the Express app
121 Object.keys(newDescriptors).forEach((prop) => {
122 const appProp = appDescriptors[prop]
123 const newProp = newDescriptors[prop]
124
125 if (appProp === undefined && newProp !== undefined) {
126 Object.defineProperty(expressApp, prop, newProp)
127 }
128 })
129
130 // Assign teardown and setup which will also make sure that hooks are initialized
131 app.setup = feathersApp.setup as any
132 app.teardown = async function teardown(server?: any) {
133 return feathersTeardown.call(this, server).then(
134 () =>
135 new Promise((resolve, reject) => {
136 if (this.server) {
137 this.server.close((e) => (e ? reject(e) : resolve(this)))
138 } else {
139 resolve(this)
140 }
141 })
142 )
143 }
144
145 app.configure(routing() as any)
146 app.use((req, _res, next) => {
147 req.feathers = { ...req.feathers, provider: 'rest' }
148 return next()
149 })
150
151 return app
152}
153
154if (typeof module !== 'undefined') {
155 module.exports = Object.assign(feathersExpress, module.exports)
156}