Deploying Next.js w/ Drizzle on Vercel with Turborepo

I have a turborepo with Next.js using drizzle-orm as a shared package. A bit of context - I'm using a monorepo with drizzle and share the tables, types and schemas across all my apps by having a shared packages/database library - that my Next.js app is consuming. One issue I've had is that in order to get drizzle-orm working in my monorepo I've had to install it globally ie. in my root level package.json. My package.json at the root level looks like this at the moment:
{
"name": ...,
"private": true,
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^3.7.2",
"@types/pg": "^8.6.6",
"drizzle-kit": "^0.17.5",
"eslint": "^7.32.0",
"prettier": "^2.8.7",
"turbo": "latest"
},
"dependencies": {
"drizzle-orm": "^0.25.4",
"drizzle-zod": "^0.4.1",
"pg": "^8.10.0",
"pg-native": "^3.0.1",
"zod": "^3.21.4"
},
"workspaces": [
"apps/*",
"packages/*"
]
}
{
"name": ...,
"private": true,
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^3.7.2",
"@types/pg": "^8.6.6",
"drizzle-kit": "^0.17.5",
"eslint": "^7.32.0",
"prettier": "^2.8.7",
"turbo": "latest"
},
"dependencies": {
"drizzle-orm": "^0.25.4",
"drizzle-zod": "^0.4.1",
"pg": "^8.10.0",
"pg-native": "^3.0.1",
"zod": "^3.21.4"
},
"workspaces": [
"apps/*",
"packages/*"
]
}
Note that all of the packages above are not in either my packages/database or apps/next dependencies. My Next.js app consume my database library like this in the apps/next application's package.json "database": "1.0.0"
12 Replies
sevenwestonroads
sevenwestonroadsOP2y ago
I've had no error in local development. But when trying to deploy to Vercel I've had this error;
.../[email protected]/node_modules/libpq install: which: no pg_config in (/vercel/path0/node_modules/.pnpm/[email protected]/node_modules/libpq/node_modules/.bin:/vercel/path0/node_modules/.pnpm/[email protected]/node_modules/.bin:/vercel/path0/node_modules/.bin:/pnpm7/node_modules/pnpm/dist/node-gyp-bin:/vercel/path0/node_modules/.pnpm/node_modules/.bin:/vercel/path0/node_modules/.bin:/pnpm7/node_modules/.bin:/vercel/.yarn/bin:/vercel/.config/yarn/global/node_modules/.bin:/node18/bin:/ruby27/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin)
.../[email protected]/node_modules/libpq install: find: ‘/usr/pg*’: No such file or directory
.../[email protected]/node_modules/libpq install: gyp: Call to 'which pg_config || find /usr/bin /usr/local/bin /usr/pg* /opt -executable -name pg_config -print -quit' returned exit status 1 while in binding.gyp. while trying to load binding.gyp
.../[email protected]/node_modules/libpq install: gyp ERR! configure error
.../[email protected]/node_modules/libpq install: gyp ERR! stack Error: `gyp` failed with exit code: 1
.../[email protected]/node_modules/libpq install: gyp ERR! stack at ChildProcess.onCpExit (/pnpm7/node_modules/pnpm/dist/node_modules/node-gyp/lib/configure.js:325:16)
.../[email protected]/node_modules/libpq install: gyp ERR! stack at ChildProcess.emit (node:events:513:28)
.../[email protected]/node_modules/libpq install: gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:291:12)
.../[email protected]/node_modules/libpq install: which: no pg_config in (/vercel/path0/node_modules/.pnpm/[email protected]/node_modules/libpq/node_modules/.bin:/vercel/path0/node_modules/.pnpm/[email protected]/node_modules/.bin:/vercel/path0/node_modules/.bin:/pnpm7/node_modules/pnpm/dist/node-gyp-bin:/vercel/path0/node_modules/.pnpm/node_modules/.bin:/vercel/path0/node_modules/.bin:/pnpm7/node_modules/.bin:/vercel/.yarn/bin:/vercel/.config/yarn/global/node_modules/.bin:/node18/bin:/ruby27/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin)
.../[email protected]/node_modules/libpq install: find: ‘/usr/pg*’: No such file or directory
.../[email protected]/node_modules/libpq install: gyp: Call to 'which pg_config || find /usr/bin /usr/local/bin /usr/pg* /opt -executable -name pg_config -print -quit' returned exit status 1 while in binding.gyp. while trying to load binding.gyp
.../[email protected]/node_modules/libpq install: gyp ERR! configure error
.../[email protected]/node_modules/libpq install: gyp ERR! stack Error: `gyp` failed with exit code: 1
.../[email protected]/node_modules/libpq install: gyp ERR! stack at ChildProcess.onCpExit (/pnpm7/node_modules/pnpm/dist/node_modules/node-gyp/lib/configure.js:325:16)
.../[email protected]/node_modules/libpq install: gyp ERR! stack at ChildProcess.emit (node:events:513:28)
.../[email protected]/node_modules/libpq install: gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:291:12)
Our friend GPT4 told me that;
The error message you're seeing is happening because the libpq library is trying to find the PostgreSQL development files, which it needs to build the native bindings. The issue is that those files aren't present in the Vercel build environment.

Here are a few ways you can solve this:

Prebuild your app before deploying: If possible, you could build your app in an environment where PostgreSQL is installed (such as your local development environment) and then deploy the built files to Vercel. This way, libpq doesn't need to build the bindings during the Vercel deploy process.

Use a PostgreSQL client that doesn't require native bindings: Instead of using a package that requires the PostgreSQL development files, you might consider using a pure JavaScript implementation. An example is pg-promise or node-postgres (pg). These libraries do not require any native bindings, so you won't run into the same issue.

Mock out or avoid using packages that require PostgreSQL in your Vercel deployment: If you're only using PostgreSQL in certain parts of your app, you could consider mocking those parts out or setting your code up to avoid them when running in the Vercel environment. This would likely involve setting up some environment-specific configuration in your code.
The error message you're seeing is happening because the libpq library is trying to find the PostgreSQL development files, which it needs to build the native bindings. The issue is that those files aren't present in the Vercel build environment.

Here are a few ways you can solve this:

Prebuild your app before deploying: If possible, you could build your app in an environment where PostgreSQL is installed (such as your local development environment) and then deploy the built files to Vercel. This way, libpq doesn't need to build the bindings during the Vercel deploy process.

Use a PostgreSQL client that doesn't require native bindings: Instead of using a package that requires the PostgreSQL development files, you might consider using a pure JavaScript implementation. An example is pg-promise or node-postgres (pg). These libraries do not require any native bindings, so you won't run into the same issue.

Mock out or avoid using packages that require PostgreSQL in your Vercel deployment: If you're only using PostgreSQL in certain parts of your app, you could consider mocking those parts out or setting your code up to avoid them when running in the Vercel environment. This would likely involve setting up some environment-specific configuration in your code.
Use Docker for your deployment: If Vercel supports it, you could use a Docker container for your deployment. This would allow you to set up the environment exactly as you need it, including installing PostgreSQL.

Remember, the core issue here is that libpq is trying to build native bindings and can't find the necessary PostgreSQL development files. Any solution will need to either make those files available, avoid the need for them, or build the bindings in a different environment.

In case you are using pg-native for performance reasons and you are willing to sacrifice some of it for compatibility, you can remove it and use pg alone, which is a pure JS implementation and doesn't require compiling any native code. As long as you don't explicitly require pg-native in your code, pg will work just fine.
Use Docker for your deployment: If Vercel supports it, you could use a Docker container for your deployment. This would allow you to set up the environment exactly as you need it, including installing PostgreSQL.

Remember, the core issue here is that libpq is trying to build native bindings and can't find the necessary PostgreSQL development files. Any solution will need to either make those files available, avoid the need for them, or build the bindings in a different environment.

In case you are using pg-native for performance reasons and you are willing to sacrifice some of it for compatibility, you can remove it and use pg alone, which is a pure JS implementation and doesn't require compiling any native code. As long as you don't explicitly require pg-native in your code, pg will work just fine.
sevenwestonroads
sevenwestonroadsOP2y ago
I've seen a related question here -> https://github.com/orgs/vercel/discussions/228
GitHub
Deploying a solution with pg gives error: Could not resolve "pg-nat...
Im using pg in a few endpoints. I get this error when I deploy a sveltekit app to vercel. Error taken from log. Am I supposed to do any further configuration?
sevenwestonroads
sevenwestonroadsOP2y ago
Ok I'm trying this now -> https://github.com/vercel/next.js/pull/48402 It seems they're fixing it
GitHub
Add pg (Postgres) to server components external packages. by leer...
While exploring using Drizzle with the App Router, I noticed they have a callout for needing to explicitly add pg to serverComponentsExternalPackages. Adding pg here prevents manually needing to mo...
Andrii Sherman
You can also try to use Postgres.js driver Instead of pg Should be just few lines of code to change a connection and drizzle() import and remove pg and pg-native deps
sevenwestonroads
sevenwestonroadsOP2y ago
Thank you @Andrew Sherman ! I got this step working, but I'm stuck at linting step with wrong types for drizzle-orm This is the error;
- info Linting and checking validity of types .Failed to compile.

../../node_modules/.pnpm/[email protected]_kdjlz5zwvqpzqszfg4d2qbuxze/node_modules/drizzle-orm/db.d-266cc4bc.d.ts:515:15
Type error: Non-abstract class 'PgSelect<TTableName, TSelection, TSelectMode, TNullabilityMap>' does not implement inherited abstract member 'getSQL' from class 'PgSelectQueryBuilder<PgSelectHKT, TTableName, TSelection, TSelectMode, TNullabilityMap>'.

513 | interface PgSelect<TTableName extends string | undefined, TSelection extends ColumnsSelection, TSelectMode extends SelectMode, TNullabilityMap extends Record<string, JoinNullability> = TTableName extends string ? Record<TTableName, 'not-null'> : {}> extends PgSelectQueryBuilder<PgSelectHKT, TTableName, TSelection, TSelectMode, TNullabilityMap>, QueryPromise<SelectResult<TSelection, TSelectMode, TNullabilityMap>[]> {
514 | }
> 515 | declare class PgSelect<TTableName extends string | undefined, TSelection, TSelectMode extends SelectMode, TNullabilityMap extends Record<string, JoinNullability> = TTableName extends string ? Record<TTableName, 'not-null'> : {}> extends PgSelectQueryBuilder<PgSelectHKT, TTableName, TSelection, TSelectMode, TNullabilityMap> {
| ^
516 | private _prepare;
517 | /**
518 | * Create a prepared statement for this query. This allows
 ELIFECYCLE  Command failed with exit code 1.
- info Linting and checking validity of types .Failed to compile.

../../node_modules/.pnpm/[email protected]_kdjlz5zwvqpzqszfg4d2qbuxze/node_modules/drizzle-orm/db.d-266cc4bc.d.ts:515:15
Type error: Non-abstract class 'PgSelect<TTableName, TSelection, TSelectMode, TNullabilityMap>' does not implement inherited abstract member 'getSQL' from class 'PgSelectQueryBuilder<PgSelectHKT, TTableName, TSelection, TSelectMode, TNullabilityMap>'.

513 | interface PgSelect<TTableName extends string | undefined, TSelection extends ColumnsSelection, TSelectMode extends SelectMode, TNullabilityMap extends Record<string, JoinNullability> = TTableName extends string ? Record<TTableName, 'not-null'> : {}> extends PgSelectQueryBuilder<PgSelectHKT, TTableName, TSelection, TSelectMode, TNullabilityMap>, QueryPromise<SelectResult<TSelection, TSelectMode, TNullabilityMap>[]> {
514 | }
> 515 | declare class PgSelect<TTableName extends string | undefined, TSelection, TSelectMode extends SelectMode, TNullabilityMap extends Record<string, JoinNullability> = TTableName extends string ? Record<TTableName, 'not-null'> : {}> extends PgSelectQueryBuilder<PgSelectHKT, TTableName, TSelection, TSelectMode, TNullabilityMap> {
| ^
516 | private _prepare;
517 | /**
518 | * Create a prepared statement for this query. This allows
 ELIFECYCLE  Command failed with exit code 1.
Andrii Sherman
did you enable skipLibCheck in tsConfig?
sevenwestonroads
sevenwestonroadsOP2y ago
That was the missing link @Andrew Sherman thank you ! You're the best man I made it work finally Next.js + serverless Lambdas + drizzle-orm in a Turborepo
Eko
Eko2y ago
@jeanhdev Can I ask if and how exactly you got this issue resolved? I couldn't get it to work Hey Andrew, Could you maybe help me out here?
sevenwestonroads
sevenwestonroadsOP2y ago
Hey @Eko sure So I finally used the postgres driver for Node instead of pg. Then I had to ‘skipLibCheck’ and it worked like a charm.
Eko
Eko2y ago
Thanks got it working 🙂 I did not have to do the skipLibCheck tho
PGT
PGT2y ago
@jeanhdev hey can i ask how you're running migrations? I'm trying to run it in the new new instrumentation.ts file and i'm running into an issue where next.js can't find the db folder
brizzle
brizzle14mo ago
very curious about this, I'm trying to get instrumentation.ts to work as well, but getting an error trying to deploy a simple script
import { migrate } from "drizzle-orm/postgres-js/migrator";
import { sql } from "@vercel/postgres";
import { drizzle } from "drizzle-orm/vercel-postgres";

export async function register(): Promise<void> {
const db = drizzle(sql);
await migrate(db, { migrationsFolder: "drizzle" });
}
import { migrate } from "drizzle-orm/postgres-js/migrator";
import { sql } from "@vercel/postgres";
import { drizzle } from "drizzle-orm/vercel-postgres";

export async function register(): Promise<void> {
const db = drizzle(sql);
await migrate(db, { migrationsFolder: "drizzle" });
}
web:build: Webpack supports "data:" and "file:" URIs by default.
web:build: You may need an additional plugin to handle "node:" URIs.
web:build: at /vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:399825
web:build: at Hook.eval [as callAsync] (eval at create (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:13:28867), <anonymous>:6:1)
web:build: at Object.processResource (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:399750)
web:build: at processResource (/vercel/path0/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5308)
web:build: at iteratePitchingLoaders (/vercel/path0/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4667)
web:build: at runLoaders (/vercel/path0/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:8590)
web:build: at NormalModule._doBuild (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:399612)
web:build: at NormalModule.build (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:401640)
web:build: at /vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:82059
web:build: at NormalModule.needBuild (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:405716)
web:build:
web:build: Import trace for requested module:
web:build: node:path
web:build: ../../node_modules/drizzle-orm/postgres-js/migrator.mjs
web:build: Webpack supports "data:" and "file:" URIs by default.
web:build: You may need an additional plugin to handle "node:" URIs.
web:build: at /vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:399825
web:build: at Hook.eval [as callAsync] (eval at create (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:13:28867), <anonymous>:6:1)
web:build: at Object.processResource (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:399750)
web:build: at processResource (/vercel/path0/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5308)
web:build: at iteratePitchingLoaders (/vercel/path0/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4667)
web:build: at runLoaders (/vercel/path0/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:8590)
web:build: at NormalModule._doBuild (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:399612)
web:build: at NormalModule.build (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:401640)
web:build: at /vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:82059
web:build: at NormalModule.needBuild (/vercel/path0/node_modules/next/dist/compiled/webpack/bundle5.js:28:405716)
web:build:
web:build: Import trace for requested module:
web:build: node:path
web:build: ../../node_modules/drizzle-orm/postgres-js/migrator.mjs
Want results from more Discord servers?
Add your server