Docker-compose swagger-ui setup which doesn't depend on a particular URL

13.9k Views Asked by At

I want to add swagger-ui to my docker-compose setup. I'm using postgres and postgrest. My setup looks roughly like:

version: '3'
services:

  postgrest:
    image: postgrest/postgrest
    ports:
      - 3000:3000
    links:
      - postgres         

  postgres:
    image: postgres
    ports:
      - 5432:5432
    volumes:
      - "./pgdata:/var/lib/postgresql/data"

  swagger:
    image: swaggerapi/swagger-ui
    expose:
      - 8080
    ports:
      - 8080:8080
    links:
      - postgrest:postgrest
    environment:
      API_URL: http://localhost:3000

This shows me the proper API docs UI when I'm testing locally. When I deploy, http://localhost:3000 isn't serving an OpenAPI definition anymore, and this breaks. I can change API_URL to the remote URL, but then it won't update locally if I'm testing some changes, and that generally seems to defeat the point anyway.

Is there a way to point swagger at "the postgrest running in the same docker compose setup"? Something like:

  swagger:
    ...
    links:
      - postgrest:postgrest
    environment:
      API_URL: http://postgrest:3000

Sometimes docker compose can do magic like this, e.g. in nginx.

Thanks in advance!

2

There are 2 best solutions below

6
On BEST ANSWER

Though you set the API_URL in your docker-compose.yml, the actual request to get the spec file is done by the browser I believe.

So, your browser should be able to resolve that URL, not the swagger-ui container itself.

Also, since this is the case, you don't really need a remote Swagger UI hosted at all. Just have a separate local container of swagger-ui running and change the URL to the swagger file when needed, in the UI itself.

UPDATE: Using SWAGGER_JSON

version: "3"
services:
  postgrest:
    image: postgrest/postgrest
    ports:
      - 3000:3000
    environment:
      PGRST_DB_URI: postgres://app_user:password@postgres:5432/app_db
      PGRST_DB_SCHEMA: public
      PGRST_DB_ANON_ROLE: app_user
    depends_on:
      - postgres

  postgres:
    image: postgres
    ports:
      - 5435:5432
    environment:
      POSTGRES_DB: app_db
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: password
    volumes:
      - "./pgdata:/var/lib/postgresql/data"

  save-swagger:
    image: busybox
    depends_on:
      - postgrest
    volumes:
      - swagger-json:/spec
    command: >
      /bin/sh -c "sleep 15
      && mkdir -p /spec
      && wget -O /spec/swagger.json http://postgrest:3000"

  swagger:
    image: swaggerapi/swagger-ui
    expose:
      - 8080
    ports:
      - 8029:8080
    links:
      - postgrest:postgrest
    environment:
      SWAGGER_JSON: /spec/swagger.json
    volumes:
      - swagger-json:/spec

volumes:
  swagger-json:

Do note that using sleep isn't the best approach. You can check out better options like using wait-on / wait-for / wait-for-it

PS: I have tried wait-for & wait-for-it, but since postgrest has its endpoint available even though the connection to the DB wasn't successful, it was responding with a 503 and both these utils just check for the TCP socket availability, so don't work as expected here.
wait-on would work since it checks for 2xx on HEAD requests but you would need a container with nodejs, so I stuck with sleep as the simplest example of what has to be done. :)

0
On

Add nginx as a router (which you want to do anyway), and change things so that the nginx container is the only container exposing a port to outside the docker-compose network. Then swagger-ui can just show the nginx route instead of a custom port.

Here's a quick starter solution similar to the one I used (that routes to an external postgres instance):

version: '3'
services:
  postgrest_server:
    image: postgrest/postgrest
    environment:
      PGRST_DB_URI: postgresql://user:pass@externalvm:port/postgres
      PGRST_DB_ANON_ROLE: anon
      PGRST_DB_SCHEMAS: api
  swagger_docs:
    image: swaggerapi/swagger-ui
    environment:
      API_URL: /kraken-api/
    depends_on:
      - postgrest_server
  nginx:
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - postgrest_server
      - swagger_docs

With the following nginx conf:

events {}
http {
  # Local services to proxy to.
  upstream postgrest {
    server postgrest_server:3000;
  }
  upstream swagger {
    server swagger_docs:8080;
  }
  server {
    # Endpoints to outside world.
    location /postgrest-api/ {
      default_type  application/json;
      proxy_hide_header Content-Location;
      add_header Content-Location  /api/$upstream_http_content_location;
      proxy_set_header  Connection "";
      proxy_http_version 1.1;
      proxy_pass http://postgrest/;
    }
    location /postgrest-api-docs/ {
      proxy_http_version 1.1;
      proxy_pass http://swagger/;
    }
  }
}

Then to connect to the server, you just connect to http://localhost/postgrest-api/ instead of specifying a port, and http://localhost/postgrest-api-docs/ for the swagger docs