Skip to content

Running the API Container

We now have an API image.

Nice.

Now let’s turn that image into a running container and see how well our two services get along without any orchestration help.

Run this command:

Terminal window
docker run -d -p 3000:3000 --name api_service portfolio-api

If all goes well, Docker will create and start a new container named api_service.

  • docker run creates and starts a container from an image
  • -d runs it in detached mode
  • -p 3000:3000 maps port 3000 on our machine to port 3000 in the container
  • --name api_service gives the container a predictable name
  • portfolio-api is the image we built earlier

We can confirm that the container started with:

Terminal window
docker ps

We should see both of these containers running:

  • db_service
  • api_service

At least, that is the dream.

Our app exposes a route at:

http://localhost:3000/api/projects

So at this point, it is tempting to open that in a browser and expect victory.

We may get a response.

We may get an error.

Either way, before we celebrate, we should inspect the logs.

Run:

Terminal window
docker logs api_service

This is where the interesting part shows up.

Instead of happily connecting to MongoDB, the API will likely report a connection failure.

Depending on timing and environment, the message may look a little different, but the core issue is the same: the API cannot find the database at the address we gave it.

The Localhost Trap

Inside the api_service container, localhost means that container itself.

It does not mean our laptop.

It does not mean Docker in general.

And it definitely does not mean the separate MongoDB container sitting beside it.

So when the app tries to connect to mongodb://localhost:27017/portfolio, it is looking for MongoDB inside its own container and finding absolutely nothing.

Conceptual diagram of the Localhost Trap. The api_service container attempts to connect to 'localhost:27017' but the connection line loops back and hits its own internal wall, triggering a failure while the db_service container sits unreachable nearby.

Figure 1: The Localhost Trap. Inside a container, ‘localhost’ is a self-referential loop. The API is looking for a database inside its own walls instead of reaching out to the service sitting right next to it.

This is not a random bug.

This is the lesson.

We now have two separate containers:

  • one running MongoDB
  • one running our Node API

But simply having both containers running does not mean they automatically know how to talk to each other.

That wiring still has to happen.

A Good Failure

This is exactly the kind of failure we want right now.

The API container starts. The database container starts. But the connection between them is missing.

That makes the real problem much easier to see.

At this point, we have proven that:

  • the database can run in one container
  • the API can run in another container
  • the API cannot reach MongoDB just by using localhost

And that sets us up perfectly for the next step.


Docker Port Publishing

Our two services are alive, but they are not cooperating yet. Next, we’ll wire them together the hard way so we can feel exactly what Docker Compose is going to save us from.