Building a Modern Photo Gallery System with Next.js, SST, and S3

nextjs
aws
sst
typescript
gallery

12/4/2024


584 words · 3 min read

Share this post:


Export:

Building a Modern Photo Gallery System with Next.js, SST, and S3

Building a photo gallery system might seem straightforward at first, but creating one that's both developer-friendly and production-ready involves solving several interesting challenges. Here's how I built an automated photo management system that handles everything from local development to production deployment.

The Vision

I wanted a system that would:

  • Automatically process new photos
  • Handle metadata and organization
  • Work seamlessly in both development and production
  • Scale well with a growing collection
  • Support future AI integration

Architecture Overview

The system consists of four main components:

  1. Local Photo Watcher: Monitors for new photos and triggers processing
  2. Queue Processor: Manages the import and analysis pipeline
  3. S3 Storage: Handles production storage and serving
  4. Gallery Viewer: Displays photos with a modern UI

Building the Foundation

The first step was defining our types. This might seem premature, but having a clear data model was crucial:

interface PhotoAsset {
  id: string;
  path: string;
  checksum: string;
  importedAt: Date;
  metadata: {
    exif?: ExifData;
    aiGenerated?: {
      description?: string;
      tags?: string[];
      // ... more AI fields
    };
    humanVerified?: {
      description?: string;
      tags?: string[];
      galleries?: string[];
      isPublic: boolean;
    };
  };
}

This structure supports both immediate needs and future features like AI tagging.

The Import Pipeline

The import system watches for new photos and processes them automatically:

export class PhotoImporter {
  async watchDirectory(path: string): Promise<FSWatcher> {
    const watcher = chokidar.watch(path, {
      ignored: /(^|[\/\\])\../,
      persistent: true,
      ignoreInitial: false,
    });

    watcher.on('add', async (filePath: string) => {
      if (this.isSupported(filePath)) {
        const checksum = await this.getFileChecksum(filePath);
        await this.queueImport(filePath, checksum);
      }
    });

    return watcher;
  }
}

Queue-Based Processing

Rather than processing photos synchronously, I implemented a queue system:

  1. New photos trigger import tasks
  2. Tasks are processed sequentially
  3. State is maintained in a queue.json file
  4. Failed tasks can be retried

This approach provides reliability and extensibility for future processing steps.

The Production Challenge

The most interesting challenge was handling the development-production transition. The solution was elegant:

  • Development: Serve photos through a Next.js API route
  • Production: Serve directly from S3
  • Same component interface for both environments
function getPhotoUrl(photo: PhotoAsset): string {
  if (process.env.NODE_ENV === 'development') {
    return `/api/photos/${photo.id}`;
  } else {
    return `https://${bucketName}.s3.amazonaws.com/photos/${photo.id}.jpg`;
  }
}

SST Integration

SST v3 made the AWS infrastructure setup clean and simple:

const photoBucket = new sst.aws.Bucket("PhotoBucket", {
  access: "public",
  cors: {
    allowOrigins: ["*"],
    allowMethods: ["GET"],
  },
});

The UI needed to be both beautiful and functional:

  • Responsive grid layout
  • Lazy loading for performance
  • Lightbox for full-screen viewing
  • Smooth transitions and animations

Future Enhancements

The system is designed for expansion:

  1. AI-powered photo analysis
  2. Automatic gallery organization
  3. Tag-based filtering
  4. Advanced search capabilities
  5. Batch processing tools

Lessons Learned

  1. Type-First Development: Starting with clear types made the implementation smoother
  2. Queue-Based Architecture: Provides flexibility for future enhancements
  3. Environment Abstraction: Clean separation between development and production
  4. SST v3 Simplicity: Modern infrastructure as code makes AWS setup painless

Try It Yourself

The system is easy to integrate into your own projects:

# Start the photo watcher
npm run watch-photos

# Deploy to production
npx sst deploy

# Sync photos to S3
npm run sync-photos

Conclusion

Building this system was a great example of how modern tools like Next.js, SST, and TypeScript can come together to create something powerful yet maintainable. The architecture supports both immediate needs and future expansion, making it a solid foundation for any photo management system.

Stay tuned for future posts about adding AI capabilities to this system!



Subscribe to the Newsletter

Get notified when I publish new blog posts about game development, AI, entrepreneurship, and technology. No spam, unsubscribe anytime.

By subscribing, you agree to receive emails from Erik Bethke. You can unsubscribe at any time.

Comments

Loading comments...

Comments are powered by Giscus. You'll need a GitHub account to comment.