Nuxt Module
The @nuxfly/core module provides runtime composables for database and storage access in your Nuxt application.
Installation
npm i @nuxfly/core
yarn add @nuxfly/core
pnpm i @nuxfly/core
bun i @nuxfly/core
deno i @nuxfly/core
Configuration
Add the module to your nuxt.config.ts
:
export default defineNuxtConfig({
modules: ['@nuxfly/core'],
nuxfly: {
litestream: true, // Enable SQLite with Litestream backup
publicStorage: true, // Enable public S3 storage
privateStorage: true, // Enable private S3 storage
}
})
Module Options
litestream
- Type:
boolean
- Default:
false
- Description: Enable SQLite database with Litestream backup integration
publicStorage
- Type:
boolean
- Default:
false
- Description: Enable public S3-compatible storage bucket
privateStorage
- Type:
boolean
- Default:
false
- Description: Enable private S3-compatible storage bucket
Runtime Configuration
The module declares some runtime settings that can be overridden via environment variables.
When deploying to Fly.io, these are overridden by the Fly Secrets, so you can just hard-code your local development settings in
nuxt.config.ts
.export default defineNuxtConfig({
// ...
// Runtime config structure example for a local Minio storage server
runtimeConfig: {
nuxfly: {
publicBucket: {
s3AccessKeyId: 'AAAAAAAAAAAAAAAAAAAA',
s3SecretAccessKey: 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
s3Endpoint: 'http://localhost:8200',
s3Bucket: 'nuxfly',
s3Region: 'auto',
},
privateBucket: {
s3AccessKeyId: 'AAAAAAAAAAAAAAAAAAAA',
s3SecretAccessKey: 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
s3Endpoint: 'http://localhost:8200',
s3Bucket: 'nuxfly',
s3Region: 'auto',
},
},
public: {
s3PublicUrl: 'http://localhost:8200/nuxfly',
},
},
Composables
The module provides several composables for accessing database and storage services:
useSqliteDatabase()
Access your SQLite database using the libSQL client.
<script setup>
const { db } = useSqliteDatabase()
// Execute queries
const users = await db.execute('SELECT * FROM users')
// Use with Drizzle ORM
import { drizzle } from 'drizzle-orm/libsql'
import * as schema from '~/server/db/schema'
const drizzleDb = drizzle(db, { schema })
const allUsers = await drizzleDb.select().from(schema.users)
</script>
The database connection is automatically configured based on your runtime config. In production on Fly.io, this connects to your SQLite database with Litestream backup.
usePublicStorage()
Access your public S3-compatible storage bucket.
<script setup>
const { minioClient, bucket } = usePublicStorage()
// Upload a file
const uploadFile = async (file: File) => {
const fileName = `uploads/${Date.now()}-${file.name}`
await minioClient.putObject(
bucket,
fileName,
file.stream(),
file.size,
{
'Content-Type': file.type
}
)
return fileName
}
// Get file URL
const getFileUrl = (fileName: string) => {
return minioClient.presignedGetObject(bucket, fileName, 24 * 60 * 60) // 24 hours
}
</script>
usePrivateStorage()
Access your private S3-compatible storage bucket.
<script setup>
const { minioClient, bucket } = usePrivateStorage()
// Store sensitive files
const storePrivateFile = async (content: string, fileName: string) => {
await minioClient.putObject(
bucket,
fileName,
Buffer.from(content),
content.length,
{
'Content-Type': 'text/plain'
}
)
}
// Retrieve private file
const getPrivateFile = async (fileName: string) => {
const stream = await minioClient.getObject(bucket, fileName)
return stream
}
</script>
useFlyProxy()
Utility composable for Fly.io proxy functionality and environment detection.
<script setup>
const {
isProduction,
isDevelopment,
flyRegion,
flyAppName
} = useFlyProxy()
// Conditional logic based on environment
if (isProduction) {
// Production-specific code
}
// Access Fly.io metadata
console.log(`Running in region: ${flyRegion}`)
console.log(`App name: ${flyAppName}`)
</script>
Server-Side Usage
All composables work in server-side contexts like API routes:
// server/api/users.get.ts
export default defineEventHandler(async (event) => {
const { db } = useSqliteDatabase()
const users = await db.execute('SELECT * FROM users')
return users.rows
})
// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
const { minioClient, bucket } = usePublicStorage()
const body = await readBody(event)
await minioClient.putObject(
bucket,
body.fileName,
Buffer.from(body.content),
body.content.length
)
return { success: true }
})
Environment Variables
Configure your storage and database connections using environment variables:
# Database
NUXT_NUXFLY_DB_URL=file:.data/db.sqlite
# Public Storage
NUXT_NUXFLY_PUBLIC_BUCKET_S3_ACCESS_KEY_ID=your_access_key
NUXT_NUXFLY_PUBLIC_BUCKET_S3_SECRET_ACCESS_KEY=your_secret_key
NUXT_NUXFLY_PUBLIC_BUCKET_S3_ENDPOINT=https://your-s3-endpoint.com
NUXT_NUXFLY_PUBLIC_BUCKET_S3_BUCKET=your-public-bucket
NUXT_NUXFLY_PUBLIC_BUCKET_S3_REGION=auto
# Private Storage
NUXT_NUXFLY_PRIVATE_BUCKET_S3_ACCESS_KEY_ID=your_access_key
NUXT_NUXFLY_PRIVATE_BUCKET_S3_SECRET_ACCESS_KEY=your_secret_key
NUXT_NUXFLY_PRIVATE_BUCKET_S3_ENDPOINT=https://your-s3-endpoint.com
NUXT_NUXFLY_PRIVATE_BUCKET_S3_BUCKET=your-private-bucket
NUXT_NUXFLY_PRIVATE_BUCKET_S3_REGION=auto
When using the
@nuxfly/cli
, these environment variables are automatically configured during deployment.TypeScript Support
The module includes full TypeScript support with proper type definitions:
// types/nuxfly.d.ts
export interface ModuleOptions {
litestream?: boolean
publicStorage?: boolean
privateStorage?: boolean
}
declare module '@nuxt/schema' {
interface RuntimeConfig {
nuxfly: {
dbUrl: string
publicBucket: {
s3AccessKeyId: string | null
s3SecretAccessKey: string | null
s3Endpoint: string | null
s3Bucket: string | null
s3Region: string
}
privateBucket: {
s3AccessKeyId: string | null
s3SecretAccessKey: string | null
s3Endpoint: string | null
s3Bucket: string | null
s3Region: string
}
}
}
}
Examples
Complete Todo App
<!-- pages/todos.vue -->
<template>
<div>
<h1>Todos</h1>
<form @submit.prevent="addTodo">
<input v-model="newTodo" placeholder="Add a todo..." />
<button type="submit">Add</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="deleteTodo(todo.id)">Delete</button>
</li>
</ul>
</div>
</template>
<script setup>
const { db } = useSqliteDatabase()
const newTodo = ref('')
const todos = ref([])
// Load todos
const loadTodos = async () => {
const result = await db.execute('SELECT * FROM todos ORDER BY created_at DESC')
todos.value = result.rows
}
// Add todo
const addTodo = async () => {
if (!newTodo.value.trim()) return
await db.execute(
'INSERT INTO todos (text, created_at) VALUES (?, ?)',
[newTodo.value, new Date().toISOString()]
)
newTodo.value = ''
await loadTodos()
}
// Delete todo
const deleteTodo = async (id: number) => {
await db.execute('DELETE FROM todos WHERE id = ?', [id])
await loadTodos()
}
// Load todos on mount
onMounted(loadTodos)
</script>
File Upload with Storage
<!-- pages/upload.vue -->
<template>
<div>
<h1>File Upload</h1>
<input
type="file"
@change="handleFileSelect"
accept="image/*"
/>
<button
@click="uploadFile"
:disabled="!selectedFile || uploading"
>
{{ uploading ? 'Uploading...' : 'Upload' }}
</button>
<div v-if="uploadedUrl">
<h3>Uploaded File:</h3>
<img :src="uploadedUrl" alt="Uploaded file" style="max-width: 300px;" />
</div>
</div>
</template>
<script setup>
const { minioClient, bucket } = usePublicStorage()
const selectedFile = ref<File | null>(null)
const uploading = ref(false)
const uploadedUrl = ref('')
const handleFileSelect = (event: Event) => {
const target = event.target as HTMLInputElement
selectedFile.value = target.files?.[0] || null
}
const uploadFile = async () => {
if (!selectedFile.value) return
uploading.value = true
try {
const fileName = `uploads/${Date.now()}-${selectedFile.value.name}`
// Upload to public storage
await minioClient.putObject(
bucket,
fileName,
selectedFile.value.stream(),
selectedFile.value.size,
{
'Content-Type': selectedFile.value.type
}
)
// Get public URL
uploadedUrl.value = await minioClient.presignedGetObject(
bucket,
fileName,
24 * 60 * 60 // 24 hours
)
} catch (error) {
console.error('Upload failed:', error)
} finally {
uploading.value = false
}
}
</script>