1

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!

Alex Lenail
  • 12,992
  • 10
  • 47
  • 79

2 Answers2

2

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. :)

  • Thanks for your answer! I agree I don't _need_ a remote swagger UI, but I do want to have nice api docs for my app bundled in to my docker-compose. Swagger UI works well for that, but the swagger-UI page opens with the API definition found at the `API_URL` link, which isn't portable. I'm looking for a solution to make the container portable, such that I can specify `API_URL: wherever-this-app-is-hosted:3000 `. Any ideas for that, or other openapi documentation containers which _are_ portable? – Alex Lenail Nov 08 '18 at 23:52
  • Surprisingly, I did find an option. :) There seems to be a `SWAGGER_JSON` option for the docker container which can be a locally mounted file on the container. Check the docs [here](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/installation.md#docker). You could use another container, like busybox, to fetch the `swagger.json` from `postgrest` into a volume and share that with `swagger-ui` – Pramodh Valavala Nov 09 '18 at 01:18
  • Thanks for pointing that out! If you expand your answer here, I'll accept it! – Alex Lenail Nov 09 '18 at 03:04
  • Once you expose the Swagger page, don't forget that you can also set a "server-proxy-url" config setting (http://postgrest.org/en/v5.1/install.html#server-proxy-uri) in postgrest to override the host and base url properly displayed in the Swagger output. – Joe Nelson Nov 09 '18 at 15:54
  • @JoeNelson that sounds very interesting -- could you elaborate? Would that permit you to set the URL swagger pointed to to always be the host that postgrest is running on? How would you do so? (feel free to break this out into a separate answer) – Alex Lenail Nov 10 '18 at 03:14
  • Late to the party, but i think we want to keep an eye on [SWAGGER_JSON_URL](https://github.com/swagger-api/swagger-ui/pull/5941) also... – joefromct Apr 10 '20 at 04:28
0

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

saolof
  • 1,097
  • 1
  • 15
  • 12