Use Cases

Claude Codeでサーバーレス関数を効率的に開発する方法

Claude Codeを使ってAWS Lambda・Vercel Functions・Cloudflare Workersなどのサーバーレス関数を効率的に開発する方法を解説します。

サーバーレス開発にClaude Codeを使うメリット

サーバーレス関数はコールドスタート対策、メモリ最適化、ペイロードサイズ制限など、固有の制約があります。Claude Codeはこれらの制約を理解した上で、最適化されたコードを生成します。

AWS Lambda関数の開発

> AWS Lambda + API Gatewayで画像リサイズAPIを作成して。
> SAMテンプレートも作成して。TypeScriptで実装して。

Lambda関数

// src/handlers/resize-image.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import sharp from 'sharp';

const s3 = new S3Client({});

interface ResizeRequest {
  key: string;
  width: number;
  height?: number;
  quality?: number;
}

export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
  try {
    const body = JSON.parse(event.body || '{}') as ResizeRequest;

    if (!body.key || !body.width) {
      return {
        statusCode: 400,
        body: JSON.stringify({ error: 'key and width are required' }),
      };
    }

    // S3から元画像を取得
    const { Body, ContentType } = await s3.send(new GetObjectCommand({
      Bucket: process.env.BUCKET_NAME!,
      Key: body.key,
    }));

    const buffer = Buffer.from(await Body!.transformToByteArray());

    // リサイズ
    const resized = await sharp(buffer)
      .resize(body.width, body.height, { fit: 'cover', withoutEnlargement: true })
      .webp({ quality: body.quality || 80 })
      .toBuffer();

    // リサイズ後の画像を保存
    const outputKey = body.key.replace(/\.[^.]+$/, `-${body.width}w.webp`);
    await s3.send(new PutObjectCommand({
      Bucket: process.env.BUCKET_NAME!,
      Key: outputKey,
      Body: resized,
      ContentType: 'image/webp',
    }));

    return {
      statusCode: 200,
      body: JSON.stringify({
        key: outputKey,
        size: resized.length,
        width: body.width,
      }),
    };
  } catch (err) {
    console.error('Resize error:', err);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Internal server error' }),
    };
  }
}

SAMテンプレート

# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: nodejs20.x
    Timeout: 30
    MemorySize: 512
    Environment:
      Variables:
        BUCKET_NAME: !Ref ImageBucket

Resources:
  ResizeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: dist/handlers/resize-image.handler
      Events:
        Api:
          Type: Api
          Properties:
            Path: /resize
            Method: post
      Policies:
        - S3CrudPolicy:
            BucketName: !Ref ImageBucket
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: es2022
        External:
          - sharp

  ImageBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${AWS::StackName}-images'

Outputs:
  ApiUrl:
    Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/'

Cloudflare Workersの開発

> Cloudflare Workersで短縮URL生成サービスを作成して。
> KVストアを使ってURLマッピングを管理して。
// src/worker.ts
interface Env {
  URL_STORE: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // 短縮URLのリダイレクト
    if (request.method === 'GET' && url.pathname.length > 1) {
      const slug = url.pathname.slice(1);
      const targetUrl = await env.URL_STORE.get(slug);

      if (targetUrl) {
        return Response.redirect(targetUrl, 302);
      }

      return new Response('Not found', { status: 404 });
    }

    // URL短縮の作成
    if (request.method === 'POST' && url.pathname === '/api/shorten') {
      const { url: targetUrl } = await request.json<{ url: string }>();

      if (!targetUrl || !isValidUrl(targetUrl)) {
        return Response.json({ error: 'Invalid URL' }, { status: 400 });
      }

      const slug = generateSlug();
      await env.URL_STORE.put(slug, targetUrl, { expirationTtl: 86400 * 365 });

      return Response.json({
        shortUrl: `${url.origin}/${slug}`,
        slug,
        originalUrl: targetUrl,
      });
    }

    return new Response('Method not allowed', { status: 405 });
  },
};

function generateSlug(): string {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  return Array.from(crypto.getRandomValues(new Uint8Array(7)))
    .map(b => chars[b % chars.length])
    .join('');
}

function isValidUrl(str: string): boolean {
  try {
    const url = new URL(str);
    return url.protocol === 'http:' || url.protocol === 'https:';
  } catch {
    return false;
  }
}

Vercel Edge Functionsの開発

// app/api/og/route.tsx
import { ImageResponse } from 'next/og';

export const runtime = 'edge';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const title = searchParams.get('title') || 'My Blog';

  return new ImageResponse(
    (
      <div
        style={{
          height: '100%',
          width: '100%',
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          backgroundColor: '#1a1a2e',
          color: '#ffffff',
          fontFamily: 'sans-serif',
        }}
      >
        <h1 style={{ fontSize: 60, textAlign: 'center', padding: '0 40px' }}>
          {title}
        </h1>
      </div>
    ),
    { width: 1200, height: 630 }
  );
}

コールドスタート対策

// Lambda のコールドスタート対策
// 接続の再利用
let dbConnection: PrismaClient | null = null;

function getDB() {
  if (!dbConnection) {
    dbConnection = new PrismaClient();
  }
  return dbConnection;
}

// ウォームアップ用ハンドラー
export async function warmupHandler(event: ScheduledEvent) {
  // CloudWatch Eventsで定期実行してコールドスタートを防ぐ
  console.log('Warmup invocation');
  return { statusCode: 200 };
}

テスト

import { handler } from './resize-image';
import { mockClient } from 'aws-sdk-client-mock';
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';

const s3Mock = mockClient(S3Client);

describe('resize-image handler', () => {
  beforeEach(() => {
    s3Mock.reset();
  });

  it('should return 400 when key is missing', async () => {
    const event = { body: JSON.stringify({ width: 200 }) } as any;
    const result = await handler(event);
    expect(result.statusCode).toBe(400);
  });

  it('should resize and upload image', async () => {
    s3Mock.on(GetObjectCommand).resolves({
      Body: createMockStream(testImageBuffer),
      ContentType: 'image/jpeg',
    });
    s3Mock.on(PutObjectCommand).resolves({});

    const event = {
      body: JSON.stringify({ key: 'test.jpg', width: 200 }),
    } as any;

    const result = await handler(event);
    expect(result.statusCode).toBe(200);
  });
});

まとめ

Claude Codeを使えば、Lambda・Cloudflare Workers・Vercel Functionsなど各種サーバーレスプラットフォームに最適化された関数を効率的に開発できます。プラットフォーム固有の制約を理解した上でコードを生成してくれるため、コールドスタート対策やメモリ最適化も適切に行われます。AWSでの本格的な運用についてはAWSデプロイの自動化も参照してください。開発の基本的な効率化は生産性を3倍にするTipsで紹介しています。

Claude Codeの詳細はAnthropic公式ドキュメントをご覧ください。

#Claude Code #サーバーレス #Lambda #Cloudflare Workers #Vercel