Search code examples
node.jstypescripttypeorm

"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?


Solution

  • 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.