tursodatabase / libsql-client-ts

TypeScript/JavaScript client API for libSQL
https://docs.turso.tech/sdk/ts/quickstart
MIT License
226 stars 32 forks source link

@libsql/client ESM not being used on Fly.io/Render #144

Closed spences10 closed 9 months ago

spences10 commented 9 months ago

I'm trying to deploy a Turso client with a embedded replica in a SvelteKit app.

exporting the client:

import { AUTH_TOKEN, DB_URL, SYNC_URL } from '$env/static/private';
import { createClient, type Client } from '@libsql/client';

let client_instance: Client | null = null;

export const turso_client = (): Client => {
    if (!client_instance) {
        const url = DB_URL?.trim();
        if (url === undefined) {
            throw new Error('DB_URL is not defined');
        }
        const sync_url = SYNC_URL?.trim();
        if (sync_url === undefined) {
            throw new Error('SYNC_URL is not defined');
        }

        const auth_token = AUTH_TOKEN?.trim();
        if (auth_token === undefined) {
            if (!url.includes('file:')) {
                throw new Error('AUTH_TOKEN is not defined');
            }
        }

        client_instance = createClient({
            url: DB_URL as string,
            authToken: AUTH_TOKEN as string,
            syncUrl: SYNC_URL as string,
        });
    }
    return client_instance;
};

The post install script will run on the platform:

postinstall.ts

import { createClient, type Client } from '@libsql/client';
import 'dotenv/config';

// this file needs to do the initial setup of Turso

// first set the remote
const set_remote = async () => {
    const client = createClient({
        url: process.env.SYNC_URL?.toString() || ``,
        authToken: process.env.AUTH_TOKEN
    });
    await run_queries(client);
};

// set the local
const set_local = async () => {
    const client = createClient({
        url: process.env.DB_URL?.toString() || ``,
        authToken: process.env.AUTH_TOKEN,
        syncUrl: process.env.SYNC_URL
    });
    // sync remote to local
    await client.sync();
    await run_queries(client);
};

// run queries
const run_queries = async (client: Client) => {
    const queries = 25;
    const start = performance.now();
    for (let i = 0; i < queries; i++) {
        const rs = await client.execute(
            'SELECT u.username, p.title, c.content FROM Users u JOIN Posts p ON u.user_id = p.user_id JOIN Comments c ON p.post_id = c.post_id;'
        );

        for (const row of rs.rows) {
            console.log(
                `User ${row.username} posted '${row.title}' with a comment: '${row.content}'`
            );
        }
    }
    const delta = performance.now() - start;
    console.log(`took ${delta}, ${delta / queries} per query`);
};

const main = async () => {
    await set_remote();
    await set_local();
};

main().catch(console.error);

This will log out the local and the sync db data fine.

svelte.config.js:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: vitePreprocess(),

    kit: {
        adapter: adapter(),
    },
};

export default config;

vite.config.ts:

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';

export default defineConfig({
    plugins: [sveltekit()],
    // same result with or without this 👇
    optimizeDeps: {
        exclude: ['@libsql/client'],
    },
    test: {
        include: ['src/**/*.{test,spec}.{js,ts}'],
    },
});

Platform errors:

Your service is live 🎉
ReferenceError: __dirname is not defined in ES module scope
This file is being treated as an ES module because it has a '.js' file extension and '/opt/render/project/src/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
    at requireLibsql (file:///opt/render/project/src/build/server/chunks/2-fPAwOy9D.js:1119:11)
    at file:///opt/render/project/src/build/server/chunks/2-fPAwOy9D.js:1410:21
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async Promise.all (index 1)
    at async render_page (file:///opt/render/project/src/build/server/index.js:3134:19)
    at async resolve2 (file:///opt/render/project/src/build/server/index.js:4229:24)
    at async respond (file:///opt/render/project/src/build/server/index.js:4120:22)
    at async Array.ssr (file:///opt/render/project/src/build/handler.js:1173:3)
ReferenceError: __dirname is not defined in ES module scope
This file is being treated as an ES module because it has a '.js' file extension and '/opt/render/project/src/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
    at requireLibsql (file:///opt/render/project/src/build/server/chunks/2-fPAwOy9D.js:1119:11)
    at file:///opt/render/project/src/build/server/chunks/2-fPAwOy9D.js:1410:21
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async Promise.all (index 1)
    at async render_page (file:///opt/render/project/src/build/server/index.js:3134:19)
    at async resolve2 (file:///opt/render/project/src/build/server/index.js:4229:24)
    at async respond (file:///opt/render/project/src/build/server/index.js:4120:22)
    at async Array.ssr (file:///opt/render/project/src/build/handler.js:1173:3)

Same sort of errors on fly.io

spences10 commented 9 months ago

Ok, so, I have found a way around this via a comment on the Prisma issues: https://github.com/prisma/prisma/issues/5030#issuecomment-1398076317

I need to require the @libsql/client

import { AUTH_TOKEN, DB_URL, SYNC_URL } from '$env/static/private';
import type { Client } from '@libsql/client';
import { createRequire } from 'module';

const require = createRequire(import.meta.url ?? __filename);

const {
    createClient: RequiredCreateClient,
} = require('@libsql/client');

let client_instance: Client | null = null;

export const turso_client = (): Client => {
    if (!client_instance) {

This builds without the __dirname in the pnpm run build output from SvelteKit.

benmccann commented 9 months ago

It look me an extremely long time to find where this was coming from because of https://github.com/tursodatabase/libsql-js/pull/72, but I managed to track it down:

https://github.com/tursodatabase/libsql-js/blob/6e705ac5d9595cc969149e208e0a9a1d9fb877b0/index.js#L41

benmccann commented 9 months ago

Looks like this issue is a duplicate of https://github.com/tursodatabase/libsql-js/issues/49

spences10 commented 9 months ago

For prosperity if anyone come up against this, referenced in https://github.com/tursodatabase/libsql-js/issues/49

Install the @libslq/client as a dependency and not a devDependency then rollup will take care of the rest