Co to jest NestJS?
NestJS to nowoczesny framework NodeJS, który wykorzystuje pod maską popularne frameworki NodeJS, takie jak Express i Fastify. NestJS został w dużej mierze zainspirowany Angularem, w wyniku czego wykorzystuje system modułów w stylu Angulara. NestJS jest napisany w TypeScript, chociaż obsługuje również natywny JavaScript.
Wymagania wstępne
Aby skorzystać z tego samouczka, musisz spełnić następujące wymagania
- Umiejętności w PostMan lub jakimkolwiek innym narzędziu do testowania API.
- Podstawowa znajomość aplikacji NodeJS i Express.
- Podstawowa znajomość języka TypeScript.
- Kompetencje w MongoDB (Mongoose).
W systemie powinny być zainstalowane następujące elementy
- NodeJS v.14 i nowsze.
- Kod Visual Studio (zalecany) lub inne IDE.
- PostMan lub inne narzędzie do testowania API.
Popularne terminologie używane w NestJS;
Oto niektóre z najczęściej używanych terminów w NestJS, z którymi często spotkasz się w tym artykule.
Interfejsy
Interfejs to definicja typu. W rezultacie jest używany jako kontroler typu/enforcer w funkcjach, klasach itp.
interface humanInterface{
name:string;
gender:string;
age:number;
}
const kevin: humanInterface={
name:'Kevin Sunders',
gender:'Male',
age: 25,
}
humanInterface
powyżej wykonuje ścisłe sprawdzenie typu na kevin
obiekt. Typescript wygeneruje błąd, jeśli dodasz inne pole lub zmienisz typ którejkolwiek z właściwości obiektu.
Kontrolery
Kontrolerzy odpowiadają za odbieranie przychodzących żądań i odpowiadanie klientowi. Kontroler współpracuje z powiązaną usługą.
Usługi
Usługa to dostawca, który przechowuje i pobiera dane i jest używany z odpowiednim kontrolerem.
Dekoratorzy
Dekorator to wyrażenie zwracające funkcję, które akceptuje target
, name
i property descriptor
jako argumenty opcjonalne. Dekoratory są zapisywane jako @decorator-name
. Zazwyczaj są one dołączone do deklaracji klas, metod i parametrów.
@Get()
getAll(): Model[] {
return this.testService.getAll();
}
@Get
dekorator powyżej oznacza blok kodu poniżej jako GET
wniosek. Więcej o tym później.
Moduł
Moduł to część programu obsługująca określone zadanie. Moduł w NestJS jest oznaczony przez adnotację klasy z adnotacją @Module()
dekorator. Nest używa metadanych dostarczonych przez @Module()
dekorator do organizowania struktury aplikacji.
Instalowanie CLI
Aby rozpocząć, musisz zainstalować NestJS CLI ****z npm
. Możesz pominąć ten krok, jeśli masz już zainstalowany NestJS CLI w swoim systemie.
npm i -g @nestjs/cli
Powyższy blok kodu zainstaluje gniazdo CLI globalnie w twoim systemie.
Tworzenie nowego projektu
Aby wygenerować nowy projekt, uruchom nest new
a następnie żądaną nazwę projektu. W tym artykule napiszemy prosty interfejs API dla blogów z funkcjonalnością CRUD, przy jednoczesnym przestrzeganiu standardów RESTful.
nest new Blog-Api
To polecenie poprosi Cię o wybranie menedżera pakietów, wybierz npm
.
Spowoduje to następnie utworzenie szkieletu całej struktury projektu z testowym punktem końcowym API, którego port jest ustawiony na 3000
domyślnie. Możesz to przetestować na http://localhost:3000
po uruchomieniu npm run start:dev
polecenie, które uruchomi serwer w trybie czuwania podobnym do tego, co robi nodemon w ekspresowych aplikacjach.
Po przetestowaniu punktu końcowego będziesz musiał usunąć niektóre domyślne pliki, ponieważ nie będziesz ich już potrzebować. Aby to zrobić;
- otwórz folder src i wewnątrz,
- usuń
app.controller.spec.ts
, - usuń
app.controller.ts
, - usuń
app.service.ts
, - Otwórz
app.module.ts
, - Usuń odniesienie do
AppController
wcontrollers
tablica i importy, - Usuń odwołanie do
AppService
wproviders
tablica i importy.
Może być również konieczna zmiana pliku README.md
aby spełnić Twoje wymagania.
Twój app.module.ts
plik powinien wyglądać tak,
//app.module.ts
import { Module } from '@nestjs/common';
@Module({
imports: [],
controllers: [],
providers: [],
})
export class AppModule {}
Zmienne środowiskowe
Dobrą praktyką jest to, że niektóre poufne informacje w Twoim kodzie nie powinny być upubliczniane. Na przykład Twój PORT
i Twój MongoDB URI
.
Naprawmy to w kodzie.
Na twoim terminalu biegnij
npm i dotenv
Następnie utwórz plik .env
w swoim katalogu i dodaj go do pliku .gitignore
plik. Przechowuj swój PORT
zmienna, będziesz musiał również przechowywać swój MongoDB URI
później w tym samym miejscu. Teraz zastąp ujawniony PORT
w swoim main.ts
plik. Aby to zrobić, zaimportuj dotenv
pakiet i wywołaj .config()
metoda na to.
import * as dotenv from 'dotenv';
dotenv.config();
To powinien być Twój main.ts
plik po wykonaniu powyższych kroków.
//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';
dotenv.config();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT);
}
bootstrap();
Generowanie modułów
Aby wygenerować moduł NestJS za pomocą NestJS CLI, uruchom poniższy fragment kodu.
nest generate module blogs
To polecenie tworzy blogs
folder zawierający blogs.module.ts
plik i rejestry BlogsModule
w swoim app.module.ts
plik.
Generowanie interfejsów
Wygenerujmy interfejs za pomocą NestJS CLI, aby sprawdzić typ obiektu, który będzie reprezentował Twoje posty na blogu. Aby to osiągnąć, musisz najpierw cd
do blogs
folder, ponieważ zaleca się, aby były przechowywane w pobliżu obiektów domeny, z którymi są skojarzone.
cd src/blogs
Następnie uruchom poniższy fragment kodu, aby wygenerować interfejs.
nest generate interface blogs
to tworzy blogs.interface.ts
plik. Tutaj zdefiniujemy nasz interfejs. nazwiemy interfejs BlogsInterface
.
export interface BlogsInterface {
title: string;
body: string;
category: string;
dateCreated: Date;
}
przed uruchomieniem kolejnych poleceń na swoim terminalu pamiętaj o cd
z src
i wróć do folderu głównego, uruchamiając
cd ../..
Generowanie usług i kontrolerów
Będziesz musiał wygenerować klasę usług do przechowywania i pobierania danych oraz obsługi całej logiki i klasę kontrolera do obsługi wszystkich przychodzących żądań i wychodzących odpowiedzi.
Usługa
Aby wygenerować usługę uruchom poniższe polecenie,
nest generate service blogs
To polecenie tworzy dwa pliki blogs.service.spec.ts
i blogs.service.ts
i rejestruje usługę w providers
tablica w blogs.module.ts
.
Kontroler
Aby wygenerować kontroler, uruchom poniższe polecenie,
nest generate controller blogs
To polecenie tworzy dwa pliki blogs.controller.spec.ts
i blogs.controller.ts
i rejestruje kontroler w controllers
tablica w blogs.module.ts
.
Dzięki nim struktura Twoich blogów jest prawie kompletna, wystarczy utworzyć BlogsService
dostępne dla innych części programu. Możesz to osiągnąć, tworząc exports
tablica w blogs.module.ts
plik i rejestracja usługi BlogsService
w tej tablicy.
//blogs.module.ts
import { Module } from '@nestjs/common';
import { BlogsService } from './blogs.service';
import { BlogsController } from './blogs.controller';
@Module({
providers: [BlogsService],
controllers: [BlogsController],
exports: [BlogsService],
})
export class BlogsModule {}
MongoDB(Mongoose).
Zainstaluj mangusę, uruchamiając,
npm install --save @nestjs/mongoose mongoose
Po instalacji zaimportuj {MongooseModule}
od '@nestjs/mongoose’
do Twojego app.module.ts
plik. Następnie pobierz swój MongoDB URI
i przechowuj go w swoim .env
plik. Powtórz kroki, aby zaimportować dotenv
w app.module.ts
plik. Następnie w imports
wywołanie tablicy .forRoot()
metoda, która pobiera Twój MongoDB URI
jako argument w MongooseModule
. Podobne do mongoose.connect()
w zwykłych aplikacjach ekspresowych.
@Module({
imports: [BlogsModule, MongooseModule.forRoot(process.env.MONGODB_URI)],
Tworzenie schematu.
Stwórzmy schemat do zdefiniowania kształtu blogów w naszej kolekcji. Aby to zrobić,
- Utwórz folder w swoich
blogs
folder, nazwij goschemas
, - Wewnątrz
schemas
folderu, utwórz plik i nazwij goblogs.schema.ts
.
Następnie
Po pierwsze, musisz,
- Importuj
prop
dekorator,Schema
dekorator iSchemaFactory
z@nestjs/mongoose
, - Utwórz klasę
Blog
i wyeksportuj go, - Zamień klasę w schemat, umieszczając
@Schema()
dekorator nad klasą, - Utwórz stały
BlogSchema
, przypisz wartość zwracaną wywołania.createForClass(Blog)
z nazwą Twojej klasy jako argumentem wSchemaFactory
które zaimportowałeś wcześniej.
//blogs.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Blog {}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Następnie musisz zdefiniować właściwości schematu.
Aby zdefiniować właściwość w schemacie, musisz oznaczyć każdą z nich za pomocą @prop()
dekorator. @prop
dekorator akceptuje obiekt opcji lub deklarację typu złożonego. Deklaracje typu złożonego mogą być tablicami i zagnieżdżonymi deklaracjami typu obiektu.
//blogs.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Blog {
@Prop({ required: true })
title: string;
@Prop({ required: true })
body: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
dateCreated: Date;
}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Następny import { Document }
z 'mongoose'
.
Następnie utwórz typ unii z klasą Schema i zaimportowanym Document
. tak jak tak,
//blogs.schema.ts
import { Document } from 'mongoose';
export type BlogDocument = Blog & Document;
Twój końcowy blogs.schema.ts
plik powinien wyglądać tak,
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type BlogDocument = Blog & Document;
@Schema()
export class Blog {
@Prop({ required: true })
title: string;
@Prop({ required: true })
body: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
dateCreated: Date;
}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Schemat rejestracji
Musisz zaimportować wszystko do swojego blogs.module.ts
plik. Aby to osiągnąć, musisz,
- Importuj
{MongooseModule}
od'@nestjs/mongoose’
, - Importuj
{Blog, BlogSchema}
z'./schemas/blogs.schema’
- Utwórz
imports
tablica wewnątrz@module
dekorator - Wywołaj
.forFeature()
metoda naMongooseModule
. Pobiera to tablicę zawierającą obiekt, który definiujename
ischema
właściwość, która powinna być ustawiona naBlog.name
i TwójBlogSchema
odpowiednio.
@Module({
imports: [
MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
],
Schemat wstrzykiwania
Musisz wstrzyknąć Blog
modelu do blogs.service.ts
za pomocą @InjectModel()
dekorator. Aby to osiągnąć, musisz
- importuj
{ Model }
z'mongoose'
, - importuj
{ InjectModel }
z'@nestjs/mongoose’
, - Importuj
{Blog, BlogDocument}
z'./schemas/blogs.schema’
, - Utwórz
constructor
wewnątrzBlogsService
klasa, - Zadeklaruj
private
zmienną i nazwij jąblogModel
i przypisz typModel<BlogDocument>
do niego. Wszystkie metody mangusty zostaną wywołane na tej zmiennej.
Przypomnij sobie, BlogDocument
jest typem unii Blog
klasa i Mongoose Model
które stworzyłeś wcześniej. Jest używany jako typ ogólny dla Twojej zmiennej.
- Udekoruj
blogModel
z@InjectModel()
i przekażBlog.name
jako argument.
constructor(
@InjectModel(Blog.name)
private blogModel: Model<BlogDocument>,
) {}
Jak działa routing
Do tej pory musiałeś zauważyć, że @Controller
dekorator ma ciąg 'blogs'
przeszedł do niego. Oznacza to, że kontroler wyśle wszystkie odpowiedzi i obsłuży wszystkie żądania wysłane na http://localhost/3000/blogs
.
Następnie zaimplementujesz logikę usługi i kontrolera.
Logika serwisowa i kontrolera.
W końcu nadszedł czas na wdrożenie funkcjonalności CRUD.
Zanim zaczniemy, musisz skonfigurować kontroler. Zacznij od zaimportowania niektórych HTTP
dekoratorów metod do kontrolera.
//blogs.controller.ts
import {
Controller,
Body,
Delete,
Get,
Post,
Put,
Param,
} from '@nestjs/common';
Następnie musisz zaimportować usługę i zarejestrować ją, aby móc uzyskać do niej dostęp i zaimportować interfejs w celu sprawdzenia typu.
//blogs.controller.ts
import { BlogsInterface } from './blogs.interface';
import { BlogsService } from './blogs.service';
Aby zarejestrować swoją usługę, utwórz constructor
wewnątrz BlogsController
klasę i zadeklaruj private readonly
zmienna service
i ustaw jego typ na BlogsService
.
constructor(private readonly service: BlogsService) {}
Teraz, gdy wszystko jest gotowe, zacznijmy.
Utwórz
Logika usługi
Importuj { BlogsInterface }
z './blogs.interface'
i dodaj async
funkcji do BlogsService
klasa o nazwie createBlog
, który przyjmie jeden parametr blog
, którego typ to BlogInterface
, a jego zwracany typ jako Promise
z ogólnym <Blog>
rodzaj.
async createBlog(blog: BlogsInterface): Promise<Blog> {
return await new this.blogModel({
...blog,
dateCreated: new Date(),
}).save();
}
Logika kontrolera
W swoim BlogsController
klasa dodaj async
funkcji do klasy. Nazwij to createBlog
i oznacz go @Post
dekorator, który definiuje go jako POST
request.createBlog
przyjmuje jeden parametr blog
, którego typ to BlogInterface
. Oznacz parametr za pomocą @Body
dekorator, który wyodrębnia całe body
obiekt z req
obiektu i wypełnia parametr dekorowany wartością body
.
@Post()
async createBlog(
@Body()
blog: BlogsInterface,
) {
return await this.service.createBlog(blog);
}
Przeczytaj
Dodaj dwa async
metody, jedna do zwrócenia pojedynczego posta na blogu, a druga do zwrócenia wszystkich postów na blogu.
Logika usługi
async getAllBlogs(): Promise<Blog[]> {
return await this.blogModel.find().exec();
}
async getBlog(id: string): Promise<Blog> {
return await this.blogModel.findById(id);
}
Logika kontrolera
@Get()
async getAllBlogs() {
return await this.service.getAllBlogs();
}
@Get(':id')
async getBlog(@Param('id') id: string) {
return await this.service.getBlog(id);
}
async
funkcje są oznaczone @Get
dekorator, który definiuje go jako GET
żądanie.
Drugi async
dekorator funkcji ma argument ':id'
. To właśnie przekażesz do @Param
dekorator. Parametr jest oznaczony @Param('id')
który wyodrębnia params
właściwość z req
obiektu i wypełnia parametr dekorowany wartością params
.
Aktualizacja
Zaimplementujmy logikę dla PUT
żądanie.
Logika usługi
async updateBlog(id: string, body: BlogsInterface): Promise<Blog> {
return await this.blogModel.findByIdAndUpdate(id, body);
}
Logika kontrolera
@Put(':id')
async updateBlog(
@Param('id')
id: string,
@Body()
blog: BlogsInterface,
) {
return await this.service.updateBlog(id, blog);
}
async
drugi parametr funkcji jest oznaczony @Body()
dekorator, który wyodrębnia całe body
obiekt z req
obiektu i wypełnia parametr dekorowany wartością body
.
Usuń
Zaimplementujmy logikę delete
prośby.
Logika usługi
async deleteBlog(id: string): Promise<void> {
return await this.blogModel.findByIdAndDelete(id);
}
Promise
typ ogólny to void
ponieważ Delete
żądanie zwraca pustą obietnicę.
Logika kontrolera
@Delete(':id')
async deleteBlog(@Param('id') id: string) {
return await this.service.deleteBlog(id);
}
Testowanie API
Aby przetestować ten interfejs API, powinieneś użyć narzędzia do testowania interfejsu API. W tym artykule będę używał popularnego narzędzia do testowania API o nazwie Postman. Do testowania użyję losowych danych dotyczących popularnych tematów.
Utwórz
Wykonaj POST
żądanie do http://localhost/3000/blogs
z następującymi obiektami JSON, spowoduje to dodanie wszystkich danych do Twojej bazy danych.
{
"title": "jeen-yuhs",
"body": "The life of superstar rapper Kanye West is currently streaming on Netflix - and according to our jeen-yuhs review, it's a fascinating watch. -credit:Radio Times",
"category":"Music"
}
{
"title": "Why You Should Always Wash Your Hands",
"body": "Germs from unwashed hands can be transferred to other objects, like handrails, tabletops, or toys, and then transferred to another person's hands.-credit cdc.gov",
"category":"Health"
}
{
"title": "Why You Should Follow me on Twitter",
"body": "Well, Because I asked nicely",
"category":"Random"
}
Powinieneś otrzymać 201
odpowiedź i utworzony blog z datą i _id
dodano.
Przeczytaj
Zrób GET
żądanie do http://localhost/3000/blogs
. Powinno to zwrócić
200
odpowiedź z tablicą wszystkich danych, które wcześniej dodałeś. Skopiuj _id
właściwość jednego z obiektów tablicy.
Zrób kolejny GET
żądanie do http://localhost/3000/blogs/id
z wcześniej skopiowanym identyfikatorem. Powinno to zwrócić 200
odpowiedź z danymi obiektu, którego identyfikator został użyty do wysłania żądania.
Aktualizacja
Wykonaj PUT
żądanie do http://localhost/3000/blogs/id
z danymi poniżej. id
powinien zostać zastąpiony tym, który skopiowałeś wcześniej. Powinno to zwrócić 200
odpowiedź i aktualizuje obiekt noszący id
za kulisami. jeśli uruchomisz inny GET
żądanie powinieneś otrzymać zaktualizowany obiekt.
{
"title": "why you Should Cut your Nails",
"body": "It's important to trim your nails regularly. Nail trimming together with manicures makes your nails look well-groomed, neat, and tidy.- credit:WebMD",
"category":"Health"
}
Usuń
Wykonaj DELETE
żądanie do http://localhost/3000/blogs/id
. Powinno to zwrócić 200
odpowiedź i usuwa obiekt noszący id
za kulisami. jeśli uruchomisz inny GET
żądanie, że nie zobaczysz usuniętego obiektu.
Wniosek
Więc jesteśmy w końcu na końcu tego artykułu. Podsumujmy, co omówiłeś.
- Co to jest NestJS,
- Terminologie w NestJS,
- Tworzenie aplikacji NestJS,
- Integracja MongoDB z aplikacją NestJS,
- Manipulowanie i aplikacja NestJS,
To całkiem sporo, gratulacje, że dotarłeś tak daleko.
Możesz znaleźć kod na github.
Powodzenia w podróży z NestJS!