No matter what I try my nextjs 14 app keeps failing on build (on this for days now). Unfortunately the error is thrown in webpack'd minimized files, but monkey patching and commenting out has me thinking its the sequelize-typescript
implemention.
In essence I'm trying to use nextJS for just an api, hence I'm using the App Router.
npm run dev
and npm run test
work without issue. Its just when npm run build
is called that things blow up. This makes me think that I am missunderstanding the app router
in nextjs and its an architecture issue with my method of implementing sequelize-typescript
?
If I touch the db anywhere, then the error is thrown on npm run build
, eg:
In the router app/api/router.ts
export async function POST(...){
// will throw Invalid URL error
await db.sequelize.sync()
await db.sequelize.authenticate()
// import from lib/binance.ts also throw if above is commented
await binanceLib.validate()
}
I've recreated the error here: https://github.com/daithi-coombes/temp-nextjs-sequelize-invalid-url-error
Error:
...
✓ Compiled successfully
✓ Linting and checking validity of types
Collecting page data ...TypeError: Invalid URL
at new URL (node:internal/url:775:36)
at 84334 (.next/server/app/api/buscuit-machine/route.js:1:6500)
at o (.next/server/webpack-runtime.js:1:143)
at 83773 (.next/server/app/api/buscuit-machine/route.js:2:357)
at o (.next/server/webpack-runtime.js:1:143)
at 77123 (.next/server/app/api/buscuit-machine/route.js:2:1898)
at o (.next/server/webpack-runtime.js:1:143)
at 37218 (.next/server/app/api/buscuit-machine/route.js:1:1908)
at o (.next/server/webpack-runtime.js:1:143)
at o (.next/server/app/api/buscuit-machine/route.js:37:781295) {
code: 'ERR_INVALID_URL',
input: 'undefined'
}
Tree structure:
.
├── app
│ └── api
│ ├── my-route-1
│ │ └── route.ts
│ ├── my-route-2
│ │ └── route.ts
├── config
│ └── config.ts
├── lib
│ ├── binance.ts
│ ├── db.ts
│ ├── providers.ts
├── models
│ ├── connection.ts
│ ├── index.ts
│ ├── order.ts
├── next.config.js
├── next-env.d.ts
└── tsconfig.json
The code flow is:
app/api/my-route/route.ts
:: POST()
lib/binance.ts
:: binance.validate()
models/index.ts
:: Order.update()
models/connection.ts
:: new Sequelize()
The Code:
app/api/my-route/route.ts
...
// @see https://stackoverflow.com/a/76401805/288644
export const dynamic = 'force-dynamic'
import { loadLibraries } from '../../../lib/providers'
export async function POST(req: NextRequest) {
const { binance, telegram } = await loadLibraries()
const data = await req.json()
// await db.sequelize.sync()
// await db.sequelize.authenticate()
trade = await DB.newTrade(await binance.validate(data))
...
If I comment out Order.update()
below, then all will build with no problem, uncomment and the error above is thrown.
lib/binance.ts
import Order from '../models/order'
class Binance{
async validate(trade:Trade):Promise<OrderI>{
...
// try{
// await Order.update({ ... }, {
// where: {
// id: trade.orderId,
// },
// })
// }catch(e) {
// console.log('e: ', e)
// throw e
// }
return order
}
models/connection.ts
const env:string = process.env.NODE_ENV || 'development'
const config:SequelizeOptions = Config[env]
let sequelize:Sequelize
config.host = config.host ? config.host : '127.0.0.1' // a hack to try remove build 'Invalid URL' error. Error still persists.
config.dialect = 'postgres'
config.dialectModule = pg
sequelize = new Sequelize(config)
export default sequelize
models/order.ts
@Table({
tableName: 'Orders'
})
class Order extends Model<OrderI> {
@Column({
type: DataType.INTEGER,
primaryKey: true,
unique: true,
field: 'id'
})
@ForeignKey(() => Strategy)
id: number
...
}
export default Order
models/index.ts
...
const db = {
sequelize,
Sequelize,
Strategy: strategy,
Order: order,
}
fs
.readdirSync(modelPath)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
})
.forEach(file => {
const model = require(__dirname + '/../models/' + file)(sequelize, Sequelize.DataTypes)
db[model.name] = model
})
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize = sequelize
db.Sequelize = Sequelize
export default db
any help appreciated
Edit:
next.config.js
/**
* @type {import('next').NextConfig}
*/
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Access-Control-Allow-Origin',
value: 'http://localhost:3000',
},
],
},
]
},
experimental: {
webpackBuildWorker: true,
serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript'],
},
}
env.local
API_URL=http://localhost:3000
NEXT_PUBLIC_APP_URL=http://localhost:3000
BINANCE_API_KEY=
BINANCE_API_SECRET=
BINANCE_ENDPOINT=https://testnet.binancefuture.com
DB_HOST=127.0.0.1
DB_USER=postgres
DB_PASSWORD=mysecretpassword
DB_NAME=foobar
TELEGRAM_BOT_TOKEN=
Versions:
^14.2.4
v20.11.0
^2.1.6
The problem lies in the minified version of your code. Take a look at the output of your api route .next/server/app/api/buscuit-machine/route.js
. It contains lots of hard to read minified code but this line is where the problem lies:
let e = new URL(process.env.DATABASE_URL);
Now this code is not an inherent problem. It simply assigns variable e
to a url. Specifically the DATABASE URL. This is where the problem is; process.env.DATABASE_URL
is undefined
. undefined
is not a valid url. So now this code simplifies to the following:
let e = new URL(undefined);
This is a bug with Sequlize I would recommend opening a new issue. However, there is a simple fix. Add the following to your .env
DATABASE_URL=postgres://user:pass@example.com:5432/dbname