Retrieve data from RxJS observable in HttpModule - rxjs

I am failing to understand how to map the data properties out of HttpService in my NestJS application. To my understanding, this Observable just wraps axios. Here's some example code:
interface Todo {
task: string,
completed: false
}
import {
Injectable,
HttpService,
Logger,
NotFoundException,
} from '#nestjs/common'
import { map } from 'rxjs/operators
async getTodo(todoUrl: string): Todo {
const resp = this.httpService
.get('https://example.com/todo_json')
.pipe(map(response => response.data)) // map task/completed properties?
return resp
}
resp in this case seems to be of type Observable. How do I retrieve just the data properties I want using map on this request to return my Todo interface?

Nest by default will subscribe to the observable for you it your return the Observable from your service. As this can be the case you can do something like
#Injectable()
export class TodoService {
constructor(private readonly http: HttpService) {}
getTodos(todoUrl: string): Observable<Todo> {
return this.http.get(todoUrl).pipe(
map(resp => resp.data),
);
}
}
And so long as you have a controller class calling this.todoSerivce.getTodos(todoUrl) and returning it, the response will be sent out.
However, if you want to instead make it a promise as you are more accustomed to them, you can tack on a .toPromise() method to the observable chain and now it it awaitable (though it will be slower because it has to wait for the observable to emit its complete event).
Example with .toPromise():
#Injectable()
export class TodoService {
constructor(private readonly http: HttpService) {}
getTodos(todoUrl: string): Todo {
const myTodo = await this.http.get(todoUrl).pipe(
map(resp => resp.data),
).toPromise();
return myTodo;
}
}
Edit 1/20/22
In RxJS#^7, toPromise() is deprecated and will be removed in v8. Instead, you can use lastValueFrom to wrap the entire observable
#Injectable()
export class TodoService {
constructor(private readonly http: HttpService) {}
getTodos(todoUrl: string): Todo {
const myTodo = await lastValueFrom(this.http.get(todoUrl).pipe(
map(resp => resp.data),
));
return myTodo;
}
}

Looking at your code:
import { map } from 'rxjs/operators'
async getTodo(todoUrl: string): Todo {
const resp = this.httpService
.get('https://example.com/todo_json')
.pipe(map(response => response.data)) // map task/completed properties?
return resp
}
getTodo returns an Observable, not the response. So your return value should be Observable<Todo>.
The code should look more like this:
getTodo(): Observable<Todo> {
return this.http.get<Todo>('https://example.com/todo_json')
.pipe(
map(response => response.data),
catchError(this.handleError)
);
}
EDIT: You can't just return the data from this method because it is asynchronous. It does not have the data yet. The method returns an Observable ... which is basically a contract saying that it will (at some later time) return the data for you.

Async functions need to return a promise, you can call toPromise on an observable to return a promise.
async getTodo(todoUrl: string): Todo {
const resp = this.httpService
.get('https://example.com/todo_json')
.pipe(map(response => response.data)) // map task/completed properties?
return resp.toPromise();
}

async getTodo(todoUrl: string): Todo {
const resp = await this.httpService
.get('https://example.com/todo_json')
.toPromise();
return resp.data;
}

Related

Tauri: Convert listen(eventName, handler) to an Observable to be handled in Angular

If you're using JS, the documentation works well. But in case of angular I would prefer to handle observables instead of promises. The problem is that this kind of promise has a handler. I tried many approaches listed below but nothing seems to work.
from(listen("click", v => v))
let x = async() => listen("click", v => v)
Does anyone know how to convert this kind of event to an Observable?
The response is always this:
function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
}
You would have to create an Observable yourself with new Observable.
const obs$ = new Observable((subscriber) => {
const unlisten = listen("click", v => subscriber.next(v))
return async () => {
(await unlisten)()
}
})
Inside the callback, we listen to the events and pass each event to subscriber.next(v).
We also want to call unlisten when the Observable is unsubscribed to clean up the event listener. We can do that by returning the unlisten. The function returned by the callback will be called when the Observable is unsubscribed.
Thanks to #Tobias S., I was able to create those 2 functions and reuse them in all my services.
import {from, map, Observable, ObservableInput, ObservedValueOf} from "rxjs";
import {emit, listen, Event} from "#tauri-apps/api/event";
export function tauriListen(listenerName: string): Observable<any> {
return new Observable<any>((subscriber) => {
// return from(listen(listenerName, v => subscriber.next(v))).subscribe()
const unlisten = listen(listenerName, v => subscriber.next(v))
return async () => {
(await unlisten)()
}
}).pipe(
map((response: Event<any>) => response.payload)
);
}
export function tauriEmit(emitterName: string, payload: any) {
return from(emit(emitterName, payload));
}

Return EventEmitter as Observable in Nest.js

EventEmitter in Nestjs is wrapper around EventEmitter2 module. I whant that Server-Sent Events return Observable with EE.
import { Controller, Post, Body, Sse } from '#nestjs/common';
import { fromEvent } from 'rxjs';
import { EventEmitter2 } from '#nestjs/event-emitter';
import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto';
#Controller('orders')
export class OrdersController {
constructor(private ordersService: OrdersService,
private eventEmitter2: EventEmitter2) {}
#Post()
createOrder(#Body() createOrderDto: CreateOrderDto) {
// save `Order` in Mongo
const newOrder = this.ordersService.save(createOrderDto);
// emit event with new order
this.eventEmitter2.emit('order.created', newOrder);
return newOrder;
}
#Sse('newOrders')
listenToTheNewOrders() {
// return Observable from EventEmitter2
return fromEvent(this.eventEmitter2, 'order.created');
}
}
But after subscribtion to this source from browser i've getting only errors
this.eventSource = new EventSource('http://localhost:3000/api/v1/orders/newOrders');
this.eventSource.addEventListener('open', (o) => {
console.log("The connection has been established.");
});
this.eventSource.addEventListener('error', (e) => {
console.log("Some erorro has happened");
console.log(e);
});
this.eventSource.addEventListener('message', (m) => {
const newOder = JSON.parse(m.data);
console.log(newOder);
});
It's quite likely that you forgot to format the event in the right way.
For SSE to work internally, each chunk needs to be a string of such format: data: <your_message>\n\n - whitespaces do matter here. See MDN reference.
With Nest.js, you don't need to create such message manually - you just need to return a JSON in the right structure.
So in your example:
#Sse('newOrders')
listenToTheNewOrders() {
// return Observable from EventEmitter2
return fromEvent(this.eventEmitter2, 'order.created');
}
would have to be adjusted to, for example:
#Sse('newOrders')
listenToTheNewOrders() {
// return Observable from EventEmitter2
return fromEvent(this.eventEmitter2, 'order.created')
.pipe(map((_) => ({ data: { newOrder } })));
}
the structure { data: { newOrder } } is key here. This will be later translated by Nest.js to earlier mentioned data: ${newOrder}\n\n

How can I excute code after the entire request for GraphQL has finished in NestJS?

I'm trying to set up Sentry transactions with something like this:
(A globally registered interceptor)
async intercept(
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<any>> {
const transaction = Sentry.startTransaction({
op: 'gql',
name: 'GraphQLTransaction'
});
this.setTransaction(context, transaction); // adds a `transaction` property to the context
return next.handle().pipe(
tap((...args) => {
transaction.finish();
}),
);
}
and then inside a FieldMiddleware I track spans with something like this:
(A globally registered field middleware)
export const checkRoleMiddleware: FieldMiddleware = async (
ctx: MiddlewareContext,
next: NextFn,
) => {
try {
const { info, context: gqlCtx } = ctx;
const transaction: Transaction = gqlCtx.transaction;
const span = transaction.startChild({
op: 'resolver',
description: `${info.parentType.name}.${info.fieldName}`,
});
const result = await next();
span.finish();
return result;
} catch (e) {
// log error to console, since for some reason errors are silenced in field middlewares
console.error(e);
Sentry.captureException(e);
return next();
}
};
However, it seems that transaction.finished() inside the tap() operator gets called before fields are resolved.
Is there another operator that I should be using?

Using RxJS Observable.from with an object that implements the observable interface

Based on the docs for RxJS's Observable.from(), it sounds like you should be able to pass it an object that implements the observable interface. However, the following
const observable = {
subscribe(observer) {
const subscription = someAsyncProcess(res => observer.next(res));
return {
unsubscribe() {
subscription.unsubscribe();
}
}
}
};
Rx.Observable.from(observable)
.subscribe({
next(res) {
console.log(res);
}
});
throws the error
Uncaught TypeError: object is not observable
Is my observable implementation incorrect? Or am I misunderstanding from?
Note: this is more of an academic question about the Observable interface--I realize Observable.create() would work in the above situation.
You can "trick" RxJS into thinking that the object you're passing it is a real Observable by implementing a "symbol function" (I don't know what is the proper name for this). However, you probably never need to do this in practise and it's better to use Observable.create.
const Rx = require('rxjs/Rx');
const Symbol_observable = Rx.Symbol.observable;
const Observable = Rx.Observable;
const observable = {
[Symbol_observable]: function() {
return this;
},
subscribe: function(observer) {
// const subscription = someAsyncProcess(res => observer.next(res));
observer.next(42);
return {
unsubscribe() {
subscription.unsubscribe();
}
}
}
};
Observable.from(observable)
.subscribe({
next(res) {
console.log('Next:', res);
}
});
This prints:
Next: 42
You can use Observable.from if it is an array of events or Observable.of if it is a simple object. It doesn't have to be implementing any interface. The code below is printing a in the console.
Rx.Observable.from("a").subscribe(data=> console.log(data));

Chained rxjs Observable operators

I'm relatively new to the ReactiveX principles, but I'm a big fan so far of what I've learned. I have a challenge that I've put a little thought into, but I'd like to get a more experienced opinion on:
I have a few methods which return an Observable. Each one creates an Observable in a similar manor and then chains the same exact operators. Is there any way of abstracting those operators away such that I don't have to repeat this code each method.
For example, this is what I have right now:
public get(endpoint: string, options?: RequestOptions): Observable<any> {
return this.http.get(endpoint, requestOptions)
.map((response: Response) => {
let responseBody = response.json();
return responseBody;
})
.catch((error: Response) => {
let responseError = new ErrorResponse(error.status.toString(), error.json());
return Observable.throw(responseError);
})
.publishReplay()
.refCount()
.share();
}
public put(endpoint: string, body: any, options?: RequestOptions): Observable<any> {
return this.http.put(endpoint, body, requestOptions)
.map((response: Response) => {
let responseBody = response.json();
return responseBody;
})
.catch((error: any) => {
this.logger.logRequestError('PUT', endpoint, error);
return Observable.throw(error);
})
.publishReplay()
.refCount()
.share();
}
I'd like to take the map, catch, publishReplay, refCount, share operators and put them in their own operator, such that I can do something like this:
public get(endpoint: string, options?: RequestOptions): Observable<any> {
return this.http.get(endpoint, requestOptions).myOperator();
}
public put(endpoint: string, body: any, options?: RequestOptions): Observable<any> {
return this.http.put(endpoint, body, requestOptions).myOperator();
}
// define myOperator as something like:
.map((response: Response) => {
let responseBody = response.json();
return responseBody;
})
.catch((error: Response) => {
let responseError = new ErrorResponse(error.status.toString(), error.json());
return Observable.throw(responseError);
})
.publishReplay()
.refCount()
.share();
// end definition
Would something like this work for you? Using bind() you can defer the execution of your http call. I don't know which library you are using, if http.get() returns an Observable then you can just call it and pass the returned observable to handleHttpCall because Observables are (almost always) lazy and will not run code until subscribed upon.
public get(endpoint:string, options?: RequestOptions): Observable<any> {
return handleHttpCall(this.http.get.bind(endpoint, requestOptions));
}
private handleHttpCall(httpAction) {
return httpAction()
.map((response: Response) => {
let responseBody = response.json();
return responseBody;
})
.catch((error: Response) => {
let responseError = new ErrorResponse(error.status.toString(), error.json());
return Observable.throw(responseError);
})
.publishReplay()
.refCount()
.share();
}

Resources