UNPKG

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