🤝

【Nuxt】 shared機能を用いて Vue app / Nuxt Server 間で共通の型を利用する

に公開

はじめに

こんにちは、がんがんです。
Nuxt ユーザーの皆さまは Nuxt v3.14 で追加された shared/ 機能を利用されていますか?
私はめちゃめちゃ利用しています。

Nuxt v3.14のブログで以下のように登場しました。

📂 Shared folder for code and types shared with client/server

You should never import Vue app code in your nitro code (or the other way around). But this has meant a friction point when it comes to sharing types or utilities that don't rely on the nitro/vue contexts.

For this, we have a new shared/ folder (#28682). You can't import Vue or nitro code into files in this folder, but it produces auto-imports (if you're using compatibilityVersion: 4) which you can consume throughout the rest of your app.

If needed you can use the new #shared alias which points to this folder.

The shared folder is alongside your server/ folder. (If you're using compatibilityVersion: 4, this means it's not inside your app/ folder.)

https://nuxt.com/blog/v3-14#shared-folder-for-code-and-types-shared-with-clientserver

本記事では実際の想定ユースケースを考えながら実験してみたいと思います。

環境

  • Nuxt: v3.17 (or >= v3.14)
    • v3.14でリリースされた機能です。v3.14以上のバージョンを利用ください
  • nuxt.config.tsfuture.compatibilityVersionオプションを追加済みである
nuxt.config.ts
export default defineNuxtConfig({
  // ...other options
  future: {
    compatibilityVersion: 4 // <- this.
  },
  // ...other options
});

ドキュメント

https://nuxt.com/docs/guide/directory-structure/shared

NuxtチームのAlexander Lichter氏が自身のYouTubeで解説動画を挙げられています。
英語にはなりますが気になった方は是非ご覧ください。

https://youtu.be/_m5ct5e8nVo?feature=shared

ディレクトリ構成

shared/ディレクトリはapp/server/public/と同一の階層に用意します。

| src/
---| app/
-----| components/
-------| ...
-----| composables/
-------| ...
-----| pages/
-------| index.vue
---| public/
---| server/
-----| api/
-------| ...
---| shared/    ...⭐️
-----| utils/   ...⭐️
-----| types/   ...⭐️
---| nuxt.config.ts
---| package.json

実験してみる

今回は以下のようなユースケースを想定して検証します。

  1. valibotを用いてスキーマを定義する。components/Form.vueでもapi/example.tsでも同じスキーマを利用したい
  2. valibotを用いて定義したスキーマから型を生成したい。この型はClient/Serverで同一のものを利用したい

shared/types/example.ts

valibotを用いたよくあるスキーマの定義です。

shared/types/example.ts
import type { InferOutput } from 'valibot';
import * as v from 'valibot';

/**
 * ユーザー入力フォーム用のスキーマ定義
 */
export const userFormSchema = v.object({
  firstName: v.pipe(v.string(), v.minLength(1, '名字を入力してください'),
  lastName: v.pipe(v.string(), v.minLength(1, '名字を入力してください'),  
});

export type UserForm = InferOutput<typeof userFormSchema>;

api/example.ts

api/example.ts
import { parser } from 'valibot';

export default defineEventHandler(async (event) => {
  const { firstName, lastName } = await readValidatedBody(event, parser(userFormSchema));

  // ...something
});

shared/の利点

sharedの利点1: Client / Serverで共通の型が利用できる

従来のNuxtではVue app側とNuxt Server(Nitro)側で型の共通利用ができませんでした。
そのため、app/types/でVue app用に定義した型情報やスキーマ、ユーティリティ関数を再度Nuxt Server側で定義する必要がありました。

同じ型を別々に定義するのは管理・運用の観点から良いものではありませんでした。
しかし、shared/機能が登場したことで共通の型が利用できるようになりました。管理効率が大幅に向上しました。
(※ 過度な共通化は逆に開発効率を下げます。本件は本記事の主題から逸れるため触れていません。)

sharedの利点2: Auto-imports対象である

Nuxtアプリを開発するときapp/types/ディレクトリを用意して型情報を管理します。package by layerパターンにおいて良く見かけるケースかと思います。

このapp/types/ディレクトリはデフォルトでAuto-imports対象外となっておりimport文を都度書く必要があります。
Auto-importsに慣れたNuxtユーザーであれば面倒と感じると思います。

app/types/ディレクトリをAuto-imports対象にするためには以下のような設定を別途追加する必要があり面倒です。

nuxt.config.ts
export default defineNuxtConfig({
  imports: {
    dirs: ["types/**"],
  }
});

https://zenn.dev/comm_vue_nuxt/articles/nuxt-auto-imports-use-by-products#q.-schemas%2F-や-types%2Fを-auto-import-したい



しかし、shared/ディレクトリはデフォルトでAuto-importsが有効になっています。そのため、Auto-importsの設定を別途追加せずともClient/Severで同一の型が利用可能です。

https://nuxt.com/docs/guide/directory-structure/shared#how-files-are-scanned

おわりに

今回はNuxt v3.14で追加された shared/機能について整理しました。
個人的には便利だと思っているのですがshared/ディレクトリの事例がなかなか見つからないなーと思ったので備忘録として整理しました。

Nuxt v3.17でも<NuxtTime>という新しいコンポーネントも追加されました。Nuxtの更なる進化が楽しみです。

Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion