简体   繁体   中英

"ConnectionNotFoundError: Connection "default" was not found" - class attribute

I'm learning NodeJS and the task is to build a simple messaging service. The teacher is using SQLite3, but I decided to use Postgres since it's the DB we use in our company projects.

Error: "ConnectionNotFoundError: Connection "default" was not found"

// server.ts

import 'reflect-metadata';
import express from 'express';
import { createConnection } from 'typeorm';

import router from './router';

(async () => {
  const PORT = 3333;

  console.log('before');
  await createConnection();
  console.log('after');

  const app = express();

  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));

  app.use(router);

  app.listen(PORT, () => {
    console.log(`App is running on port ${PORT}`);
  });
})();

I have the following MessagesService

// this works just fine
class MessagesService {
  async create({ user_id, text, admin_id }: MessagesCreateInterface): Promise<Message> {
    const repository = getCustomRepository(MessagesRepository);

    const message = repository.create({
      admin_id,
      text,
      user_id,
    });

    await repository.save(message);

    return message;
  }


  async listByUser(user_id: string): Promise<Array<Message>> {
    const repository = getCustomRepository(MessagesRepository);
    const messages = await repository.find({ user_id });
    return messages;
  }
}

Since getCustomRepository is called in both functions, and tried converting it to a class attribute:

class MessagesService {
  repository: MessagesRepository;

  constructor() {
    console.log('constructor');
    this.repository = getCustomRepository(MessagesRepository);
  }
  ...
}

But then I get ConnectionNotFoundError: Connection "default" was not found. .

Experiments

  1. Using a setTimeout inside constructor : The connection is accessed.

  2. Consoles.log: I get "before" and "constructor" print, but not "after".

Can someone help me understand what's going on? As I'm using async/await, MessageService shouldn't be called until the connection was established. Am I'm breaking some pattern here?

You are not passing to TypeORM the connection options so it doesn't know how to connect and setup the connection (which is the default one).

Calling await createConnection() without parameters will load connection options from files and (as far as I can see) you have not any ormconfig.X file.

To pass connection options to TypeORM you have three options:

  1. Connection Options Object
    See https://typeorm.io/#/connection .
    Pass the connection options (object) directly when calling createConnection({ ... }) .
    Example:
const connection = await createConnection({
    type: "postgres",
    host: "localhost",
    port: 5432,
    username: "test",
    password: "test",
    database: "test"
});
  1. ormconfig.X file
    See https://typeorm.io/#/using-ormconfig .
    Load an ormconfig.[extension] file in the project root.
    Supported ormconfig file extensions are: .json, .js, .ts, .env, .yml and .xml.
    Example (ormconfig.json):
{
   "type": "postgres",
   "host": "localhost",
   "port": 5432,
   "username": "test",
   "password": "test",
   "database": "test"
}
  1. Environment variables
    See https://typeorm.io/#/using-ormconfig/using-environment-variables .
    TypeORM automatically searches connection options in the environment variables or in .env or ormconfig.env in the project root (near package.json).
    Example:
TYPEORM_CONNECTION = postgres
TYPEORM_HOST = localhost
TYPEORM_PORT = 5432
TYPEORM_USERNAME = test
TYPEORM_PASSWORD = test
TYPEORM_DATABASE = test

The problem is that router.js is being imported before createConnection is called on server.ts .

Since the controllers are instantiated inside router.js , alongside the services and repositories, they were indeed trying to access a database connection before it was created.

The solution I found is to lazily import router.js after the connection has been established, but I'm not sure if that's an anti-pattern.

// server.ts

import 'reflect-metadata';
import express from 'express';
import { createConnection } from 'typeorm';

(async () => {
  await createConnection();
  // Importing routes after connection has been established
  const router = (await import('./router').default);
  const PORT = 3333;

  const app = express();

  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));

  app.use(router);

  app.listen(PORT, () => {
    console.log(`App is running on port ${PORT}`);
  });
})();

The suggested way in the lectures was to create an instance of the controller inside every route, but it doesn't seem right to me.

router.js

// suggested way
router.post('/messages', (request, response) => {
  const messagesController = new MessagesController();
  return messagesController.create(request, response); 
});

router.get('/messages/:id', (request, response) => {
  const messagesController = new MessagesController();
  return messagesController.showByUser(request, response); 
});

Since I'm still learning NodeJS, feel free to correct me and point me to the right way.

I know that the question has been marked as answered, but I had a similar issue recently(related to unit tests with mocha). the solution, in my case, was to stub typeorm getCustomRepository :

import sinon from 'sinon';
import * as typeorm from 'typeorm';
import {Connection, ConnectionManager} from 'typeorm';

describe('groupService', () => {

    let getCustomRepositoryStub;

    beforeEach(() => {
        getCustomRepositoryStub = sinon.stub(typeorm, 'getCustomRepository');
        sinon.stub(ConnectionManager.prototype, 'get').returns({getCustomRepository: getCustomRepositoryStub} as  Connection);
    });

    afterEach(() => {
        sinon.restore();
    });

    it('findById returns empty result', async() => {
        const groupId: string = 'mock-group-id';
        getCustomRepositoryStub.withArgs(GroupRepository)
            .returns({
                findById: () => Promise.resolve([])
            });
        const groups = await groupService.getGroup(groupId);
        expect(groups.length).equal(0);
    });

    ...

});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM