⛰️

Nuxt 3 Beta から Laravel 8 で構築したAPIを叩いてみる

2022/02/28に公開

はじめに

趣味と(業務で使用することになるLaravel + Nuxt構成の)キャッチアップもかねてLaravel + Nuxt構成の個人開発を始めています。

どうせなら「なるべく新しいものを使用したい!」との考えからそれぞれの現状の最新メジャーバージョンであるLaravel8 + Nuxt3(Vue3)の構成でアプリケーションを構築することにいたしました。(ただNuxt3は現時点でBetaであり、案件での採用は全くもって現実的とは言えないようです。先んじてキャッチアップしておくことで来るべき本採用に備えようという魂胆です。)

今回の目的

Dockerを使用してLaravel8のAPIサーバーコンテナとNuxt3のフロントコンテナを用意して、nginxでそれぞれをリバースプロキシさせます。その上でNuxt側からLaravelのAPIを叩く処理までやっていきます。

注意事項

この記事から学べること・学べないこと

  • 学べること
    • nuxt3 + Laravel8環境のセットアップ
    • Laravel8で作成したAPIをnuxt3から実行する方法
    • 上記の環境をDockerを用いて構築する
    • nginxを用いたリバースプロキシの設定
  • 学べないこと
    • nuxtやLaravelなどの各技術の基礎知識
    • 各技術の新しいバージョンに関する詳細情報
    • Docker以外の開発環境のセットアップ
    • フロントとサーバーを分離しない構成

使用技術

技術名 バージョン この記事での用途
Node 17.5.0 Nuxt用のコンテナに使用されるNode.js
Nuxt 3.0.0 フロント用のフレームワーク(APIサーバーとは分離想定)
Vue 3.2.31 Javascriptライブラリ
Laravel 8.80.0 APIサーバー
Docker 20.10.12 ローカル開発環境の構築
Nginx 1.20.2 フロント・サーバーそれぞれのリバースプロキシ

実際のアプリケーションは上記の他にもデータベースなどありますが、本記事の趣旨と反するため割愛させていただきます。

実際にやっていきます

ディレクトリ構成

最終的には以下のようなディレクトリ構成になります。

- sample
  - frontend # フロントエンドリソース
  - backend # バックエンドリソース
  - docker
    - front
      Dockerfile
    - php
      Dockerfile
      php.ini
      - php-fpm.d
        zzz-www.conf
    - nginx
      Dockerfile
      default.conf
  docker-compose.yml

docker-composeファイルの作成

sampleディレクトリの中にdocker-compose.ymlを作成します。

version: "3.9"
volumes:
  php-fpm-socket:
  psysh-store:

services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    container_name: sample_app
    volumes:
      - type: volume
        source: php-fpm-socket
        target: /var/run/php-fpm
        volume:
          nocopy: true
      - type: bind
        source: ./backend
        target: /work/backend
      - type: volume
        source: psysh-store
        target: /root/.config/psysh
        volume:
          nocopy: true

  web:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    container_name: sample_nginx
    ports:
      - ${WEB_PORT}:80
      - "9000:9000"
    volumes:
      - type: volume
        source: php-fpm-socket
        target: /var/run/php-fpm
        volume:
          nocopy: true
      - type: bind
        source: ./backend
        target: /work/backend
    
  front:
    build:
      context: .
      dockerfile: ./docker/front/Dockerfile
    container_name: sample_front
    volumes:
      - ./frontend:/var/www/nuxt
    ports:
      - ${FRONT_PORT}:3000
      - '24678:24678'

docker-composeのバージョンは3.9を使用しています。
appコンテナがLaravel webコンテナがnginx frontコンテナがnuxtとなります。
container_nameは必ず指定してください。(コンテナ間通信する際にこの名前を使用します。)

ports:
   - ${WEB_PORT}:80
   - "9000:9000"

上記のようにnginxでポートを2つ指定しているのはフロント、サーバー共にリバースプロキシさせるためです。(80番アクセスでフロント、9000番アクセスでAPI)

PHPコンテナ

Dockerfile

docker/php/Dockerfile
FROM php:8.1-fpm-bullseye
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

# timezone environment
ENV TZ=UTC \
  # locale
  LANG=en_US.UTF-8 \
  LANGUAGE=en_US:en \
  LC_ALL=en_US.UTF-8 \
  # composer environment
  COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.1 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  locale-gen en_US.UTF-8 && \
  localedef -f UTF-8 -i en_US en_US.UTF-8 && \
  mkdir /var/run/php-fpm && \
  docker-php-ext-install intl pdo_mysql zip bcmath && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://packagist.org

COPY ./docker/php/php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini

WORKDIR /work/backend

php.iniとzzz-www.confのコードは割愛させていただきます。

nginxコンテナ

Dockerfile

docker/nginx/Dockerfile
FROM node:16-alpine as node
FROM nginx:1.20-alpine
SHELL ["/bin/ash", "-oeux", "pipefail", "-c"]

ENV TZ=UTC

RUN apk update && \
  apk add --update --no-cache --virtual=.build-dependencies g++

# node command
COPY --from=node /usr/local/bin /usr/local/bin
# npm command
COPY --from=node /usr/local/lib /usr/local/lib
# yarn command
COPY --from=node /opt /opt
# nginx config file
COPY ./docker/nginx/*.conf /etc/nginx/conf.d/

WORKDIR /work/backend

default.conf

docker/nginx/default.conf
access_log /dev/stdout main;
error_log /dev/stderr warn;

server {
    listen 80;
    server_name localhost;

    charset utf-8;

    location / {
        proxy_pass http://host.docker.internal:3000/;
        proxy_redirect off;
    }
}

server {
    listen 9000;
    server_name localhost;
    root /work/backend/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

リバースプロキシの設定は主にこのファイルで行っています。詳細は割愛しますが、localhost:9000にアクセスするとLaravelが立ち上がるようになっており、localhost:80にアクセスするとlocalhost:3000にリバースプロキシされるようになっております。

location / {
   proxy_pass http://host.docker.internal:3000/;
   proxy_redirect off;
}

Nuxtコンテナ

Dockerfile

docker/front/Dockerfile
FROM node:17.5.0-alpine

WORKDIR /var/www/nuxt

RUN apk update && \
    apk add git curl

alpineにはgitcurlも入っていないため、追加でインストールしています。

Laravelのインストール

cd sample
mkdir -p backend
docker compose build --no-cache --force-rm
docker compose up -d
docker compose exec app composer create-project --prefer-dist laravel/laravel .
docker compose exec app php artisan key:generate
docker compose exec app php artisan storage:link
docker compose exec app chmod -R 777 storage bootstrap/cache
docker compose exec app php artisan migrate:fresh --seed

Laravelのインストールが完了したらlocalhost:9000にアクセスしてwelcomeページが表示されることを確認します。

Nuxtのインストール

nuxt2までのコマンドと変わっており、質問責めもなくなっています。

docker compose exec front ash
npx nuxi init front # front の部分にはアプリの名前(任意)を入れます
cd front
npm run dev # サーバーの立ち上げ

Nuxtのインストールが完了したらlocalhost:80にアクセスしてwelcomeページが表示されることを確認します。

LaravelでAPIを作成する

corsの設定をします。以下のようにcors.phpを修正し、環境変数から接続を許可するURLを指定します。(今回であればHOST_URL=http://localhost:3000)

backend/config/cors.php
...
'allowed_origins' => [env('HOST_URL')],
...

次にAPI用のルーティングを作成します。

backend/routes/api.php
Route::get('/check', function (){
    return response()->json([
        'message'=>'hello world.'
    ]);
});

以上でLaravel側での作業は完了です。最後にlocalhost:9000/api/checkへアクセスしてjsonが取得できているかを確認します。

NuxtからAPIに接続

nuxtのapp.vueファイルを下記のように書き換えてください。

frontend/front/app.vue
<template>
  <div>
    <p>{{data}}</p>
  </div>
</template>

<script setup>
    const {data} = await useFetch('http://sample_nginx:9000/api/check');
</script>

Nuxt3では新機能(?)としてuseFetchというものがあり、こちらは主に外部のAPIに接続して非同期処理を行うものだそうです。(蛇足ですがこれとuseAsyncDataはNuxt3の目玉機能のように紹介されていました。)
URLの注意点としてlocalhostではないことに注目してください。localhostにしてしまうとホストのアドレス(127.0.0.1)に接続してしまいます。コンテナのアドレスを名前解決するためにcontainer_nameを指定し、それを使用することでコンテナ間通信を実現します。またlaravelはnginxを噛ませているので、APIアクセス時もLaravelコンテナにアクセスするのではなく、nginxコンテナにアクセスしています。

localhost:80にアクセスしてjsonが取得できていれば、無事完了となります。お疲れ様でした。

おわりに

zennへの初めての投稿となります。
完全に私信ではあるのですが、私自身サーバーサイドエンジニア歴4ヶ月(うち2ヶ月はQA業務に従事していたため実質は2ヶ月です。)という若輩者であり、まだまだエンジニアとして至らない点も多いかと思いますが、今回思い切って筆をとってみました。

目的としては自分用のメモとしての側面も強いですが、このようなフロント・サーバーを分離する構成というのも珍しくはないため、このような知見が活きる場面は実は多いんじゃないか?と思って記事にしようと考えました。

文章を書くということも今までそれほど多くはなかったので、あまり読みやすい記事には仕上がっていないかもしれません。それにもかかわらず最後まで読んでいただけたのであれば感無量です。
お手数ですがもし宜しければ、文章の誤りや知識不足、認識の誤りなどご指摘いただけますと大変助かります。

おまけ

直接的に関係があるわけではありませんが、参考にさせていただいた(これからもおそらく参考にさせていただく)素敵な記事を紹介いたします。

公式Doc
https://v3.nuxtjs.org/

Nuxt 3 を今すぐオススメしたい 15 のポイント
https://zenn.dev/ytr0903/articles/d0a91f6180d34e

Nust3の新しい機能
https://zenn.dev/azukiazusa/articles/nuxt3-new-features

Nuxt 3がパブリックベータ版になったので新機能の紹介や所感など
https://zenn.dev/miruoon_892/articles/e06b1c7533d754df8a8c

Discussion