next()
middleware function in the application’s request-response cycle.HttpException
(and subclasses of it)@Post()
@UseFilters(new HttpExceptionFilter())
// @UseFilters(HttpExceptionFilter)
// you may pass the class (instead of an instance), leaving responsibility for instantiation to the framework, and enabling dependency injection.
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
//
import { Module } from "@nestjs/common";
import { APP_FILTER } from "@nestjs/core";
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
@Injectable()
decorator, which implements the PipeTransform
interface.@Injectable()
decorator, which implements the CanActivate
interface.request
object are not strongly connected with a particular route context (and its metadata).next()
function. On the other hand, Guards have access to the ExecutionContext
instance, and thus know exactly what's going to be executed next. They're designed, much like exception filters, pipes, and interceptors, to let you interpose processing logic at exactly the right point in the request/response cycle, and to do so declaratively. This helps keep your code DRY and declarative.1. An interceptor is a class annotated with the `@Injectable()` decorator and implements the `NestInterceptor` interface.
2. Interceptors have a set of useful capabilities which are inspired by the **[[Aspect Oriented Programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming)](https://en.wikipedia.org/wiki/Aspect-oriented_programming)** (AOP) technique. They make it possible to:
- bind extra logic before / after method execution
- transform the result returned from a function
- transform the exception thrown from a function
- extend the basic function behavior
- completely override a function depending on specific conditions (e.g., for caching purposes)
```tsx
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log("Before...");
const now = Date.now();
return next
.handle()
.pipe(tap(() => console.log(`After... ${Date.now() - now}ms`)));
}
}
```
```tsx
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
export interface Response<T> {
data: T;
}
@Injectable()
export class TransformInterceptor<T>
implements NestInterceptor<T, Response<T>>
{
intercept(
context: ExecutionContext,
next: CallHandler
): Observable<Response<T>> {
return next.handle().pipe(map((data) => ({ data })));
}
}
```
1. Utilities provide information about the current execution context which can be used to build generic **[[guards](https://docs.nestjs.com/guards)](https://docs.nestjs.com/guards)**, **[[filters](https://docs.nestjs.com/exception-filters)](https://docs.nestjs.com/exception-filters)**, and **[[interceptors](https://docs.nestjs.com/interceptors)](https://docs.nestjs.com/interceptors)** that can work across a broad set of controllers, methods, and execution contexts. We cover two such classes in this chapter: `ArgumentsHost` and `ExecutionContext`.
2. The `ArgumentsHost` class provides methods for retrieving the arguments being passed to a handler.
if (host.getType() === "http") {
// do something that is only important in the context of regular HTTP requests (REST)
} else if (host.getType() === "rpc") {
// do something that is only important in the context of Microservice requests
} else if (host.getType<GqlContextType>() === "graphql") {
// do something that is only important in the context of GraphQL requests
}
const [req, res, next] = host.getArgs();
/* Switch context to RPC */
switchToRpc(): RpcArgumentsHost;
/* Switch context to HTTP */
switchToHttp(): HttpArgumentsHost;
/* Switch context to WebSockets */
switchToWs(): WsArgumentsHost;
const ctx = host.switchToHttp();
const request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();
ExecutionContext
extends ArgumentsHost
Nest provides an instance of ExecutionContext
in places you may need it, such as in the canActivate()
method of a guard and the intercept()
method of an interceptor
export interface ExecutionContext extends ArgumentsHost {
/* Returns the type of the controller class which the current handler belongs to */
getClass<T>(): Type<T>;
/* Returns a reference to the handler (method) that will be invoked next in the request pipeline */
getHandler(): Function;
}
const methodKey = ctx.getHandler().name; // "create"
const className = ctx.getClass().name; // "CatsController"
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
// the better way is below
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
//
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
//
@Injectable()
export class RolesGuard {
constructor(private reflector: Reflector) {}
const roles = this.reflector.get<string[]>('roles', context.getHandler());
}
- https://docs.nestjs.com/fundamentals/lifecycle-events
- graceful shotdown 같은 것 🙂
ConfigModule
that exposes a ConfigService
which loads the appropriate .env
file. While you may choose to write such a module yourself, for convenience Nest provides the @nestjs/config
package out-of-the box..env
file from the default location (the project root directory), merge key/value pairs from the .env
file with environment variables assigned to process.env
, and store the result in a private structure that you can access through the ConfigService
. The forRoot()
method registers the ConfigService
provider, which provides a get()
method for reading these parsed/merged configuration variables. Since @nestjs/config
relies on dotenv, it uses that package's rules for resolving conflicts in environment variable names. When a key exists both in the runtime environment as an environment variable (e.g., via OS shell exports like export DATABASE_USER=test
) and in a .env
file, the runtime environment variable takes precedence.// model.ts
import { Field, Int, ObjectType } from "@nestjs/graphql";
import { Post } from "./post";
@ObjectType()
export class Author {
@Field((type) => Int)
id: number;
@Field({ nullable: true })
firstName?: string;
@Field({ nullable: true })
lastName?: string;
@Field((type) => [Post]) // type 중복 지정 설명
posts: Post[];
}
// resolver.ts
@Resolver((of) => Author) // parent
export class AuthorsResolver {
constructor(
private authorsService: AuthorsService,
private postsService: PostsService
) {}
@Query((returns) => Author) // return type
// arg type 설명
async author(@Args("id", { type: () => Int }) id: number) {
return this.authorsService.findOneById(id);
}
@ResolveField()
async posts(@Parent() author: Author) {
const { id } = author;
return this.postsService.findAll({ authorId: id });
}
}
이렇게 유용한 정보를 공유해주셔서 감사합니다.