skip to Main Content

I’m trying to make an API that uses PostgreSQL to store shortened links. However, when trying to execute a query to the database, postgreClient is undefined every time.

short.controller.ts:

import { Request, Response, NextFunction } from "express";
import client from "../Database";

export default class ShortController {
    private postgreClient;

    constructor() {
        this.postgreClient = client;
    }

    async post(request: Request, response: Response, next: NextFunction) {
        const url = request.query.url;
        const randomString = Math.random().toString(36).substring(2, 7);

        const shortUrl = `${process.env.API_URL}/api/short/${randomString}`;

        try {
            const sql = "INSERT INTO short (short_url, url) VALUES ($1, $2)";
            await this.postgreClient.query(sql, [shortUrl, url]);
            await response.json({ shortUrl });
        } catch (exception) {
            console.error(exception);
            await response.json({ message: "Error" });
        }
    }
}

Database.ts:

import { Client } from "pg";
import * as dotenv from "dotenv";

dotenv.config();

const dbHost = process.env.DB_HOST || "localhost";
const dbPort = process.env.DB_PORT || 5432;
const dbUser = process.env.DB_USER || "postgres";
const dbPassword = process.env.DB_PASSWORD || "password";
const dbDatabase = process.env.DB_DATABASE || "kyowakoku";

const connectionString = `postgres://${dbUser}:${dbPassword}@${dbHost}:${dbPort}/${dbDatabase}`;

const client = new Client({ connectionString });

export default client;

I tried rewriting to a especially class thinking it would do something, but without result.

2

Answers


  1. The only issue I see is that you did not call connect() on the postgres client . This means you’ve built a connection string and failed to do the actual connection to the DB.

    There are 2 ways you can solve this.

    1. Calling the connect method in your database.ts file
    import {
      Client
    } from "pg";
    import * as dotenv from "dotenv";
    
    dotenv.config();
    
    const dbHost = process.env.DB_HOST || "localhost";
    const dbPort = process.env.DB_PORT || 5432;
    const dbUser = process.env.DB_USER || "postgres";
    const dbPassword = process.env.DB_PASSWORD || "password";
    const dbDatabase = process.env.DB_DATABASE || "kyowakoku";
    
    // i'm editing this code as response to your follow up comment. 
    // if the client is having issues on multiple connections, switch to using a pool. 
    // he pool is initially created empty and will create new clients lazily as they are needed
    
    
    const client = new Pool({
      host: dbHost,
      user: dbUser,
      //add password , port etc 
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    })
    
    // const connectionString = //`postgres://${dbUser}:${dbPassword}@${dbHost}:${dbPort}/${dbDatabase}`;
    //const client = new Client({
    //  connectionString
    // });
    
    // call the connect() to initialize the actual db connection before exporting it 
    //client.connect()
    
    export default client;
    1. Leave the database.ts file as it is, and initiate the connection from your controller.ts file
    import {
      Request,
      Response,
      NextFunction
    } from "express";
    import client from "../Database";
    
    export default class ShortController {
      private postgreClient;
    
      constructor() {
        this.postgreClient = client;
        this.connectToDatabase(); // Call a method to connect to the database
      }
    
      // method to connect to the database 
      private async connectToDatabase() {
        try {
          await this.postgreClient.connect(); // Connect to the database
          console.log("Connected to PostgreSQL database");
        } catch (error) {
          console.error("Error connecting to PostgreSQL database:", error);
        }
      }
    
      async post(request: Request, response: Response, next: NextFunction) {
        const url = request.query.url;
        const randomString = Math.random().toString(36).substring(2, 7);
    
        const shortUrl = `${process.env.API_URL}/api/short/${randomString}`;
    
        try {
          const sql = "INSERT INTO short (short_url, url) VALUES ($1, $2)";
          await this.postgreClient.query(sql, [shortUrl, url]);
          await response.json({
            shortUrl
          });
        } catch (exception) {
          console.error(exception);
          await response.json({
            message: "Error"
          });
        }
      }
    }

    Other general rules

    1. Ensure your .env file is loaded correctly and contains the specified env variables for the DB_HOST, DB_USER, DB_PORT etc
    2. Ensure you have postgres running on your machine and can be accessed from the specified DB_PORT stated in your env file
    3. Ensure the database specified exist or can be created
    Login or Signup to reply.
  2. I assume that the TypeError: Cannot read properties of undefined (reading 'postgresClient') error occurs around these lines:

    // short.controller.ts
            try {
                const sql = "INSERT INTO short (short_url, url) VALUES ($1, $2)";
                await this.postgreClient.query(sql, [shortUrl, url]); <- error occurs because of this line
                await response.json({ shortUrl });
    

    That means that this refers to an undefined value, in other words, the context of the function has been lost. So i’d suggest you to check where and how the method ShortController#post is being called.

    I can assume that the method was called without it’s context. Maybe somewhere in your code you passed the method to the express instance in such a manner:

    app.post('/', shortControllerInstance.post) <- here the context is lost. Any reference to `this` inside the method `post` will most likely refer to `undefined`
    

    To fix this you need to explicitly bind the function to the correct context:

    app.post('/', shortControllerInstance.post.bind(shortControllerInstance)) <- now the context is bound to the function
    

    This probably will fix the issue with lost function’s context, but for further checks see @big-bob-little’s answer also.

    Note that it is just an assumption based on the amount of code provided by you. It is always better to post the whole reproduction code. Also the entire error message with the stack trace is also helpful.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search