1
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?

FastAPIにSPAアプリケーションを統合する

Posted at

はじめに

この記事では、FastAPIを使用してSPA(シングルページアプリケーション)を統合する方法について検証します。
FastAPIは主にバックエンドAPIサーバーとして使用されますが、SPAと統合することで単一のアプリケーションとして動作させることができます。

FastAPIにSPAを統合する

FastAPIには静的ファイルを配信する機能があります。
FastAPIの静的ファイル配信機能

例えば、以下のようなコードをmain.pyに追加することで、staticディレクトリにあるファイルを配信することができます。

app = FastAPI(
    title="FastAPI, SPA",
    description="FastAPIでSPAを作成する",
    version="1.0.0",
)

app.mount("/", StaticFiles(directory="static"), name="static")

そのため、Nuxt.jsで作成したSPAのフロントエンドをstaticディレクトリに配置することで、FastAPIに統合することができます。

SPAのフロントエンドをNuxt.jsで作成する

Nuxt.jsを使用してSPAのフロントエンドを作成する手順を説明します。
まず、Nuxt.jsプロジェクトを初期化します。

npx nuxi init --package-manager yarn frontend

その後、nuxt.config.tsssr: false を指定し、SPAであることを明示します。

export default defineNuxtConfig({
  ...
  ssr: false,
})

その後、Nuxt.jsプロジェクトを起動し、
localhost:3000にアクセスして、Nuxt.jsプロジェクトが正常に起動していることを確認します。

cd frontend
yarn dev

Vuetifyを使用するように設定する

Nuxt.jsでVuetifyを使用するように設定します。

yarn add vuetify @mdi/font 

nuxt.config.ts

import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'

export default defineNuxtConfig({
  ...
  build: {
    transpile: ['vuetify'],
  },
  modules: [
    (_options, nuxt) => {
      nuxt.hooks.hook('vite:extendConfig', (config) => {
        // @ts-expect-error
        config.plugins.push(vuetify({ autoImport: true }))
      })
    }
  ],
  vite: {
    vue: {
      template: {
        transformAssetUrls,
      },
    },
  },
})

plugins/vuetify.ts

import '@mdi/font/css/materialdesignicons.css'

import 'vuetify/styles'
import { createVuetify } from 'vuetify'

import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'

export default defineNuxtPlugin((app) => {
  const vuetify = createVuetify({
    ssr: false,
    components,
    directives,
  })
  app.vueApp.use(vuetify)
})

フロントエンドのページ作成

Nuxt.jsでページを作成するには、pagesディレクトリにファイルを作成します。

mkdir -p pages/index.vue

pages/index.vue

<template>
  <div>
    <h1>Home Page</h1>
    <p>Welcome to the Home page!</p>
  </div>
</template>

pages/about.vue

<template>
  <div>
    <h1>About Page</h1>
    <p>Learn more about us on this page.</p>
  </div>
</template>

フロントエンドのレイアウト作成

Nuxt.jsでレイアウトを作成するには、layoutsディレクトリにファイルを作成します。

mkdir -p layouts/default.vue

layouts/default.vue

<template>
  <v-app>
    <v-container :class="{ 'drawer-open': drawer }">
      <v-row>
        <v-col>
          <v-app-bar app>
            <v-btn icon @click="drawer = !drawer">
              <v-icon>mdi-menu</v-icon>
            </v-btn>
            <v-toolbar-title>My Website</v-toolbar-title>
            <v-spacer></v-spacer>
            <v-btn text to="/">Home</v-btn>
            <v-btn text to="/about">About</v-btn>
          </v-app-bar>
          <v-navigation-drawer app v-model="drawer">
            <v-list>
              <v-list-item link to="/">
                <v-list-item-title>Home</v-list-item-title>
              </v-list-item>
              <v-list-item link to="/about">
                <v-list-item-title>About</v-list-item-title>
              </v-list-item>
            </v-list>
          </v-navigation-drawer>
        </v-col>
      </v-row>
      <v-row>
        <v-col>
          <v-main>
            <slot />
          </v-main>
        </v-col>
      </v-row>
      <v-row>
        <v-col>
          <v-footer app fixed class="justify-center">
            <p>&copy; 2023 My Website</p>
          </v-footer>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</template>

<script>
export default {
  data() {
    return {
      drawer: false,
    };
  },
};
</script>

<style>
.drawer-open {
  margin-left: 256px;
}
</style>

以下のVuetifyコンポーネントが使用されています:

  1. v-app: アプリケーションのルートコンポーネントで、他のすべてのVuetifyコンポーネントを包含します。
  2. v-app-bar: アプリケーションの上部にナビゲーションバーを表示します。ここにはホームページとアバウトページへのリンクが含まれています。
  3. v-navigation-drawer: サイドバーで、ナビゲーションリンクを含んでいます。このドロワーは動的に表示または非表示にすることができます。
  4. v-listv-list-item: ナビゲーションドロワー内で使用され、ナビゲーションリンクのリストを表示します。
  5. v-main: ページの主要コンテンツを表示するためのコンテナです。スロットを使用して、ページ固有のコンテンツを挿入できます。
  6. v-footer: フッター部分で、著作権情報などを表示します。

app.vueの修正

を使用して、ページのレイアウトとコンテンツを管理します。これにより、Nuxt.jsのページコンポーネントを動的に表示することができます。

app.vue

<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Nuxt.jsプロジェクトを起動する

上記の修正を行った内容が反映されているかを確認するために、Nuxt.jsプロジェクトを起動します。

yarn dev

Nuxt.jsプロジェクトが起動したら、ブラウザでhttp://localhost:3000にアクセスして、SPAのフロントエンドが正常に表示されることを確認します。

index.png

about.png

Nuxt.jsでビルドする

Nuxt.jsでビルドを行い、静的ファイルを生成します。

yarn generate

上記コマンドで、output/publicディレクトリに静的ファイルが生成されます。
(distディレクトリには、output/publicへのシンボリックリンクが作成されるだけのため、コピーする場合は注意が必要です。)

FastAPIに静的ファイルを配信する

※FastAPIの前提は以下記事リンク先の内容です。
Databricks Apps でカスタムアプリのデプロイ

FastAPIに静的ファイルを配信するには、main.pyに以下のコードを追加しつつ、既存APIのパスを変更します。

from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles

app = FastAPI()

@app.get("/api")
def read_api_root():
    return {"Hello": "World"}

@app.get("/api/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

app.mount("/", StaticFiles(directory="./static", html=True), name="static")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

app.mount("/", StaticFiles(directory="./static", html=True), name="static")

この行は、FastAPIアプリケーションに静的ファイルを配信するための設定を追加します。ここで、"/"というルートパスに対して、"./static"ディレクトリ内の静的ファイルを配信するように設定しています。html=Trueオプションを指定することで、HTMLファイルを適切に扱うことができます。

また、backend側staticディレクトリを作成し、Nuxt.jsでビルドしたoutput/publicディレクトリ内の静的ファイルをコピーします。

上記対応後、以下コマンドで、FastAPIアプリケーションを起動します。

gunicorn main:app -k uvicorn.workers.UvicornWorker -w 1

ブラウザでhttp://localhost:8000にアクセスして、SPAのフロントエンドが正常に表示されることを確認します。

index.png

about.png

また、http://localhost:8000/api/にcurlでアクセスして、APIが正常に動作していることを確認します。

curl http://localhost:8000/api/
# {"Hello": "World"}
curl http://localhost:8000/api/items/1?q=hoge
# {"item_id": 1, "q": "hoge}

これで、FastAPIで既存のバックエンドAPIエンドポイントとSPAのフロントエンドを統合することができました。

まとめ

FastAPIとNuxt.jsを使用してSPAを統合する方法を検証しました。
FastAPIの静的ファイル配信機能を利用して、Nuxt.jsでビルドしたSPAのフロントエンドを配信することで実現できました。
この対応でフロントエンドとバックエンドが一つのアプリとして共存することが可能になりました。

参考

1
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
1
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?