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 https://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
AppControllerwcontrollerstablica i importy, - Usuń odwołanie do
AppServicewproviderstablica 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
blogsfolder, nazwij goschemas, - Wewnątrz
schemasfolderu, utwórz plik i nazwij goblogs.schema.ts.
Następnie
Po pierwsze, musisz,
- Importuj
propdekorator,Schemadekorator iSchemaFactoryz@nestjs/mongoose, - Utwórz klasę
Blogi 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 wSchemaFactoryktó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
importstablica wewnątrz@moduledekorator - Wywołaj
.forFeature()metoda naMongooseModule. Pobiera to tablicę zawierającą obiekt, który definiujenameischemawłaściwość, która powinna być ustawiona naBlog.namei TwójBlogSchemaodpowiednio.
@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
constructorwewnątrzBlogsServiceklasa, - Zadeklaruj
privatezmienną i nazwij jąblogModeli 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
blogModelz@InjectModel()i przekażBlog.namejako 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 https://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 https://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 https://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 https://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 https://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 https://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!