serhiisol / node-decorators Goto Github PK
View Code? Open in Web Editor NEWnode-decorators
License: MIT License
node-decorators
License: MIT License
It will be nice to have a possibility to set route middleware as a class which implements an interface.
export interface MiddlewareInterface {
use(): void;
}
node-decorators/express/src/express.ts
Line 44 in 69047a8
Would it be better to have a parameter for the option "mergeParams"?
const router: Router = Router({ mergeParams: true });
because i found trouble to have a controller like this
@Injectable()
@Controller('/:id/abc')
export default class Abc {}
where req.params.id === undefined on any method
Decorators - function. In JS all function write in lowercase. Why method/parameter/property names decorator is start from capital symbol?
p.s. Class decorators need to start from capital, because class first symbol - capital. Also this notation help other developer to know - what is class decorator and what is method/property/parameter decorator
Example:
@Injectable() // capital, because class decorator
class SomeClass{
@someProperty() public readonly myInjection; // start from lowercase, because this is property
@someMethod() // start from lowercase, because this is method
public method(@someParameter() myParameter: any){} // start from lowercase, because this is parameter
}
Im looking for passing parameters to a middleware.
maybe a code example would be more self explaining
@Get('/hello', [
new ValidationMiddleware({
rules: [], // rules for the current route
}),
])
hello(
@Response() res: express.Response,
@Query('word') word: string,
) {
}
i there a way to do somthing like that ?
I want to have a way to changes settings on my middleware on every routes.
Nice to have inject dependencies to Controllers by using external decorators.
For example:
@Injectable()
class MyService {
}
@Controller('/')
@Injectable()
class Controller {
constructor(@Inject() myService: MyService) {}
}
`
@controller('/')
class Controller {
@Connection()
public async onConnection(@Socket socket: SocketIO.Socket) {
console.log('Connected' + socket.id);
}
@Disconnect()
public async onDisconnect(@Socket socket: SocketIO.Socket) {
console.log('Disconnected' + socket.id);
}
}
`
socket.id return "null".
It always return Socket Server instance.
I'm wrong?
Hey guys,
What version of Socket.IO is this lib intended to support?
I personally don't really care much about the DI of node-decorators and would like to inject my dependencies manually like so:
@Controller("/hello")
export class HelloController {
private someThing: any ;
constructor(dependency: any) {
this.someThing = dependency;
}
@Get("/world")
async create(@Res() response: Response) {
return response.send(this.someThing.foo());
}
}
attachControllers(app, [ new HelloController({ foo: () => ({yay: 42 })) ]);
If you really don't want to support such a thing, then some guidance on how I can achieve this using the DI package would be appreciated.
Thanks for this project, solves my main issue with the mongoose/typescript combination.
Just wondering how I can add a pre-save hook, I can't get it to work like this and I'm not sure how else to do it:
export type UserType = UserClass & mongoose.Document
export type UserModel = mongoose.Model<UserType>
export const User = model<UserType>({ provide: UserClass })
export const UserSchema = schema({ provide: UserClass })
UserSchema.pre('save', async function (next) {
})
New possible Decorators:
TBD
Mongoose provides different context to the model, so regular DI doesn't work properly. Need to find a way to provide deps properly
Hi,
Is it possible to use standard async middleware functions in the array of middleware’s for @controller or @RestMethod e.g. @get?
if so, do you have an example?
thanks
Hi! First of all, thanks for the work, simply amazing! I've using it on my personal projects and I am think to use it on company's projects too. But I am afraid that there is no recent updates, so I really would like to know if you guys pretend to continue this project. If you do, I'll be more than happy to contribute 😄
As title, it would be nice :)
Hi, i was trying to create a server with express and the node-decorators module starting from your example, i noticed that if a second http endpoint is created in another file it is not found and i get the 404 error.
I hope I managed to explain myself well.
This is my index.ts and entrypoint file:
import express, { Express, Request } from 'express';
import { Response, Params, Controller, Get, attachControllers } from '@decorators/express';
import { Injectable } from '@decorators/di';
@Controller('/')
@Injectable()
class UsersController {
// constructor(userService: UserService) {}
@Get('/users/:id')
getData(@Response() res:any, @Params('id') id: string) {
// res.send(this.userService.findById(id));
console.log(typeof(res));
res.send("Ciao");
}
}
let app: Express = express();
attachControllers(app, [UsersController]);
app.listen(3000);
This is my second file where there is another endpoint:
import express, { Express, Request } from 'express';
import { Response, Params, Controller, Get, attachControllers } from '@decorators/express';
import { Injectable } from '@decorators/di';
@Controller('/')
@Injectable()
class UsersController {
// constructor(userService: UserService) {}
@Get('/hello/')
getData(@Response() res:any) {
// res.send(this.userService.findById(id));
console.log(typeof(res));
res.send("hello");
}
}
Hello,
I use your library in my project and everything works but I receive type errors.
controller class:
import { Inject, Injectable } from '@decorators/di';
import { Ack, Args, Controller, Event } from '@decorators/socket/src';
import { logger } from '../../../logger';
import { StateService } from '../../../state/service';
@Injectable()
@Controller('/')
export class DoorController {
constructor(@Inject(StateService) private stateService: StateService) { }
@Event('api/door')
getDoor(@Args() message, @Ack() callback) {
logger.debug(`api/door: ${message.groupAddress}`);
callback(this.stateService.getDoorOpener(message.groupAddress));
}
}
Error:
src\api\door\controller\index.ts(12,4): error TS2345: Argument of type 'DoorController' is not assignable to parameter of type 'SocketClass'.
Property '__socket_meta__' is missing in type 'DoorController'.
In my index.ts I attach all controllers.
Any ideas how to fix this issue?
BR Jakob
Seems like if I create async controller function any thrown error will result in a swallowed exception and won't be caught by the global error middleware unless I explicitly wrap it in try/catch block.
@Controller('/users', [ AuthMiddleware ])
export class UserController {
@Get('/:id')
async getUser(@Request() req: Request @Response() res: IAppResponse) {
throw new Error('Error occurred');
res.send('Hello World');
}
}
Possible solution would be in express.ts
file under routeHandler
function
const handler = controller[methodName].apply(controller, args);
if (handler instanceof Promise || handler instanceof global.Promise) {
handler.catch(e => {
next(e);
});
} else {
return handler;
}
We test for instance of promise and then catch possible errors and pass them to the global error middleware.
global.Promise used to support user custom promise libraries.
What do you think ?
Hello, I'm trying to use your express decorators with inversifyjs.
The problem that i face off is in attachControllers
method.
So, I have the service class with registered in intersifyjs container, plus I have controller which looks like this
import { Controller, Get } from '@decorators/express';
import { Container } from 'inversify';
...
import { GoogleCloudDataStorageService } from '../services/GoogleCloudDataStorageService';
import { TYPES } from '../types';
declare var app: any;
@Controller('/car')
export class CarController {
@Get('/:id')
public async getCarAction(req: RequestInterface, res: ResponseInterface) {
const googleCloudDataStoreService = (app.get('container') as Container).get<GoogleCloudDataStorageService>(TYPES.GoogleCloudDataStorageService);
const result = await googleCloudDataStoreService.sayHello();
return res.status(HttpStatus.OK).json({
message: result,
});
}
}
In my app.ts
file I'm registering controller like this
...dependecies
import { attachControllers } from '@decorators/express';
import { CarController } from './controllers/CarController';
...
const apiRouter = express.Router();
attachControllers(app, [CarController]);
Then I get the error from inversify container
Missing required @injectable annotation in: GoogleCloudDataStorageService.
However if I write example route like this
app.use('/api/v2', async (req, res) => {
try {
const googleCloudDataStoreService = container.get<GoogleCloudDataStorageService>(TYPES.GoogleCloudDataStorageService);
const result = await googleCloudDataStoreService.sayHello();
return res.json({ a: result });
} catch (error) {
console.error(error);
}
});
And remove attaching controllers function - this route will works normal.
After few hours of monkey clicking i found that your @decorators/express
somehow rewrites the reflect-metadata
which I'm using for inversifyjs
and clears the metadata for my classes.
So the solution is to replace importing reflect-metadata
, on the beginning of the project, with your @decorators/express
.
Maybe you could add some checking for it? Or maybe there's better solution to do it?
Thank you.
Hi, the DI module throws RecursiveProviderError
when there is no recursion.
For example in the following dep tree, trying to resolve ModA it says: DI recursive dependency: ModA => ModB => ModD => Config => ModC => Config.
The problem is at line 26 of container.ts, you push every provider in the same array. Creating a new array each step (simulating a tree structure) should solve the problem.
The following code works for me.
private static resolveProvider(provider: StoreProvider, requesters: StoreProvider[] = []): any {
if (provider.value) {
return provider.value;
}
const _requesters = requesters.concat([provider]);
const deps = provider
.deps.map((dep: Dependency) => {
const requesterProvider: StoreProvider =
_requesters.find((requester: StoreProvider) => requester.id === dep.id);
if (requesterProvider) {
throw new RecursiveProviderError(_requesters, requesterProvider);
}
const depService: StoreProvider = Store.findProvider(dep.id);
if (!depService && !dep.optional) {
throw new MissingProviderError(provider, dep);
}
if (!depService && dep.optional) {
return null;
}
return this.resolveProvider(depService, _requesters);
});
provider.value = provider.factory ?
provider.factory(...deps) : new provider.type(...deps);
return provider.value;
}
If you prefer i can submit a PR.
I was waiting for a good node DI for years, great work!
I am bit confused , how do i implement socket io decorators with express decorators i have the following code can you please provide some examples to implement socket and express decorators
import { LandingController } from "@controllers/LandingController";
import { MessageController } from "@controllers/MessageController";
import { attachControllers as ExpressAttachControllers } from "@decorators/express";
import { attachControllers as SocketAttachControllers } from "@decorators/socket";
import express from "express";
import { Server } from "socket.io";
const ExpressApp = express();
const ExpressControllers = [LandingController];
const SocketControllers = [MessageController];
ExpressAttachControllers(ExpressApp, ExpressControllers);
const server = ExpressApp.listen(3000);
const io = new Server(server);
SocketAttachControllers(io, SocketControllers);
It throws following error
MissingProviderError:
In order to get DI working, you have to provide Injectable.
DI attempt for MessageController.
Hi,
Is there any way to create multiple controllers for the same namespace?
When I create two controllers with @controller("/") the message events are not registered.
What do you think about adding the ability to attach controllers not only to the app but to Express Router as well?
This is useful when you want to create a namespace for your controllers, ie:
/v1/api
we can allow passing a router instance or app instance to the current attachControllers method. Since both use the .use
method to attach endpoints.
This works just fine so maybe just:
const apiRouter = express.Router();
app.use('/v1/api', apiRouter);
attachControllers(apiRouter as any, [
...APP_CONTROLLERS
]);
attachControllers([
{use: Controller, deps: [ CoolService ] }
])
How tu use this module to implements error middleware?
Recently found about swagger decorators made by nestjs framework.
https://docs.nestjs.com/recipes/swagger
IMO it provides a very clean API for annotating SwaggerAPI.
I think the main issue is the discovery of all the controller endpoints, and then attaching all the relevant metadata to construct the swagger definitions.
What do you think? Took a quick look at nestjs implementation, and I think the implementation here will be pretty similar.
[ts]
Argument of type '(typeof TodosController)[]' is not assignable to parameter of type '(Injectable | ExpressClass)[]'.
Type 'typeof TodosController' is not assignable to type 'Injectable | ExpressClass'.
Type 'typeof TodosController' is not assignable to type 'ExpressClass'.
Property 'meta' is missing in type 'typeof TodosController'.
just followed docs
Firstly, Thanks for sharing this brilliant idea & your efforts putting this together.
After spent whole afternoon today, I could't get options provided in @Model
work in the generated mongoose schema. It seems to me that @Model
decorator doesn't work properly at the moment when setting SchemaTypeOpts
. What I assume is because the following reasons:
SchemaTypeOpts
has not been properly parsed into meta.options
. Please look into the following line from decorators/model.ts
mongooseMeta.options.push(key, options[key]);
# suggest to replace with following
mongooseMeta.options.push([key, options[key]]);
buildSchema
function for setting schema options. Refer to the following in mongoose.ts
meta.options.forEach((option: string) => {
schema.set(option, classInstance[option]);
});
# suggest to replace with following
meta.options.forEach(([name, opt]: [string, any]) => {
schema.set(name, opt);
});
Thanks for your anticipated help on this.
Hi, sorry for my noob question, but I'm trying to add DI follow the example in the DOC, but my userService is not found, this is the example code:
import {
Response, Params, Controller, Get,
attachControllers, Middleware,
} from '@decorators/express';
import { Injectable } from '@decorators/di';
@Injectable()
@Controller('/')
class UsersController {
constructor(userService: UserService) {}
@Get('/users/:id')
getData(@Response() res: any, @Params('id') id: string) {
res.send(userService.findById(id)); // **PROBLEM: userService not FOUND!!!**
}
}
@Injectable()
class UserService{
findById(id: string) {
}
}
I can't find the right solution.
It would be very useful to have options to validate or filter (mapping) socket or express payload.
It is possible to create injectable object without decorating a class ? I want to inject RestClient that is installed using NPM.
I want to do something like this
Container.register(RestClient, new RestClient("http://example.com"))
and then
@Injectable() export class SomeDao(){ constructor(private restClient: RestClient){} }
I'm able to register an error handler as follows. It works fine.
@Injectable()
class ErrorHandler implements ErrorMiddleware {
public use(error: Error, request: Request, response: Response, next: NextFunction) {
console.log('ErrorHandler.use()');
response.status(500).send(error.message);
}
}
Similarly, is there any provision for injection of response handler to process a response?
It should get invoked with the handler registered using @Next
in a controller method. All the routes should return through a common response handler.
Thanks in anticipation!
attachControllers([
{use: Controller, deps: [ CoolService ] }
])
Hello
I've got a problem with swapping between @Socket()
and @Ack
in socket
decorators.
Socket
is sometimes Ack
and Ack
becomes socket. Having function like this:
@Event(MESSAGE)
async onMessage(@Args() msg, @Socket() socket, @Ack() ack) {
console.log('type of socket: ', typeof socket);
console.log('type of ack: ', typeof ack);
}
Sending message, I'm getting an output
type of socket: object
type of ack: function
type of socket: function
type of ack: object
type of socket: object
type of ack: function
type of socket: function
type of ack: object
Hello,
in readme of mongoose, you see here :
export let TestModel = model(TestModelClass);
I think it's mistake. This should be :
export let AnimalModel = Animal.model(Animal);
and you not specify how import AbstractModel
form node-decorators/playground/mongoose/abstract.model.ts
:
import AbstractModel from ????
Regards,
I have two packages, "core" and "api". Core is api's dependency.
Core exposes an injection token that is meant to be used by the api.
Now, the issue is that since both packages depend on "@decorators/di", npm creates two separate instances of the package - one in node_modules insode api, the other one inside core. Therefore, api's DI container is unable to make use of core's injectables.
Is there a way to make the two "talk" to each other, even a hacky one?
Hello,
this is not a issue but a feature request , i am using express decorators in my project but one thing is really a big frustrating for me is i cannot typecast Request decorator variable as express Request type ,i also don't want it to set Any as well, so to solve this problem i have to do code something like below
import { Body, Controller, Post, Request as Req, Response as Res } from "@decorators/express";
import { Request, Response } from "express";
@Post("/detail")
async detail(@Req() req: Request, @Res() res: Response) {
res.send("works");
}
as you can see i have to put little extra time for the imports to rename Request to Req and Response to Res, i really do not like to use ANY if there is a type available for param. also in NEST.js framework they have named Request to Req and Response to Res.
When implementing the provided code example for the Express decorators:
@Controller('/')
class UsersController {
@Get('/users/:id') // <<< ERROR HERE
getData(@Response() res, @Params('id') id: string) {
}
}
I get the following error in my IDE:
[ts]
Unable to resolve signature of method decorator when called as an expression.
Supplied parameters do not match any signature of call target.
Issue may apply to other Route directives as well.
TypeScript version: ^2.1.6
@decorators/express: 1.2.0
This is my code,but the dependency injection is failed.
MissingProviderError:
In order to get DI working, you have to provide Injectable.
DI attempt for UserService and dependency [object Object].
at provider.deps.map (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/di/src/container.ts:81:17)
at Array.map (<anonymous>)
at Function.resolveProvider (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/di/src/containe0:13)
at provider.deps.map (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/di/src/container.ts:88:21)
at Array.map (<anonymous>)
at Function.resolveProvider (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/di/src/containe0:13)
at Function.get (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/di/src/container.ts:51:17)
at registerController (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/express/src/exprts:32:46)
at controllers.forEach (/Users/lavyun/Code/毕业设计项目/server/node_modules/_@[email protected]@@decorators/express/src/exps:19:45)
at Array.forEach (<anonymous>)
Hello
When I'm using your quick example from socket decorators I'm getting an error:
/node_modules/@decorators/socket/src/middleware.js:23
instance = new middleware();
^
TypeError: middleware is not a constructor
How to deal with it ?
What is more, when there is no @Injectable()
above controller, I'm getting:
/node_modules/@decorators/di/src/container.js:33
throw new errors_1.MissingProviderError(injectable);
^
MissingProviderError:
In order to get DI working, you have to provide Injectable.
DI attempt for MessageController.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.