You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker化NestJS调用Strapi出现ECONNREFUSED错误的解决求助

问题描述

我正在Docker化基于Nest框架的NodeJS(16.20.0版本)服务,环境包含运行在http://localhost:1337/的Strapi容器和http://localhost:4000/的nodejs_api容器。当Docker化的NodeJS服务向Strapi发送GET请求时,出现以下错误:

[2] FetchError: request to http://127.0.0.1:1337/api/login-page failed, reason: connect ECONNREFUSED 127.0.0.1:1337

非Docker化的相同服务可以正常请求,但Docker化服务始终报错。已尝试使用http://localhost:3000/、http://127.0.0.1:1337/发起请求,也重建过Docker镜像,问题仍未解决。

相关代码与配置

panel-content.controller.ts

import { Controller, Get } from '@nestjs/common';
import { GetLabelsService } from '../get-labels/get-labels.service';
import fetch from 'node-fetch';

//Get all the labels related to the admin-panel
@Controller('panel-content')
export class PanelContentController {
    constructor(private readonly getLabels: GetLabelsService){}

    @Get('ad-board-page')
    async getAdBoardLabels(){
        const labels = this.getLabels.getRouteLabels('/api/ad-board-page');
        return labels
    }

    @Get('chats-page')
    async getChatsLabels(){
        const labels = this.getLabels.getRouteLabels('/api/chats-page');
        return labels
    }
    
    @Get('collections-page')
    async getCollectionsLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/collections-page');
        return labels
    }

    @Get('events-page')
    async getEventsLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/events-page');
        return labels
    }

    @Get('home-page')
    async getHomepageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/home-page');
        return labels
    }

    @Get('login-page')
    async getLoginPageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/login-page');
        
        return labels
    }

    @Get('news-page')
    async getNewsPageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/news-page');
        return labels
    }

    @Get('notifications-page')
    async getNotificationsPageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/notifications-page');
        return labels
    }

    @Get('orders-page')
    async getOrdersPageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/orders-page');
        return labels
    }

    @Get('shop-page')
    async getShopPageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/shop-page');
        return labels
    }

    @Get('users-page')
    async getUsersPageLabels(){
        const labels = await this.getLabels.getRouteLabels('/api/users-page');
        return labels
    }
}

getLabels.service.ts

import { Injectable } from "@nestjs/common";
import fetch from 'node-fetch';

@Injectable()
export class GetLabelsService {
    async getRouteLabels(labelPath: string) {
        const request = `http://127.0.0.1:1337${labelPath}`;
        console.log(request)
        const res = await fetch(request);
        const labels_data = await res.json();
        delete labels_data.data.attributes.createdAt;
        delete labels_data.data.attributes.publishedAt;
        delete labels_data.data.attributes.updatedAt;
        delete labels_data.meta;
        return labels_data
    }
}

docker-compose.yml

version: "3.8"
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile-api
    environment:
      DB_TYPE: ${DB_TYPE}
      DB_HOST: ${DB_HOST}
      DB_PORT: ${DB_PORT}
      DB_USERNAME: ${DB_USERNAME}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_DATABASE: ${DB_DATABASE}
      APP_HOST: ${APP_HOST}
      JWT_SECRET: ${JWT_SECRET}
      JWT_EXIPRY_TIME: ${JWT_EXIPRY_TIME}
      STRIPE_PUBLISH_KEY: ${STRIPE_PUBLISH_KEY}
      STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
      currency: ${currency}
      FCM_DATABASE_URL: ${FCM_DATABASE_URL}
      SMTP_HOST: ${SMTP_HOST}
      SMTP_USERNAME: ${SMTP_USERNAME}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
    ports:
      - 4000:4000
    networks:
      - app-network

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin_container
    restart: always
    environment:
      PMA_HOST: ${DB_HOST}
      PMA_USER: ${DB_USERNAME}
      PMA_PASSWORD: ${DB_PASSWORD}
      UPLOAD_LIMIT: 64M
    ports:
      - "8080:80"
    networks:
      - app-network
      
  strapi:
    build:
      context: ./cms
      dockerfile: Dockerfile-cms
    environment:
      DATABASE_CLIENT: ${DATABASE_CLIENT}
      DATABASE_HOST: ${DATABASE_HOST}
      DATABASE_PORT: ${DATABASE_PORT}
      DATABASE_NAME: ${DATABASE_NAME}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      APP_KEYS: ${APP_KEYS}
      API_TOKEN_SALT: ${API_TOKEN_SALT}
      ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
      JWT_SECRET: ${JWT_SECRET}
      NODE_ENV: ${NODE_ENV}
      MASTER_KEY: ${MASTER_KEY}
      MEILI_HOST: http://meilisearch:7700
      CLOUDINARY_KEY: ${CLOUDINARY_KEY}
      CLOUDINARY_NAME: ${CLOUDINARY_NAME}
      CLOUDINARY_SECRET: ${CLOUDINARY_SECRET}
    volumes:
      - ./cms/config:/srv/app/config
      - ./cms/src:/srv/app/src
      - ./cms/package.json:/srv/package.json
      - ./cms/yarn.lock:/srv/yarn.lock
      - ./cms/datadump:/srv/app/datadump
      - ./cms/public:/srv/app/public
    ports:
      - 1337:1337
    depends_on:
      - postgres
    networks:
      - app-network
    command: yarn develop

  meilisearch:
    image: getmeili/meilisearch:latest
    environment:
      MEILI_MASTER_KEY: ${MASTER_KEY}
    ports:
      - 7700:7700
    networks:
      - app-network
    volumes:
      - ./data:/srv/app/data
      
  pgadmin:
    image: dpage/pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
    ports:
      - 8088:80
    depends_on:
      - postgres
    networks:
      - app-network
      
  # Back-end services 
  postgres:
    image: postgres:12
    environment:
      POSTGRES_USER: ${DATABASE_USERNAME}
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
      POSTGRES_DB: ${DATABASE_NAME}
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - 5432:5432
    networks:
      - app-network

volumes:
  db_data:
  pgdata:

networks:
  app-network:
    driver: bridge
问题原因与解决方法

核心原因

Docker容器内的127.0.0.1或localhost指向容器自身,而非宿主机。你的NodeJS容器和Strapi容器虽在宿主机上有端口映射,但容器间无法通过宿主机的localhost地址互相访问。好在两个容器已加入同一Docker网络app-network,可直接通过容器服务名称通信。

具体修复步骤

  1. 修改请求地址
    在getLabels.service.ts中,将硬编码的http://127.0.0.1:1337替换为http://strapi:1337(strapi是docker-compose中Strapi服务的名称):

    @Injectable()
    export class GetLabelsService {
        async getRouteLabels(labelPath: string) {
            // 替换为容器服务名称作为主机
            const request = `http://strapi:1337${labelPath}`;
            console.log(request)
            const res = await fetch(request);
            const labels_data = await res.json();
            delete labels_data.data.attributes.createdAt;
            delete labels_data.data.attributes.publishedAt;
            delete labels_data.data.attributes.updatedAt;
            delete labels_data.meta;
            return labels_data
        }
    }
    
  2. 推荐:用环境变量管理地址(可选)
    为避免硬编码,建议通过环境变量配置Strapi地址:

    • 在docker-compose的api服务中添加环境变量:
      api:
        build:
          context: .
          dockerfile: Dockerfile-api
        environment:
          # ... 其他环境变量
          STRAPI_URL: http://strapi:1337
        # ... 其他配置
      
    • 在getLabels.service.ts中读取环境变量:
      @Injectable()
      export class GetLabelsService {
          async getRouteLabels(labelPath: string) {
              const strapiUrl = process.env.STRAPI_URL || 'http://127.0.0.1:1337';
              const request = `${strapiUrl}${labelPath}`;
              console.log(request)
              const res = await fetch(request);
              const labels_data = await res.json();
              delete labels_data.data.attributes.createdAt;
              delete labels_data.data.attributes.publishedAt;
              delete labels_data.data.attributes.updatedAt;
              delete labels_data.meta;
              return labels_data
          }
      }
      
  3. 验证网络连通性(可选排查)
    进入NodeJS容器内部,测试是否能ping通Strapi容器:

    # 进入api容器
    docker exec -it <api_container_name> sh
    # ping strapi服务
    ping strapi
    

    若能ping通,说明网络正常,修改请求地址后即可通信。

额外注意事项

  • depends_on仅保证容器启动顺序,不保证Strapi服务完全就绪。若Strapi启动较慢,需添加健康检查或等待逻辑,避免NodeJS服务在Strapi未就绪时发起请求。
  • 确保两个容器均加入同一Docker网络(你的配置已满足,无需额外修改)。

内容的提问来源于stack exchange,提问作者George Marwanqana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:09:53