
## Introduction

This guide will show you how to use Rabata.io as a storage backend for your Next.js application. You'll learn how to upload files directly from the browser, handle file uploads through API routes, and display stored files in your application.

## Prerequisites

Before you begin, make sure you have:

- Node.js 18.17 or later installed
- A Rabata.io account with access credentials
- A bucket created in your Rabata.io account
- Basic knowledge of Next.js and React

## Installation

### Create a New Next.js Application

If you're starting from scratch, create a new Next.js application:

```bash
$ npx create-next-app@latest my-nextjs-app
$ cd my-nextjs-app
```

### Install Required Dependencies

Install the AWS SDK for JavaScript and other necessary packages:

```bash
$ npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner uuid
```

## Configuration

### Set Up Environment Variables

Create a `.env.local` file in the root of your project to store your Rabata.io credentials:

```
RABATA_ACCESS_KEY=YOUR_ACCESS_KEY
RABATA_SECRET_KEY=YOUR_SECRET_KEY
RABATA_BUCKET_NAME=your-bucket-name
RABATA_REGION=us-east-1
RABATA_ENDPOINT=https://s3.us-east-1.rabata.io
```

### Create a Configuration File

Create a utility file to configure the S3 client:

```javascript
// lib/s3.js
import { S3Client } from '@aws-sdk/client-s3';

export const s3Client = new S3Client({
  region: process.env.RABATA_REGION || 'us-east-1',
  endpoint: process.env.RABATA_ENDPOINT || 'https://s3.us-east-1.rabata.io',
  credentials: {
    accessKeyId: process.env.RABATA_ACCESS_KEY || '',
    secretAccessKey: process.env.RABATA_SECRET_KEY || ''
  },
  forcePathStyle: true // Required for Rabata.io
});
```

## Basic Usage

### Server-Side File Upload with API Routes

Create an API route to handle file uploads:

```javascript
// app/api/upload/route.js (App Router)
import { NextResponse } from 'next/server';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { v4 as uuidv4 } from 'uuid';

// Initialize the S3 client
const s3Client = new S3Client({
  region: process.env.RABATA_REGION || 'us-east-1',
  endpoint: process.env.RABATA_ENDPOINT || 'https://s3.us-east-1.rabata.io',
  credentials: {
    accessKeyId: process.env.RABATA_ACCESS_KEY || '',
    secretAccessKey: process.env.RABATA_SECRET_KEY || ''
  },
  forcePathStyle: true
});

export async function POST(request) {
  try {
    const formData = await request.formData();
    const file = formData.get('file');

    if (!file) {
      return NextResponse.json({ error: 'No file provided' }, { status: 400 });
    }

    // Convert file to buffer
    const buffer = Buffer.from(await file.arrayBuffer());
    const fileName = `${uuidv4()}-${file.name}`;

    // Upload to Rabata.io
    await s3Client.send(
      new PutObjectCommand({
        Bucket: process.env.RABATA_BUCKET_NAME,
        Key: fileName,
        Body: buffer,
        ContentType: file.type
      })
    );

    return NextResponse.json({
      message: 'File uploaded successfully',
      fileName: fileName,
      fileUrl: `${process.env.RABATA_ENDPOINT}/${process.env.RABATA_BUCKET_NAME}/${fileName}`
    });
  } catch (error) {
    console.error('Error uploading file:', error);
    return NextResponse.json({ error: 'Error uploading file' }, { status: 500 });
  }
}
```

For Pages Router:

```javascript
// pages/api/upload.js (Pages Router)
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { v4 as uuidv4 } from 'uuid';
import formidable from 'formidable';
import fs from 'fs';

// Disable the default body parser
export const config = {
  api: {
    bodyParser: false,
  },
};

// Initialize the S3 client
const s3Client = new S3Client({
  region: process.env.RABATA_REGION || 'us-east-1',
  endpoint: process.env.RABATA_ENDPOINT || 'https://s3.us-east-1.rabata.io',
  credentials: {
    accessKeyId: process.env.RABATA_ACCESS_KEY || '',
    secretAccessKey: process.env.RABATA_SECRET_KEY || ''
  },
  forcePathStyle: true
});

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  try {
    const form = formidable({});
    const [fields, files] = await form.parse(req);
    const file = files.file?.[0];

    if (!file) {
      return res.status(400).json({ error: 'No file provided' });
    }

    // Read file from disk
    const fileContent = fs.readFileSync(file.filepath);
    const fileName = `${uuidv4()}-${file.originalFilename}`;

    // Upload to Rabata.io
    await s3Client.send(
      new PutObjectCommand({
        Bucket: process.env.RABATA_BUCKET_NAME,
        Key: fileName,
        Body: fileContent,
        ContentType: file.mimetype
      })
    );

    return res.status(200).json({
      message: 'File uploaded successfully',
      fileName: fileName,
      fileUrl: `${process.env.RABATA_ENDPOINT}/${process.env.RABATA_BUCKET_NAME}/${fileName}`
    });
  } catch (error) {
    console.error('Error uploading file:', error);
    return res.status(500).json({ error: 'Error uploading file' });
  }
}
```

### Client-Side File Upload Component

Create a React component for file uploads:

```jsx
// components/FileUpload.jsx
'use client'; // For App Router

import { useState } from 'react';

export default function FileUpload() {
  const [file, setFile] = useState(null);
  const [uploading, setUploading] = useState(false);
  const [uploadedFileUrl, setUploadedFileUrl] = useState('');
  const [error, setError] = useState('');

  const handleFileChange = (e) => {
    if (e.target.files && e.target.files[0]) {
      setFile(e.target.files[0]);
      setError('');
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    if (!file) {
      setError('Please select a file');
      return;
    }

    setUploading(true);
    setError('');

    try {
      const formData = new FormData();
      formData.append('file', file);

      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData,
      });

      const data = await response.json();

      if (!response.ok) {
        throw new Error(data.error || 'Error uploading file');
      }

      setUploadedFileUrl(data.fileUrl);
      setFile(null);
    } catch (error) {
      console.error('Error:', error);
      setError(error.message);
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="upload-container">
      <h2>Upload File to Rabata.io</h2>

      <form onSubmit={handleSubmit}>
        <div className="form-group">
          <label htmlFor="file">Select File</label>
          <input
            type="file"
            id="file"
            onChange={handleFileChange}
            disabled={uploading}
          />
        </div>

        {error && <p className="error">{error}</p>}

        <button type="submit" disabled={!file || uploading}>
          {uploading ? 'Uploading...' : 'Upload'}
        </button>
      </form>

      {uploadedFileUrl && (
        <div className="success">
          <p>File uploaded successfully!</p>
          <a href={uploadedFileUrl} target="_blank" rel="noopener noreferrer">
            View Uploaded File
          </a>
        </div>
      )}
    </div>
  );
}
```

### Generating Presigned URLs for Direct Uploads

For larger files, you might want to generate presigned URLs for direct uploads to Rabata.io:

```javascript
// app/api/presigned-upload/route.js (App Router)
import { NextResponse } from 'next/server';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { v4 as uuidv4 } from 'uuid';

// Initialize the S3 client
const s3Client = new S3Client({
  region: process.env.RABATA_REGION || 'us-east-1',
  endpoint: process.env.RABATA_ENDPOINT || 'https://s3.us-east-1.rabata.io',
  credentials: {
    accessKeyId: process.env.RABATA_ACCESS_KEY || '',
    secretAccessKey: process.env.RABATA_SECRET_KEY || ''
  },
  forcePathStyle: true
});

export async function POST(request) {
  try {
    const { fileName, fileType } = await request.json();

    if (!fileName || !fileType) {
      return NextResponse.json({ error: 'File name and type are required' }, { status: 400 });
    }

    const key = `${uuidv4()}-${fileName}`;

    const command = new PutObjectCommand({
      Bucket: process.env.RABATA_BUCKET_NAME,
      Key: key,
      ContentType: fileType
    });

    const presignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 });

    return NextResponse.json({
      presignedUrl,
      key,
      fileUrl: `${process.env.RABATA_ENDPOINT}/${process.env.RABATA_BUCKET_NAME}/${key}`
    });
  } catch (error) {
    console.error('Error generating presigned URL:', error);
    return NextResponse.json({ error: 'Error generating presigned URL' }, { status: 500 });
  }
}
```

### Listing Files from Rabata.io

Create an API route to list files from your bucket:

```javascript
// app/api/files/route.js (App Router)
import { NextResponse } from 'next/server';
import { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3';

// Initialize the S3 client
const s3Client = new S3Client({
  region: process.env.RABATA_REGION || 'us-east-1',
  endpoint: process.env.RABATA_ENDPOINT || 'https://s3.us-east-1.rabata.io',
  credentials: {
    accessKeyId: process.env.RABATA_ACCESS_KEY || '',
    secretAccessKey: process.env.RABATA_SECRET_KEY || ''
  },
  forcePathStyle: true
});

export async function GET() {
  try {
    const command = new ListObjectsV2Command({
      Bucket: process.env.RABATA_BUCKET_NAME
    });

    const response = await s3Client.send(command);

    const files = response.Contents?.map(item => ({
      key: item.Key,
      size: item.Size,
      lastModified: item.LastModified,
      url: `${process.env.RABATA_ENDPOINT}/${process.env.RABATA_BUCKET_NAME}/${item.Key}`
    })) || [];

    return NextResponse.json({ files });
  } catch (error) {
    console.error('Error listing files:', error);
    return NextResponse.json({ error: 'Error listing files' }, { status: 500 });
  }
}
```

## Advanced Usage

### CORS Configuration for Direct Uploads

If you're using direct uploads with presigned URLs, you need to configure CORS for your Rabata.io bucket:

```json
[
  {
    "AllowedHeaders": [
      "Authorization",
      "Content-Type",
      "Content-Length",
      "Content-MD5",
      "x-amz-content-sha256",
      "x-amz-date",
      "x-amz-security-token",
      "x-amz-user-agent"
    ],
    "AllowedMethods": [
      "GET",
      "PUT",
      "POST",
      "DELETE"
    ],
    "AllowedOrigins": [
      "http://localhost:3000",
      "https://your-production-domain.com"
    ],
    "ExposeHeaders": [
      "ETag"
    ],
    "MaxAgeSeconds": 3600
  }
]
```

### Image Optimization with Next.js

Next.js provides built-in image optimization through the `next/image` component. You can use it with Rabata.io by configuring a custom loader:

```jsx
// components/OptimizedImage.jsx
import Image from 'next/image';

const rabataLoader = ({ src, width, quality }) => {
  return `${src}?w=${width}&q=${quality || 75}`;
};

export default function OptimizedImage({ src, alt, width, height, ...props }) {
  return (
    <Image
      loader={rabataLoader}
      src={src}
      alt={alt}
      width={width}
      height={height}
      {...props}
    />
  );
}
```

### Handling File Deletion

Create an API route to delete files from your bucket:

```javascript
// app/api/delete-file/route.js (App Router)
import { NextResponse } from 'next/server';
import { S3Client, DeleteObjectCommand } from '@aws-sdk/client-s3';

// Initialize the S3 client
const s3Client = new S3Client({
  region: process.env.RABATA_REGION || 'us-east-1',
  endpoint: process.env.RABATA_ENDPOINT || 'https://s3.us-east-1.rabata.io',
  credentials: {
    accessKeyId: process.env.RABATA_ACCESS_KEY || '',
    secretAccessKey: process.env.RABATA_SECRET_KEY || ''
  },
  forcePathStyle: true
});

export async function DELETE(request) {
  try {
    const { key } = await request.json();

    if (!key) {
      return NextResponse.json({ error: 'File key is required' }, { status: 400 });
    }

    await s3Client.send(
      new DeleteObjectCommand({
        Bucket: process.env.RABATA_BUCKET_NAME,
        Key: key
      })
    );

    return NextResponse.json({ message: 'File deleted successfully' });
  } catch (error) {
    console.error('Error deleting file:', error);
    return NextResponse.json({ error: 'Error deleting file' }, { status: 500 });
  }
}
```

## Complete Example

Here's a complete example of a Next.js application that uses Rabata.io for file storage:

```jsx
// app/page.js (App Router)
import FileUpload from '../components/FileUpload';
import FileList from '../components/FileList';

export default function Home() {
  return (
    <main className="container">
      <h1>Next.js with Rabata.io</h1>
      <FileUpload />
      <FileList />
    </main>
  );
}
```

```jsx
// components/FileList.jsx
'use client'; // For App Router

import { useState, useEffect } from 'react';

export default function FileList() {
  const [files, setFiles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    const fetchFiles = async () => {
      try {
        const response = await fetch('/api/files');
        const data = await response.json();

        if (!response.ok) {
          throw new Error(data.error || 'Error fetching files');
        }

        setFiles(data.files);
      } catch (error) {
        console.error('Error:', error);
        setError(error.message);
      } finally {
        setLoading(false);
      }
    };

    fetchFiles();
  }, []);

  const handleDelete = async (key) => {
    try {
      const response = await fetch('/api/delete-file', {
        method: 'DELETE',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ key }),
      });

      const data = await response.json();

      if (!response.ok) {
        throw new Error(data.error || 'Error deleting file');
      }

      // Remove the deleted file from the list
      setFiles(files.filter(file => file.key !== key));
    } catch (error) {
      console.error('Error:', error);
      alert(error.message);
    }
  };

  if (loading) return <p>Loading files...</p>;
  if (error) return <p className="error">Error: {error}</p>;

  return (
    <div className="file-list">
      <h2>Files in Rabata.io Bucket</h2>

      {files.length === 0 ? (
        <p>No files found in the bucket.</p>
      ) : (
        <ul>
          {files.map((file) => (
            <li key={file.key}>
              <a href={file.url} target="_blank" rel="noopener noreferrer">
                {file.key}
              </a>
              <span className="file-size">
                {(file.size / 1024).toFixed(2)} KB
              </span>
              <button
                className="delete-button"
                onClick={() => handleDelete(file.key)}
              >
                Delete
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}
```

## Troubleshooting

### Common Issues

1. **CORS Errors**: If you're getting CORS errors when uploading directly to Rabata.io, make sure you've configured CORS for your bucket as shown in the Advanced Usage section.

2. **Environment Variables Not Loading**: Make sure you've created a `.env.local` file in the root of your project and restarted your development server.

3. **Access Denied Errors**: Ensure your Rabata.io credentials are correct and the bucket exists.

### Debugging Tips

1. **Check Server Logs**: Use `console.log` statements in your API routes to debug issues.

2. **Verify Environment Variables**: Add a debug endpoint to verify your environment variables are loaded correctly:

```javascript
// app/api/debug/route.js (App Router)
import { NextResponse } from 'next/server';

export async function GET() {
  return NextResponse.json({
    region: process.env.RABATA_REGION,
    endpoint: process.env.RABATA_ENDPOINT,
    bucketName: process.env.RABATA_BUCKET_NAME,
    // Don't include sensitive credentials in production!
    hasAccessKey: !!process.env.RABATA_ACCESS_KEY,
    hasSecretKey: !!process.env.RABATA_SECRET_KEY
  });
}
```

<div class="docs-article__actions">
  <a href="/docs/quickstart/node/" class="btn btn--secondary">Node.js Quickstart</a>
  <a href="/docs/quickstart/nuxt/" class="btn btn--secondary">Nuxt Integration</a>
  <a href="/docs/quickstart/express/" class="btn btn--secondary">Express Integration</a>
</div>
