Skip to main content

Nuxt

Nuxt 3 ships with Nitro, which gives you full server-side capabilities including streaming uploads. Pair Nitro with the AWS SDK for JavaScript v3 to use Filebase as your storage backend.

Install

npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner

Configure runtime

nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
filebaseKey: process.env.FILEBASE_KEY,
filebaseSecret: process.env.FILEBASE_SECRET,
filebaseBucket: process.env.FILEBASE_BUCKET || 'my-nuxt-app',
},
});

Server-side S3 client

server/utils/filebase.ts
import { S3Client } from '@aws-sdk/client-s3';

export function useFilebase() {
const config = useRuntimeConfig();
return new S3Client({
endpoint: 'https://s3.filebase.io',
region: 'auto',
credentials: {
accessKeyId: config.filebaseKey,
secretAccessKey: config.filebaseSecret,
},
});
}

Upload from a Nuxt server route

server/api/upload.post.ts
import { Upload } from '@aws-sdk/lib-storage';
import { PutObjectCommand } from '@aws-sdk/client-s3';

export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const s3 = useFilebase();
const formData = await readMultipartFormData(event);

if (!formData) {
throw createError({ statusCode: 400, message: 'No file' });
}

const file = formData.find((f) => f.name === 'file');
if (!file?.data) {
throw createError({ statusCode: 400, message: 'No file' });
}

const key = `uploads/${Date.now()}-${file.filename}`;

await s3.send(
new PutObjectCommand({
Bucket: config.filebaseBucket,
Key: key,
Body: file.data,
ContentType: file.type,
}),
);

return { key, url: `https://${config.filebaseBucket}.s3.filebase.io/${key}` };
});

Pre-signed URL endpoint for direct uploads

For larger files or to bypass your Nuxt server, issue a pre-signed URL the browser can PUT to directly:

server/api/presign-upload.post.ts
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { PutObjectCommand } from '@aws-sdk/client-s3';

export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const s3 = useFilebase();
const body = await readBody<{ filename: string; contentType: string }>(event);

const key = `uploads/${Date.now()}-${body.filename}`;

const url = await getSignedUrl(
s3,
new PutObjectCommand({
Bucket: config.filebaseBucket,
Key: key,
ContentType: body.contentType,
}),
{ expiresIn: 600 },
);

return { url, key };
});

Browser-side:

components/Uploader.vue
<script setup lang="ts">
async function uploadFile(file: File) {
// 1. Get a pre-signed URL from our backend
const { url, key } = await $fetch<{ url: string; key: string }>('/api/presign-upload', {
method: 'POST',
body: { filename: file.name, contentType: file.type },
});

// 2. PUT the file directly to Filebase
await fetch(url, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type },
});

console.log('Uploaded to', key);
}
</script>

What's next