Nuxt Module

The @nuxfly/core module provides runtime composables for database and storage access in your Nuxt application.

Installation

npm 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>
Read more in Cli.