Skip to content

Commit ba80649

Browse files
committed
feat(express, koa): make transports similar
1 parent ee67131 commit ba80649

File tree

24 files changed

+400
-336
lines changed

24 files changed

+400
-336
lines changed

packages/authentication/src/hooks/authenticate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const debug = createDebug('@feathersjs/authentication/hooks/authenticate');
88

99
export interface AuthenticateHookSettings {
1010
service?: string;
11-
strategies: string[];
11+
strategies?: string[];
1212
}
1313

1414
export default (originalSettings: string | AuthenticateHookSettings, ...originalStrategies: string[]) => {

packages/client/test/fixture.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,18 @@ class TodoService extends Service {
3131

3232
export default (configurer?: (app: Application) => void) => {
3333
const app = express.default(feathers())
34+
// Parse HTTP bodies
35+
.use(express.json())
36+
.use(express.urlencoded({ extended: true }))
37+
// Host the current directory (for index.html)
38+
.use(express.static(process.cwd()))
3439
.configure(express.rest());
3540

3641
if (typeof configurer === 'function') {
3742
configurer.call(app, app);
3843
}
3944

40-
// Parse HTTP bodies
41-
app.use(express.json())
42-
.use(express.urlencoded({ extended: true }))
43-
// Host the current directory (for index.html)
44-
.use(express.static(process.cwd()))
45+
app
4546
// Host our Todos service on the /todos path
4647
.use('/todos', new TodoService({
4748
multi: true

packages/express/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,16 @@
4949
"access": "public"
5050
},
5151
"dependencies": {
52+
"@feathersjs/authentication": "^5.0.0-pre.14",
5253
"@feathersjs/commons": "^5.0.0-pre.14",
5354
"@feathersjs/errors": "^5.0.0-pre.14",
5455
"@feathersjs/feathers": "^5.0.0-pre.14",
5556
"@feathersjs/transport-commons": "^5.0.0-pre.14",
5657
"@types/express": "^4.17.13",
5758
"@types/express-serve-static-core": "^4.17.24",
58-
"express": "^4.17.1",
59-
"lodash": "^4.17.21"
59+
"express": "^4.17.1"
6060
},
6161
"devDependencies": {
62-
"@feathersjs/authentication": "^5.0.0-pre.14",
6362
"@feathersjs/authentication-local": "^5.0.0-pre.14",
6463
"@feathersjs/tests": "^5.0.0-pre.14",
6564
"@types/lodash": "^4.14.176",
Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,61 @@
1+
import { RequestHandler, Request, Response } from 'express';
2+
import { HookContext } from '@feathersjs/feathers';
13
import { createDebug } from '@feathersjs/commons';
2-
import { merge, flatten } from 'lodash';
3-
import { NextFunction, RequestHandler, Request, Response } from 'express';
4+
import { authenticate as AuthenticateHook } from '@feathersjs/authentication';
5+
6+
import { Application } from './declarations';
47

58
const debug = createDebug('@feathersjs/express/authentication');
69

7-
type StrategyOptions = {
8-
service?: string;
9-
strategies: string[]
10+
const toHandler = (func: (req: Request, res: Response, next: () => void) => Promise<void>): RequestHandler => {
11+
return (req, res, next) => func(req, res, next).catch(error => next(error));
1012
};
1113

12-
const normalizeStrategy = (_settings: string|StrategyOptions, ..._strategies: string[]) =>
13-
typeof _settings === 'string'
14-
? { strategies: flatten([ _settings, ..._strategies ]) }
15-
: _settings;
14+
export type AuthenticationSettings = {
15+
service?: string;
16+
strategies?: string[];
17+
};
1618

17-
export function parseAuthentication (settings: any = {}): RequestHandler {
18-
return function (req, res, next) {
19-
const app = req.app as any;
20-
const service = app.defaultAuthentication ? app.defaultAuthentication(settings.service) : null;
19+
export function parseAuthentication (settings: AuthenticationSettings = {}): RequestHandler {
20+
return toHandler(async (req, res, next) => {
21+
const app = req.app as any as Application;
22+
const service = app.defaultAuthentication?.(settings.service);
2123

22-
if (service === null) {
24+
if (!service) {
2325
return next();
2426
}
2527

2628
const config = service.configuration;
27-
const authStrategies = config.parseStrategies || config.authStrategies || [];
29+
const authStrategies = settings.strategies || config.parseStrategies || config.authStrategies || [];
2830

2931
if (authStrategies.length === 0) {
3032
debug('No `authStrategies` or `parseStrategies` found in authentication configuration');
3133
return next();
3234
}
3335

34-
service.parse(req, res, ...authStrategies)
35-
.then((authentication: any) => {
36-
if (authentication) {
37-
debug('Parsed authentication from HTTP header', authentication);
38-
merge(req, {
39-
authentication,
40-
feathers: { authentication }
41-
});
42-
}
43-
44-
next();
45-
}).catch(next);
46-
};
47-
}
36+
const authentication = await service.parse(req, res, ...authStrategies)
37+
38+
if (authentication) {
39+
debug('Parsed authentication from HTTP header', authentication);
40+
req.feathers = { ...req.feathers, authentication };
41+
}
4842

49-
export function authenticate (_settings: string|StrategyOptions, ..._strategies: string[]) {
50-
const settings = normalizeStrategy(_settings, ..._strategies);
43+
return next();
44+
});
45+
}
5146

52-
if (!Array.isArray(settings.strategies) || settings.strategies.length === 0) {
53-
throw new Error('\'authenticate\' middleware requires at least one strategy name');
54-
}
47+
export function authenticate (settings: string | AuthenticationSettings, ...strategies: string[]): RequestHandler {
48+
const hook = AuthenticateHook(settings, ...strategies);
5549

56-
return (_req: Request, _res: Response, next: NextFunction) => {
57-
const req = _req as any;
58-
const { app, authentication } = req;
59-
const service = app.defaultAuthentication(settings.service);
50+
return toHandler(async (req, _res, next) => {
51+
const app = req.app as any as Application;
52+
const params = req.feathers;
53+
const context = { app, params } as any as HookContext;
6054

61-
debug('Authenticating with Express middleware and strategies', settings.strategies);
55+
await hook(context);
6256

63-
service.authenticate(authentication, req.feathers, ...settings.strategies)
64-
.then((authResult: any) => {
65-
debug('Merging request with', authResult);
66-
merge(req, authResult);
57+
req.feathers = context.params;
6758

68-
next();
69-
}).catch(next);
70-
};
59+
return next();
60+
});
7161
}

packages/express/src/declarations.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import http from 'http';
22
import express, { Express } from 'express';
33
import {
44
Application as FeathersApplication, Params as FeathersParams,
5-
HookContext, ServiceMethods, ServiceInterface
5+
HookContext, ServiceMethods, ServiceInterface, RouteLookup
66
} from '@feathersjs/feathers';
77

88
interface ExpressUseHandler<T, Services> {
@@ -33,28 +33,30 @@ export type Application<Services = any, Settings = any> =
3333

3434
declare module '@feathersjs/feathers/lib/declarations' {
3535
interface ServiceOptions {
36-
middleware?: {
37-
before: express.RequestHandler[],
38-
after: express.RequestHandler[]
39-
}
36+
express?: {
37+
before?: express.RequestHandler[];
38+
after?: express.RequestHandler[];
39+
composed?: express.RequestHandler;
40+
};
4041
}
4142
}
4243

4344
declare module 'express-serve-static-core' {
4445
interface Request {
45-
feathers?: Partial<FeathersParams>;
46+
feathers?: Partial<FeathersParams>;
47+
lookup?: RouteLookup;
4648
}
4749

4850
interface Response {
49-
data?: any;
50-
hook?: HookContext;
51+
data?: any;
52+
hook?: HookContext;
5153
}
5254

5355
interface IRouterMatcher<T> {
54-
// eslint-disable-next-line
55-
<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any>(
56-
path: PathParams,
57-
...handlers: (RequestHandler<P, ResBody, ReqBody> | Partial<ServiceMethods> | Application)[]
58-
): T;
56+
// eslint-disable-next-line
57+
<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any>(
58+
path: PathParams,
59+
...handlers: (RequestHandler<P, ResBody, ReqBody> | Partial<ServiceMethods> | Application)[]
60+
): T;
5961
}
6062
}

packages/express/src/handlers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ export function notFound ({ verbose = false } = {}): RequestHandler {
1818
}
1919

2020
export type ErrorHandlerOptions = {
21-
public?: string,
22-
logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void },
23-
html?: any,
24-
json?: any
21+
public?: string;
22+
logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void };
23+
html?: any;
24+
json?: any;
2525
};
2626

2727
export function errorHandler (_options: ErrorHandlerOptions = {}): ErrorRequestHandler {

packages/express/src/index.ts

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
1-
import express, {
2-
Express, static as _static, json, raw, text, urlencoded, query
3-
} from 'express';
4-
import {
5-
Application as FeathersApplication, defaultServiceMethods
6-
} from '@feathersjs/feathers';
1+
import express, { Express } from 'express';
2+
import { Application as FeathersApplication, defaultServiceMethods } from '@feathersjs/feathers';
3+
import { routing } from '@feathersjs/transport-commons';
74
import { createDebug } from '@feathersjs/commons';
85

96
import { Application } from './declarations';
10-
import { errorHandler, notFound } from './handlers';
11-
import { parseAuthentication, authenticate } from './authentication';
127

13-
export {
14-
_static as serveStatic, _static as static, json, raw, text,
15-
urlencoded, query, errorHandler, notFound, express as original,
16-
authenticate, parseAuthentication
17-
};
8+
export { default as original, static, static as serveStatic, json, raw, text, urlencoded, query } from 'express';
189

19-
export * from './rest';
10+
export * from './authentication';
2011
export * from './declarations';
12+
export * from './handlers';
13+
export * from './rest';
2114

2215
const debug = createDebug('@feathersjs/express');
2316

@@ -30,10 +23,12 @@ export default function feathersExpress<S = any, C = any> (feathersApp?: Feather
3023
throw new Error('@feathersjs/express requires a valid Feathers application instance');
3124
}
3225

33-
const { use, listen } = expressApp as any;
34-
// A mixin that provides the extended functionality
35-
const mixin: any = {
36-
use (location: string, ...rest: any[]) {
26+
const app = expressApp as any as Application<S, C>;
27+
const { use: expressUse, listen: expressListen } = expressApp as any;
28+
const feathersUse = feathersApp.use;
29+
30+
Object.assign(app, {
31+
use (location: string & keyof S, ...rest: any[]) {
3732
let service: any;
3833
let options = {};
3934

@@ -60,46 +55,56 @@ export default function feathersExpress<S = any, C = any> (feathersApp?: Feather
6055
// Check for service (any object with at least one service method)
6156
if (hasMethod(['handle', 'set']) || !hasMethod(defaultServiceMethods)) {
6257
debug('Passing app.use call to Express app');
63-
return use.call(this, location, ...rest);
58+
return expressUse.call(this, location, ...rest);
6459
}
6560

6661
debug('Registering service with middleware', middleware);
6762
// Since this is a service, call Feathers `.use`
68-
(feathersApp as FeathersApplication).use.call(this, location, service, {
63+
feathersUse.call(this, location, service, {
6964
...options,
70-
middleware
65+
express: middleware
7166
});
7267

7368
return this;
7469
},
7570

7671
async listen (...args: any[]) {
77-
const server = listen.call(this, ...args);
72+
const server = expressListen.call(this, ...args);
7873

7974
await this.setup(server);
8075
debug('Feathers application listening');
8176

8277
return server;
8378
}
84-
};
79+
} as Application<S, C>);
8580

86-
const feathersDescriptors = {
81+
const appDescriptors = {
82+
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(app)),
83+
...Object.getOwnPropertyDescriptors(app)
84+
};
85+
const newDescriptors = {
8786
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(feathersApp)),
8887
...Object.getOwnPropertyDescriptors(feathersApp)
8988
};
9089

9190
// Copy all non-existing properties (including non-enumerables)
9291
// that don't already exist on the Express app
93-
Object.keys(feathersDescriptors).forEach(prop => {
94-
const feathersProp = feathersDescriptors[prop];
95-
const expressProp = Object.getOwnPropertyDescriptor(expressApp, prop);
92+
Object.keys(newDescriptors).forEach(prop => {
93+
const appProp = appDescriptors[prop];
94+
const newProp = newDescriptors[prop];
9695

97-
if (expressProp === undefined && feathersProp !== undefined) {
98-
Object.defineProperty(expressApp, prop, feathersProp);
96+
if (appProp === undefined && newProp !== undefined) {
97+
Object.defineProperty(expressApp, prop, newProp);
9998
}
10099
});
101100

102-
return Object.assign(expressApp, mixin);
101+
app.configure(routing() as any);
102+
app.use((req, _res, next) => {
103+
req.feathers = { ...req.feathers, provider: 'rest' };
104+
return next();
105+
});
106+
107+
return app;
103108
}
104109

105110
if (typeof module !== 'undefined') {

0 commit comments

Comments
 (0)