# Implementing Media Uploads for Prompts with Required Media Types in prompts.chat

> Implement media uploads with required types in prompts.chat. Use set_media_requirements AI tool and API endpoints for seamless file handling and validation.

- Repository: [Fatih Kadir Akın/prompts.chat](https://github.com/f/prompts.chat)
- Tags: how-to-guide
- Published: 2026-04-02

---

**To implement media uploads for prompts with required media types in prompts.chat, use the `set_media_requirements` AI tool to declare upload constraints, persist them via the `/api/prompts` endpoint, and handle file validation through the `/api/upload` route with configurable storage plugins.**

The `f/prompts.chat` repository enables prompt creators to mandate user-supplied media through a structured three-layer architecture. By implementing media uploads for prompts with required media types, you can enforce that consumers provide specific images, videos, or documents before executing a prompt. This implementation spans AI tool definitions, database persistence, and pluggable storage backends.

## Declaring Media Requirements in the Prompt Builder

The system exposes a `set_media_requirements` tool to the AI prompt builder, allowing dynamic declaration of media constraints during prompt creation.

### The set_media_requirements Tool Definition

In [`src/lib/ai/prompt-builder-tools.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/ai/prompt-builder-tools.ts) (lines 85-102), the tool schema defines three parameters that control upload behavior:

- **`requiresMediaUpload`** (`boolean`): Whether the prompt requires a file upload.
- **`mediaType`** (`"IMAGE" | "VIDEO" | "DOCUMENT"`): The allowed media category.
- **`mediaCount`** (`number`): How many files the user must provide, clamped between 1 and 10.

### Updating Builder State

When the AI invokes this tool, the handler in [`src/lib/ai/prompt-builder-tools.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/ai/prompt-builder-tools.ts) (lines 516-529) mutates the builder state to store these requirements. The state persists these values until the prompt is saved to the database.

```typescript
// Tool execution handler updates state
{
  name: "set_media_requirements",
  arguments: {
    requiresMediaUpload: true,
    mediaType: "IMAGE",
    mediaCount: 3
  }
}

```

## Persisting Media Constraints to the Database

The `/api/prompts` route validates and stores media requirements alongside the prompt record. In [`src/app/api/prompts/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/prompts/route.ts) (lines 23-55), a Zod schema validates the incoming payload including `requiresMediaUpload`, `requiredMediaType`, and `requiredMediaCount`.

The persistence logic (lines 180-185) stores these fields in the database via Prisma, ensuring that media constraints are permanently associated with the prompt.

```typescript
// POST /api/prompts request body
{
  title: "Visual Analysis",
  description: "Analyze user-uploaded images",
  requiresMediaUpload: true,
  requiredMediaType: "IMAGE",
  requiredMediaCount: 2
}

```

## Exposing Requirements in the Public Feed

When the public JSON feed is generated at [`/prompts.json`](https://github.com/f/prompts.chat/blob/main//prompts.json), the system includes the media requirement flags so that client applications can render appropriate upload widgets. In [`src/app/prompts.json/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/prompts.json/route.ts) (line 85), the `requiresMediaUpload` field is explicitly included in the feed response.

## Handling File Uploads with Validation and Compression

The `/api/upload` endpoint serves as the core of the media-upload pipeline, handling authentication, validation, compression, and storage delegation.

### Authentication and Storage Plugin Selection

The handler in [`src/app/api/upload/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/upload/route.ts) (lines 1-121) first validates the user session via NextAuth, then selects a storage plugin based on the `ENABLED_STORAGE` environment variable (defaulting to `url`).

The storage plugin is retrieved from the registry at [`src/lib/plugins/registry.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/registry.ts) and must implement `isConfigured()` to verify deployment readiness.

### File Type and Size Validation

The endpoint enforces strict validation before processing:

- **Allowed types**: `ALLOWED_IMAGE_TYPES` and `ALLOWED_VIDEO_TYPES` arrays whitelist specific MIME types.
- **Size limits**: 4 MiB maximum for both images and videos (matching Vercel serverless limits).

```typescript
// Validation logic from src/app/api/upload/route.ts
if (!ALLOWED_IMAGE_TYPES.includes(file.type) && !ALLOWED_VIDEO_TYPES.includes(file.type)) {
  // Reject unsupported types with 400 error
}

if (file.size > maxSize) {
  // Reject oversized payloads
}

```

### Image Compression with Sharp

For image uploads, the system uses **sharp** to compress files to JPEG format at 90% quality before storage:

```typescript
import sharp from 'sharp';

async function compressToJpg(buffer: Buffer): Promise<Buffer> {
  return await sharp(buffer)
    .jpeg({ quality: 90, mozjpeg: true })
    .toBuffer();
}

```

The endpoint returns a JSON response containing the public URL and file size:

```json
{ "url": "https://cdn.example.com/prompt-media-12345.jpg", "size": 312345 }

```

## Configuring Storage Plugins (S3, DigitalOcean Spaces, or URL-Only)

The architecture supports three storage backends selected via environment configuration.

### URL-Only Mode

The default `url` plugin ([`src/lib/plugins/storage/url.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/storage/url.ts), lines 15-20) acts as a fallback that rejects uploads with a helpful message, forcing users to provide external URLs instead of direct uploads.

### AWS S3 Implementation

The S3 plugin in [`src/lib/plugins/storage/s3.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/storage/s3.ts) (lines 45-70) creates signed PUT requests and streams the buffer to S3. Configuration requires `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_S3_BUCKET`, and `AWS_REGION` environment variables.

### DigitalOcean Spaces

For DigitalOcean Spaces, [`src/lib/plugins/storage/do-spaces.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/storage/do-spaces.ts) (lines 65-90) implements the same interface but targets the DO Spaces endpoint. It requires `DO_SPACES_KEY`, `DO_SPACES_SECRET`, `DO_SPACES_BUCKET`, and `DO_SPACES_ENDPOINT`.

Both cloud plugins validate configuration via `isConfigured()` before upload attempts.

## End-to-End Implementation Example

To create a prompt that requires three images and handle the upload flow:

1. **Define the prompt** via the API:

```typescript
await fetch("/api/prompts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    title: "Photo Comparison",
    requiresMediaUpload: true,
    requiredMediaType: "IMAGE",
    requiredMediaCount: 3,
    // additional fields...
  })
});

```

2. **Upload files** from the client:

```javascript
const form = new FormData();
form.append('file', fileInput.files[0]);

fetch('/api/upload', {
  method: 'POST',
  body: form,
  credentials: 'include'
})
.then(r => r.json())
.then(({ url }) => {
  // Store URL for prompt execution
  console.log('Media available at:', url);
});

```

## Summary

- **Declare requirements** using the `set_media_requirements` AI tool in [`src/lib/ai/prompt-builder-tools.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/ai/prompt-builder-tools.ts) to set `requiresMediaUpload`, `mediaType`, and `mediaCount`.
- **Persist constraints** through the `/api/prompts` endpoint which validates fields via Zod in [`src/app/api/prompts/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/prompts/route.ts).
- **Expose flags** in the public feed via [`src/app/prompts.json/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/prompts.json/route.ts) so client UIs know to render upload widgets.
- **Validate uploads** at `/api/upload` with type checking, 4 MiB size limits, and sharp-based image compression.
- **Configure storage** using S3, DigitalOcean Spaces, or URL-only mode via environment variables and the plugin registry.

## Frequently Asked Questions

### How do I configure S3 for media uploads in prompts.chat?

Set the `ENABLED_STORAGE` environment variable to `s3` and provide `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_S3_BUCKET`, and `AWS_REGION`. The system will automatically use the S3 plugin in [`src/lib/plugins/storage/s3.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/storage/s3.ts) to generate signed URLs and upload buffers.

### What file types and sizes are supported for uploads?

The upload endpoint in [`src/app/api/upload/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/upload/route.ts) accepts only MIME types listed in `ALLOWED_IMAGE_TYPES` and `ALLOWED_VIDEO_TYPES` arrays. All files must be under 4 MiB due to Vercel serverless function limits. Images are automatically compressed to JPEG at 90% quality using sharp.

### How does the frontend know when to show an upload widget?

The prompt record includes `requiresMediaUpload`, `requiredMediaType`, and `requiredMediaCount` fields. These are exposed in the public JSON feed ([`src/app/prompts.json/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/prompts.json/route.ts)) and returned by the `/api/prompts` endpoint. The client UI checks these flags to conditionally render the upload component and enforce the required file count.

### Can I use DigitalOcean Spaces instead of AWS S3?

Yes. Set `ENABLED_STORAGE` to `do-spaces` and configure `DO_SPACES_KEY`, `DO_SPACES_SECRET`, `DO_SPACES_BUCKET`, and `DO_SPACES_ENDPOINT`. The implementation in [`src/lib/plugins/storage/do-spaces.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/storage/do-spaces.ts) follows the same interface as the S3 plugin, allowing seamless substitution of the storage backend.