mcpField
N
nodejs-development

nodejs-development

github.com/mindrally開発287751 回インストール3 週間前に更新
リポジトリ

nodejs-development について

Node.jsとTypeScriptの開発向けスキルです。Payload CMSとNext.js、Vue.js、Zodによる検証、API、テスト、非同期処理の実践例を扱います。

主な機能

  1. 01Payload CMSとNext.jsのパターン
  2. 02Vue.jsのコンポーネントとコンポーザブル
  3. 03Piniaストアの例
  4. 04Zodスキーマ検証
  5. 05APIエラーと非同期処理
  6. 06テストの実践方法

活用シーン

  1. 01Payload CMSプロジェクトの構成
  2. 02型付きVue.jsインターフェースの構築
  3. 03ZodによるAPI入力の検証
  4. 04Node.jsアプリのテスト計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindrally/skills --skill nodejs-development

インストール後、Claude Code で/nodejs-developmentを入力して読み込まれたか確認します。

詳細

Node.js 開発ガイドライン

TypeScript を使用した Node.js 開発のエキスパートとして、さまざまなフレームワークとパターンを扱います。

Next.js を使用した Payload CMS

プロジェクトのセットアップ
project/
├── src/
│   ├── app/              # Next.js app directory
│   ├── collections/      # Payload collections
│   ├── blocks/          # Custom blocks
│   ├── fields/          # Custom fields
│   └── access/          # Access control functions
├── payload.config.ts    # Payload configuration
└── next.config.js       # Next.js configuration
コレクションの定義
import { CollectionConfig } from 'payload/types';

const Posts: CollectionConfig = {
  slug: 'posts',
  admin: {
    useAsTitle: 'title',
  },
  access: {
    read: () => true,
    create: ({ req: { user } }) => Boolean(user),
    update: ({ req: { user } }) => Boolean(user),
    delete: ({ req: { user } }) => Boolean(user),
  },
  fields: [
    {
      name: 'title',
      type: 'text',
      required: true,
    },
    {
      name: 'content',
      type: 'richText',
    },
    {
      name: 'publishedDate',
      type: 'date',
    },
  ],
};

export default Posts;
API ルート
// app/api/posts/route.ts
import { getPayloadClient } from '@/lib/payload';

export async function GET() {
  const payload = await getPayloadClient();
  const posts = await payload.find({
    collection: 'posts',
    limit: 10,
  });
  return Response.json(posts);
}

TypeScript を使用した Vue.js

コンポーネントの構成
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';

interface User {
  id: number;
  name: string;
  email: string;
}

const props = defineProps<{
  userId: number;
}>();

const emit = defineEmits<{
  (e: 'update', user: User): void;
}>();

const user = ref<User | null>(null);
const isLoading = ref(false);

const displayName = computed(() => user.value?.name ?? 'Unknown');

async function fetchUser() {
  isLoading.value = true;
  try {
    const response = await fetch(`/api/users/${props.userId}`);
    user.value = await response.json();
  } finally {
    isLoading.value = false;
  }
}

onMounted(fetchUser);
</script>

<template>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="user">
    <h2>{{ displayName }}</h2>
    <p>{{ user.email }}</p>
  </div>
</template>
コンポーザブル
// composables/useApi.ts
import { ref } from 'vue';

export function useApi<T>(url: string) {
  const data = ref<T | null>(null);
  const error = ref<Error | null>(null);
  const isLoading = ref(false);

  async function execute() {
    isLoading.value = true;
    error.value = null;

    try {
      const response = await fetch(url);
      if (!response.ok) throw new Error('Request failed');
      data.value = await response.json();
    } catch (e) {
      error.value = e as Error;
    } finally {
      isLoading.value = false;
    }
  }

  return { data, error, isLoading, execute };
}
Pinia ストア
// stores/user.ts
import { defineStore } from 'pinia';

interface UserState {
  user: User | null;
  isAuthenticated: boolean;
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    user: null,
    isAuthenticated: false,
  }),

  getters: {
    displayName: (state) => state.user?.name ?? 'Guest',
  },

  actions: {
    async login(credentials: LoginCredentials) {
      const user = await authService.login(credentials);
      this.user = user;
      this.isAuthenticated = true;
    },

    logout() {
      this.user = null;
      this.isAuthenticated = false;
    },
  },
});

Zod を使用した TypeScript

スキーマの定義
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string().min(1),
  email: z.string().email(),
  role: z.enum(['user', 'admin']),
  createdAt: z.date(),
});

type User = z.infer<typeof UserSchema>;

const CreateUserSchema = UserSchema.omit({ id: true, createdAt: true });
type CreateUserInput = z.infer<typeof CreateUserSchema>;
バリデーション
function createUser(input: unknown): User {
  const validatedInput = CreateUserSchema.parse(input);
  // Input is now typed as CreateUserInput
  return {
    ...validatedInput,
    id: generateId(),
    createdAt: new Date(),
  };
}

// Safe parsing (doesn't throw)
function validateUser(input: unknown) {
  const result = UserSchema.safeParse(input);
  if (!result.success) {
    console.error(result.error.issues);
    return null;
  }
  return result.data;
}
API バリデーションミドルウェア
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';

function validate<T extends z.ZodSchema>(schema: T) {
  return (req: Request, res: Response, next: NextFunction) => {
    const result = schema.safeParse(req.body);
    if (!result.success) {
      return res.status(400).json({ errors: result.error.issues });
    }
    req.body = result.data;
    next();
  };
}

// Usage
app.post('/users', validate(CreateUserSchema), createUserHandler);

一般的なベストプラクティス

エラー処理
  • カスタムエラークラスを使用します
  • グローバルエラーハンドラーを実装します
  • コンテキストとともにエラーをログに記録します
  • 適切なステータスコードを返します
非同期処理のパターン
  • async/await を一貫して使用します
  • Promise の拒否を処理します
  • 適切なタイムアウトを実装します
  • 並列処理には Promise.all を使用します
テスト
  • ユーティリティの単体テストを作成します
  • API の統合テストを作成します
  • テストフィクスチャを使用します
  • 外部依存関係をモックします

コメント