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

Add `@libsql/serverless` package #107

Closed penberg closed 11 months ago

penberg commented 1 year ago

We already support this via the @libsql/client/web import, but people are not associating it with AWS Lambda or Vercel and instead tripping over @libsql/client import pulling the native library. Let's do a @libsql/serverless package that people can import instead.

CodingDoug commented 1 year ago

Not all serverless environments should have a problem with this. Google Cloud Functions (one of the OG serverless products) will indeed run a full npm install during deployment, runs in a true node environment at runtime, and should load native bits from node_modules.

jansuchomel commented 1 year ago

it would be great to make a completely separate npm package, so I would not need to make forked lib for usage in cloudflare pages (workers) (i've tried vercel edge and netlify edge with same problems) with sveltekit/vite https://github.com/jansuchomel/libsql-client-ts.... the problem is that libsql depends on neon that uses quite a few node specific modules

philippviereck commented 1 year ago

I'm facing the same issue as jansuchomel.

marchellodev commented 1 year ago

Also facing the same issue,

web:deploy: > Using @sveltejs/adapter-cloudflare-workers
web:deploy: ✘ [ERROR] Could not resolve "child_process"
web:deploy:
web:deploy:     ../../node_modules/detect-libc/lib/detect-libc.js:6:29:
web:deploy:       6 │ const childProcess = require('child_process');
web:deploy:         ╵                              ~~~~~~~~~~~~~~~
web:deploy:
web:deploy:   The package "child_process" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
web:deploy:
web:deploy: ✘ [ERROR] Could not resolve "fs"
web:deploy:
web:deploy:     ../../node_modules/detect-libc/lib/filesystem.js:6:19:
web:deploy:       6 │ const fs = require('fs');
web:deploy:         ╵                    ~~~~
web:deploy:
web:deploy:   The package "fs" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
web:deploy:
web:deploy: ✘ [ERROR] Could not resolve "path"
web:deploy:
web:deploy:     ../../node_modules/@neon-rs/load/dist/index.js:27:34:
web:deploy:       27 │ const path = __importStar(require("path"));
web:deploy:          ╵                                   ~~~~~~
web:deploy:
web:deploy:   The package "path" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
web:deploy:
web:deploy: ✘ [ERROR] Could not resolve "fs"
web:deploy:
web:deploy:     ../../node_modules/@neon-rs/load/dist/index.js:28:32:
web:deploy:       28 │ const fs = __importStar(require("fs"));
web:deploy:          ╵                                 ~~~~
web:deploy:
web:deploy:   The package "fs" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
web:deploy:
glommer commented 11 months ago

Folks, do you happen to have a simple reproducer for this? We had one, but after a Drizzle update it doesn't reproduce anymore

marchellodev commented 11 months ago

Same, seems like it was an issue on drizzle's side

glommer commented 11 months ago

Drizzle is involved here, but we'd like to defend better against those issues by having a full serverless driver that doesn't even list the node symbols as dependencies (the current driver lists it as a dependency, because we build the non-serverless driver from the same source).

If anybody has a current reproducer, that will help a lot.

philippviereck commented 11 months ago

https://github.com/philippviereck/turso-cf-sveltekit

pnpm build

@glommer

giovannibenussi commented 11 months ago

Hi, here is what I found about this.

The Problem

Looks like the issue, in this case, is that the build command is running in a node environment and drizzle is importing types from @libsql/client, which breaks everything.

We know that Drizzle is involved as @glommer mentioned, because if you remove drizzle from the turso-cf-sveltekit example, it works. Also, if you put the code below on src/routes/+page.server.ts, it breaks too:

import { type Client } from '@libsql/client';

export const load = async () => {
  return { hello: 'world' };
};

However, if you apply the same fix that Drizzle, it works:

import type { Client } from '@libsql/client';

export const load = async () => {
  return { hello: 'world' };
};

The Solution

How to fix this? Well, there's a way! If for any reason @libsql/client is imported on a web browser environment, you can add a browser entry as documented on the packages documentation.

    "exports": {
        ".": {
            "types": "./lib-esm/node.d.ts",
            "import": {
                "workerd": "./lib-esm/web.js",
                "deno": "./lib-esm/web.js",
                "edge-light": "./lib-esm/web.js",
                "netlify": "./lib-esm/web.js",
                "node": "./lib-esm/node.js",
                "browser": "./lib-esm/web.js", <----- Add this line
                "default": "./lib-esm/node.js"
            },
            "require": "./lib-cjs/node.js"
        },

Please note that default must come last as specified here:

"default" - the generic fallback that always matches. Can be a CommonJS or ES module file. This condition should always come last.

To ~hackily~ test this fix, you can edit the package file directly from node_modules/.pnpm/@libsql+client@0.3.5/node_modules/@libsql/client/package.json and run pnpm build.

Implications

The suggested code fixes the issue that we had with Drizzle and acts as a safety net for future issues.

However, this also opens a new door of improvement: what if we just rely on @libsql/client for all environments? With package.json exports, this is possible and actually looks like what I did should work. This way the docs just mention @libsql/client and the runtime will choose the correct module to use.

This is probably out of the scope of this issue, but I want to leave this here for future discussions! There are extra things to investigate related to this. For example, would this work in all environments? I've been researching this for a day and I'll stop now, but I'm willing to investigate further if you're interested in following this path.