Using https with Axios request in Nestjs - javascript

I currently have a Nestjs server setup and am attempting to perform an Axios request when one of the endpoints is hit with a GET request. Here is the controller.ts code:
#Controller()
export class TestController {
constructor(private readonly testService: TestService) {}
#Get('testData')
testData() {
return this.testService.testData();
}
}
Service.ts:
#Injectable()
export class TestService {
status(): string {
return 'OK'
}
testData(): Promise<any> {
return helper.getTestData();
}
}
Where helper.getTestData() is just a call to a helper file with the following function:
export async function getTestData(): Promise<any> {
const result = await axios({
url: tempURL,
method: 'GET',
timeout: 3000,
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
I am able to hit this endpoint tempURL but encounter the following error message: Cannot read property 'Agent' of undefined. I know that the endpoint I am attempting to hit requires a cert, which is why I must include the httpsAgent argument inside the Axios request. If I don't include the httpsAgent argument, I receive the following message Error: unable to verify the first certificate in nodejs.
Is there a way to configure Nestjs to work with https? Or is there another way to handle this authorization issue inside of Nestjs? Using Postman everything works fine so I'm assuming it is a Nestjs issue. Any help is appreciated.

instead of import https from 'https'; you should use the namespace import: import * as https from 'https'; or set the esModuleInterop to true in your tsconfig file (under compilerOptions)

Related

Problem with dynamic request header in axios template

In my react app, I created an api.js file which creates an api object with axios.create and exports it as default. So, I use that template to make the API requests. The problem is that, one of the headers in created axios api object must be dynamic.
For example, see the locale header below:
At first it may be something like this:
export default api = axios.create({
baseURL: process.env.REACT_APP_API_URL,
headers: {
post: {
"Content-Type": "application/json;charset=utf-8",
"Access-Control-Allow-Origin": "*",
locale: "en",
},
get: {
locale: "en",
},
},
});
But after some time it can be updated to some other locale, like "en" should be changed with "fr" for example. How can I update it, and make sure when it gets updated it changes in every place api is imported.
I can't use ContextApi etc, because I need to use that api in index.js file too, which, because of not being a react component, doesn't support use of hooks.
Sounds like a job for Axios interceptors...
import axios from "axios"
// some kind of storage for your locale
let locale = "en"
// some way of changing it
export const setLocale = (newLocale) => {
locale = newLocale
}
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL,
})
// register a synchronous request interceptor
api.interceptors.request.use(config => ({
...config,
headers: {
...config.headers,
locale // merge the "locale" into the request config headers
}
}), null, { synchronous: true })
export default api
Also, Access-Control-Allow-Origin is a response header that comes from the server. It does not belong in your request and in general will more than likely cause CORS errors.
Also, the default content-type when posting JS objects in Axios is application/json so you typically don't need to set it.

How can I get TOKEN value from an ASYNC config service in JwtStrategy's super call in NestJS?

I want to get the TOKEN's value from an ASYNC config service in JwtStrategy's constructor super.
For example:
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private authConfig: AuthConfig) {
super({token: await authConfig.getConfig.jwtToken});
}
}
I am using AWS Secret Manager for storing sensitive values.
Here is my config file:
async getConfig() {
var sm = await SecretManager();
if('POOL_ID' in sm && sm.POOL_ID !== undefined) {
return {
region: envConfig.AWS_REGION,
userPoolId: sm.POOL_ID,
clientId: sm.APP_CLIENT_ID,
jwtToken: sm.jwtToken,
}
}
}
PS: I am forbidden to store the config variables in a .js or .ts file or even in .env file directly. I am only allowed to get the values from secret manager only. I've debugged and checked that I am getting correct values in authConfig only this is I can not use it in constructors.
You can't await in a constructor, so I think you need to have the config object injected.
You can make an AUTH_CONFIG useFactory provider that makes the call to secrets manager and returns the config object containing the token. It'll need to be an asynchronous provider to await the secrets manager call. On app startup, NestJS will wait for the config to be fetched so it can provide the results toJwtStrategy.

Nuxt.Js axios not using baseURL despite it being set correctly

I want to call an API in asyncData()
async asyncData({ $axios, params, store }) {
let itemUUID = params.item;
let item = await $axios.get("/item/" + itemUUID);
return {item};
}
Problem: Axios is still making the request on http://localhost:3000
if I do a console.log($axios.defaults.baseURL) the correct baseURL of my API is printed.
This also works if I use my store action & make the call by using this.$axios
I am using #nuxtjs/axios 5.13.1 with Nuxt 2.15.6 in SSR mode and configured it with the correct baseURL in the nuxt.config.js
Interestingly, if I edit my page content and a hot module reload is triggered, the correct URL is used. Maybe the question should be if Axios is triggered in the right time, on the server?
Edit: I checked the request that was made on HMR and this was triggered in the client.js.
If I call my store inside the created() hook the request gets executed successfully.
My nuxt.config.js:
publicRuntimeConfig: {
axios: {
baseURL: process.env.EXPRESS_SERVER_URL
}
},
privateRuntimeConfig: {
axios: {
baseURL: process.env.EXPRESS_SERVER_URL,
}
},
I'm not sure what is the NODE_TLS_REJECT_UNAUTHORIZED=0 thing doing but your frontend configuration (Nuxt) is working well so far.
Sorry if I cannot help on the Express part.
Maybe try to setup HTTPS locally on Nuxt: How to run NUXT (npm run dev) with HTTPS in localhost?
TLDR; This was not related at all - I forgot to set the auth token for my backend. At the time of axios init it's not present. $axios object doesn't have auth - backend fails.
On page load the nuxt function nuxtServerInit() is used to get the auth token out of the acces_token cookie.
I am using a plugin to initialize Axios - with the token from the store.
But of couse the token is not present at the time axios is initialized as nuxtServerInit is called after plugin init.
In my axios.js plugin I changed:
export default function({ app, error: nuxtError, store }) {
const token = const token = store.state.user.token;
app.$axios.setToken(token, "Bearer");
}
to;
export default function({ app, error: nuxtError, store }) {
const token = app.$cookies.get("access_token");
app.$axios.setToken(token, "Bearer");
}
Now the token is present & used for every request happening server-side.

call http cloud function from firebase angular-ionic app

This is probably something trivial but I am new to GCP and app development.
I have an angular-ionic app that integrates with firebase (I am able to read and write to firestore) now I am trying to call a http triggered cloud python function.
In my firebase console under functions I can see that the function is present:
Request
https://europe-west6-tmaker-bd14b.cloudfunctions.net/generate_tournament8
I call the function in my app (running locally at the moment) as : generateTournament8(eventRefPath:string) and the implementation is in the following file:
import { AngularFireFunctions } from '#angular/fire/functions';
import { AlertController } from '#ionic/angular';
#Injectable({
providedIn: 'root'
})
export class CloudFunctionsService {
constructor(private firestoreFunctions: AngularFireFunctions, private alertController: AlertController) { }
generateTournament8(eventRefPath:string){
const callable = this.firestoreFunctions.httpsCallable('generate_tournament8');
var res = callable({'event_ref_path': eventRefPath });
res.subscribe(async res => {
const alert = await this.alertController.create({
header: `Time: ${res.date}`,
message: res.msg,
buttons: ['OK']
});
await alert.present();
});
}
}
when trying to call the function I get the following error in the console:
Access to fetch at 'https://us-central1-tmaker-bd14b.cloudfunctions.net/generate_tournament8' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
I think I am missing something trivial. Like not correct permissions. Can you help me?
You didn't share the code of your Cloud Function, but it seems that you are mixing up HTTPS Cloud Functions and callable Cloud Functions.
On one hand, based on your question title and function URL, as well as on the fact that there is no option for creating a Callable Cloud Function from the Google Cloud Console (which is used for Python Cloud Functions), your Cloud Function seems to be an HTTPS Cloud Function.
On the other hand, in your front-end code (this.firestoreFunctions.httpsCallable()), you actually define a callable Cloud Function.
You should call your HTTPS Cloud Function exactly like a "standard" REST API endpoint, either with fetch, axios or any other ionic/angular similar library.
Note that while Callable Cloud Functions are usually deployed via the Firebase CLI, it may be possible to create and deploy a Callable Cloud Function from Google Cloud Console but you would need to follow the specification for the HTTPS request and response.
As you're reporting, your function is available at:
Request
https://europe-west6-tmaker-bd14b.cloudfunctions.net/generate_tournament8
So, it looks like you chose europe-west6 as your region for cloud functions.
In order to properly configure #angular/fire, you need to set the REGION token in your root module:
// src/app/app.module.ts
import { NgModule } from '#angular/core';
import { AngularFireModule } from '#angular/fire';
import { AngularFireFunctionsModule, REGION } from '#angular/fire/functions';
import { environment } from '../environments/environment';
#NgModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireFunctionsModule,
],
providers: [
{ provide: REGION, useValue: 'europe-west6' },
]
})
export class AppModule {
}
See: https://github.com/angular/angularfire/blob/master/docs/functions/functions.md#functions-region

Angular 2 Webworkers Http Uncaught (in promise): not implemented

I've been stuck on an error that I'm not completely sure how to solve.
My application is made in Angular2 and runs completely in a webworker largely based on this tutorial http://www.syntaxsuccess.com/viewarticle/web-workers-in-angular-2.0
My first feature was an implementation of socket.io which is working perfectly(also with observables etc..) but now I want to use the Http service of Angular2 and I get the following error:
My code of the service is like this and the error arrises when I call validateAccessToken (I have to add the .js on my imports otherwise I get a 404 on the files within the webworker):
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from "#angular/http";
import { environment } from "../../../environments/environment.js";
import { Observable } from "rxjs/Observable.js";
import 'rxjs/add/operator/toPromise.js';
import 'rxjs/add/operator/map.js';
#Injectable()
export class AuthService {
headers: Headers;
options: RequestOptions;
url: string;
constructor(private http:Http) {
this.url = environment.authServerUrl;
}
validateAccessToken(token) {
return this.http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
};
extractData(response: Response) {...}
handleError(error: any) {...}
}
I know the question is quite vague but with the information I get from the error it's not really clear what's going wrong for me.
The CookieXSRFStrategy is default enabled by Angular2 and used by http.
The webworker does not have DOM access to get the cookie to insert in the http headers. And thus throws the error Uncaught not implemented.
You should implement your own CookieXSRFStrategy strategy which at least does not throw this error ;)

Categories