Nest.js And Restful CRUD, Why Isn't This Posting? - nestjs

I have an Angular service that has successfully posted to Firebase and to Postgres through a PHP middleware called DreamFactory. The Angular app works. The problem is in the Nestjs controller #Post() or service add() below. I want to post a json object called recordData. I'm getting an empty object instead of my json data, which is correct in the Angular service. Server console.log results:
recordData in controller: {}
req: {}
recordData in service: {}
The Angular CORS proxy server is working in the Angular dev terminal:
[HPM] POST /api/members -> http://localhost:3000
Angular is using the dev server port 4200, Nestjs is on 3000. The typical development setup.
What's wrong? The payload isn't arriving in the controller. I'm new to server coding.
Angular http.service.ts:
private api = '/api/';
...
public addRecord(dbTable: string, recordData): Observable<any> {
return this.http
.post(`${this.api}${dbTable}`, recordData);
// For this example I'm posting to localhost:3000/api/members db table.
}
My members Nest controller. #Get works, #Post doesn't.
#Controller('api/members') // /members route
export class MembersController {
constructor(private readonly membersService: MembersService) {}
#Get()
async findAll(): Promise<Members[]> {
return await this.membersService.findAll();
}
#Post()
async addItem(#Req() req, #Body() recordData: AddMemberDto) {
console.log('recordData in controller: ', recordData);
console.log('req: ', req.body);
const result: Members = await this.membersService.addItem(recordData);
if (!result)
throw new HttpException('Error adding new Member', HttpStatus.BAD_REQUEST);
return result;
}

There were several problems, some of which I eventually fixed in the edits above. However, the main problem was I needed header info as such. While I had these for other backends they didn't seem to be required for Nestjs. Wrong idea. This is my Angular http.service setup.
private headers = new HttpHeaders()
.set('content-type', 'application/json')
.set('observe', 'response');
public addRecord(dbTable: string, recordData): Observable<any> {
return this.http
.post(`${this.api}${dbTable}`, recordData, {headers: this.headers});
}
I also want to note that many implementations of Nestjs use a dto type for the data param, so recordData: AddMemberDto in the Nestjs controller. I removed it and it works fine.

Related

NestJS websockets - Get client URL and hostname

I am trying to access the hostname of a client in NestJS websockets when a message is received. I have searched here, NestJS docs, and the project Github issues & code but have not found a solution.
I have tried accessing properties on the client object, but the hostname is not included. I was able to find it in the handleConnection subscriber, but I need it in the SubscribeMessage handler.
Would love any help!
Sample of the code from the docs:
export class SessionsGateway
implements OnGatewayConnection, OnGatewayDisconnect
{
constructor() {}
async handleDisconnect() {}
async afterInit(server) {}
#SubscribeMessage('init')
async onInit(
#MessageBody() event,
#ConnectedSocket() client,
) {
try {
// GET THE HOSTNAME HERE
} catch (error) {
console.log(error);
client.close();
}
}

Google Cloud Trace NodeJS for bundled applications

I have a server side rendered web application using express that we were sending tracing info to Google using #google-cloud/trace-agent which worked fine. But THEN.... we bundled our application, and all trace information disappeared. We still see the requests in google cloud console, but now there are no child spans.
I scoured the documentation and stumbled upon this disappointing passage:
Tracing bundled or webpacked server code.
unsupported
The Trace Agent does not support bundled server code, so bundlers like webpack or #zeit/ncc will not work.
From: https://github.com/googleapis/cloud-trace-nodejs#tracing-bundled-or-webpacked-server-code
which I thought just meant that I need to start my own root spans... but that doesn't seem to be part of the API.
Does this mean I can't send spans in a bundled server? Is there a programmatic way to manually start root spans in express?
I ended up copying the middleware function from https://github.com/googleapis/cloud-trace-nodejs/blob/main/src/plugins/plugin-express.ts and adding it to my express app manually via app.use(...).
For completeness, the code is:
import { get as getTracer } from '#google-cloud/trace-agent';
import type { RootSpanOptions } from '#google-cloud/trace-agent/build/src/plugin-types';
import type { NextFunction, Request, Response } from 'express';
export function googleCloudTracing(request: Request, response: Response, next: NextFunction): void {
const tracer = getTracer();
const options: RootSpanOptions = {
name: request.path,
traceContext: tracer.propagation.extract(key => request.get(key)),
url: request.originalUrl,
method: request.method,
skipFrames: 1,
};
tracer.runInRootSpan(options, rootSpan => {
// Set response trace context.
const responseTraceContext = tracer.getResponseTraceContext(
options.traceContext!,
tracer.isRealSpan(rootSpan)
);
if (responseTraceContext) {
// This propagates the likes of tracer.constants.TRACE_CONTEXT_HEADER_NAME for cross-service tracing.
tracer.propagation.inject((k, v) => response.setHeader(k, v), responseTraceContext);
}
if (!tracer.isRealSpan(rootSpan)) {
next();
return;
}
tracer.wrapEmitter(request);
tracer.wrapEmitter(response);
const url = `${request.protocol}://${request.headers.host}${request.originalUrl}`;
rootSpan.addLabel(tracer.labels.HTTP_METHOD_LABEL_KEY, request.method);
rootSpan.addLabel(tracer.labels.HTTP_URL_LABEL_KEY, url);
rootSpan.addLabel(tracer.labels.HTTP_SOURCE_IP, request.ip);
response.on('finish', () => {
request.route?.path && rootSpan.addLabel('express/request.route.path', request.route.path);
rootSpan.addLabel(tracer.labels.HTTP_RESPONSE_CODE_LABEL_KEY, response.statusCode);
rootSpan.endSpan();
logger.info('Request tracing ended.');
});
next();
});
}

Getting the http post canceled when subscribing from a service

Stuck in this error for a week. Cannot send a simple post request to the heroku server using the Angular HttClient. Defined all the services in the provider section in the main app Module. The Error Handling service is not logging any error after sending the post request(This service works fine that i have tested in another project).
The component is defined in a different module but services are defined and provided in the root app.module.ts
These Modules that components live are imported in the main app module.
But no matter what the post request being canceled!!.
API Params
email: string
password: string
AuthModel
Model using for defining the data params for API
export interface AuthModel {
email: string;
password: string
}
AuthService.ts
This is the service i used to inject into my component.ts file for subscribing. This service also uses another service to handle the error cases HandleError
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
#Injectable({
providedIn: 'root'
})
export class AuthService {
signUpUrl = 'myurl';
private handleError: HandleError;
constructor(private http: HttpClient, private httpErrorService: HttpErrorService) {
this.handleError = this.httpErrorService.createHandleError('AuthService'); //Problem lies here
}
signUp(authModel: AuthModel) : Observable<AuthModel>
{
return this.http.post<AuthModel>(this.signUpUrl,authModel,httpOptions).pipe( catchError(this.handleError('signup',authModel)) );
}
}
component.ts
Submit function is called when button is clicked after entering the data
Submit(): void {
this.authModel!.email = this.emailHolder.value;
this.authModel!.password = this.passwordHolder.value;
this.authService.signUp(this.authModel).subscribe((res)=> {console.log(res)});
}
The problem was with my handleError function, it get's canceled not matter the response was. So make sure guys to write a proper errorHandling function, else you will get unintended result.

Using cookies with axios and Vue

I have created a Node.js express server that connects to Salesforce.com using the SOAP interface provided by 'jsforce'. It uses session cookies for authorization via the 'express-session' package. So far, it has a POST method for login and a GET to perform a simple query. Testing with Postman has proven that this server is working as expected.
As the browser interface to this server, I have wrttien a Vue application that uses axios to perform the GET and POST. I need to save the session cookie created during login POST then attach attach the cookie to subsequent CRUD operations.
I have tried various methods to handle the cookies. One method I have tried is using axios response interceptors on the POST
axios.interceptors.response.use(response => {
update.update_from_cookies();
return response;
});
The function 'update_from_cookies' attempts to get the cookie named 'js-force' but it does not find it although I know it is being sent
import Cookie from 'js-cookie';
import store from './store';
export function update_from_cookies() {
let logged_in = Cookie.get('js-force');
console.log('cookie ' + logged_in);
if (logged_in && JSON.parse(logged_in)) {
store.commit('logged_in', true);
} else {
store.commit('logged_in', false);
}
}
I have also seen various recommendations to add parameters to the axios calls but these also do not work.
I would appreciate some advice about how to handle cookies using axios or some similar package that works with Vue
Thanks
The problem has been resolved. I was using the wrong syntax for the axios call
The correct syntax has the {withCredentials: true} as the last parameter
this.axios.post(uri, this.sfdata, {withCredentials: true})
.then( () => {
this.$router.push( {name : 'home' });
})
.catch( () => {
});

API Controller : Cannot PUT, Cannot DELETE (404 not found)

With Nest.js, and a basic controller :
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '#nestjs/common';
import { Hero } from '../entities/hero.entity';
import { HeroService } from './hero.service';
#Controller('hero')
export class HeroController {
constructor(private readonly heroesService: HeroService) {}
#Get()
async get(#Query() query): Promise<Hero[]> {
return await this.heroesService.find(query);
}
#Get(':id')
async getById(#Param('id') id): Promise<Hero> {
return await this.heroesService.findById(id);
}
#Post()
async add(#Body() hero: Hero): Promise<Hero> {
return await this.heroesService.save(hero);
}
//TODO: doesn't seem to work, never called (request 404)
#Put(':id')
async update(#Param('id') id, #Body() hero): Promise<Hero> {
console.log('hey');
return await this.heroesService.update(id, hero);
}
//TODO: doesn't seem to work, never called (request 404)
#Delete('/delete/:id')
async remove(#Param('id') id): Promise<Hero> {
console.log('hey');
return await this.heroesService.remove(id);
}
}
Following the basic documentation of nest.js, a module with a controller and a service, injecting a typeorm repository for the entity 'Hero'.
Using Postman, both #Get, #Get(':id') and #Post work perfectly, my entity->repository->service->controller connects to my local Postgres DB, and I can get/add/update data from the Hero table with those API endpoints.
However, PUT and DELETE requests respond with :
{
"statusCode": 404,
"error": "Not Found",
"message": "Cannot PUT /hero"
}
X-Powered-By →Express
Content-Type →application/json; charset=utf-8
Content-Length →67
ETag →W/"43-6vi9yb61CRVGqX01+Xyko0QuUAs"
Date →Sun, 02 Dec 2018 11:40:41 GMT
Connection →keep-alive
The request for this is localhost:3000/hero (same endpoint as GET and POST), i've tried either by adding a id:1 in Params or in the Body with x-www-form-urlencoded.
The requests don't ever seem to arrive at the controller (nothing called), i've added a globalinterceptor to Nest.js that just does this :
intercept(
context: ExecutionContext,
call$: Observable<any>,
): Observable<any> {
console.log(context.switchToHttp().getRequest());
return call$;
}
But again it only logs GET and POST requests, the others never appear.
What confuses me is that I've pretty much followed the Nest.js doc, made a basic controller and service, entity/repository connected to DB, there doesn't seem to be anything else needed for this to work, and yet PUT and DELETE appear to not exist.
Judging from msg Cannot PUT /hero you are making a /hero request rather than for example /hero/1
The request for this is localhost:3000/hero (same endpoint as GET and POST), i've tried either by adding a id:1 in Params or in the Body with x-www-form-urlencoded.
The PUT request should be done with localhost:3000/hero/<id_here> Think you are confusing query params with path params.
Simmilarly DELETE should be done on localhost:3000/hero/delete/<id_here>

Resources