使用Node.js开发API时,Bookshelf.js模型调用ModelName.forge(...).destroy出现TypeError错误求助
TypeError: ModelName.forge(...).destroy is not a function in Bookshelf.js Let's break down this issue step by step and get it fixed. The error tells us the destroy method isn't available on your model instance, which usually stems from either a misconfigured model, incorrect test stubbing, or a problem with your Bookshelf setup.
1. First, Fix Your Model Definition
Looking at your model code, there are a couple of red flags that could break the inheritance of Bookshelf's built-in methods like destroy:
Missing import for
OtherModel: Your model referencesOtherModelin theotherModelrelation, but you haven't imported it. This will throw an error during model initialization, preventing it from inheriting Bookshelf's core methods. Add the import:const bookshelf = require('./bookshelf') const Intake = require('./intake') const OtherModel = require('./otherModel') // Replace with the correct path to your model const ModelName = bookshelf.Model.extend( { tableName: 'modelName', hasTimestamps: true, uuid: true, otherModel: function() { return this.hasOne(OtherModel) } }, { dependents: ['otherModel'] } ) module.exports = ModelNameCheck the
uuid: truesetting: This requires thebookshelf-uuidplugin. If you haven't registered it with Bookshelf, your model might fail to initialize properly. Add this to yourbookshelf.jssetup:const bookshelf = require('bookshelf')(knexInstance) bookshelf.plugin('bookshelf-uuid') // Ensure this line existsVerify model inheritance: Quick sanity check—add a log before exporting your model to confirm
destroyexists on the prototype:console.log(ModelName.prototype.destroy) // Should log a function, not undefined module.exports = ModelName
2. Fix Your Test Stubbing (The Big Issue Here)
Your test code isn't stubbing the actual code path being executed. You created a local deleteModelName stub, but it has no connection to the ModelNameService.deleteModelName method or the Bookshelf model calls inside it. That's why the real code is running and throwing the error.
Here's the corrected test stubbing approach:
describe('#deleteModelName', () => { it('should call deleteModelName and resolve correctly', async () => { // Create a mock model instance with a stubbed destroy method const mockModel = { destroy: sandbox.stub().resolves(CreatedModelName) } // Stub ModelName.forge to return our mock instance sandbox.stub(ModelName, 'forge').returns(mockModel) // Call the service method with a test ID const result = await ModelNameService.deleteModelName('test-id-123') // Verify the calls happened as expected expect(ModelName.forge).to.have.been.calledWith({ id: 'test-id-123' }) expect(mockModel.destroy).to.have.been.calledOnce() expect(result).to.equal(CreatedModelName) // Your destroy resolves this value, so result won't be undefined }) })
This stubs the exact ModelName.forge() and destroy() calls your service uses, so you're testing the service's logic without hitting the database.
3. Double-Check Your Bookshelf/Knex Setup
Ensure your root bookshelf.js file correctly initializes Knex with valid database credentials. A broken Knex connection can prevent Bookshelf models from loading their core methods:
const knex = require('knex')({ client: 'pg', // Or your database type (mysql, sqlite3, etc.) connection: { host: 'your-db-host', user: 'your-db-user', password: 'your-db-password', database: 'your-db-name' } }) const bookshelf = require('bookshelf')(knex) module.exports = bookshelf
Final Quick Checks
- Restart your test environment to ensure all code changes take effect.
- Test the
deleteModelNamefunction in isolation (outside tests) with a valid ID to confirm the model works as expected:const ModelNameService = require('./modelNameService') ModelNameService.deleteModelName('existing-id') .then(() => console.log('Delete successful')) .catch(err => console.error('Delete failed:', err))
内容的提问来源于stack exchange,提问作者Julissa DC

