Skip to content

Commit

Permalink
feat(client): add grpc to http interceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
mohsenbostan committed Jan 18, 2023
1 parent a7b714d commit 5b7dbb2
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/gentle-kangaroos-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nestjs-grpc-exceptions": minor
---

add grpc to http interceptor
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,24 @@ import { GrpcServerExceptionFilter } from "nestjs-grpc-exceptions";
export class UserModule {}
```

Now you can use the exception classes:
Add the client interceptor to your client:

```ts
import { GrpcToHttpInterceptor } from 'nestjs-grpc-exceptions';

@Get(':id')
@UseInterceptors(GrpcToHttpInterceptor)
function findUser(@Param('id') id: number): void;
```

Now you can use the exception classes in your servers:

```ts
import {
GrpcNotFoundException,
GrpcInvalidArgumentException,
} from "nestjs-grpc-exceptions";

throw new GrpcNotFoundException("User Not Found.");
throw new GrpcInvalidArgumentException("input 'name' is not valid.");
```
Expand Down
27 changes: 27 additions & 0 deletions lib/filters/grpc-http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpStatus,
} from "@nestjs/common";
import { RpcException } from "@nestjs/microservices";
import { HTTP_CODE_FROM_GRPC } from "../utils";

@Catch(RpcException)
export class GrpcHttpExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const details = JSON.parse(exception.details);

const ctx = host.switchToHttp();
const response = ctx.getResponse();

const httpCode: number = HTTP_CODE_FROM_GRPC[Number(exception.code)] || 500;
const error = HttpStatus[httpCode];

response.json({
message: details.error,
statusCode: httpCode,
error,
});
}
}
1 change: 1 addition & 0 deletions lib/filters/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./grpc-server-exception.filter";
export * from "./grpc-http-exception.filter";
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./filters";
export * from "./exceptions";
export * from "./interceptors";
61 changes: 61 additions & 0 deletions lib/interceptors/grpc-to-http.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
CallHandler,
ExecutionContext,
HttpException,
HttpStatus,
Injectable,
NestInterceptor,
} from "@nestjs/common";
import { RpcException } from "@nestjs/microservices";
import { Observable, throwError } from "rxjs";
import { catchError } from "rxjs/operators";
import { HTTP_CODE_FROM_GRPC } from "../utils";

@Injectable()
export class GrpcToHttpInterceptor implements NestInterceptor {
intercept(
_context: ExecutionContext,
next: CallHandler<any>
): Observable<any> | Promise<Observable<any>> {
return next.handle().pipe(
catchError((err) => {
if (
!(
typeof err === "object" &&
"details" in err &&
err.details &&
typeof err.details === "string"
)
)
throwError(() => err);

const exception = JSON.parse(err.details) as {
error: string | object;
type: string;
exceptionName: string;
};

if (exception.exceptionName !== RpcException.name)
throwError(() => err);

const statusCode =
HTTP_CODE_FROM_GRPC[200] || HttpStatus.INTERNAL_SERVER_ERROR;

return throwError(
() =>
new HttpException(
{
message: exception.error,
statusCode,
error: HttpStatus[statusCode],
},
statusCode,
{
cause: err,
}
)
);
})
);
}
}
1 change: 1 addition & 0 deletions lib/interceptors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./grpc-to-http.interceptor";
2 changes: 2 additions & 0 deletions lib/utils/error-object.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { status as GrpcStatusCode } from "@grpc/grpc-js";
import { RpcException } from "@nestjs/microservices";

export type GrpcExceptionPayload = {
message: string;
Expand All @@ -19,6 +20,7 @@ export function errorObject(
message: JSON.stringify({
error,
type: typeof error === "string" ? "string" : "object",
exceptionName: RpcException.name,
}),
code,
};
Expand Down
22 changes: 22 additions & 0 deletions lib/utils/http-codes-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { status as Status } from "@grpc/grpc-js";
import { HttpStatus } from "@nestjs/common";

export const HTTP_CODE_FROM_GRPC: Record<number, number> = {
[Status.OK]: HttpStatus.OK,
[Status.CANCELLED]: HttpStatus.METHOD_NOT_ALLOWED,
[Status.UNKNOWN]: HttpStatus.BAD_GATEWAY,
[Status.INVALID_ARGUMENT]: HttpStatus.UNPROCESSABLE_ENTITY,
[Status.DEADLINE_EXCEEDED]: HttpStatus.REQUEST_TIMEOUT,
[Status.NOT_FOUND]: HttpStatus.NOT_FOUND,
[Status.ALREADY_EXISTS]: HttpStatus.CONFLICT,
[Status.PERMISSION_DENIED]: HttpStatus.FORBIDDEN,
[Status.RESOURCE_EXHAUSTED]: HttpStatus.TOO_MANY_REQUESTS,
[Status.FAILED_PRECONDITION]: HttpStatus.PRECONDITION_REQUIRED,
[Status.ABORTED]: HttpStatus.METHOD_NOT_ALLOWED,
[Status.OUT_OF_RANGE]: HttpStatus.PAYLOAD_TOO_LARGE,
[Status.UNIMPLEMENTED]: HttpStatus.NOT_IMPLEMENTED,
[Status.INTERNAL]: HttpStatus.INTERNAL_SERVER_ERROR,
[Status.UNAVAILABLE]: HttpStatus.NOT_FOUND,
[Status.DATA_LOSS]: HttpStatus.INTERNAL_SERVER_ERROR,
[Status.UNAUTHENTICATED]: HttpStatus.UNAUTHORIZED,
};
1 change: 1 addition & 0 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./error-object";
export * from "./http-codes-map";

0 comments on commit 5b7dbb2

Please sign in to comment.