Cloudflare Nuxt 3 with Data Pipeline
This guide walks through deploying a full-stack Nuxt 3 application with a backend Pipeline to Cloudflare using Alchemy.
Create a new Nuxt 3 Project
Start by creating a new Nuxt 3 project:
bun create nuxt-app
cd my-nuxt-app
bun install
Install alchemy and Cloudflare:
bun add alchemy cloudflare
Configure Nuxt for Cloudflare
Update nuxt.config.ts
to work with Cloudflare Workers:
// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: "2025-04-21",
devtools: { enabled: true },
nitro: {
preset: "cloudflare-module",
prerender: {
routes: ["/"],
autoSubfolderIndex: false,
},
},
});
Create alchemy.run.ts
Create an alchemy.run.ts
file in the root of your project. We'll build this file step by step:
1. Set up imports and initialize app
// ./alchemy.run.ts
import alchemy from "alchemy";
import { Pipeline, R2Bucket, Nuxt } from "alchemy/cloudflare";
const R2_BUCKET_NAME = "example-bucket";
const PIPELINE_NAME = "example-pipeline";
const app = await alchemy("nuxt-pipeline-app", {
stage: process.env.USER ?? "dev",
phase: process.argv.includes("--destroy") ? "destroy" : "up",
quiet: !process.argv.includes("--verbose"),
password: process.env.ALCHEMY_PASS,
});
2. Create R2 bucket for data storage
const bucket = await R2Bucket("bucket", {
name: R2_BUCKET_NAME,
});
3. Configure data pipeline
const pipeline = await Pipeline("pipeline", {
name: PIPELINE_NAME,
source: [{ type: "binding", format: "json" }],
destination: {
type: "r2",
format: "json",
path: {
bucket: bucket.name,
},
credentials: {
accessKeyId: alchemy.secret(process.env.R2_ACCESS_KEY_ID),
secretAccessKey: alchemy.secret(process.env.R2_SECRET_ACCESS_KEY),
},
batch: {
maxMb: 10,
maxSeconds: 5,
maxRows: 100,
},
},
});
CAUTION
Set R2_ACCESS_KEY_ID
, R2_SECRET_ACCESS_KEY
, and ALCHEMY_PASS
environment variables before deployment.
4. Configure Nuxt website with bindings
export const website = await Nuxt("website", {
bindings: {
R2_BUCKET: bucket,
PIPELINE: pipeline,
},
});
console.log({
url: website.url,
});
await app.finalize();
Infer Binding Types
Create an src/env.d.ts
file to support type hints for Cloudflare bindings:
// src/env.d.ts
/// <reference types="@cloudflare/workers-types" />
import type { website } from './alchemy.run';
export type WorkerEnv = typeof website.Env;
declare module 'cloudflare:workers' {
namespace Cloudflare {
export interface Env extends WorkerEnv {}
}
}
Add API Route for Pipeline
Create a Nuxt server API route to send data to the pipeline:
// server/api/pipeline.post.ts
import { env } from "cloudflare:workers";
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event);
const pipeline = env.PIPELINE;
const data = body.data;
if (!data) {
throw new Error("Missing 'data' property in request body");
}
await pipeline.send([{ value: data }]);
return { success: true, message: "Data sent to pipeline." };
} catch (error) {
console.error("Error sending data to pipeline:", error);
throw createError({
statusCode: 500,
statusMessage: error instanceof Error ? error.message : "Pipeline error",
});
}
});
Create Frontend Interface
Create a simple form to interact with the pipeline:
<!-- pages/index.vue -->
<template>
<div>
<h1>Nuxt 3 + Alchemy + Cloudflare Pipeline Demo</h1>
<form @submit.prevent="sendToPipeline">
<label for="dataInput">Data to send:</label>
<input id="dataInput" v-model="dataToSend" type="text" required />
<button type="submit" :disabled="loading">Send to Pipeline</button>
</form>
<p v-if="message">{{ message }}</p>
<p v-if="error" style="color: red">{{ error }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
const dataToSend = ref("");
const loading = ref(false);
const message = ref("");
const error = ref("");
async function sendToPipeline() {
loading.value = true;
message.value = "";
error.value = "";
try {
const response = await fetch("/api/pipeline", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ data: dataToSend.value }),
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.statusMessage || "Failed to send data");
}
message.value = result.message || "Data sent successfully!";
dataToSend.value = "";
} catch (err) {
error.value =
err instanceof Error ? err.message : "An unknown error occurred.";
console.error("Error sending to pipeline:", err);
} finally {
loading.value = false;
}
}
</script>
<style scoped>
form {
display: flex;
flex-direction: column;
gap: 10px;
max-width: 300px;
margin-top: 20px;
}
button {
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
Deploy Your Application
Login to Cloudflare:
wrangler login
Run your Alchemy script to deploy the application:
bun ./alchemy.run
It should output the URL of your deployed site:
{
url: "https://your-site.your-account.workers.dev"
}
Click the URL to see your site. Test sending data via the form; it should appear in your R2 bucket shortly after.
Local Development
To run your application locally, use the Nuxt development server:
bun run dev
This will start a local development server:
Nuxt 3.9.0 with Nitro 2.8.1
➜ Local: http://localhost:3000/
➜ Network: use --host to expose this
Tear Down
When you're finished experimenting, you can tear down the application:
bun ./alchemy.run --destroy