これは Nest.js の速習コースです。このチュートリアルでは、わずか数ステップで、10 分未満で最初のコード行からデプロイまでのブログを構築します。
これほど速い理由は、このチュートリアルではプロセスの詳細な説明には踏み込まず、代わりに完成品を構築するように直接案内するからです。フレームワークの学習は、既存のプロジェクトを自分のニーズに合わせて変更する方が速いと信じています。
このブログは、純粋な Node.js バックエンドプロジェクトで一般的なテクノロジースタックを表す 3 つの機能モジュールで構成されています。
- Nest.js
- データベースとして PostgreSQL
- ページレンダリングのために ejs
早速始めましょう。
1. プロジェクトの初期化
Nest.js プロジェクトは、プロジェクト管理のために CLI ツールに大きく依存しています。まず、Nest.js CLI をインストールしましょう。
npm i -g @nestjs/cli
CLI を使用して新しいプロジェクトを作成します。
nest new personal-blog
これにより、personal-blog
という名前の新しいフォルダが作成され、必要なすべての依存関係がインストールされます。お好みのエディタでこのディレクトリを開いて、正式に編集を開始してください。
2. PostgreSQL データベースへの接続
次に、PostgreSQL データベースを統合します。公式の推奨事項に従って、ORM として TypeORM を使用します。ORM の役割は、データベースをコードに統合することです。
依存関係のインストール
npm install @nestjs/typeorm typeorm pg
-
@nestjs/typeorm
: TypeORM の公式 Nest.js モジュールで、TypeORM を Nest.js に適応させます。 -
typeorm
: TypeORM ライブラリ自体。 -
pg
: PostgreSQL 用の Node.js ドライバーで、Node.js が PostgreSQL の読み書きを可能にします。
データベースの設定
チュートリアルを高速化するために、ローカルでのデータベースのインストールとセットアップの手順はスキップします。代わりに、オンラインデータベースを直接プロビジョニングします。
Leapcellでワンクリックで無料データベースを作成できます。
ウェブサイトでアカウントを登録した後、「Create Database」をクリックします。
データベース名を入力し、デプロイメントリージョンを選択すると、PostgreSQL データベースを作成できます。
表示される新しいページで、データベースに接続するために必要な情報が見つかります。下部にはコントロールパネルが用意されており、ウェブページ上で直接データベースを読み取ったり変更したりできます。
データベース接続の設定
src/app.module.ts
ファイルを開き、TypeOrmModule
をインポートします。
Leapcell からのデータベース設定を使用して接続情報に記入します。ssl
はtrue
に設定する必要があることに注意してください。そうしないと、接続が失敗します。
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'your_leapcell_host', // Replace with your Leapcell host
port: 5432,
username: 'your_postgres_username', // Replace with your PostgreSQL username
password: 'your_postgres_password', // Replace with your PostgreSQL password
database: 'personal_blog_db', // Replace with your database name
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // Set to true in development, it automatically syncs the database schema
ssl: true, // Required for services like Leapcell
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Posts モジュールの作成
次に、ブログ投稿を管理するモジュールを作成します。
Nest CLI を使用して、必要なファイルを迅速に生成できます。
nest generate module posts
nest generate controller posts
nest generate service posts
その後、データベースに接続するためにPost
エンティティファイルを作成する必要があります。src/posts
ディレクトリで、post.entity.ts
という名前のファイルを作成します。
// src/posts/post.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
@Entity()
export class Post {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
title: string;
@Column('text')
content: string;
@CreateDateColumn()
createdAt: Date;
}
次に、データベースに接続する必要があります。
PostsModule
でTypeOrmModule
を登録します。src/posts/posts.module.ts
を開き、TypeOrmModule.forFeature([Post])
をインポートします。
// src/posts/posts.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PostsController } from './posts.controller';
import { PostsService } from './posts.service';
import { Post } from './post.entity';
@Module({
imports: [TypeOrmModule.forFeature([Post])],
controllers: [PostsController],
providers: [PostsService],
})
export class PostsModule {}
Leapcellのデータベース詳細ページに移動し、Web エディタで次のコマンドを実行して、対応するテーブルを生成します。
CREATE TABLE "post" (
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"title" VARCHAR NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
最後に、PostsService
を作成する必要があります。PostsService
は、投稿に関連するすべてのビジネスロジックを処理する責任があります。src/posts/posts.service.ts
を開き、次のコードを追加します。
// src/posts/posts.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Post } from './post.entity';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Post)
private postsRepository: Repository<Post>
) {}
findAll(): Promise<Post[]> {
return this.postsRepository.find({
order: {
createdAt: 'DESC',
},
});
}
findOne(id: string): Promise<Post> {
return this.postsRepository.findOneBy({ id });
}
async create(post: Partial<Post>): Promise<Post> {
const newPost = this.postsRepository.create(post);
return this.postsRepository.save(newPost);
}
}
EJS によるページレンダリングの設定
次に、動的な HTML ページをレンダリングできるように EJS を設定します。
依存関係のインストール
npm install ejs
ビューエンジンの統合
src/main.ts
ファイルを開き、次のように変更します。
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('ejs');
await app.listen(3000);
}
bootstrap();
プロジェクトのルートディレクトリ(src
と同じレベル)に、views
とpublic
フォルダを作成します。
- personal-blog
- src
- views
- public
フロントエンドページの実装
views
フォルダに次のファイルを作成します。
-
_header.ejs
(再利用可能なヘッダー)<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title><%= title %></title> <link rel="stylesheet" href="/css/style.css" /> </head> <body> <header> <h1><a href="/">My Blog</a></h1> <a href="/posts/new" class="new-post-btn">New Post</a> </header> <main></main> </body> </html>
-
_footer.ejs
(再利用可能なフッター)</main> <footer> <p>© 2025 My Blog</p> </footer> </body> </html>
-
index.ejs
(ブログホームページ)<%- include('_header', { title: 'Home' }) %> <div class="post-list"> <% posts.forEach(post => { %> <article class="post-item"> <h2><a href="/posts/<%= post.id %>"><%= post.title %></a></h2> <p><%= post.content.substring(0, 150) %>...</p> <small><%= new Date(post.createdAt).toLocaleDateString() %></small> </article> <% }) %> </div> <%- include('_footer') %>
-
post.ejs
(投稿詳細ページ)<%- include('_header', { title: post.title }) %> <article class="post-detail"> <h1><%= post.title %></h1> <small><%= new Date(post.createdAt).toLocaleDateString() %></small> <div class="post-content"><%- post.content.replace(/\n/g, '<br />') %></div> </article> <a href="/" class="back-link">← Back to Home</a> <%- include('_footer') %>
-
new-post.ejs
(新規投稿ページ)<%- include('_header', { title: 'New Post' }) %> <form action="/posts" method="POST" class="post-form"> <div class="form-group"> <label for="title">Title</label> <input type="text" id="title" name="title" required /> </div> <div class="form-group"> <label for="content">Content</label> <textarea id="content" name="content" rows="10" required></textarea> </div> <button type="submit">Submit</button> </form> <%- include('_footer') %>
2. CSS スタイルの追加
public/css/style.css
に簡単なスタイルを追加します。
/* public/css/style.css */
body {
font-family: sans-serif;
line-height: 1.6;
margin: 0;
background-color: #f4f4f4;
color: #333;
}
header {
background: #333;
color: #fff;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
header a {
color: #fff;
text-decoration: none;
}
main {
max-width: 800px;
margin: 2rem auto;
padding: 1rem;
background: #fff;
border-radius: 5px;
}
.post-item {
margin-bottom: 2rem;
border-bottom: 1px solid #eee;
padding-bottom: 1rem;
}
.post-item h2 a {
text-decoration: none;
color: #333;
}
.post-detail .post-content {
margin-top: 1rem;
}
.new-post-btn {
background: #5cb85c;
padding: 0.5rem 1rem;
border-radius: 5px;
}
.post-form .form-group {
margin-bottom: 1rem;
}
.post-form label {
display: block;
margin-bottom: 0.5rem;
}
.post-form input,
.post-form textarea {
width: 100%;
padding: 0.5rem;
}
.post-form button {
background: #337ab7;
color: #fff;
padding: 0.7rem 1.5rem;
border: none;
cursor: pointer;
}
footer p {
text-align: center;
}
バックエンドとフロントエンドページの接続
HTTP リクエストを処理し、EJS テンプレートをレンダリングするために、src/posts/posts.controller.ts
を更新します。
// src/posts/posts.controller.ts
import { Controller, Get, Render, Param, Post, Body, Res } from '@nestjs/common';
import { PostsService } from './posts.service';
import { Response } from 'express';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
@Render('index')
async root() {
const posts = await this.postsService.findAll();
return { posts };
}
@Get('new')
@Render('new-post')
newPostForm() {
return;
}
@Post()
async create(@Body() body: { title: string; content: string }, @Res() res: Response) {
await this.postsService.create(body);
res.redirect('/posts');
}
@Get(':id')
@Render('post')
async post(@Param('id') id: string) {
const post = await this.postsService.findOne(id);
return { post };
}
}
次に、ルートパス/
がブログホームページにリダイレクトされるようにsrc/app.controller.ts
を更新します。
// src/app.controller.ts
import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
root(@Res() res: Response) {
return res.redirect('/posts');
}
}
ブログの実行
ブログを開始するには、ターミナルで次のコマンドを実行します。
npm run start:dev
ブラウザでhttp://localhost:3000
を開いてブログページを表示します。新しい投稿を書いてみてください!
これで、ウェブサイトと API を自由にカスタマイズして、Nest.js の理解を深めることができます。
ブログのオンラインデプロイ
さて、自分で作ったウェブサイトを他の人に見せて、誰でもアクセスできるようにするにはどうすればよいか考えているかもしれません。
以前データベースを作成するために使用したLeapcellを覚えていますか?Leapcell はデータベースを作成するだけでなく、Nest.js を含むさまざまな言語やフレームワークのプロジェクトをホストできる Web アプリケーションホスティングプラットフォームでもあります。
以下の手順に従ってください。
- プロジェクトを GitHub にコミットします。GitHub の公式ドキュメントを参照して手順を確認してください。Leapcell は後で GitHub リポジトリからコードをプルします。
- Leapcell ページで「Create Service」をクリックします。
- Nest.js リポジトリを選択すると、Leapcell が必要な構成を自動的に入力します。
- 下部にある「Submit」をクリックしてデプロイします。デプロイはすぐに完了し、デプロイメントホームページに戻ります。ここで、Leapcell がドメインを提供していることがわかります。これがブログのオンラインアドレスです。
これで、このリンクを友人と共有すれば、誰もがオンラインであなたのブログを見ることができます!
X でフォローする:@LeapcellJP
関連記事: