I'm writing an app using Nuxt3 and Dexie Cloud, deploying with PM2 to my dev Ubuntu webserver on my LAN. All was well, until I decided to to add another deployment, which I would treat as "production", to mimic the end user's setup. I won't be able to just delete databases and tables there, so I am preparing to run it on a separate database.
I planned changing the db name in the instantiation to a variable (new Dexie(bdVar)
) and the same for the url in db.cloud.configure
.
And that's where things blew up... Cause... Where do I get the values for those variables from? .env
doesn't work as I thought it would (Node, runtime, etc). Nuxt's runtimceConfig
... well, still trying to wrap my head around that.
Let me give an overview of what I have:
// db/index.js
import { Dexie } from 'dexie'
import { dexieCloud } from 'dexie-cloud-addon'
export const db = new Dexie(dexieDb***, {addons: [dexieCloud]})
db.version(1).stores({
// table definitions
})
db.cloud.configure({
databaseUrl: dexieDbUrl***,
requireAuth: true
})
*** means it works fine when string provided
Then I have a few other db files, each with related queries/transactions
// db/items.dexie.js
import { db } from '~/db/index.js'
export const fetchItems = async (realmId) => {
const result = await db.items.where('realmId').equals(realmId).sortBy('last_update')
return result
}
...
Then I have my Pinia stores, items store, shortened, as example
// stores/items.js
import { fetchItems } from '~/db/items.dexie.js'
export const useItemStore = defineStore('items', () => {
let items = ref([])
const getItems = async () => {
if (items.value.length === 0) items.value = await fetchItems(currentSpace.value.realmId)
return items
}
...
return {
items,
getItems,
...
}
})
A simple example of a page:
// pages/items.vue
<script setup>
const { items } = storeToRefs(useItemStore())
onMounted(async () => {
if (items.value.length < 1) items.value = await useItemStore().getItems()
})
</script>
<template>
<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>
</template>
I tried adding the runtimeConfig in the nuxt.conf.ts:
export default defineNuxtConfig({
runtimeConfig: {
dexieDb: '', // .env entry: NUXT_DEXIE_DB
dexieDbUrl: '', // .env entry: NUXT_DEXIE_DB_URL
},
})
The next logical step (in my head, of course) would be to now access the runtimeConfig in db/index.js:
// db/index.js
import ...
const runtime = useRuntimeConfig()
export const db = new Dexie(runtime.dexieDb, {addons: [dexieCloud]})
...
The 500 error does clearly explain that this won't work:
A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.
And this is where I'm stuck at the moment. How do I make the Dexie db available, while using variables that will have different values, depending on the environment, while using "a plugin, Nuxt hook, Nuxt middleware, or Vue setup function"?
Note: Some of the information I need to have as variables is sensitive.
I went to bed with this question in my head. Woke up with a "solution". Although there might be a more elegant way of doing this, this is what I came up with:
First, I moved db/index.js
into a plugin
// plugins/dexie.js
import { Dexie } from 'dexie'
import { dexieCloud } from 'dexie-cloud-addon'
export default defineNuxtPlugin(nuxtApp => {
const runtimeConfig = useRuntimeConfig()
const db = new Dexie(runtimeConfig.public.dexieDb, {addons: [dexieCloud]})
db.version(1).stores({
// table definitions
})
db.cloud.configure({
databaseUrl: runtimeConfig.public.dexieDbUrl,
requireAuth: true
})
nuxtApp.db = db
})
Moved the variables to the public runtimeConfig
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
dexieDb: '', // env var NUXT_PUBLIC_DEXIE_DB
dexieDbUrl: '', // env var NUXT_PUBLIC_DEXIE_DB_URL
},
},
...
})
Then I changed the db methods to expect a reference to the db
// db/items.dexie.js
export const fetchItems = async (db, realmId) => {
return await db.items.where('realmId').equals(realmId).sortBy('last_update')
}
...
In the stores I get the db from the NuxtApp, and pass it along
// stores/items.js
import { fetchItems } from '~/db/items.dexie.js'
export const useItemStore = defineStore('items', () => {
const { db } = useNuxtApp()
let items = ref([])
const getItems = async () => {
if (items.value.length === 0) items.value = await fetchItems(db, currentSpace.value.realmId)
return items
}
...
return {
items,
getItems,
...
}
})
This worked great in dev! It uses the .env
in the project root.
For "production" I needed to update the ecosystem.config.cjs
, adding the env variables (I'm using PM2)
module.exports = {
apps: [{
name: 'InstanceName',
port: '3005',
exec_mode: 'cluster',
instances: '2',
script: './.output/server/index.mjs',
env: {
'NUXT_PUBLIC_DEXIE_DB': 'home-db',
'NUXT_PUBLIC_DEXIE_DB_URL': 'https://xxxxxxxxx.dexie.cloud'
},
}]
}
Now I have my local dev, and two instances on my LAN webserver: One "dev" (using the same db as my local, so I can delete and update. and mess around as much as I want) and one "production" (using a separate db, as it would be in the actual production.
I hope this helps someone who gets stuck with this...