I'm building a NestJS api and need to make a call to an outside API and parse the responding data. This is a healthCheck to see if another API is up and running. Here is the basic call:
#Get('healthCheck')
public async healthCheck(
#Req() req: Request,
#Res() res: Response,
)
{
const obj = this.test(req);
console.log(obj)
}
test(req) {
const testingURL = 'https://google.com';
return this.http.get(testingURL).pipe(
map((obj: AxiosResponse) => { return obj.data } ),
tap(console.log)
);
}
I've worked extensively with angular in the past and doing any sort of ".toPromise()" or "subscribe()" on the returned data causes a "Converting circular structure to JSON".
Currently the "console.log(obj)" in the first section is printing out an observable object that cannot be parsed:
Observable {
source: Observable {
source: Observable { _subscribe: [Function (anonymous)] },
operator: [Function (anonymous)]
},
operator: [Function (anonymous)]
}
Any advice or helpful hints would be helpful. Most other posts that discuss this issue say that simple mapping the response or adding a promise fixes the issue, but it does not explain further on how to parse that observable once it's been fetched.
Edit: solution posted below.
Thanks to https://stackoverflow.com/users/9576186/jay-mcdoniel for help on this and this project reference https://github.com/jmcdo29/heart-watch/blob/feat/nest-commander/src/character-reader/character-reader.api.service.ts
Here was my working solution:
#Get('healthCheck')
public async healthCheck() {
const obj = await this.getEndpoint('https://google.com');
console.log(obj);
}
private getEndpoint(url): Promise<any> {
return lastValueFrom(
this.http.get<any>(url).pipe(
map((res) => {
return res.data;
}),
),
);
}
This should parse any get (or post) endpoint, as long as parameters/auth isn't required, and return the observable parsed into a promise in nestjs using axios.
Edit: as Jay suggested, lastValueFrom used instead of depricated toPromise()
unless you have a reason, don't inject #Res() to the route handler, just return your data and let Nest handle it rather than having to call res.send(). If you need access to response headers or setting cookies, use #Res({ passthrough: true })
You can return an observable in Nest directly and Nest will handle reading the data and sending it back for you.
if you need to read a value inside the observable, you can use the tap operator from RxJS and pipe(tap(console.log)) to read the data
EDIT 10/15/2021
Now that I've also read that you're wanting to use this data in another API call, you have two options:
use lastValueFrom to convert the RxJS Observable into a Promise so that it can be awaited. Straightforward and easy to work with
use operators like mergeMap or switchMap and chain together your Observables, still returning a single observable in the end. RxJS is like really powerful callbacks with extra options on top, but they can also get complex so this option usually requires a bit more nuance and dedication to ensure the operators are chained correctly.
You should inject HttpService from HttpModule (#nestjs/axios) and get the axios reference like this this.httpService.axiosRef.get(...)
See official documentation for more details.
Related
I am really struggling here. Admittedly I am no guru especially when it comes to node and asynchronous programming, I am an old C# .net developer so I am comfortable with code but struggling here.
Here's the back story, short and sweet. I have a pg database and I am using the sequelize ORM tools to create a relatively simple CRUD app.
Here's what I want to do.
Make a call to the findAll function on one object.
I need a piece of information from that first call so that I can make a subsequent call.
For instance. Lookup the current user to get their details, grab their ID and now lookup their display preferences.
I know I can run two requests that are not linked using Promise.all, here is an example of this already working.
var delConfig = deliverabiltyConfigs.findAll2(req.signedCookies.tsUser);
var delPack = deliverabilityPackages.findAll2();
Promise.all([delConfig, delPack]).then((results) =>{
res.render('index', { title: 'Deliverability Calculator', UserEmail : req.signedCookies.tsUser, UserName : req.signedCookies.tsUserName, data:results[0], packs:results[1]});
});
Where I am stuck is passing data from one promise to then next and needing them to run asynchronously.
Please help!
There are a few way you can do this. Either use promise chaining or with async & await.
Promise chaining might be the simplest way to do this now, but I would suggest using async await as its easier to read. Since you didn't really provide a sample of what you were trying to do I will make something generic that should hopefully help.
So using promise chaining you would do something like:
pgConnection.findAll().then((data) => {
const foo = data.foo;
pgConnection.findSomething(foo).then((data2) => {
console.log(data2);
});
});
What is happening here is once the promise from findAll() is resolved successfully it will call the .then method and will pass the resulting data there for you to use in your next db query and then I am just printing out the result of the final db query.
This is how you could do it using async & await:
async function getFoo() {
const data = await pgConnection.findAll();
const foo = data.foo;
const data2 = await pgConnection.findSomething(foo);
console.log(data2);
}
The await keyword can only be used inside of an async function so it might not be as simple to change as just using a .then promise chain.
I am trying to make a POST request using #nestjs/axios and return the response.
This is my code:
verifyResponse(captcha_response: String): Observable<AxiosResponse<any>> {
return this.httpService.post('<url of rest endpoint to verify captcha>', {
captcha_response
});
}
However, Visual Studio Code says Cannot find name 'AxiosResponse'. Where can I import this from? This type was used in the Nest.js docs as Observable<AxiosResponse<Cat[]>>. I decided to remove this and just use the type Observable<any>.
This works. However, if I use console.log() to view the response I get this:
Observable { _subscribe: [Function (anonymous)] }
Most answers I found on StackOverflow suggest to use some variation of this:
return this.httpService.post('<url of rest endpoint to verify captcha>', {
captcha_response
}).pipe(map(response => response.data));
...but console.log() gives me this:
Observable {
source: Observable { _subscribe: [Function (anonymous)] },
operator: [Function (anonymous)]
}
Some other answers on StackOverflow suggest to use .toPromise(), but apparently this is deprecated.
All in all, I'm new to Nest.js and I'm super confused. Since I can't find one, I would be really grateful for a complete and up-to-date example of how to make a simple POST request and return the response as JSON.
If you want to make the HttpService use a promise instead of on RxJS Observable you can use lastValueFrom wrapping around the this.httpService.post() call. This will transform the Observable into a promise and you can await it as normal. Otherwise, if you just return the observable, Nest will handle waiting for the response for you. Either way, you'll need to make sure to use that map((resp) => resp.data) function you have so you don't end up with circular data in the response object (as Axios's response object is circular by design).
If you're trying to console.log() the data, you'll want to use tap along with map in the form of
this.httpService.post(url, data, options).pipe(
tap((resp) => console.log(resp)),
map((resp) => resp.data),
tap((data) => console.log(data)),
);
tap is essentially a spy method to tap into the observable, look at the data, and let it pass through. It can mutate the data, but generally it's a good idea to leave that to map or other rxjs operators.
For just the simple await ability, something like this is all that's needed
const data = await lastValueFrom(
this.httpService.post(url, data, options).pipe(
map(resp => res.data)
)
);
Example with AxiosRequestConfig object type (TypeScript)
const requestConfig: AxiosRequestConfig = {
headers: {
'Content-Type': 'YOUR_CONTENT_TYPE_HEADER',
},
params: {
param1: 'YOUR_VALUE_HERE'
},
};
const responseData = await lastValueFrom(
this.httpService.post(requestUrl, null, requestConfig).pipe(
map((response) => {
return response.data;
}),
),
);
AxiosResponse should be imported from axios:
import { AxiosResponse } from 'axios'
You can make use of import suggestions by pressing ctrl+space (or options + esc on mac) or by using ctrl+.
I just recently did an npm update on my Aurelia CLI project using TypeScript in Visual Studio 2015. I'm using aurelia-fetch-client for making calls to my Web API (.NET Core) backend.
This is an example of the code that was previously compiling and running fine:
import { autoinject } from "aurelia-framework";
import { HttpClient, json } from "aurelia-fetch-client";
import { SupportedCultureInfo } from "../../model/Resources/SupportedCultureInfo";
#autoinject()
export class ResourceService {
constructor(private http: HttpClient) {
}
getSupportedCultures(): Promise<SupportedCultureInfo[]> {
return this.http.fetch("resource/cultures").then(response => response.json());
}
}
Neither Visual Studio nor ReSharper provides any indication in the code editor UI that this will not compile, however after the recent update my build is now broken with this error:
TS2322: Type 'Promise<Response>' is not assignable to type 'Promise<SupportedCultureInfo[]>'
The only workaround I've found so far is to return Promise<any> instead. What I really want to do here though is return classes mapped from the JSON result and have the return type of the method be a strongly-typed Promise as well.
Does anyone know what has changed recently that could cause this? It's very frustrating.
UPDATE:
This is the code that I was able to get working:
import { autoinject } from "aurelia-framework";
import { HttpClient, json } from "aurelia-fetch-client";
import { SupportedCultureInfo } from "../../model/resources/SupportedCultureInfo";
#autoinject()
export class ResourceService {
constructor(private http: HttpClient) {
}
getSupportedCultures(): Promise<SupportedCultureInfo[]> {
return this.http.fetch("resource/cultures")
.then(response => response.json())
.then<SupportedCultureInfo[]>((data: any) => {
const result = new Array<SupportedCultureInfo>();
for (let i of data) {
const info = new SupportedCultureInfo();
info.cultureCode = i.cultureCode;
info.name = i.name;
info.isCurrentCulture = i.isCurrentCulture;
result.push(info);
}
return result;
});
}
}
I had to do two things that weren't immediately obvious:
Use the generic overload of then() to specify the return type I want to use
Explicitly declare any type for the JSON parameter in the second callback, otherwise the gulp transpiler thinks it's still typed as Response.
UPDATE:
I've accidentally hit Enter causing the comment to be posted unfinished. I'm unable to edit the comment so updating my answer as per the updated question.
Chaining Callbacks:
If the API resource/cultures returns the response as SupportedCultureInfo[] and getSupportedCultures() just need to return the response as it is, then there's no need for that second callback. The answer I've posted previously would be sufficient.
I'm guessing a second callback is most likely required in either of these two cases (or for some other reason)
The API returns a different type that has to be mapped to SupportedCultureInfo[]
The API response requires any further processing before sending it back from getSupportedCultures()
If you require a second callback to process the response further then you should call the generic then<TResult> method while you're reading the response as json instead of at a later part in the callback chain.
Reason for reported error:
In the updated code, the reason gulp transpiler treats data as type of Response is due to the fact that a non-generic then(response => response.json()) is being used which returns Promise<Response>.
Instead use then<SupportedCultureInfo[]>(response => response.json()) which would return Promise<SupportedCultureInfo[]> or then<any>(response => response.json()) which would return Promise<any>.
Using either then<SupportedCultureInfo[]>(response => response.json()) or then<any>(response => response.json()) would give you the data in the second callback as SupportedCultureInfo[] or any respectively.
getSupportedCultures(): Promise<SupportedCultureInfo[]> {
return this.http.fetch("resource/cultures")
.then<SupportedCultureInfo[]>(response => response.json())
.then(data => {
// data will be of type SupportedCultureInfo[]
});
}
As the method signature for then<TResult> is intact it should give a strongly typed data varaible.
Solution
The changes to typescript implies that we specify the return type as a type parameter to then<TResult> callback as shown below.
getSupportedCultures(): Promise<SupportedCultureInfo[]> {
return this.http.fetch("resource/cultures").then<SupportedCultureInfo[]>(response => response.json());
}
Details:
I ran into the same situation when I did an npm update. Although my initial thoughts were to blame aurelia-fetch-client as well, I did some digging into the source code to contribute a fix for this issue. But, in my quest I found that typescript is the real culprit here.
The interface Promise<T> had some changes in the way then callback handles the return types. Now, the desired return type needs to be passed in as a type parameter TResult in then<TResult> callback.
I am trying to understand why these console.log statements behave differently. I expect them to behave the same:
Using Node 7. Consider the following cases:
1. Promise.resolve(object)
Promise.resolve handles objects as I'd expect:
Promise.resolve({ a: `hello` }).then(console.log) // { a:'hello' }
2. Directly console.log a class instance from a library.
If I store an Osmosis instance I can console.log it:
const osmosis = require(`osmosis`)
console.log(new osmosis.get(url))
/* { prev:
{ instance: Osmosis:1,
name: 'get',
args: [ 'http://www.google.com', , ],
getURL: [Function: getURLArg],
url: 'http://www.google.com',
params: undefined,
cb: [Function: Get],
next: [Circular] } }
*/
3. Promise.resolve(class instance)
But if I try to resolve an Osmosis instance I don't see parity:
Promise.resolve(new osmosis.get(url)).then(console.log) // nothing
What's going on here? Am I misunderstanding something about Promise.resolve()...? Or console.log?
Why doesn't [3] log the same as [2], given the behavior in [1]?
Context: I don't think my immediate practical goals matter to answering this question. But here you go just in case. I don't see how anything about the library itself should affect the output of the final example. Here's the docs on that new osmosis.get() though: http://rchipka.github.io/node-osmosis/Osmosis.html#toc1__anchor
new osmosis.get(url) doesn't perform an async http request. It instantiates an instance of the scraper, which can be built up with a set of declarative instructions and told to "run" at some arbitrary later time.
I want to be able to build this set of instructions in a promise chain for several reasons.
The main one is that it would be the easiest way to break up the instruction definitions into different functions that are easier to test and understand. e.g. instead of osmosis.get(url).set({some stuff}).find(#something), I'd like to:
function defineSearch(instance){
return instance.set({some stuff})
}
function definePath(instance) {
return instance.find(#something)
}
Promise.resolve(new osmosis.get(url))
.then(defineSearch)
.then(definePath)
.then(instance => instance.run())
The documentation is horrible and uses techniques that are rather unconventional. What new osmosis.get(url) returns is not an Osmosis instance but a Command one. And those do have a then method.
When you pass something to Promise.resolve, it is tested for being a thenable or not, and if it looks like a promise it is tried to be assimilated: a callback is passed into the then method which will resolve the new promise.
So when you do Promise.resolve(new osmosis.get(url)), you get back an unresolved promise that will fulfill when the then callback is called (which happens when you run the command). In your case, it never does.
The solution to your particular problem is not to use promises at all (since you're not doing anything asynchronous):
definePath(defineSearch(new osmosis.get(url))).run())
but you probably should also report a bug that Commands look like promises with being properly thenable, which breaks a lot of things in ES6.
I'm implementing an API where the result needs to be return wrapped by a result key, as such
{
result: [
{
id: 1,
name: "Bob"
}
]
}
What I'd like to do is add a piece of middleware (if possible) that does this wrapping to every response without having to think about it every time. What would be the best way to accomplish this? I could see modifying response.body and then calling next() instead of doing res.send(obj) (what I'm doing now).
Thanks!
I ended up extending the response object to add a new function (used like res.sendWrapped(data)) per In Express and Node.js, is it possible to extend or override methods of the response object?
as such:
express.response.sendWrapped = function(obj) {
return this.send({ result: obj });
};