Problem Statement:
I have working on project in Angular using AWS Amplify. The project uses cognito and successfully created AWS api for client calls. Now, I need an interceptor to intercept the requests as I need to do perform some action.
What I DID:
I have tried using Angular HTTP Interceptors but they don't work. According to my research that AWS Amplify uses axios interceptors under the hood to intercept the calls. I tried implementing using Axios package
https://www.npmjs.com/package/axios
My Implementation:
MyIntercept.ts
import { Injectable } from '#angular/core';
import axios from 'axios';
#Injectable({providedIn: 'root'})
export class MyInterceptor {
intercept() {
console.log("Hello this is my interceptor")
axios.interceptors.request.use(request => {
console.log("*******************Inside My Interceptor*************");
console.log("Call: ", request);
return request;
});
}
}
export function InterceptorFactory(myIntercept: MyInterceptor): any {
return () => myIntercept.intercept();
}
and in the app Module file
import { InterceptorFactory, MyInterceptor } from './myInterceptor.service';
import { APP_INITIALIZER } from '#angular/core';
providers: [
{
provide: APP_INITIALIZER,
useFactory: InterceptorFactory,
deps: [MyInterceptor],
multi: true
}
]
What I get:
When I run the code, all I see the line outside the console for once only which is:
Hello this is my interceptor
I need to ask you if I am doing something wrong here or is there any other approach to achieve this?
Related
I'm learning Nest, but there is a practice that i don't really like even in the official tutorial. It's one of Handling HTTP specific errors inside services. If later, for some services i'll used a protocol other that HTTP that will use a Service that handle specific HTTP errors, it don't think it's a best practice. As I'm not yet a Nestjs expert, here is how i'm trying to handle this situation:
// errors.interface.ts
export interface IError {
errorCode: number;
errorMessage: string;
}
import { Injectable } from '#nestjs/common';
import { IError } from './errors.interface';
#Injectable()
export class UserService {
// ...
async remove(id: number): Promise<Partial<User> | IError> {
const user = await this.userRepository.findOne({ where: { id } });
if (!user) {
return { errorCode: 404, errorMessage: 'user not found' };
}
await this.userRepository.remove(user);
return {
id,
};
}
}```
Here is my controller.
```// user.controller.ts
import { Controller, Get, HttpException, HttpStatus } from '#nestjs/common';
import { UserService } from './user.service';
import { IError } from './errors.interface';
#Controller('users')
export class UserController {
constructor(private userService: UserService) {}
#Get(':id')
async remove(#Param('id') id: number) {
const result = await this.userService.remove(id);
if ('errorCode' in result) {
throw new HttpException(result.errorMessage, result.errorCode);
}
return result;
}
}
As you can see, I'm trying to handle HTTP-specific errors inside HTTP controllers.
I don't have enough experience with Nestjs, maybe there are better ways to tackle these kinds of problems. I would like to know what is the best practice.
To create a Nest application instance, we use the core NestFactory class. NestFactory exposes a few static methods that allow creating an application instance. The create() method returns an application object, which fulfills the INestApplication interface. This object provides a set of methods which are described in the coming chapters. In the main.ts example above, we simply start up our HTTP listener, which lets the application await inbound HTTP requests.
so you can only work with HTTP requests and handling HTTP specific errors inside services is in fact the best practice.
I am new to Nest JS Framework and I dont whether I can use json-server to mock external API.
I have already checked NestJS documentation but its not having any example.
I found one question on Stack-overflow but it not complete Example Json-server mocking API instead of calls
I simply want to make one POST call and 1 GET Call.
Dont know which file I write these mocked POST and GET calls.
My first question is, what is your purpose for using NestJS?
You can think of NestJS and json-server as "similar" in their goal; you would use one OR the other. Not both.
If your goal is just to mock data and server, then you have everything you need with json-server. You wouldn't need NestJS.
If what you are looking for is to mock data to retrieve instead of creating a database, you can simply create a simple object in NestJS and retrieve data from there.
For the latter, it might look something like this (not tested):
// app.controller.ts
import { Controller, Get } from '#nestjs/common';
import { AppService } from '../services/app.service';
#Controller('api')
export class AppController {
constructor(private readonly appService: AppService) {}
#Get('/users')
findAllUsers(): Promise<any> {
return this.appService.findAllUsers();
}
}
// app.service.ts
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
private getMockData() {
return {
users: [
{
name: 'john',
email: 'john#doe.com',
},
{
name: 'jane',
email: 'jane#doe.com',
},
],
};
}
findAllUsers(): Promise<any> {
return new Promise(function (resolve, reject) {
const data = this.getMockData();
if (data.users.length > 0) resolve(data.users);
else reject('No users found');
});
}
}
Now you would just have to do the request GET <host>/api/users where the host will be something like http://localhost:8080 in your local machine.
How can I implement Azure-Ad Passport Authentication? Can't find any documentation for it, and read online that there are problems with that.
Use MSAL for FrontEnd.
For Backend use Passport and passport-azure-ad npm.
Then, you should create your app in Azure AD
then, get tenantId and
appId from app settings page,
then, use code like that to get access
token and check user auth.
// my auth-guard.ts
import { AuthGuard, PassportStrategy } from '#nestjs/passport';
import { BearerStrategy } from 'passport-azure-ad';
import { Injectable } from '#nestjs/common';
#Injectable()
export class AzureADStrategy extends PassportStrategy(
BearerStrategy,
'azure-ad',
) {
constructor() {
super({
identityMetadata: `https://login.microsoftonline.com/${tenantID}/v2.0/.well-known/openid-configuration`,
clientID,
});
}
async validate(data) {
return data;
}
}
export const AzureADGuard = AuthGuard('azure-ad');
// app.controller.ts
#UseGuards(AzureADGuard)
#Get('/api')
get_api(): string {
return 'OK';
}
}
try it should work.
I have a MEAN app.
Angular CLI: 7.1.4
Node: 10.1.0
OS: win32 x64
Angular: 7.1.4
recently the http requests from HttpClientModule have been getting stuck and not posting to the node server:
Img of chrome dev tools xhr pending request
The nodejs server ( running locally and in production (azure web app) does not indicate that it ever received the request. This happens inconsistently. some times it completes other times it just hangs.
Here is a snippet of a testConnection call from Angular to the server:
Angular service
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
const Headers: any = { withCredentials: true, responseType: 'json', headers: { 'Content-Type': 'application/json' } };
#Injectable({
providedIn: 'root',
})
export class UserService {
constructor(private _http: HttpClient) {}
loginStatus() {
return this._http.get(`${environment.serverURL}/api/login-status`, Headers).pipe(catchError(this.handleError));
}}
Angular Component:
ngOnInit() {
this._userSvc.loginStatus().subscribe(
(result:any)=>{console.log(result)},
(error:any)=>{console.log(error)})
}
Node/express:
router.get('/login-status', (req, res, next) => {
if (req.isAuthenticated()) {
res.status(200).json(req.user);
} else {
res.status(403).json({
success: false,
error: 'User not Authenticated',
message: "Please return to the login in page and try again."
})
}
})
Node is using passport to authenticate
Don't get tied up with the passport issue because its not always this route that fails. I have simple routes that do no validation and just return some text that fail too.
I tried modifying my CORS options but I've only managed to block myself.
There are times when restarting the server will allow the request to finish but not always.
I found the problem and i'm embarrassed to say it was in the SQL connection string for mssql.
I had ((config,error)=>{}) instead of the correct (config,(err)=>{}); This was in the de-serialize user function of passport. Nothing like looking over thousands of lines of code to spot one little problem.
I'm trying to make secure my GraphQL endpoint with passportJS in order that every call to this endpoint uses the AuthGuard for validating the token and setting the user on request.user, just as it does in a controller with this code:
#Get('findAll')
#UseGuards(AuthGuard('jwt'))
findAll(#Req() request): Promise<Array<Thing>> {
return this.thingService.findByUser(request.user.email);
}
The thing is I want to use it in the graphQL endpoint, which is created like this:
consumer
.apply(graphiqlExpress({ endpointURL: '/graphql' }))
.forRoutes('/graphiql')
.apply(
graphqlExpress(req => ({ schema, rootValue: req })),
¿?,
)
.forRoutes('/graphql');
I suppose I can just set it like a middleware function after the graphqlExpress function, but I have not been successful. Any thoughts?
Thank you in advance!
Edit
As a workaround I have implemented the solution proposed on Nest Docs where it uses the #UseGuard in every query/mutation that must be protected.
However, I want to protect the entire endpoint so that the guard is not called for every protected resolver, but only once on the main request. Is this even possible?
This technically is possible, but it's a pretty sloppy thing to write, and there's absolutely no guarantees it will work with Fastify so heads up. The meat of the functionality comes from the module where you implement the middleware. I ended up doing this all with the AppModule which I do not suggest (at least not all of the code there), but it works nonetheless.
You need to make the guard a custom provider so it can be injected into any context.
Then you need to mock up the ExecutionContext using req, res, next. This is easier said than done if you want type safety, but if you don't care about that (which I didn't for this) then slap up an as any and call it a day.
After that, in the middleware consumer you run the apply and make use of this.guard.canActivate with that mock ExecutionContext you created. Make this middleware async and await the canActivate call. Check that it comes back as true and if not then throw new <ErrorOfYourChoice>() and boom. It's set up. The code would look (vaguely) like this:
import {
BadRequestException,
CanActivate,
Inject,
MiddlewareConsumer,
Module,
NestModule,
} from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AppResolver } from './app.resolver';
import { GraphQLModule } from '#nestjs/graphql';
import { JwtModule } from '#nestjs/jwt';
import { AuthGuard, PassportModule } from '#nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
#Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: true,
}),
JwtModule.register({ secret: 'secret' }),
PassportModule.register({ defaultStrategy: 'jwt' }),
],
controllers: [AppController],
providers: [
AppService,
AppResolver,
JwtStrategy,
{ provide: 'CustomGuard', useClass: AuthGuard() },
],
})
export class AppModule implements NestModule {
constructor(#Inject('CustomGuard') private readonly guard: CanActivate) {}
configure(consumer: MiddlewareConsumer) {
consumer
.apply(async (req, res, next) => {
const canActivate = await this.guard.canActivate({
switchToHttp: () => ({
getRequest: () => req,
getResponse: () => res,
getNext: () => next,
}),
} as any);
if (canActivate) {
next();
} else {
throw new BadRequestException();
}
})
.forRoutes('graphql');
}
}
You can check this repository for everything wired up and working. Login with POST /login -d 'username=test1&password=changeme', get the JWT and play around with it as you like.
However, I want to protect the entire endpoint so that the guard is not called for every protected resolver, but only once on the main request. Is this even possible?
I was able to get a middleware function to resolve on every query/mutation by using the reference global approach from NestJS here: https://docs.nestjs.com/graphql/field-middleware#global-field-middleware.