Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move rxjs to peerDependencies to allow custom versions usage #53

Closed
artaommahe opened this issue May 10, 2017 · 22 comments
Closed

Move rxjs to peerDependencies to allow custom versions usage #53

artaommahe opened this issue May 10, 2017 · 22 comments

Comments

@artaommahe
Copy link

Error for client example from https://kamilmysliwiec.gitbooks.io/nest/content/real-time/microservices/basics.html

        this.client.send(pattern, data)
            .catch((err) => Observable.empty())
[ts] Property 'catch' does not exist on type 'Observable<{}>'.

this does not help

import { Observable } from 'rxjs';
import 'rxjs/add/operator/catch';

skipping via

(<any> this.client).send(pattern, data)

provides error at runtime

[Nest] 9905   - 5/10/2017, 3:44:13 AM   [ExceptionsHandler] this.client.send(...).catch is not a function
TypeError: this.client.send(...).catch is not a function
@beagleknight
Copy link

beagleknight commented May 10, 2017

Hi @artaommahe !

This worked for me:

import { Observable } from 'rxjs/Observable';
import "rxjs/add/operator/catch";
import "rxjs/add/observable/empty";

I'm importing Observable from its file instead of index file "rxjs". You need to import the Observable empty as well. I hope it helps! 😄

@artaommahe
Copy link
Author

@beagleknight yep, i tried this cases and have an error..
image

@beagleknight
Copy link

I think you are missing a return statement in the catch callback:

this.client.send(pattern, data)
  .catch((err: any) => { console.log('error', err); return Observable.empty(); })
  .subscribe(...)

I have this code working:

 const dogs = await this.client.send({ cmd: "woof" }, [])
                  .catch((err) => Observable.of(err))
                  .toPromise();

@beagleknight
Copy link

I uploaded the code to a public repository so you can check it 😄
https://github.com/beagleknight/nest-test/blob/master/lib/users/users.controller.ts

@zachgrayio
Copy link

zachgrayio commented May 10, 2017

Yeah, missing return in catch, also, FYI .empty() only fires onComplete, and your subscribe doesn't supply an onComplete, so the res wouldnt be sent even if you added a return. Observable.of() is likely what you want to be returning.

@artaommahe
Copy link
Author

artaommahe commented May 10, 2017

I think you are missing a return statement in the catch callback:

i miised, but error marks

[ts] Property 'catch' does not exist on type 'Observable<{}>'.

I found a reason - having "rxjs": "^5.4.0", in my package.json provides this error.
Looks like we forced to use nestsrxjs version without ability to update to last one..

IMHO this should be fixed via moving rxjs nest dependency to peerDependencies block like angular team did. Same to other used libs like express, socket.io.

@artaommahe artaommahe changed the title Microservice client example error Move rxjs to peerDependencies to allow custom versions usage May 10, 2017
@zachgrayio
Copy link

zachgrayio commented May 10, 2017

@artaommahe Peer deps is deprecated with npm3 if I recall. You're able to use whatever version of rxjs you wish without issue regardless of what version Nest uses, assuming you have npm3 installed (I'm currently doing this now).

Given the above error, I'd assume you need to be sure you include the full Rx library if you want to use catch, or use the operator add import to import it. Have a look at the import section under Installation and Usage here:
https://github.com/ReactiveX/rxjs

import Rx from 'rxjs/Rx';
Rx.Observable.of(1,2,3)

or

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
Observable.of(1,2,3).map(x => x + '!!!'); // etc

edit: just reread the OP, I see you tried the import. Taking another look

@zachgrayio
Copy link

zachgrayio commented May 10, 2017

As far as the dependencies - grep 'rxjs@5.4.0' in my npm list output and you'll see I'm running 5.4.0 alongside nest:

$ npm list
projectnamehere
├── @types/jsonwebtoken@7.2.0
├── @types/node@7.0.18
├─┬ @types/passport@0.3.3
│ └─┬ @types/express@4.0.35
│   ├── @types/express-serve-static-core@4.0.44
│   └─┬ @types/serve-static@1.7.31
│     └── @types/mime@0.0.29
├─┬ @types/passport-jwt@2.0.19
│ └── @types/passport-strategy@0.2.30
├─┬ bcrypt-as-promised@1.1.0
│ ├─┬ bcrypt@0.8.7
│ │ ├── bindings@1.2.1
│ │ └── nan@2.3.5
│ └── when@3.7.8
├─┬ body-parser@1.17.1
│ ├── bytes@2.4.0
│ ├── content-type@1.0.2
│ ├── debug@2.6.1
│ ├── depd@1.1.0
│ ├─┬ http-errors@1.6.1
│ │ ├── inherits@2.0.3
│ │ ├── setprototypeof@1.0.3
│ │ └── statuses@1.3.1
│ ├── iconv-lite@0.4.15
│ ├─┬ on-finished@2.3.0
│ │ └── ee-first@1.1.1
│ ├── qs@6.4.0
│ ├─┬ raw-body@2.2.0
│ │ └── unpipe@1.0.0
│ └─┬ type-is@1.6.15
│   ├── media-typer@0.3.0
│   └─┬ mime-types@2.1.15
│     └── mime-db@1.27.0
├─┬ jsonwebtoken@7.4.0
│ ├─┬ joi@6.10.1
│ │ ├── hoek@2.16.3
│ │ ├── isemail@1.2.0
│ │ ├── moment@2.18.1
│ │ └── topo@1.1.0
│ ├─┬ jws@3.1.4
│ │ ├── base64url@2.0.0
│ │ ├─┬ jwa@1.1.5
│ │ │ ├── buffer-equal-constant-time@1.0.1
│ │ │ └── ecdsa-sig-formatter@1.0.9
│ │ └── safe-buffer@5.0.1
│ ├── lodash.once@4.1.1
│ ├── ms@0.7.2
│ └── xtend@4.0.1
├─┬ mysql@2.13.0
│ ├── bignumber.js@3.1.2
│ ├─┬ readable-stream@1.1.14
│ │ ├── core-util-is@1.0.2
│ │ ├── isarray@0.0.1
│ │ └── string_decoder@0.10.31
│ └── sqlstring@2.2.0
├─┬ nest.js@1.0.6
│ ├─┬ cli-color@1.1.0
│ │ ├── ansi-regex@2.1.1
│ │ ├── d@0.1.1
│ │ ├─┬ es5-ext@0.10.16
│ │ │ └─┬ es6-symbol@3.1.1
│ │ │   └── d@1.0.0
│ │ ├─┬ es6-iterator@2.0.1
│ │ │ └── d@1.0.0
│ │ ├─┬ memoizee@0.3.10
│ │ │ ├─┬ es6-weak-map@0.1.4
│ │ │ │ ├── es6-iterator@0.1.3
│ │ │ │ └── es6-symbol@2.0.1
│ │ │ ├─┬ event-emitter@0.3.5
│ │ │ │ └── d@1.0.0
│ │ │ ├── lru-queue@0.1.0
│ │ │ └── next-tick@0.2.2
│ │ └─┬ timers-ext@0.1.2
│ │   └── next-tick@1.0.0
│ ├─┬ express@4.14.0
│ │ ├─┬ accepts@1.3.3
│ │ │ └── negotiator@0.6.1
│ │ ├── array-flatten@1.1.1
│ │ ├── content-disposition@0.5.1
│ │ ├── cookie@0.3.1
│ │ ├── cookie-signature@1.0.6
│ │ ├─┬ debug@2.2.0
│ │ │ └── ms@0.7.1
│ │ ├── encodeurl@1.0.1
│ │ ├── escape-html@1.0.3
│ │ ├── etag@1.7.0
│ │ ├─┬ finalhandler@0.5.0
│ │ │ └─┬ debug@2.2.0
│ │ │   └── ms@0.7.1
│ │ ├── fresh@0.3.0
│ │ ├── merge-descriptors@1.0.1
│ │ ├── methods@1.1.2
│ │ ├── parseurl@1.3.1
│ │ ├── path-to-regexp@0.1.7
│ │ ├─┬ proxy-addr@1.1.4
│ │ │ ├── forwarded@0.1.0
│ │ │ └── ipaddr.js@1.3.0
│ │ ├── qs@6.2.0
│ │ ├── range-parser@1.2.0
│ │ ├─┬ send@0.14.1
│ │ │ ├── debug@2.2.0
│ │ │ ├── destroy@1.0.4
│ │ │ ├─┬ http-errors@1.5.1
│ │ │ │ └── setprototypeof@1.0.2
│ │ │ ├── mime@1.3.4
│ │ │ └── ms@0.7.1
│ │ ├─┬ serve-static@1.11.2
│ │ │ └─┬ send@0.14.2
│ │ │   ├─┬ debug@2.2.0
│ │ │   │ └── ms@0.7.1
│ │ │   └─┬ http-errors@1.5.1
│ │ │     └── setprototypeof@1.0.2
│ │ ├── utils-merge@1.0.0
│ │ └── vary@1.1.1
│ ├── json-socket@0.2.1
│ ├─┬ redis@2.6.5
│ │ ├── double-ended-queue@2.1.0-0
│ │ ├── redis-commands@1.3.1
│ │ └── redis-parser@2.6.0
│ ├── rxjs@5.0.3
│ └─┬ socket.io@1.7.2
│   ├── debug@2.3.3
│   ├─┬ engine.io@1.8.2
│   │ ├── base64id@1.0.0
│   │ ├── debug@2.3.3
│   │ ├─┬ engine.io-parser@1.3.2
│   │ │ ├── after@0.8.2
│   │ │ ├── arraybuffer.slice@0.0.6
│   │ │ ├── base64-arraybuffer@0.1.5
│   │ │ ├── blob@0.0.4
│   │ │ └── wtf-8@1.0.0
│   │ └─┬ ws@1.1.1
│   │   ├── options@0.0.6
│   │   └── ultron@1.0.2
│   ├── has-binary@0.1.7
│   ├── object-assign@4.1.0
│   ├─┬ socket.io-adapter@0.5.0
│   │ └── debug@2.3.3
│   ├─┬ socket.io-client@1.7.2
│   │ ├── backo2@1.0.2
│   │ ├── component-bind@1.0.0
│   │ ├── component-emitter@1.2.1
│   │ ├── debug@2.3.3
│   │ ├─┬ engine.io-client@1.8.2
│   │ │ ├── component-emitter@1.2.1
│   │ │ ├── component-inherit@0.0.3
│   │ │ ├── debug@2.3.3
│   │ │ ├── has-cors@1.1.0
│   │ │ ├── parsejson@0.0.3
│   │ │ ├── parseqs@0.0.5
│   │ │ ├── xmlhttprequest-ssl@1.5.3
│   │ │ └── yeast@0.1.2
│   │ ├── indexof@0.0.1
│   │ ├── object-component@0.0.3
│   │ ├─┬ parseuri@0.0.5
│   │ │ └─┬ better-assert@1.0.2
│   │ │   └── callsite@1.0.0
│   │ └── to-array@0.1.4
│   └─┬ socket.io-parser@2.3.1
│     ├── component-emitter@1.1.2
│     ├─┬ debug@2.2.0
│     │ └── ms@0.7.1
│     └── json3@3.3.2
├─┬ passport@0.3.2
│ ├── passport-strategy@1.0.0
│ └── pause@0.0.1
├── passport-jwt@2.2.1
├── reflect-metadata@0.1.10
├─┬ rxjs@5.4.0
│ └── symbol-observable@1.0.4
├─┬ ts-node@2.1.2
│ ├── arrify@1.0.1
│ ├─┬ chalk@1.1.3
│ │ ├── ansi-styles@2.2.1
│ │ ├── escape-string-regexp@1.0.5
│ │ ├── has-ansi@2.0.0
│ │ ├── strip-ansi@3.0.1
│ │ └── supports-color@2.0.0
│ ├── diff@3.2.0
│ ├── make-error@1.2.3
│ ├── minimist@1.2.0
│ ├─┬ mkdirp@0.5.1
│ │ └── minimist@0.0.8
│ ├── pinkie@2.0.4
│ ├─┬ source-map-support@0.4.15
│ │ └── source-map@0.5.6
│ ├─┬ tsconfig@6.0.0
│ │ ├── strip-bom@3.0.0
│ │ └── strip-json-comments@2.0.1
│ ├─┬ v8flags@2.1.1
│ │ └── user-home@1.1.1
│ └─┬ yn@1.3.0
│   └── object-assign@4.1.1
├─┬ typeorm@0.0.11
│ ├── app-root-path@2.0.1
│ ├─┬ glob@7.1.1
│ │ ├── fs.realpath@1.0.0
│ │ ├─┬ inflight@1.0.6
│ │ │ └── wrappy@1.0.2
│ │ ├─┬ minimatch@3.0.4
│ │ │ └─┬ brace-expansion@1.1.7
│ │ │   ├── balanced-match@0.4.2
│ │ │   └── concat-map@0.0.1
│ │ ├── once@1.4.0
│ │ └── path-is-absolute@1.0.1
│ ├─┬ yargonaut@1.1.2
│ │ ├── figlet@1.2.0
│ │ └── parent-require@1.0.0
│ └─┬ yargs@6.6.0
│   ├── camelcase@3.0.0
│   ├─┬ cliui@3.2.0
│   │ └── wrap-ansi@2.1.0
│   ├── decamelize@1.2.0
│   ├── get-caller-file@1.0.2
│   ├─┬ os-locale@1.4.0
│   │ └─┬ lcid@1.0.0
│   │   └── invert-kv@1.0.0
│   ├─┬ read-pkg-up@1.0.1
│   │ ├─┬ find-up@1.1.2
│   │ │ ├── path-exists@2.1.0
│   │ │ └── pinkie-promise@2.0.1
│   │ └─┬ read-pkg@1.1.0
│   │   ├─┬ load-json-file@1.1.0
│   │   │ ├── graceful-fs@4.1.11
│   │   │ ├─┬ parse-json@2.2.0
│   │   │ │ └─┬ error-ex@1.3.1
│   │   │ │   └── is-arrayish@0.2.1
│   │   │ ├── pify@2.3.0
│   │   │ └─┬ strip-bom@2.0.0
│   │   │   └── is-utf8@0.2.1
│   │   ├─┬ normalize-package-data@2.3.8
│   │   │ ├── hosted-git-info@2.4.2
│   │   │ ├─┬ is-builtin-module@1.0.0
│   │   │ │ └── builtin-modules@1.1.1
│   │   │ ├── semver@5.3.0
│   │   │ └─┬ validate-npm-package-license@3.0.1
│   │   │   ├─┬ spdx-correct@1.0.2
│   │   │   │ └── spdx-license-ids@1.2.2
│   │   │   └── spdx-expression-parse@1.0.4
│   │   └── path-type@1.1.0
│   ├── require-directory@2.1.1
│   ├── require-main-filename@1.0.1
│   ├── set-blocking@2.0.0
│   ├─┬ string-width@1.0.2
│   │ ├── code-point-at@1.1.0
│   │ └─┬ is-fullwidth-code-point@1.0.0
│   │   └── number-is-nan@1.0.1
│   ├── which-module@1.0.0
│   ├── y18n@3.2.1
│   └── yargs-parser@4.2.1
└── typescript@2.3.2

@artaommahe
Copy link
Author

As far as the dependencies - grep 'rxjs@5.4.0' in my npm list output and you'll see I'm running 5.4.0 alongside nest:

ofc it will be here due to nest dependency. I'm talking about explicitly added rxjs in your own package.json.
Just clone example repo above (https://github.com/beagleknight/nest-test), add

    "rxjs": "^5.4.0"

to package.json "dependencies" list, run

yarn

open ./lib/users/users.controller.ts and see same error
image

[ts] Property 'catch' does not exist on type 'Observable<{}>'.

@zachgrayio
Copy link

zachgrayio commented May 10, 2017

perhaps you're misreading that list output. If you'd run the search, youd see 5.4.0 is brought in directly under my project, due to being in package.json, and 5.0.3 is brought in by Nest.

Note that 5.4.0 is a primary dependency and 5.0.3 is a 2nd or "peer" but npm3 nests it under Nest

├─┬ nest.js@1.0.6
...
│ ├── rxjs@5.0.3
├─┬ rxjs@5.4.0
│ └── symbol-observable@1.0.4

Here's my dependencies block:

"dependencies": {
    "bcrypt-as-promised": "^1.1.0",
    "body-parser": "^1.17.1",
    "jsonwebtoken": "^7.4.0",
    "mysql": "^2.13.0",
    "nest.js": "1.0.6",
    "passport": "^0.3.2",
    "passport-jwt": "^2.2.1",
    "reflect-metadata": "0.1.10",
    "rxjs": "^5.4.0",
    "typeorm": "0.0.11",
    "typescript": "^2.2.1"
  },

and here's a snippet of my use of catch

        this.connectionSubject
            .map(n => `Connected to ${databaseConfigInfo}`)
            .catch(e => Rx.Observable.of(`Failed to connect to ${databaseConfigInfo}. Here's why: ${e}`))
            .subscribe(console.log);

@artaommahe
Copy link
Author

@zachgrayio yep, missed it. But there is still an error for example repo above with added to tsconfig rxjs lib.

@zachgrayio
Copy link

I don't use yarn much, but off top of my head, I'm wondering if you have a cached version of rxjs in yarn and it's still using it instead of 5.4.0 dependency you added to your package.json.

under npm, when changing versions im always sure to rm -rf ./node_modules && npm i to ensure the version of libs i think im using are always in use.

have you run a yarn cache clean and yarn install after updating package.json?

@artaommahe
Copy link
Author

artaommahe commented May 10, 2017

@zachgrayio you can simply repeat it with npm (just made it)

git clone https://github.com/beagleknight/nest-test
cd nest-test

add rxjs to package.json dependencies list

  "dependencies": {
    "body-parser": "^1.17.1",
    "express": "^4.15.2",
    "nest.js": "^1.0.2",
    "rxjs": "^5.4.0"
  },
npm cache clean
npm i

open ./lib/users/users.controller.ts and see error on catch

@zachgrayio
Copy link

alright, ill take a look

@zachgrayio
Copy link

Looks like an rxjs5 modularity issue.

Making this change in client-prox.d.ts solves the issue it seems:

import * as Rx from 'rxjs/Rx'; // import all of rx. using the operator add import HERE solves the issue too
import { Observer } from 'rxjs/Observer';

export declare abstract class ClientProxy {
    abstract sendSingleMessage(pattern: any, callback: any): any;
    send<T>(pattern: any, data: any): Rx.Observable<T>;
    createObserver<T>(observer: Observer<T>): (err: any, result: any) => void;
}

Might be best to open an issue over on rxjs to see what the preferred solution here is, I'm not entirely sure - though seems like its probably a pretty common issue.

@zachgrayio
Copy link

zachgrayio commented May 10, 2017

In the meantime though, this works either way because subscribe is always present on Observable.

    this.client.send<string>({ cmd: "woof" }, [])
      .subscribe(
          dogs => {
            res.status(HttpStatus.OK).json(dogs);
          }, e => {
            console.log(e);
          })

also its worth noting that you were returning an Observable<Error> from your catch, which you were turning into a promise, then resolving and returning in your call to json() which would try to send it down in the response... where as the success case would have been some other type, maybe a domain model or something you'd want to serialize and return to the user. Observable.catch() is meant to catch errors and recover without breaking the chain, maybe return an empty array or something, usually not the error itself.

edited to add: perhaps some attention should be given to method send<T>(pattern: any, data: any): Observable<T>; to avoid implementations like the above that don't include strong typings and thus don't benefit from typescript compile errors when types are botched. Also, am I correct in assuming that T really should be the type of whatever the ack is going to be, not the type of what we're sending? (in the case above, it picked up the type as Observable<{}> I think.

@artaommahe
Copy link
Author

artaommahe commented May 10, 2017

@zachgrayio yep, i understand that it's too synthetic case with catch but looks like there will be issues with any operator (that is not imported in nest lib) and 2 rxjs versions (from nest and current app).

@artaommahe
Copy link
Author

artaommahe commented May 11, 2017

@zachgrayio also i dont find anything about peerDependencies deprecation for last year. Official npm docs have only this

NOTE: npm versions 1 and 2 will automatically install peerDependencies
if they are not explicitly depended upon higher in the dependency tree.
In the next major version of npm (npm@3), this will no longer be the case.
You will receive a warning that the peerDependency is not installed instead.

and old blog posts about deprecation without smth official

@zachgrayio
Copy link

To be clear, this isn't a problem with bringing in other versions of Rx alongside Nest. I've done this just fine a few times.

The issue is really that use of import 'rxjs/add/operator/catch'; and so on fails to effect the Observable returned by the send() method of the ClientProxy abstract class.

@artaommahe
Copy link
Author

artaommahe commented May 13, 2017

The issue is really that use of import 'rxjs/add/operator/catch'; and so on fails to effect the Observable returned by the send() method of the ClientProxy abstract class.

Cause there is 2 different rxjs versions running at the same time - from nest dependencies and from your app. Importing operator in app code affects only app rxjs version instance.
Same thing with any other main lib used by nest - adding newer version of express as dependency of your app will not affect version that will be used by used.
This is the reason why peerDependencies using is important and why angular team did so for rxjs and zone.js libs - upgrading to newest verison by app developer could be very important when there is critical fixes or important new features.

@kamilmysliwiec
Copy link
Member

Hi @artaommahe,
Since ~2.0.0 rxjs is used as a peerDependency.

@lock
Copy link

lock bot commented Sep 25, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants