0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NestJS, GraphQL API, REST API, Apollo, Prismaについてのまとめ!

0
Last updated at Posted at 2026-06-03

目次

本記事について

普段インターン先で使用しているbackendの主な技術スタックの概念を、復習を兼ねてまとめたものです。

NestJSとは?

TypeScriptでバックエンドAPIを作るためのフレームワーク

  • Reactがフロントエンドを作るためのフレームワーク/ライブラリだとすると、NestJSはサーバー側を作るためのもの
  • JavaScript/TypeScriptをサーバー側で動かすための実行環境であるNode.jsで、バックエンドを作りやすくするためのフレームワークがNestJS
  • Expressも同様に有名だが、ファイルをどう分けるかなどがバラバラになりやすい
    • NestJSMVC(Model-View-Controllerの構成)で整理することができる

NestJSの基本構造

  • Controller=HTTPリクエストを受け取る場所
    以下のように書くと、ブラウザで例えばhttp://localhost:3000/helloにアクセスすると、appService.getHello()が実行されることになる
@Controller('hello')
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}
  • Service=実際の処理を書く場所
    ビジネスロジックを書く。Controllerに全部書くのではなく、Serviceにメインの処理を書き分けることでディレクトリの構造がわかりやすくなる
@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}
  • Module=ControllerやServiceをまとめて、NestJSにモジュールとして登録する場所
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

NestJSの導入方法

npx @nestjs/cli new . --skip-git --package-manager npm

をbackendディレクトリで実行してNestJSの雛形を作る。(ルートで既にgit管理している場合は、--skip-gitでbackend用に別のgitを作らせない)

このコマンドによりbackend/src/にできる主なファイル

ファイル 役割
main.ts アプリの入口。サーバーを起動する(デフォルト3000番)
app.module.ts モジュール。機能をまとめる箱。NestJSの中心概念
app.controller.ts コントローラ。HTTPリクエストの受け口(今はREST)
app.service.ts サービス。実際のロジックを書く場所

上述したようにNestJSは「Controller(受け口)→ Service(処理)」と役割を分ける設計が基本。
デフォルトではHTTPリクエスト(POST, GETなど)のエントリーポイントがapp.controller.tsREST APIになっている。
もし、GraphQL APIを使用する場合は、以下の表から分かるように、RESTのControllerGraphQLのResolverに置き換えていくということ。

REST(Controller)API GraphQL(Resolver)API
クラスの目印 @Controller() @Resolver()
取得(読み取り) @Get() @Query()
作成・更新など @Post() @Put() @Patch() @Delete() @Mutation()
入口URL http://localhost:3000/ http://localhost:3000/graphql

要は簡単に言えばどちらも、HTTPリクエストを受け取って、HTTPレスポンスを返すAPIです。
GraphQLの場合、画面ごとに必要なデータが異なる場合には、取得するデータを分けることも可能のため、柔軟性が高いです。

REST APIGraphQL APIについて

どちらもフロントエンドとバックエンドがデータをやり取りするためのAPIの方式

React / Flutter などのフロントエンド
  ↓
REST または GraphQL
  ↓
NestJS などのバックエンド
  ↓
Prisma
  ↓
PostgreSQL

RESTとは?

URLとHTTPメソッドで操作を表すAPI方式

GET /users/1

というリクエストをサーバーに送ったら、バックエンドは例えば以下のようなJSONを返す。

{
  "id": 1,
  "name": "James",
  "email": "james@example.com",
  "age": 10
}

返すデータの形は基本的にサーバー側が決定する。
ただ、フロントエンド側が本当はnameだけ欲しくても、APIがemailageも返す設計なら、不要なそれらも取得してしまうというデメリットもある。

RESTのメリット

シンプルで分かりやすいこと

GET /users
POST /users
PATCH /users/james
DELETE /users/james

のようにURLとHTTPメソッドを見れば何をするAPIなのかをイメージしやすい。
小規模アプリや単純なCRUD(Create-Read-Update-Delete)ならRESTで十分なことが多い。

RESTのデメリット

例えば、一覧画面ではid, nameだけほしいが、詳細画面ではid, name, email, postsもほしい。
その場合、RESTではAPIを複数作ったり、余計なデータを返したりしがち。

  • オーバーフェッチ : 必要ないデータまで取ってしまう
  • アンダーフェッチ : 必要なデータが足りず、追加でAPIを呼ぶ必要がある

GraphQLとは?

フロントエンド側がほしいデータの形を指定できるAPI方式

query {
  user(id: 1) {
    id
    name
  }
}

というGraphQLクエリをサーバーに送ったら、バックエンドは例えば以下のようなJSONを返す。

{
  "data": {
    "user": {
      "id": 1,
      "name": "James"
    }
  }
}

というように、emailを指定しなければ、emailを含めないでレスポンスを得ることができ、不要なデータを取得しないため、パフォーマンスが良い。

GraphQLのメリット

必要なデータだけを指定して取得できること
例えば一覧画面では、

query {
  users {
    id
    name
  }
}

詳細画面では、

query {
  user(id: 1) {
    id
    name
    email
    posts {
      id
      title
    }
  }
}

のように、画面ごとに欲しいデータを変えられる。

GraphQLのデメリット

  • 便利だが、RESTより覚えるものが多くて学習コストが高い
schema
type
query
mutation
resolver
input
fragment
Apollo Client
codegen
  • フロント側が自由にデータを指定できる分、バックエンド側ではパフォーマンスに注意が必要
    • 深い関連データを大量に取得すると、DBクエリが重くなることがある
    • オーバーフェッチやN+1問題につながることもある

RESTGraphQLの違い

項目 REST GraphQL
基本の考え方 URLとHTTPメソッドで操作する クエリで欲しいデータを指定する
エントリーポイント Controllerで受け取る Resolverで受け取る
エンドポイント 複数になりやすい 基本は /graphql 1つ
データの形 サーバー側が決める フロント側が指定する
取得 GET Query
作成・更新・削除 POST, (PUT, PATCH), DELETE mutation
柔軟性 普通 高い
小規模アプリ 向いている 少し大げさな場合もある
複雑な業務画面 APIが増えやすい 関連データをまとめやすい
// REST APIのController
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller('hello')
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}
// GraphQL APIのResolver
import { Query, Resolver } from '@nestjs/graphql';
import { AppService } from './app.service';

@Resolver()
export class AppResolver {
  constructor(private readonly appService: AppService) {}
  
  @Query(() => String)
  getHello(): string {
    return this.appService.getHello();
  }
}

Apolloとは?

=GraphQLをフロントエンド・バックエンドで扱いやすくするためのライブラリ群
ReactからGraphQLを使うときによく出てくる。
特に主要なライブラリがApollo ClientApollo Serverの2つ。

Apollo Client : フロントエンド側で GraphQL API を呼ぶためのライブラリ

  • ReactからGraphQL APIを呼び出すために使う
    以下のようにgqlを使って@Query()を定義し、

    import { gql, useQuery } from '@apollo/client'
    
    const GET_ITEMS = gql`
      query GetItems {
        items {
          id
          name
        }
      }
    `    
    

    useQueryを使うことでGraphQLにおけるデータ取得の@Query()を実行できる

    const { data, loading, error } = useQuery(GET_ITEMS)
    
    • data : 取得できたデータ
    • loading : 読み込み中かどうか
    • error : エラー情報

    同様に、以下のようにgqlを使うことで@mutation()を定義し、useMutationを使うことでGraphQLにおけるデータの追加・更新・削除の@mutation()を実行できる

    import { gql, useMutation } from '@apollo/client'
    
    const CREATE_ITEM = gql`
      mutation CreateItem($name: String!) {
        createItem(input: { name: $name }) {
          id
          name
        }
      }
    `  
    export function AddItemButton() {
        const [createItem, { loading }] = useMutation(CREATE_ITEM)    
        const handleClick = async () => {
            await createItem({
              variables: {
                title: '勉強する',
              },
            })
          }     
    }
    
    

Apollo Clientの初期設定

ReactアプリでApollo Clientを使う時は、最初にクライアントを作る

import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'

const client = new ApolloClient({
  uri: 'http://localhost:3000/graphql',
  cache: new InMemoryCache(),
})

そしてアプリ全体をApolloProviderで包むことで、アプリ内のどこでもuseQueryuseMutationが使えるようになる

<ApolloProvider client={client}>
  <App />
</ApolloProvider>

InMemoryCache=Apollo Clientのキャッシュ機能

GraphQLで取得したデータをメモリ上に保存しておける
例えば一度取得したデータを別の画面でまた使う場合に、毎回サーバーへ取りに行かずに済む!
ただ、キャッシュが効くことでDBでは更新されているのに画面が古いままということも起きる
fetchPolicyを設定する

fetchPolicy=データをキャッシュから読むか、毎回ネットワークから取るかを指定する

以下の場合は、キャッシュを使わず、毎回サーバーから取得する

const { data } = useQuery(GET_ITEMS, {
  fetchPolicy: 'no-cache',
})
fetchPolicy 意味
cache-first まずキャッシュを見る。なければ通信する
network-only 毎回通信するが、結果はキャッシュに保存する
no-cache 毎回通信し、キャッシュにも保存しない
cache-and-network キャッシュを即表示しつつ、裏で最新データも取りに行く

network-only のキャッシュ保存結果は、以下のような場面で使われます。

  1. 他の cache-first / cache-only のクエリで再利用できる
  2. 同じエンティティを使う別画面・別コンポーネントの表示に使える
  3. nextFetchPolicy で2回目以降 cache-first にしたときに使える
  4. mutation後のキャッシュ更新などと連動できる

Apollo Server : バックエンド側で GraphQL API を作るためのライブラリ

NestJSを使う場合は、NestJSのGraphQL機能の裏側でApollo Serverを使うことが多い

GraphQLModule.forRoot<ApolloDriverConfig>({
  driver: ApolloDriver,
  autoSchemaFile: true,
})

ApolloDriverがNestJSでApollo系(Apollo以外にもう一つMercuriusがあるが、Fastify向けのため、今回は無視)のGraphQLサーバーを使うための設定
RESTならController、GraphQLならResolverを作るが、Resolverに来たGraphQLリクエストを処理するための土台としてApollo Server/Apollo Driverが使われている

GraphQL APIApolloのまとめ

項目 GraphQL Apollo
正体 APIの仕様・問い合わせ言語 GraphQLを使いやすくするライブラリ群
役割 「どういう形式でデータを取得・更新するか」を決める GraphQLの通信・キャッシュ・状態管理などを便利にする
例えるなら ルール・言語 そのルールを使うための道具
フロントエンドでの役割 querymutation の書き方を決める useQuery / useMutation でGraphQL APIを呼ぶ
バックエンドでの役割 Schema / Query / Mutation / Type の構造を決める Apollo ServerとしてGraphQL APIを動かす
Reactとの関係 Reactから直接GraphQLを送ることもできる ReactでGraphQLを扱いやすくするApollo Clientがよく使われる
NestJSとの関係 NestJSでGraphQL APIを作れる ApolloDriver を使うと、NestJSのGraphQLサーバー実装としてApolloを使う
必須か? GraphQL APIを使うなら必要 必須ではない。Apollo以外に urql / Relay / Mercurius などもある
代表的なコード query { todos { id title } } const { data } = useQuery(GET_TODOS)

Prismaとは?

TypeScript / Node.jsからDBを型安全に 操作するためのORM
→ NestJSのServiceなどからDBを直接SQLで操作する代わりに、Prismaを使ってDB操作を行う

  • ORM=Object Relational Mapping
    • プログラム上のオブジェクトとDBのテーブルを自動で繋ぐ技術・手法
    • SQLを直接書く代わりにプログラミング言語の構文でDB操作ができるため、可読性が高い

Prismaの構成要素

1. Prisma Schema

schema.prismaというDBの構造を書くファイル

2. Prisma Client

実際にTypeScriptからDBを操作するためのもの
schema.prismaを書いた後に、

npx prisma generate

を実行すると、schema.prismaを元にPrisma Clientが生成される

3. Prisma Migrate

schema.prismaの変更をDBに反映する仕組み

npx prisma migrate dev --name マイグレーション名

を実行すると、PrismaがSQL migrationファイルを作って、schema.prismaの変更をDBに反映する

4. Prisma Studio

DBの中身をブラウザで確認・編集できるGUIツール

npx prisma studio

複雑なSQLやpgvectorのような特殊な検索では$queryRawを使い生のSQLを書くこともある

const results = await prisma.$queryRaw`
  SELECT *
  FROM documents
  ORDER BY embedding <=> ${embedding}::vector
  LIMIT 5
`

つまり、
普段のCRUD→PrismaClient
複雑なSQL→$queryRaw

Prismaの導入(Prisma7)

backendディレクトリで以下のコマンドを実行して、必要なパッケージをインストールする。

npm install prisma @prisma/adapter-pg pg @prisma/client
npm install -D @types/pg
  • @prisma/adapter-pg : Prisma7がPostgreSQLに接続するためのadapter
  • pg : Node.js用のPostgreSQLドライバ
  • @types/pg : TypeScript用の型定義
  • @prisma/client : PrismaClientの生成場所がoutputで指定した場所になったPrisma7だが、outputに生成されたgenerated/prisma/client.tsの内部では`import * as runtime from "@prisma/client/runtime/client"というruntimeを読んでいるため、@prisma/clientは必要

インストールが済んだら、以下のコマンドでPrismaの初期化する。

npx prisma init

このコマンドによって、Prisma 7の場合、backendディレクトリに以下が生成される。

backend/
├── prisma/schema.prisma
├── .env
├── prisma.config.ts

schema.prismaは以下のようになる。

generator client {
  provider = "prisma-client"
  output = "../generated/prisma" 
}

datasource db {
  provider = "postgresql"
}
  • provider = "prisma-client"
    • Prisma7推奨寄りの新しいPrisma Client生成方式
    • 従来のPrisma6のprovider = "prisma-client-js"@prisma/clientパッケージの中に生成する方式
    • Prisma7 → outputで指定した場所からimport
    • Prisma6 → @prisma/clientパッケージからimport

prisma.config.tsは以下のようになっている。
スキーマの場所、migrationファイルの場所、dbのurlの指定を行っている。

import 'dotenv/config';
import { defineConfig } from 'prisma/config';

export default defineConfig({
  schema: 'prisma/schema.prisma',
  migrations: {
    path: 'prisma/migrations',
  },
  datasource: {
    url: process.env['DATABASE_URL'],
  },
});

最後に

普段使用している技術がどのようなものなのかについて、使用する前にも学習したが、ある程度使用した後に改めて学習すると、理解できることも多いと思います。以上です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?