リリース・改善中
NestJS · Express ガイド · 3/6
この章は現在、英語でのみ提供しています。
Express revolves around middleware and routing; NestJS revolves around modules, controllers, providers and dependency injection. This chapter covers both, then the request pipeline and decorators.
A request passes through Express middleware in registration order. Each function takes (req, res, next) and either ends the response or calls next(). express.Router() groups routes, and a function with four arguments is an error handler.
const express = require("express");
const app = express();
// Runs for every request
app.use(express.json());
app.use((req, res, next) => {
console.log(req.method, req.url);
next();
});
// A router groups related routes
const users = express.Router();
users.get("/:id", (req, res) => {
res.json({ id: req.params.id });
});
users.post("/", (req, res) => {
res.status(201).json(req.body);
});
app.use("/users", users);
// Error handler: four arguments
app.use((err, req, res, next) => {
res.status(500).json({ message: err.message });
});next(err) jumps to the error handler; current Express also forwards errors from async handlers.
A module is a @Module() class grouping related controllers and providers. imports lists modules it depends on and exports the providers it shares; the module graph, starting at the root module, is the application.
Controllers receive HTTP requests and return responses. @Controller("cats") sets the path prefix, and @Get(), @Post() and friends set the method and the rest of the path. The real work lives in providers marked @Injectable(), typically services, which the controller receives through its constructor. That is dependency injection: Nest's IoC container creates the instance and passes it in, and with the default scope one instance is shared across the app.
import { Body, Controller, Get, Injectable, NotFoundException, Param, ParseIntPipe, Post } from "@nestjs/common";
@Injectable()
export class CatsService {
private readonly cats = [{ id: 1, name: "Nabi" }];
findOne(id: number) {
const cat = this.cats.find((c) => c.id === id);
if (!cat) throw new NotFoundException(`Cat ${id} not found`);
return cat;
}
create(name: string) {
const cat = { id: this.cats.length + 1, name };
this.cats.push(cat);
return cat;
}
}
@Controller("cats")
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Get(":id")
findOne(@Param("id", ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
@Post()
create(@Body("name") name: string) {
return this.catsService.create(name);
}
}Throwing a built-in exception such as NotFoundException produces the matching status (404) with a JSON body. Custom providers (useValue, useFactory, useClass) inject values or factories instead of classes.
Nest handles a request in this order; each stage can be bound globally, per controller or per handler.
NestMiddleware classesA pipe, like ParseIntPipe above, transforms or validates input and answers 400 when it fails. A guard implements CanActivate: true lets the request through, false yields 403. An interceptor wraps the handler and works with the result as an RxJS stream.
import { CallHandler, CanActivate, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
import { Observable, tap } from "rxjs";
@Injectable()
export class ApiKeyGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
return request.headers["x-api-key"] === process.env.API_KEY;
}
}
@Injectable()
export class TimingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const started = Date.now();
return next.handle().pipe(tap(() => console.log(`${Date.now() - started}ms`)));
}
}Attach them with @UseGuards() and @UseInterceptors() on a controller or method, or globally with app.useGlobalGuards() and app.useGlobalInterceptors().
Nest decorators attach metadata to classes and methods. A custom decorator can record required roles, and a guard reads them with Reflector.
import { CanActivate, ExecutionContext, Injectable, SetMetadata } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
export const Roles = (...roles: string[]) => SetMetadata("roles", roles);
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.getAllAndOverride<string[]>("roles", [context.getHandler(), context.getClass()]);
if (!roles) return true;
const { user } = context.switchToHttp().getRequest();
return roles.some((role) => user?.roles?.includes(role));
}
}With @Roles("admin") and @UseGuards(RolesGuard) on a handler, only admins get through (assuming authentication already set user on the request).
Reflector declare cross-cutting rules like role checks.
コメント 0件
ログイン · ログインするとコメントできます。
最初のコメントを書いてみましょう。