tursodatabase / tursodatabase/libsql-client-ts
Vercel Turso integration returning 401 for `@libsql/client`
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 576
- Forks
- 69
- PR merge metrics
- No merged PRs in 30d
Description
Description
Hi!
When accessing data from my site hosted on Vercel, I'm getting a 401 HTTP status code being thrown from the @libsql/client and I have no clue why. When I connect to the database locally, I simply cannot reproduce the issue.
I tried recreating the database, recreating the group, and tweaking the Drizzle configuration, but none of it seemed to work. I have no idea what's throwing this exception and why the request from Vercel isn't getting authorized properly.
If more information is necessary, I'd be more than happy to provide this!
Thanks in advance!
More details
Stacktrace
LibsqlError: SERVER_ERROR: Server returned HTTP status 401
at mapHranaError (file:///var/task/node_modules/.pnpm/@libsql+client@0.14.0/node_modules/@libsql/client/lib-esm/hrana.js:268:16)
at file:///var/task/node_modules/.pnpm/@libsql+client@0.14.0/node_modules/@libsql/client/lib-esm/http.js:76:23
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async LibSQLPreparedQuery.get (file:///var/task/node_modules/.pnpm/drizzle-orm@0.35.3_@libsql+client-wasm@0.14.0_@libsql+client@0.14.0_react@19.0.0-rc-cae764ce-_beykhe332nz34cv7xpk57h4rsy/node_modules/drizzle-orm/libsql/session.js:155:18)
at async login (file:///var/task/build/server/nodejs-eyJydW50aW1lIjoibm9kZWpzIn0/index.js:505:17)
at async file:///var/task/build/server/nodejs-eyJydW50aW1lIjoibm9kZWpzIn0/index.js:1955:20
at async Object.callRouteAction (/var/task/node_modules/.pnpm/@remix-run+server-runtime@2.13.1_typescript@5.6.3/node_modules/@remix-run/server-runtime/dist/data.js:36:16)
at async /var/task/node_modules/.pnpm/@remix-run+router@1.20.0/node_modules/@remix-run/router/dist/router.cjs.js:4719:19
at async callLoaderOrAction (/var/task/node_modules/.pnpm/@remix-run+router@1.20.0/node_modules/@remix-run/router/dist/router.cjs.js:4785:16)
at async Promise.all (index 2) {
code: 'SERVER_ERROR',
rawCode: undefined,
[cause]: HttpServerError: Server returned HTTP status 401
at errorFromResponse (file:///var/task/node_modules/.pnpm/@libsql+hrana-client@0.7.0/node_modules/@libsql/hrana-client/lib-esm/http/stream.js:352:16)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
status: 401
}
}
package.json
{
"name": "[REDACTED]",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix vite:build",
"dev": "run-p dev:*",
"dev:app": "remix vite:dev",
"dev:turso": "turso dev --db-file './drizzle/db/data.db'",
"dev:drizzle": "drizzle-kit studio",
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
"start": "remix-serve ./build/server/index.js",
"db:push": "drizzle-kit push",
"db:generate": "drizzle-kit generate",
"db:migrate": "dotenv -e .env -- tsx ./drizzle/migrate.ts",
"db:seed": "dotenv -e .env -- tsx ./drizzle/seed.ts",
"db:studio": "drizzle-kit studio",
"typecheck": "tsc",
"format": "prettier --check --cache .",
"prepare": "husky"
},
"dependencies": {
"@conform-to/react": "^1.2.2",
"@conform-to/zod": "^1.2.2",
"@epic-web/invariant": "^1.0.0",
"@epic-web/totp": "^2.0.0",
"@libsql/client": "^0.14.0",
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3",
"@react-email/components": "^0.0.25",
"@remix-run/node": "^2.13.1",
"@remix-run/react": "^2.13.1",
"@remix-run/router": "^1.20.0",
"@remix-run/serve": "^2.13.1",
"@t3-oss/env-core": "^0.11.1",
"@vercel/remix": "^2.13.1",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"drizzle-orm": "^0.35.3",
"input-otp": "^1.2.4",
"isbot": "^5.1.17",
"lucide-react": "^0.453.0",
"react": "rc",
"react-dom": "rc",
"remix-themes": "^1.5.1",
"resend": "^4.0.0",
"sonner": "^1.5.0",
"spin-delay": "^2.0.1",
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@faker-js/faker": "^9.0.3",
"@remix-run/dev": "^2.13.1",
"@total-typescript/tsconfig": "^1.0.4",
"@types/bcryptjs": "^2.4.6",
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc",
"autoprefixer": "^10.4.20",
"dotenv-cli": "^7.4.2",
"drizzle-kit": "^0.26.2",
"eslint": "^9.13.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-import-x": "^4.3.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-perfectionist": "^3.9.1",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"tailwindcss": "^3.4.14",
"tsx": "^4.19.1",
"typescript": "^5.6.3",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10",
"vite-tsconfig-paths": "^5.0.1"
},
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
},
"engines": {
"node": ">=20.0.0"
},
"packageManager": "pnpm@9.12.2",
"lint-staged": {
"*": "prettier --write --ignore-unknown"
}
}
app/db/index.ts
import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";
import { env } from "~/lib/env.server";
import * as schema from "./schema";
export const connection = createClient({
authToken: env.TURSO_AUTH_TOKEN,
url: env.TURSO_DATABASE_URL,
});
export const db = drizzle(connection, { schema });
app/routes/temp._index/route.tsx
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { db } from "~/db";
export async function loader() {
const users = await db.query.user.findMany();
return json({ users });
}
export default function Route() {
const data = useLoaderData<typeof loader>();
return (
<div>
<h1>Users</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dbCredentials: {
authToken: process.env.TURSO_AUTH_TOKEN || undefined,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
url: process.env.TURSO_DATABASE_URL!,
},
dialect: "turso",
out: "./drizzle/migrations",
schema: "./app/db/schema.ts",
});
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with app/db/index.ts and compare the deployed Vercel environment with the local values used by drizzle.config.ts. Run the database query from app/routes/temp._index/route.tsx in both environments and trace the @libsql/client request. Done means the deployed query is authorized without a 401 and the cause is documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100