最初の1行のコードからライブデプロイまで10分:超高速Nest.jsブログコース
Daniel Hayes
Full-Stack Engineer · Leapcell

これは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がドメインを提供していることがわかります。これがブログのオンラインアドレスです。
これで、このリンクを友人と共有すれば、誰もがオンラインであなたのブログを見ることができます!