├── .circleci └── config.yml ├── .github └── workflows │ ├── local-gateway.yaml │ ├── local-router-no-code.yaml │ ├── local-router-rhai.yaml │ ├── local-router-rust-dev.yaml │ ├── local-router-rust-plugin.yaml │ ├── studio-gateway.yaml │ ├── studio-router-no-code.yaml │ └── studio-router-rust-dev.yaml ├── .gitignore ├── .scripts ├── config.sh ├── deps-check.sh ├── docker-prune.sh ├── graph-api-env-export.sh ├── graph-api-env.sh ├── publish.sh ├── query.sh ├── smoke-defer.sh ├── smoke.sh ├── subgraphs.sh ├── subgraphs │ ├── docker-compose-networking.sh │ └── localhost-networking.sh └── unpublish.sh ├── CODEOWNERS ├── LICENSE ├── Makefile ├── README.md ├── client └── defer │ └── apollo-client │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── docker-compose.yaml │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ └── logo.png ├── docker-compose.yaml ├── docs └── media │ ├── apollo-client │ ├── defer-immediate-response.png │ └── deferred-response.png │ ├── apollo-kotlin │ └── apollo-kotlin-defer-demo.png │ ├── fed2 │ ├── 0-apollo-key.png │ ├── 0-create-graph.png │ ├── 1-publish-success.png │ ├── 2-studio-launches.png │ ├── 4-studio-working.png │ └── 5-apollo-explorer.png │ ├── honeycomb.png │ ├── opentelemetry.png │ ├── product-subgraph-sandbox.png │ ├── router │ ├── make_run-router-local.png │ ├── make_run-router-rust-plugin-compile.png │ ├── make_run-router-rust-plugin-run.png │ ├── make_run-router.png │ ├── query-plan-router-sandbox.png │ └── rhai-custom-logging.png │ ├── sandbox │ ├── defer-immediate-response.png │ └── deferred-response.png │ ├── studio-sign-up.png │ └── supergraph.png ├── examples ├── README.md └── composition │ ├── README.md │ ├── basic │ ├── A.graphql │ ├── B.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── complex_key │ ├── A.graphql │ ├── B.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── cost_handling │ ├── A.graphql │ ├── B.graphql │ ├── C.graphql │ ├── D.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── efficient_parallels │ ├── A.graphql │ ├── B.graphql │ ├── C.graphql │ ├── D.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── entity_in_list │ ├── A.graphql │ ├── B.graphql │ ├── C.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── interface_simple │ ├── A.graphql │ ├── B.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── products │ ├── A.graphql │ ├── B.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ ├── provides │ ├── A.graphql │ ├── B.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml │ └── requires │ ├── A.graphql │ ├── B.graphql │ ├── README.md │ ├── query.graphql │ └── supergraph.yaml ├── misc ├── README.md ├── advanced │ └── router-dev │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── Dockerfile │ │ ├── router.yaml │ │ └── src │ │ └── main.rs ├── local │ ├── README.md │ ├── docker-compose.gateway.yaml │ ├── docker-compose.router-no-code.yaml │ ├── docker-compose.router-no-otel.yaml │ ├── docker-compose.router-rhai.yaml │ ├── docker-compose.router-rust-dev.yaml │ └── docker-compose.router-rust-plugin.yaml └── studio │ ├── README.md │ ├── docker-compose.gateway.yaml │ ├── docker-compose.router-no-code.yaml │ ├── docker-compose.router-rhai.yaml │ ├── docker-compose.router-rust-dev.yaml │ └── docker-compose.router-rust-plugin.yaml ├── opentelemetry ├── collector-config.yml ├── docker-compose.otel.yaml └── prometheus.yml ├── renovate.json ├── subgraphs ├── inventory │ ├── .gitignore │ ├── Dockerfile │ └── app │ │ ├── build.gradle.kts │ │ ├── settings.gradle │ │ └── src │ │ └── main │ │ ├── kotlin │ │ └── inventory │ │ │ ├── Application.kt │ │ │ ├── GraphQLConfiguration.kt │ │ │ ├── WebConfiguration.kt │ │ │ └── model │ │ │ └── Product.kt │ │ └── resources │ │ ├── application.properties │ │ └── graphql │ │ └── inventory.graphqls ├── pandas │ ├── Dockerfile │ ├── package.json │ ├── pandas.graphql │ └── pandas.js ├── products │ ├── Dockerfile │ ├── package.json │ ├── products.graphql │ └── products.js ├── reviews │ ├── Dockerfile │ ├── app.py │ ├── pdm.lock │ ├── pyproject.toml │ ├── reviews.graphql │ ├── schema.py │ └── tracing.py └── users │ ├── Dockerfile │ ├── package.json │ ├── users.graphql │ └── users.js └── supergraph ├── README.md ├── gateway ├── Dockerfile ├── gateway.js └── package.json ├── router-no-code └── README.md ├── router-rhai-script ├── Dockerfile ├── router.yaml └── test.rhai ├── router-rust-plugin ├── .gitignore ├── Cargo.toml ├── Dockerfile ├── router.yaml └── src │ ├── hello_world.rs │ └── main.rs ├── router.yaml └── schema ├── docker.graphql ├── docker.yaml ├── local.graphql └── local.yaml /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | secops: apollo/circleci-secops-orb@2.0.7 5 | 6 | workflows: 7 | security-scans: 8 | jobs: 9 | - secops/gitleaks: 10 | context: 11 | - platform-docker-ro 12 | - github-orb 13 | - secops-oidc 14 | git-base-revision: <<#pipeline.git.base_revision>><><> 15 | git-revision: << pipeline.git.revision >> 16 | - secops/semgrep: 17 | context: 18 | - secops-oidc 19 | - github-orb 20 | git-base-revision: <<#pipeline.git.base_revision>><><> 21 | -------------------------------------------------------------------------------- /.github/workflows/local-gateway.yaml: -------------------------------------------------------------------------------- 1 | name: CI Gateway 2 | on: 3 | pull_request: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-local: 11 | name: CI Gateway 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout 15 | uses: actions/checkout@v3 16 | - 17 | name: install rover 18 | run: | 19 | echo --------------------------------------------------------------- 20 | echo rover - installing ... 21 | echo --------------------------------------------------------------- 22 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 23 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 24 | - 25 | name: update docker-compose 26 | run: | 27 | which docker-compose && exit 0 || true 28 | echo --------------------------------------------------------------- 29 | echo docker-compose - installing ... 30 | echo --------------------------------------------------------------- 31 | BIN_DIR=$HOME/.docker-compose/bin 32 | FILE=$BIN_DIR/docker-compose 33 | mkdir -p $BIN_DIR 34 | set -x 35 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 36 | chmod +x $FILE 37 | echo "downloaded $($FILE --version)" 38 | echo "$BIN_DIR" >> ${GITHUB_PATH} 39 | set +x 40 | echo --------------------------------------------------------------- 41 | - name: check tools 42 | run: | 43 | make deps-check || true 44 | - name: make supergraph 45 | run: | 46 | make config 47 | make compose 48 | cat supergraph/schema/docker.graphql 49 | - name: docker-compose build 50 | run: | 51 | set -x 52 | docker-compose \ 53 | -f docker-compose.yaml \ 54 | -f opentelemetry/docker-compose.otel.yaml \ 55 | -f misc/local/docker-compose.gateway.yaml \ 56 | build --parallel --progress plain 57 | - name: docker-compose up -d 58 | run: | 59 | set -x 60 | docker-compose \ 61 | -f docker-compose.yaml \ 62 | -f opentelemetry/docker-compose.otel.yaml \ 63 | -f misc/local/docker-compose.gateway.yaml \ 64 | up -d 65 | 66 | sleep 8 67 | 68 | docker-compose \ 69 | -f docker-compose.yaml \ 70 | -f opentelemetry/docker-compose.otel.yaml \ 71 | -f misc/local/docker-compose.gateway.yaml \ 72 | logs 73 | - name: smoke test 74 | run: .scripts/smoke.sh 4000 75 | - name: docker-compose down 76 | run: docker-compose down --remove-orphans 77 | -------------------------------------------------------------------------------- /.github/workflows/local-router-no-code.yaml: -------------------------------------------------------------------------------- 1 | name: CI Router 2 | on: 3 | pull_request: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-local: 11 | name: CI Router 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout 15 | uses: actions/checkout@v3 16 | - 17 | name: install rover 18 | run: | 19 | echo --------------------------------------------------------------- 20 | echo rover - installing ... 21 | echo --------------------------------------------------------------- 22 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 23 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 24 | - 25 | name: update docker-compose 26 | run: | 27 | which docker-compose && exit 0 || true 28 | echo --------------------------------------------------------------- 29 | echo docker-compose - installing ... 30 | echo --------------------------------------------------------------- 31 | BIN_DIR=$HOME/.docker-compose/bin 32 | FILE=$BIN_DIR/docker-compose 33 | mkdir -p $BIN_DIR 34 | set -x 35 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 36 | chmod +x $FILE 37 | echo "downloaded $($FILE --version)" 38 | echo "$BIN_DIR" >> ${GITHUB_PATH} 39 | set +x 40 | echo --------------------------------------------------------------- 41 | - name: check tools 42 | run: | 43 | make deps-check || true 44 | - name: make supergraph 45 | run: | 46 | make config 47 | make compose 48 | cat supergraph/schema/docker.graphql 49 | - name: docker-compose build 50 | run: | 51 | set -x 52 | docker-compose \ 53 | -f docker-compose.yaml \ 54 | -f opentelemetry/docker-compose.otel.yaml \ 55 | -f misc/local/docker-compose.router-no-code.yaml \ 56 | build --parallel --progress plain 57 | - name: docker-compose up -d 58 | run: | 59 | set -x 60 | docker-compose \ 61 | -f docker-compose.yaml \ 62 | -f opentelemetry/docker-compose.otel.yaml \ 63 | -f misc/local/docker-compose.router-no-code.yaml \ 64 | up -d 65 | 66 | sleep 8 67 | 68 | docker-compose \ 69 | -f docker-compose.yaml \ 70 | -f opentelemetry/docker-compose.otel.yaml \ 71 | -f misc/local/docker-compose.router-no-code.yaml \ 72 | logs 73 | - name: smoke test 74 | run: .scripts/smoke.sh 4000 75 | - name: docker compose logs 76 | if: ${{ failure() }} 77 | run: | 78 | docker-compose \ 79 | -f docker-compose.yaml \ 80 | -f opentelemetry/docker-compose.otel.yaml \ 81 | -f examples/local/docker-compose.router-no-code.yaml \ 82 | logs 83 | - name: docker-compose down 84 | run: docker-compose down --remove-orphans 85 | -------------------------------------------------------------------------------- /.github/workflows/local-router-rhai.yaml: -------------------------------------------------------------------------------- 1 | name: CI Router Rhai 2 | on: 3 | pull_request: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-local: 11 | name: CI Router Rhai 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout 15 | uses: actions/checkout@v3 16 | - 17 | name: install rover 18 | run: | 19 | echo --------------------------------------------------------------- 20 | echo rover - installing ... 21 | echo --------------------------------------------------------------- 22 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 23 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 24 | - 25 | name: update docker-compose 26 | run: | 27 | which docker-compose && exit 0 || true 28 | echo --------------------------------------------------------------- 29 | echo docker-compose - installing ... 30 | echo --------------------------------------------------------------- 31 | BIN_DIR=$HOME/.docker-compose/bin 32 | FILE=$BIN_DIR/docker-compose 33 | mkdir -p $BIN_DIR 34 | set -x 35 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 36 | chmod +x $FILE 37 | echo "downloaded $($FILE --version)" 38 | echo "$BIN_DIR" >> ${GITHUB_PATH} 39 | set +x 40 | echo --------------------------------------------------------------- 41 | - name: check tools 42 | run: | 43 | make deps-check || true 44 | - name: make supergraph 45 | run: | 46 | make config 47 | make compose 48 | cat supergraph/schema/docker.graphql 49 | - name: docker-compose build 50 | run: | 51 | set -x 52 | docker-compose \ 53 | -f docker-compose.yaml \ 54 | -f opentelemetry/docker-compose.otel.yaml \ 55 | -f misc/local/docker-compose.router-rhai.yaml \ 56 | build --parallel --progress plain 57 | - name: docker-compose up -d 58 | run: | 59 | set -x 60 | docker-compose \ 61 | -f docker-compose.yaml \ 62 | -f opentelemetry/docker-compose.otel.yaml \ 63 | -f misc/local/docker-compose.router-rhai.yaml \ 64 | up -d 65 | 66 | sleep 8 67 | 68 | docker-compose \ 69 | -f docker-compose.yaml \ 70 | -f opentelemetry/docker-compose.otel.yaml \ 71 | -f misc/local/docker-compose.router-rhai.yaml \ 72 | logs 73 | - name: smoke test 74 | run: .scripts/smoke.sh 4000 75 | - name: docker-compose down 76 | run: docker-compose down --remove-orphans 77 | -------------------------------------------------------------------------------- /.github/workflows/local-router-rust-dev.yaml: -------------------------------------------------------------------------------- 1 | name: CI Router Main 2 | on: 3 | push: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-local: 11 | name: CI Router Main 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout 15 | uses: actions/checkout@v3 16 | - 17 | name: install rover 18 | run: | 19 | echo --------------------------------------------------------------- 20 | echo rover - installing ... 21 | echo --------------------------------------------------------------- 22 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 23 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 24 | - 25 | name: update docker-compose 26 | run: | 27 | which docker-compose && exit 0 || true 28 | echo --------------------------------------------------------------- 29 | echo docker-compose - installing ... 30 | echo --------------------------------------------------------------- 31 | BIN_DIR=$HOME/.docker-compose/bin 32 | FILE=$BIN_DIR/docker-compose 33 | mkdir -p $BIN_DIR 34 | set -x 35 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 36 | chmod +x $FILE 37 | echo "downloaded $($FILE --version)" 38 | echo "$BIN_DIR" >> ${GITHUB_PATH} 39 | set +x 40 | echo --------------------------------------------------------------- 41 | - name: check tools 42 | run: | 43 | make deps-check || true 44 | - name: make supergraph 45 | run: | 46 | make config 47 | make compose 48 | cat supergraph/schema/docker.graphql 49 | - name: docker-compose build 50 | run: | 51 | set -x 52 | docker-compose \ 53 | -f docker-compose.yaml \ 54 | -f opentelemetry/docker-compose.otel.yaml \ 55 | -f misc/local/docker-compose.router-rust-dev.yaml \ 56 | build --parallel --progress plain 57 | - name: docker-compose up -d 58 | run: | 59 | set -x 60 | docker-compose \ 61 | -f docker-compose.yaml \ 62 | -f opentelemetry/docker-compose.otel.yaml \ 63 | -f misc/local/docker-compose.router-rust-dev.yaml \ 64 | up -d 65 | 66 | sleep 8 67 | 68 | docker-compose \ 69 | -f docker-compose.yaml \ 70 | -f opentelemetry/docker-compose.otel.yaml \ 71 | -f misc/local/docker-compose.router-rust-dev.yaml \ 72 | logs 73 | - name: smoke test 74 | run: .scripts/smoke.sh 4000 75 | - name: docker-compose down 76 | run: docker-compose down --remove-orphans 77 | -------------------------------------------------------------------------------- /.github/workflows/local-router-rust-plugin.yaml: -------------------------------------------------------------------------------- 1 | name: CI Router Rust Plugin 2 | on: 3 | push: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-local: 11 | name: CI Router Rust Plugin 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout 15 | uses: actions/checkout@v3 16 | - 17 | name: install rover 18 | run: | 19 | echo --------------------------------------------------------------- 20 | echo rover - installing ... 21 | echo --------------------------------------------------------------- 22 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 23 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 24 | - 25 | name: update docker-compose 26 | run: | 27 | which docker-compose && exit 0 || true 28 | echo --------------------------------------------------------------- 29 | echo docker-compose - installing ... 30 | echo --------------------------------------------------------------- 31 | BIN_DIR=$HOME/.docker-compose/bin 32 | FILE=$BIN_DIR/docker-compose 33 | mkdir -p $BIN_DIR 34 | set -x 35 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 36 | chmod +x $FILE 37 | echo "downloaded $($FILE --version)" 38 | echo "$BIN_DIR" >> ${GITHUB_PATH} 39 | set +x 40 | echo --------------------------------------------------------------- 41 | - name: check tools 42 | run: | 43 | make deps-check || true 44 | - name: make supergraph 45 | run: | 46 | make config 47 | make compose 48 | cat supergraph/schema/docker.graphql 49 | - name: docker-compose build 50 | run: | 51 | set -x 52 | docker-compose \ 53 | -f docker-compose.yaml \ 54 | -f opentelemetry/docker-compose.otel.yaml \ 55 | -f misc/local/docker-compose.router-rust-plugin.yaml \ 56 | build --parallel --progress plain 57 | - name: docker-compose up -d 58 | run: | 59 | set -x 60 | docker-compose \ 61 | -f docker-compose.yaml \ 62 | -f opentelemetry/docker-compose.otel.yaml \ 63 | -f misc/local/docker-compose.router-rust-plugin.yaml \ 64 | up -d 65 | 66 | sleep 8 67 | 68 | docker-compose \ 69 | -f docker-compose.yaml \ 70 | -f opentelemetry/docker-compose.otel.yaml \ 71 | -f misc/local/docker-compose.router-rust-plugin.yaml \ 72 | logs 73 | - name: smoke test 74 | run: .scripts/smoke.sh 4000 75 | - name: docker-compose down 76 | run: docker-compose down --remove-orphans 77 | -------------------------------------------------------------------------------- /.github/workflows/studio-gateway.yaml: -------------------------------------------------------------------------------- 1 | name: CI Gateway Studio 2 | on: 3 | push: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ci-docker-managed: 15 | name: CI Gateway Studio 16 | runs-on: ubuntu-latest 17 | env: 18 | CI: "true" 19 | APOLLO_KEY: ${{ secrets.APOLLO_KEY }} 20 | APOLLO_GRAPH_REF: ${{ secrets.APOLLO_GRAPH_REF }} 21 | steps: 22 | - name: checkout 23 | uses: actions/checkout@v3 24 | - 25 | name: install rover 26 | run: | 27 | echo --------------------------------------------------------------- 28 | echo rover - installing ... 29 | echo --------------------------------------------------------------- 30 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 31 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 32 | - 33 | name: update docker-compose 34 | run: | 35 | which docker-compose && exit 0 || true 36 | echo --------------------------------------------------------------- 37 | echo docker-compose - installing ... 38 | echo --------------------------------------------------------------- 39 | BIN_DIR=$HOME/.docker-compose/bin 40 | FILE=$BIN_DIR/docker-compose 41 | mkdir -p $BIN_DIR 42 | set -x 43 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 44 | chmod +x $FILE 45 | echo "downloaded $($FILE --version)" 46 | echo "$BIN_DIR" >> ${GITHUB_PATH} 47 | set +x 48 | echo --------------------------------------------------------------- 49 | - name: check tools 50 | run: | 51 | make deps-check || true 52 | - name: unpublish-subgraphs 53 | run: | 54 | make unpublish-subgraphs || true 55 | - name: publish-subgraphs 56 | run: | 57 | make publish-subgraphs-docker-compose 58 | - name: docker-compose build 59 | run: | 60 | set -x 61 | docker-compose \ 62 | -f docker-compose.yaml \ 63 | -f opentelemetry/docker-compose.otel.yaml \ 64 | -f misc/studio/docker-compose.gateway.yaml \ 65 | build --parallel --progress plain 66 | - name: docker-compose up -d 67 | run: | 68 | set -x 69 | docker-compose \ 70 | -f docker-compose.yaml \ 71 | -f opentelemetry/docker-compose.otel.yaml \ 72 | -f misc/studio/docker-compose.gateway.yaml \ 73 | up -d 74 | 75 | sleep 8 76 | 77 | docker-compose \ 78 | -f docker-compose.yaml \ 79 | -f opentelemetry/docker-compose.otel.yaml \ 80 | -f misc/studio/docker-compose.gateway.yaml \ 81 | logs 82 | - name: smoke test 83 | run: .scripts/smoke.sh 4000 250 84 | - name: docker-compose down 85 | run: docker-compose down --remove-orphans 86 | -------------------------------------------------------------------------------- /.github/workflows/studio-router-no-code.yaml: -------------------------------------------------------------------------------- 1 | name: CI Router Studio 2 | on: 3 | push: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-managed: 11 | name: CI Router Studio 12 | runs-on: ubuntu-latest 13 | env: 14 | CI: "true" 15 | APOLLO_KEY: ${{ secrets.APOLLO_KEY }} 16 | APOLLO_GRAPH_REF: ${{ secrets.APOLLO_GRAPH_REF_ROUTER }} 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v3 20 | - 21 | name: install rover 22 | run: | 23 | echo --------------------------------------------------------------- 24 | echo rover - installing ... 25 | echo --------------------------------------------------------------- 26 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 27 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 28 | - 29 | name: update docker-compose 30 | run: | 31 | which docker-compose && exit 0 || true 32 | echo --------------------------------------------------------------- 33 | echo docker-compose - installing ... 34 | echo --------------------------------------------------------------- 35 | BIN_DIR=$HOME/.docker-compose/bin 36 | FILE=$BIN_DIR/docker-compose 37 | mkdir -p $BIN_DIR 38 | set -x 39 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 40 | chmod +x $FILE 41 | echo "downloaded $($FILE --version)" 42 | echo "$BIN_DIR" >> ${GITHUB_PATH} 43 | set +x 44 | echo --------------------------------------------------------------- 45 | - name: check tools 46 | run: | 47 | make deps-check || true 48 | - name: unpublish-subgraphs 49 | run: | 50 | make unpublish-subgraphs || true 51 | - name: publish-subgraphs 52 | run: | 53 | make publish-subgraphs-docker-compose 54 | - name: docker-compose build 55 | run: | 56 | set -x 57 | docker-compose \ 58 | -f docker-compose.yaml \ 59 | -f opentelemetry/docker-compose.otel.yaml \ 60 | -f misc/studio/docker-compose.router-no-code.yaml \ 61 | build --parallel --progress plain 62 | - name: docker-compose up -d 63 | run: | 64 | set -x 65 | docker-compose \ 66 | -f docker-compose.yaml \ 67 | -f opentelemetry/docker-compose.otel.yaml \ 68 | -f misc/studio/docker-compose.router-no-code.yaml \ 69 | up -d 70 | 71 | sleep 8 72 | 73 | docker-compose \ 74 | -f docker-compose.yaml \ 75 | -f opentelemetry/docker-compose.otel.yaml \ 76 | -f misc/studio/docker-compose.router-no-code.yaml \ 77 | logs 78 | - name: smoke test 79 | run: .scripts/smoke.sh 4000 250 80 | - name: docker-compose down 81 | run: docker-compose down --remove-orphans 82 | -------------------------------------------------------------------------------- /.github/workflows/studio-router-rust-dev.yaml: -------------------------------------------------------------------------------- 1 | name: CI Router Main Studio 2 | on: 3 | push: 4 | branches: [ main ] 5 | schedule: 6 | - cron: '30 7 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci-docker-managed: 11 | name: CI Router Main Studio 12 | runs-on: ubuntu-latest 13 | env: 14 | CI: "true" 15 | APOLLO_KEY: ${{ secrets.APOLLO_KEY }} 16 | APOLLO_GRAPH_REF: ${{ secrets.APOLLO_GRAPH_REF_ROUTER_DEV }} 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v3 20 | - 21 | name: install rover 22 | run: | 23 | echo --------------------------------------------------------------- 24 | echo rover - installing ... 25 | echo --------------------------------------------------------------- 26 | curl -sSL https://rover.apollo.dev/nix/latest | sh -s -- --elv2-license accept 27 | echo "$HOME/.rover/bin" >> ${GITHUB_PATH} 28 | - 29 | name: update docker-compose 30 | run: | 31 | which docker-compose && exit 0 || true 32 | echo --------------------------------------------------------------- 33 | echo docker-compose - installing ... 34 | echo --------------------------------------------------------------- 35 | BIN_DIR=$HOME/.docker-compose/bin 36 | FILE=$BIN_DIR/docker-compose 37 | mkdir -p $BIN_DIR 38 | set -x 39 | curl -L --fail https://github.com/docker/compose/releases/download/v2.11.1/docker-compose-`uname -s`-`uname -m` -o $FILE 40 | chmod +x $FILE 41 | echo "downloaded $($FILE --version)" 42 | echo "$BIN_DIR" >> ${GITHUB_PATH} 43 | set +x 44 | echo --------------------------------------------------------------- 45 | - name: check tools 46 | run: | 47 | make deps-check || true 48 | - name: unpublish-subgraphs 49 | run: | 50 | make unpublish-subgraphs || true 51 | - name: publish-subgraphs 52 | run: | 53 | make publish-subgraphs-docker-compose 54 | - name: docker-compose build 55 | run: | 56 | set -x 57 | docker-compose \ 58 | -f docker-compose.yaml \ 59 | -f opentelemetry/docker-compose.otel.yaml \ 60 | -f misc/studio/docker-compose.router-rust-dev.yaml \ 61 | build --parallel --progress plain 62 | - name: docker-compose up -d 63 | run: | 64 | set -x 65 | docker-compose \ 66 | -f docker-compose.yaml \ 67 | -f opentelemetry/docker-compose.otel.yaml \ 68 | -f misc/studio/docker-compose.router-rust-dev.yaml \ 69 | up -d 70 | 71 | sleep 8 72 | 73 | docker-compose \ 74 | -f docker-compose.yaml \ 75 | -f opentelemetry/docker-compose.otel.yaml \ 76 | -f misc/studio/docker-compose.router-rust-dev.yaml \ 77 | logs 78 | - name: smoke test 79 | run: .scripts/smoke.sh 4000 250 80 | - name: docker-compose down 81 | run: docker-compose down --remove-orphans 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | graph-api.env 10 | docker.secrets 11 | 12 | bin 13 | router 14 | acme_router 15 | 16 | # Diagnostic reports (https://nodejs.org/api/report.html) 17 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 18 | 19 | # Runtime data 20 | pids *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | *.lcov 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | .DS_Store 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | bower_components 41 | 42 | # node-waf configuration 43 | .lock-wscript 44 | 45 | # Compiled binary addons (https://nodejs.org/api/addons.html) 46 | build/Release 47 | 48 | # Dependency directories 49 | node_modules/ 50 | jspm_packages/ 51 | 52 | # lock file for demo to keep it simple 53 | package-lock.json 54 | 55 | # TypeScript v1 declaration files 56 | typings/ 57 | 58 | # TypeScript cache 59 | *.tsbuildinfo 60 | 61 | # Optional npm cache directory 62 | .npm 63 | 64 | # Optional eslint cache 65 | .eslintcache 66 | 67 | # Microbundle cache 68 | .rpt2_cache/ 69 | .rts2_cache_cjs/ 70 | .rts2_cache_es/ 71 | .rts2_cache_umd/ 72 | 73 | # Optional REPL history 74 | .node_repl_history 75 | 76 | # Output of 'npm pack' 77 | # *.tgz 78 | 79 | # Yarn Integrity file 80 | .yarn-integrity 81 | 82 | # dotenv environment variables file 83 | .env 84 | .env.test 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | 89 | # Next.js build output 90 | .next 91 | 92 | # Nuxt.js build / generate output 93 | .nuxt 94 | dist 95 | 96 | # Gatsby files 97 | .cache/ 98 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 99 | # https://nextjs.org/blog/next-9-1#public-directory-support 100 | # public 101 | 102 | # vuepress build output 103 | .vuepress/dist 104 | 105 | # Serverless directories 106 | .serverless/ 107 | 108 | # FuseBox cache 109 | .fusebox/ 110 | 111 | # DynamoDB Local files 112 | .dynamodb/ 113 | 114 | # TernJS port file 115 | .tern-port 116 | 117 | .vscode/ 118 | 119 | __pycache__/ 120 | .venv 121 | 122 | # Gradle Ignore 123 | .gradle 124 | build 125 | gradlew 126 | gradlew.bat 127 | gradle/ 128 | 129 | # IntelliJ 130 | .idea 131 | -------------------------------------------------------------------------------- /.scripts/config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname $0)/subgraphs.sh" 4 | 5 | echo "federation_version: 2" 6 | echo "subgraphs:" 7 | for subgraph in ${subgraphs[@]}; do 8 | url="url_$subgraph" 9 | schema="schema_$subgraph" 10 | echo " ${subgraph}:" 11 | echo " routing_url: ${!url}" 12 | echo " schema:" 13 | echo " file: ../../${!schema}" 14 | done -------------------------------------------------------------------------------- /.scripts/deps-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo --------------------------------------------------------------- 4 | rover info 5 | echo --------------------------------------------------------------- 6 | which ./router 7 | echo "Router $(./router --version)" 8 | echo --------------------------------------------------------------- 9 | which docker 10 | echo "$(docker --version)" 11 | echo --------------------------------------------------------------- 12 | which docker-compose 13 | echo "docker-compose: $(docker-compose --version)" 14 | echo "docker compose: $(docker compose version)" 15 | echo --------------------------------------------------------------- -------------------------------------------------------------------------------- /.scripts/docker-prune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | docker image prune -f 5 | docker kill $(docker ps -aq) 6 | docker rm $(docker ps -aq) 7 | docker volume rm $(docker volume ls -qf dangling=true) 8 | -------------------------------------------------------------------------------- /.scripts/graph-api-env-export.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # load defaults 4 | if ls graph-api.env > /dev/null 2>&1; then 5 | eval "$(cat graph-api.env)" 6 | else 7 | echo "run 'make publish-subgraphs' to set APOLLO_KEY and APOLLO_GRAPH_REF environment variables" 8 | exit 1 9 | fi 10 | 11 | export APOLLO_KEY=$APOLLO_KEY 12 | export APOLLO_GRAPH_REF=$APOLLO_GRAPH_REF -------------------------------------------------------------------------------- /.scripts/graph-api-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "${CI}" != "true" ]]; then 4 | 5 | # optional overrides 6 | graph="$OVERRIDE_APOLLO_GRAPH_REF" 7 | key="$OVERRIDE_APOLLO_KEY" 8 | 9 | # load defaults if present 10 | if ls graph-api.env > /dev/null 2>&1; then 11 | eval "$(cat graph-api.env)" 12 | fi 13 | 14 | # -------------------------------------------------------------------------- 15 | # APOLLO_KEY 16 | # -------------------------------------------------------------------------- 17 | if [[ "$key" == "default" ]]; then 18 | if [[ -n $APOLLO_KEY ]]; then 19 | key=$APOLLO_KEY 20 | echo "---------------------------------------" 21 | echo "Using default APOLLO_KEY" 22 | echo "---------------------------------------" 23 | else 24 | unset key 25 | fi 26 | fi 27 | 28 | if [[ -z "${key}" ]]; then 29 | echo "---------------------------------------" 30 | echo "Enter your APOLLO_KEY" 31 | echo "---------------------------------------" 32 | echo "Go to your graph settings in https://studio.apollographql.com/" 33 | echo "then create a Graph API Key with Contributor permissions" 34 | echo "(for metrics reporting) and enter it at the prompt below." 35 | 36 | if [[ -n "$APOLLO_KEY" ]]; then 37 | echo "" 38 | echo "press to use existing key: *************** (from ./graph-api.env)" 39 | fi 40 | 41 | read -s -p "> " key 42 | echo 43 | if [[ -z "$key" ]]; then 44 | if [[ -n "$APOLLO_KEY" ]]; then 45 | key=$APOLLO_KEY 46 | else 47 | >&2 echo "---------------------------------------" 48 | >&2 echo "APOLLO_KEY not found" 49 | >&2 echo "---------------------------------------" 50 | exit 1 51 | fi 52 | fi 53 | fi 54 | 55 | export APOLLO_KEY=$key 56 | 57 | # -------------------------------------------------------------------------- 58 | # APOLLO_GRAPH_REF 59 | # -------------------------------------------------------------------------- 60 | echo "" 61 | if [[ "$graph" == "default" ]]; then 62 | if [[ -n $APOLLO_GRAPH_REF ]]; then 63 | graph=$APOLLO_GRAPH_REF 64 | echo "---------------------------------------" 65 | echo "Using APOLLO_GRAPH_REF: ${graph}" 66 | echo "---------------------------------------" 67 | else 68 | unset graph 69 | fi 70 | fi 71 | 72 | if [[ -z "${graph}" ]]; then 73 | echo "---------------------------------------" 74 | echo "Enter your APOLLO_GRAPH_REF" 75 | echo "---------------------------------------" 76 | echo "Go to your graph settings in https://studio.apollographql.com/" 77 | echo "then copy your Graph NAME and optionally @ and enter it at the prompt below." 78 | echo "@ will default to @current, if omitted." 79 | echo "" 80 | echo "Enter the @ of a federated graph in Apollo Studio:" 81 | if [[ -n "$APOLLO_GRAPH_REF" ]]; then 82 | echo "" 83 | echo "press for default: $APOLLO_GRAPH_REF" 84 | fi 85 | read -p "> " graph 86 | if [[ -z "$graph" ]]; then 87 | if [[ -n "$APOLLO_GRAPH_REF" ]]; then 88 | graph=$APOLLO_GRAPH_REF 89 | else 90 | >&2 echo "---------------------------------------" 91 | >&2 echo "APOLLO_GRAPH_REF not found" 92 | >&2 echo "---------------------------------------" 93 | exit 1 94 | fi 95 | fi 96 | fi 97 | 98 | export APOLLO_GRAPH_REF=$graph 99 | fi 100 | 101 | # for docker-compose.managed.yaml env_file and to save defaults for next time 102 | echo "APOLLO_KEY=${APOLLO_KEY}" > graph-api.env 103 | echo "APOLLO_GRAPH_REF=${APOLLO_GRAPH_REF}" >> graph-api.env 104 | 105 | ok=1 106 | if [[ -z "${APOLLO_KEY}" ]]; then 107 | >&2 echo "---------------------------------------" 108 | >&2 echo "APOLLO_KEY not found" 109 | >&2 echo "---------------------------------------" 110 | ok=0 111 | fi 112 | 113 | if [[ -z "${APOLLO_GRAPH_REF}" ]]; then 114 | >&2 echo "---------------------------------------" 115 | >&2 echo "APOLLO_GRAPH_REF not found" 116 | >&2 echo "---------------------------------------" 117 | ok=0 118 | fi 119 | 120 | if [[ $ok -eq 0 ]]; then 121 | exit 1 122 | fi 123 | -------------------------------------------------------------------------------- /.scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SUBGRAPH_PORTS="${1:default}" 4 | 5 | echo "=======================================" 6 | echo "PUBLISH SUBGRAPHS TO APOLLO REGISTRY" 7 | echo "=======================================" 8 | 9 | source "$(dirname $0)/subgraphs.sh" 10 | source "$(dirname $0)/graph-api-env.sh" 11 | 12 | 13 | # note: use --allow-invalid-routing-url to allow localhost without confirmation prompt 14 | 15 | for subgraph in ${subgraphs[@]}; do 16 | echo "---------------------------------------" 17 | echo "subgraph: ${subgraph}" 18 | echo "---------------------------------------" 19 | url="url_$subgraph" 20 | schema="schema_$subgraph" 21 | (set -x; ${ROVER_BIN:-'rover'} subgraph publish ${APOLLO_GRAPH_REF} \ 22 | --routing-url "${!url}" \ 23 | --schema "${!schema}" \ 24 | --name ${subgraph} \ 25 | --allow-invalid-routing-url \ 26 | --convert) 27 | echo "" 28 | done 29 | -------------------------------------------------------------------------------- /.scripts/query.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PORT="${1:-4000}" 4 | 5 | read -r -d '' QUERY <<"EOF" 6 | { 7 | allProducts { 8 | id, 9 | name, 10 | sku, 11 | createdBy { 12 | email 13 | } 14 | } 15 | } 16 | EOF 17 | 18 | QUERY=$(echo "${QUERY}" | awk -v ORS= -v OFS= '{$1=$1}1') 19 | 20 | echo ------------------------------------------------------------------------------------------- 21 | ( set -x; curl -i -X POST http://localhost:$PORT \ 22 | -H 'Content-Type: application/json' \ 23 | --data-raw '{ "query": "'"${QUERY}"'" }' ) 24 | echo 25 | echo ------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /.scripts/smoke-defer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "=====================" 4 | echo "Defer multi" 5 | echo "=====================" 6 | ( set -x; curl -i -X POST http://localhost:4000/ \ 7 | -H 'accept: multipart/mixed; deferSpec=20220824, application/json' \ 8 | -H 'content-type: application/json' \ 9 | --data-raw '{"operationName":"deferVariation","variables":{},"query":"query deferVariation { allProducts { sku, name, ... on Product @defer { delivery { estimatedDelivery, fastestDelivery } }, ... on Product @defer { createdBy { email, name } } } }"}') 10 | echo "----------------------" 11 | printf "done.\n\n" 12 | sleep 2 13 | 14 | exit 15 | 16 | echo "=====================" 17 | echo "Defer returns chunked responses without --compressed" 18 | echo "=====================" 19 | ( set -x; curl -i -X POST http://localhost:4000/ \ 20 | -H 'accept: multipart/mixed; deferSpec=20220824, application/json' \ 21 | -H 'content-type: application/json' \ 22 | --data-raw '{"operationName":"deferVariation","variables":{},"query":"query deferVariation { allProducts { ...MyFragment @defer sku id } } fragment MyFragment on Product { variation { name } }"}') 23 | echo "----------------------" 24 | printf "done.\n\n" 25 | sleep 2 26 | 27 | echo "=====================" 28 | echo "Defer returns chunked responses with --compressed" 29 | echo "=====================" 30 | ( set -x; curl --compressed -i -X POST http://localhost:4000/ \ 31 | -H 'accept: multipart/mixed; deferSpec=20220824, application/json' \ 32 | -H 'content-type: application/json' \ 33 | --data-raw '{"operationName":"deferVariation","variables":{},"query":"query deferVariation { allProducts { ...MyFragment @defer sku id } } fragment MyFragment on Product { variation { name } }"}') 34 | echo "----------------------" 35 | printf "done.\n\n" 36 | sleep 2 37 | 38 | echo "=====================" 39 | echo "Defer with trailing __typename without --compressed" 40 | echo "=====================" 41 | ( set -x; curl -i -X POST http://localhost:4000/ \ 42 | -H 'accept: multipart/mixed; deferSpec=20220824, application/json' \ 43 | -H 'content-type: application/json' \ 44 | --data-raw '{"operationName":"deferVariation","variables":{},"query":"query deferVariation { allProducts { ...MyFragment @defer sku id __typename } } fragment MyFragment on Product { variation { name __typename } __typename }"}') 45 | echo "----------------------" 46 | printf "done.\n\n" 47 | sleep 2 48 | 49 | echo "=====================" 50 | echo "Defer with trailing __typename with --compressed" 51 | echo "=====================" 52 | ( set -x; curl --compressed -i -X POST http://localhost:4000/ \ 53 | -H 'accept: multipart/mixed; deferSpec=20220824, application/json' \ 54 | -H 'content-type: application/json' \ 55 | --data-raw '{"operationName":"deferVariation","variables":{},"query":"query deferVariation { allProducts { ...MyFragment @defer sku id __typename } } fragment MyFragment on Product { variation { name __typename } __typename }"}') 56 | echo "----------------------" 57 | printf "done.\n\n" 58 | sleep 2 59 | 60 | -------------------------------------------------------------------------------- /.scripts/smoke.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PORT="${1:-4000}" 4 | COUNT="${2:-1}" 5 | 6 | OK_CHECK="\xE2\x9C\x85" 7 | FAIL_MARK="\xE2\x9D\x8C" 8 | ROCKET="\xF0\x9F\x9A\x80" 9 | 10 | err() { 11 | local red=`tput setaf 1 2>/dev/null || echo ''` 12 | local reset=`tput sgr0 2>/dev/null || echo ''` 13 | echo "${red}ERROR${reset}: $1" >&2 14 | exit 1 15 | } 16 | 17 | exec_curl() { 18 | ERR="" 19 | RES=$("$@" 2>/dev/null) 20 | EXIT_CODE=$? 21 | if [ $EXIT_CODE -ne 0 ]; then 22 | echo "$@" 23 | if [ $EXIT_CODE -eq 7 ]; then 24 | ERR="CURL ERROR 7: Failed to connect() to host or proxy." 25 | elif [ $EXIT_CODE -eq 52 ]; then 26 | ERR="CURL ERROR 52: Empty reply from server." 27 | elif [ $EXIT_CODE -eq 56 ]; then 28 | ERR="CURL ERROR 56: Recv failure: Connection reset by peer." 29 | else 30 | ERR="CURL ERROR $EXIT_CODE\n" 31 | fi 32 | fi 33 | return $EXIT_CODE 34 | } 35 | 36 | TESTS=(0 1 2 3 4 5) 37 | DEFER_TESTS=(6 7) 38 | DEPRECATED_TESTS=(8 9) 39 | 40 | HAS_DEFER=0 41 | 42 | # introspection query 43 | exec_curl curl -X POST http://localhost:$PORT/ \ 44 | -H "Content-Type: application/json" \ 45 | --data '{ "query": "query { __schema { directives { name }}}" }' 46 | if [ $? != 0 ]; then err "$ERR"; fi 47 | printf "$OK_CHECK Introspection Success!\n" 48 | 49 | if echo "$RES" | grep -q '{"name":"defer"}'; then HAS_DEFER=1; fi 50 | 51 | if [ $HAS_DEFER -eq 1 ]; then 52 | echo " - has @defer support" 53 | TESTS=("${TESTS[@]}" "${DEFER_TESTS[@]}") 54 | else 55 | echo " - no @defer support" 56 | fi 57 | printf "\n" 58 | 59 | TESTS=("${TESTS[@]}" "${DEPRECATED_TESTS[@]}") 60 | 61 | # -------------------------------------------------------------------- 62 | # TEST 0 63 | # -------------------------------------------------------------------- 64 | DESCR_0="product subgraph only" 65 | OPNAME_0="allProd" 66 | ACCEPT_0="application/json" 67 | read -r -d '' QUERY_0 <<"EOF" 68 | query allProd { 69 | allProducts { 70 | id, 71 | sku 72 | } 73 | } 74 | EOF 75 | 76 | OP_0=equals 77 | 78 | read -r -d '' EXP_0 <<"EOF" 79 | {"data":{"allProducts":[{"id":"converse-1","sku":"converse-1"},{"id":"vans-1","sku":"vans-1"}]}} 80 | EOF 81 | 82 | 83 | # -------------------------------------------------------------------- 84 | # TEST 1 85 | # -------------------------------------------------------------------- 86 | DESCR_1="allProducts with delivery" 87 | OPNAME_1="allProdDelivery" 88 | ISSLOW_1="true" 89 | ACCEPT_1="application/json" 90 | read -r -d '' QUERY_1 <<"EOF" 91 | query allProdDelivery { 92 | allProducts { 93 | delivery { 94 | estimatedDelivery, 95 | fastestDelivery 96 | }, 97 | createdBy { 98 | name, 99 | email 100 | } 101 | } 102 | } 103 | EOF 104 | 105 | OP_1=equals 106 | 107 | read -r -d '' EXP_1 <<"EOF" 108 | {"data":{"allProducts":[{"delivery":{"estimatedDelivery":"6/25/2023","fastestDelivery":"6/24/2023"},"createdBy":{"name":"Converse","email":"info@converse.com"}},{"delivery":{"estimatedDelivery":"6/25/2023","fastestDelivery":"6/24/2023"},"createdBy":{"name":"Van Doren","email":"info@vans.com"}}]}} 109 | EOF 110 | 111 | # -------------------------------------------------------------------- 112 | # TEST 2 113 | # -------------------------------------------------------------------- 114 | DESCR_2="allProducts with totalProductsCreated" 115 | OPNAME_2="allProdCreated" 116 | ACCEPT_2="application/json" 117 | read -r -d '' QUERY_2 <<"EOF" 118 | query allProdCreated { 119 | allProducts { 120 | id, 121 | sku, 122 | createdBy { 123 | email, 124 | totalProductsCreated 125 | } 126 | } 127 | } 128 | EOF 129 | 130 | OP_2=equals 131 | 132 | read -r -d '' EXP_2 <<"EOF" 133 | {"data":{"allProducts":[{"id":"converse-1","sku":"converse-1","createdBy":{"email":"info@converse.com","totalProductsCreated":1099}},{"id":"vans-1","sku":"vans-1","createdBy":{"email":"info@vans.com","totalProductsCreated":1099}}]}} 134 | EOF 135 | 136 | # -------------------------------------------------------------------- 137 | # TEST 3 - @inaccessible in subgraphs 138 | # -------------------------------------------------------------------- 139 | DESCR_3="hidden: String @inaccessible should return error" 140 | OPNAME_3="inaccessibleError" 141 | ACCEPT_3="application/json" 142 | read -r -d '' QUERY_3 <<"EOF" 143 | query inaccessibleError { 144 | allProducts { 145 | id, 146 | hidden, 147 | dimensions { 148 | size, 149 | weight 150 | } 151 | } 152 | } 153 | EOF 154 | 155 | OP_3=contains 156 | 157 | # note: error message varies across Gateway and Router 158 | read -r -d '' EXP_3 <<"EOF" 159 | {"errors":[{"message": 160 | EOF 161 | 162 | # -------------------------------------------------------------------- 163 | # TEST 4 164 | # -------------------------------------------------------------------- 165 | DESCR_4="exampleQuery with pandas" 166 | OPNAME_4="exampleQuery" 167 | ISSLOW_4="true" 168 | ACCEPT_4="application/json" 169 | read -r -d '' QUERY_4 <<"EOF" 170 | query exampleQuery { 171 | allProducts { 172 | id, 173 | sku, 174 | dimensions { 175 | size, 176 | weight 177 | } 178 | delivery { 179 | estimatedDelivery, 180 | fastestDelivery 181 | } 182 | } 183 | allPandas { 184 | name, 185 | favoriteFood 186 | } 187 | } 188 | EOF 189 | 190 | OP_4=equals 191 | 192 | read -r -d '' EXP_4 <<"EOF" 193 | {"data":{"allProducts":[{"id":"converse-1","sku":"converse-1","dimensions":{"size":"1","weight":1},"delivery":{"estimatedDelivery":"6/25/2023","fastestDelivery":"6/24/2023"}},{"id":"vans-1","sku":"vans-1","dimensions":{"size":"1","weight":1},"delivery":{"estimatedDelivery":"6/25/2023","fastestDelivery":"6/24/2023"}}],"allPandas":[{"name":"Basi","favoriteFood":"bamboo leaves"},{"name":"Yun","favoriteFood":"apple"}]}} 194 | EOF 195 | 196 | # -------------------------------------------------------------------- 197 | # TEST 5 198 | # -------------------------------------------------------------------- 199 | DESCR_5="exampleQuery with reviews and override" 200 | OPNAME_5="allProductsWithReviews" 201 | ACCEPT_5="application/json" 202 | read -r -d '' QUERY_5 <<"EOF" 203 | query allProductsWithReviews { 204 | allProducts { 205 | id, 206 | sku, 207 | dimensions { 208 | size, 209 | weight 210 | } 211 | delivery { 212 | estimatedDelivery, 213 | fastestDelivery 214 | } 215 | reviewsScore, 216 | reviews { 217 | body 218 | } 219 | } 220 | } 221 | EOF 222 | 223 | OP_5=equals 224 | 225 | read -r -d '' EXP_5 <<"EOF" 226 | {"data":{"allProducts":[{"id":"converse-1","sku":"converse-1","dimensions":{"size":"1","weight":1},"delivery":{"estimatedDelivery":"6/25/2023","fastestDelivery":"6/24/2023"},"reviewsScore":4.6,"reviews":[]},{"id":"vans-1","sku":"vans-1","dimensions":{"size":"1","weight":1},"delivery":{"estimatedDelivery":"6/25/2023","fastestDelivery":"6/24/2023"},"reviewsScore":4.6,"reviews":[]}]}} 227 | EOF 228 | 229 | # -------------------------------------------------------------------- 230 | # TEST 6 231 | # -------------------------------------------------------------------- 232 | DESCR_6="defer variation query" 233 | OPNAME_6="deferVariation" 234 | ACCEPT_6="multipart/mixed; deferSpec=20220824, application/json" 235 | ISSLOW_6="true" 236 | read -r -d '' QUERY_6 <<"EOF" 237 | query deferVariation { 238 | allProducts { 239 | ...MyFragment @defer 240 | sku, 241 | id 242 | } 243 | } 244 | fragment MyFragment on Product { 245 | variation { name } 246 | } 247 | EOF 248 | OP_6=equals 249 | 250 | IFS= read -r -d '' EXP_6 <<"EOF" 251 | 252 | --graphql 253 | content-type: application/json 254 | 255 | {"data":{"allProducts":[{"sku":"converse-1","id":"converse-1"},{"sku":"vans-1","id":"vans-1"}]},"hasNext":true} 256 | --graphql 257 | content-type: application/json 258 | 259 | {"hasNext":false,"incremental":[{"data":{"variation":{"name":"Converse Chuck Taylor"}},"path":["allProducts",0]},{"data":{"variation":{"name":"Vans Classic Sneaker"}},"path":["allProducts",1]}]} 260 | --graphql-- 261 | EOF 262 | 263 | # -------------------------------------------------------------------- 264 | # TEST 7 265 | # -------------------------------------------------------------------- 266 | DESCR_7="deferred user query" 267 | OPNAME_7="deferUser" 268 | ACCEPT_7="multipart/mixed; deferSpec=20220824, application/json" 269 | read -r -d '' QUERY_7 <<"EOF" 270 | query deferUser { 271 | allProducts { 272 | createdBy { 273 | ...MyFragment @defer 274 | } 275 | sku 276 | id 277 | } 278 | } 279 | 280 | fragment MyFragment on User { name } 281 | EOF 282 | 283 | OP_7=equals 284 | 285 | IFS= read -r -d '' EXP_7 <<"EOF" 286 | 287 | --graphql 288 | content-type: application/json 289 | 290 | {"data":{"allProducts":[{"sku":"converse-1","id":"converse-1"},{"sku":"vans-1","id":"vans-1"}]},"hasNext":true} 291 | --graphql 292 | content-type: application/json 293 | 294 | {"hasNext":false,"incremental":[{"data":{"name":"Converse"},"path":["allProducts",0,"createdBy"]},{"data":{"name":"Van Doren"},"path":["allProducts",1,"createdBy"]}]} 295 | --graphql-- 296 | EOF 297 | 298 | # -------------------------------------------------------------------- 299 | # TEST 8 300 | # -------------------------------------------------------------------- 301 | DESCR_8="deprecatedQuery" 302 | OPNAME_8="deprecatedQuery" 303 | ACCEPT_8="application/json" 304 | read -r -d '' QUERY_8 <<"EOF" 305 | query deprecatedQuery { 306 | allProducts { 307 | id, 308 | sku, 309 | oldField 310 | } 311 | } 312 | EOF 313 | 314 | OP_8=equals 315 | 316 | read -r -d '' EXP_8 <<"EOF" 317 | {"data":{"allProducts":[{"id":"converse-1","sku":"converse-1","oldField":"deprecated"},{"id":"vans-1","sku":"vans-1","oldField":"deprecated"}]}} 318 | EOF 319 | 320 | # -------------------------------------------------------------------- 321 | # TEST 9 322 | # -------------------------------------------------------------------- 323 | DESCR_9="deprecatedIntrospectionQuery" 324 | OPNAME_9="deprecatedIntrospectionQuery" 325 | ACCEPT_9="application/json" 326 | read -r -d '' QUERY_9 <<"EOF" 327 | query deprecatedIntrospectionQuery { 328 | __type(name:\"ProductItf\" 329 | ) { 330 | fields(includeDeprecated: true) { 331 | name, 332 | isDeprecated, 333 | deprecationReason 334 | } 335 | } 336 | } 337 | EOF 338 | 339 | OP_9=contains 340 | 341 | read -r -d '' EXP_9 <<"EOF" 342 | {"name":"oldField","isDeprecated":true,"deprecationReason":"refactored out"} 343 | EOF 344 | 345 | set -e 346 | 347 | printf "Running smoke tests ... $ROCKET $ROCKET $ROCKET\n" 348 | trap 'rm -f *.tmp' EXIT 349 | sleep 2 350 | 351 | run_tests ( ){ 352 | for (( i=1; i<=$COUNT; i++ )); do 353 | for test in ${TESTS[@]}; do 354 | descr_var="DESCR_$test" 355 | query_var="QUERY_$test" 356 | exp_var="EXP_$test" 357 | op_var="OP_$test" 358 | opname_var="OPNAME_$test" 359 | accept_var="ACCEPT_$test" 360 | is_slow_var="ISSLOW_$test" 361 | 362 | DESCR="${!descr_var}" 363 | QUERY=$(echo "${!query_var}" | tr '\n' ' ' | awk '$1=$1') 364 | EXP="${!exp_var}" 365 | OP="${!op_var}" 366 | OPNAME="${!opname_var}" 367 | ACCEPT="${!accept_var}" 368 | ISSLOW="${!is_slow_var}" 369 | CMD=(curl -i -X POST -H "Content-Type: application/json" -H "apollographql-client-name: smoke-test" -H "accept:${ACCEPT}" --data "{ \"query\": \"${QUERY}\", \"operationName\": \"$OPNAME\" }" http://localhost:$PORT/ ) 370 | 371 | if [ $i -gt 1 ]; then 372 | if [ "$ISSLOW" == "true" ]; then 373 | continue 374 | fi 375 | fi 376 | 377 | if [ $COUNT -le 1 ]; then 378 | echo "" 379 | echo "==============================================================" 380 | echo "TEST $test: $DESCR" 381 | echo "==============================================================" 382 | echo "${CMD[@]}" 383 | fi 384 | 385 | # execute operation 386 | set +e 387 | RESULT=$("${CMD[@]}" 2>/dev/null) 388 | EXIT_CODE=$? 389 | if [ $EXIT_CODE -ne 0 ]; then 390 | if [ $EXIT_CODE -eq 7 ]; then 391 | printf "CURL ERROR 7: Failed to connect() to host or proxy.\n" 392 | elif [ $EXIT_CODE -eq 52 ]; then 393 | printf "CURL ERROR 52: Empty reply from server.\n" 394 | elif [ $EXIT_CODE -eq 56 ]; then 395 | printf "CURL ERROR 56: Recv failure: Connection reset by peer.\n" 396 | else 397 | printf "CURL ERROR $EXIT_CODE\n" 398 | fi 399 | printf "${RESULT}" 400 | printf '\n' 401 | exit 1 402 | fi 403 | set -e 404 | 405 | echo "$RESULT" | awk -v bl=1 'bl{bl=0; h=($0 ~ /HTTP\/1/)} /^\r?$/{bl=1} {print $0>(h?"headers.tmp":"body.tmp")}' 406 | HEADERS=$(&2 echo "" 6 | if [[ "$SUBGRAPH_NETWORKING" == "docker-compose" ]]; then 7 | >&2 echo "Subgraphs will listen on different docker-compose hostnames (all on port 4000)" 8 | source "$(dirname $0)/subgraphs/docker-compose-networking.sh" 9 | else 10 | >&2 echo "Subgraphs will listen on different localhost ports (4001, 4002, ...)" 11 | source "$(dirname $0)/subgraphs/localhost-networking.sh" 12 | fi 13 | >&2 echo "" -------------------------------------------------------------------------------- /.scripts/subgraphs/docker-compose-networking.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | subgraphs=("inventory" "products" "users" "pandas" "reviews") 4 | 5 | url_inventory="http://inventory:4000/graphql" 6 | url_products="http://products:4000/graphql" 7 | url_users="http://users:4000/graphql" 8 | url_pandas="http://pandas:4000/graphql" 9 | url_reviews="http://reviews:4000/graphql" 10 | 11 | schema_inventory="subgraphs/inventory/app/src/main/resources/graphql/inventory.graphqls" 12 | schema_products="subgraphs/products/products.graphql" 13 | schema_users="subgraphs/users/users.graphql" 14 | schema_pandas="subgraphs/pandas/pandas.graphql" 15 | schema_reviews="subgraphs/reviews/reviews.graphql" 16 | -------------------------------------------------------------------------------- /.scripts/subgraphs/localhost-networking.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | subgraphs=("products" "inventory" "users" "pandas" "reviews") 4 | 5 | url_products="http://localhost:4001/graphql" 6 | url_inventory="http://localhost:4002/graphql" 7 | url_users="http://localhost:4003/graphql" 8 | url_pandas="http://localhost:4004/graphql" 9 | url_reviews="http://localhost:4005/graphql" 10 | 11 | schema_products="subgraphs/products/products.graphql" 12 | schema_inventory="subgraphs/inventory/app/src/main/resources/graphql/inventory.graphqls" 13 | schema_users="subgraphs/users/users.graphql" 14 | schema_pandas="subgraphs/pandas/pandas.graphql" 15 | schema_reviews="subgraphs/reviews/reviews.graphql" 16 | -------------------------------------------------------------------------------- /.scripts/unpublish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "=======================================" 4 | echo "SUBGRAPH UNPUBLISH" 5 | echo "=======================================" 6 | 7 | source "$(dirname $0)/subgraphs.sh" 8 | source "$(dirname $0)/graph-api-env.sh" 9 | 10 | echo "" > unpublish.log 11 | echo "subgraphs:" 12 | for subgraph in ${subgraphs[@]}; do 13 | ( set -x; ${ROVER_BIN:-'rover'} subgraph delete ${APOLLO_GRAPH_REF} --name ${subgraph} --confirm 2>> unpublish.log ) 14 | done 15 | 16 | if grep -Eq 'error:(.+) Graph has no implementing services' unpublish.log; then 17 | echo "Success, all subgraphs removed!" 18 | rm unpublish.log 19 | exit 0 20 | elif grep -Eq 'error:(.+) invalid input syntax for uuid: ""' unpublish.log; then 21 | echo "Success, no subgraphs found!" 22 | rm unpublish.log 23 | exit 0 24 | else 25 | cat unpublish.log 26 | rm unpublish.log 27 | exit 1 28 | fi 29 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by the Apollo SecOps team 2 | # Please customize this file as needed prior to merging. 3 | 4 | * @prasek 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Apollo Graph, Inc. 2 | 3 | Source code in this repository is covered by (i) the Elastic License 2.0 or (ii) an MIT compatible license, in each case, as designated by a licensing file in a subdirectory or file header. The default throughout the repository is a license under the Elastic License 2.0, unless a file header or a licensing file in a subdirectory specifies another license. 4 | 5 | -------------------------------------------------------------------------------- 6 | 7 | Elastic License 2.0 8 | 9 | ## Acceptance 10 | 11 | By using the software, you agree to all of the terms and conditions below. 12 | 13 | ## Copyright License 14 | 15 | The licensor grants you a non-exclusive, royalty-free, worldwide, 16 | non-sublicensable, non-transferable license to use, copy, distribute, make 17 | available, and prepare derivative works of the software, in each case subject to 18 | the limitations and conditions below. 19 | 20 | ## Limitations 21 | 22 | You may not provide the software to third parties as a hosted or managed 23 | service, where the service provides users with access to any substantial set of 24 | the features or functionality of the software. 25 | 26 | You may not move, change, disable, or circumvent the license key functionality 27 | in the software, and you may not remove or obscure any functionality in the 28 | software that is protected by the license key. 29 | 30 | You may not alter, remove, or obscure any licensing, copyright, or other notices 31 | of the licensor in the software. Any use of the licensor’s trademarks is subject 32 | to applicable law. 33 | 34 | ## Patents 35 | 36 | The licensor grants you a license, under any patent claims the licensor can 37 | license, or becomes able to license, to make, have made, use, sell, offer for 38 | sale, import and have imported the software, in each case subject to the 39 | limitations and conditions in this license. This license does not cover any 40 | patent claims that you cause to be infringed by modifications or additions to 41 | the software. If you or your company make any written claim that the software 42 | infringes or contributes to infringement of any patent, your patent license for 43 | the software granted under these terms ends immediately. If your company makes 44 | such a claim, your patent license ends immediately for work on behalf of your 45 | company. 46 | 47 | ## Notices 48 | 49 | You must ensure that anyone who gets a copy of any part of the software from you 50 | also gets a copy of these terms. 51 | 52 | If you modify the software, you must include in any modified copies of the 53 | software prominent notices stating that you have modified the software. 54 | 55 | ## No Other Rights 56 | 57 | These terms do not imply any licenses other than those expressly granted in 58 | these terms. 59 | 60 | ## Termination 61 | 62 | If you use the software in violation of these terms, such use is not licensed, 63 | and your licenses will automatically terminate. If the licensor provides you 64 | with a notice of your violation, and you cease all violation of this license no 65 | later than 30 days after you receive that notice, your licenses will be 66 | reinstated retroactively. However, if you violate these terms after such 67 | reinstatement, any additional violation of these terms will cause your licenses 68 | to terminate automatically and permanently. 69 | 70 | ## No Liability 71 | 72 | *As far as the law allows, the software comes as is, without any warranty or 73 | condition, and the licensor will not be liable to you for any damages arising 74 | out of these terms or the use or nature of the software, under any kind of 75 | legal claim.* 76 | 77 | ## Definitions 78 | 79 | The **licensor** is the entity offering these terms, and the **software** is the 80 | software the licensor makes available under these terms, including any portion 81 | of it. 82 | 83 | **you** refers to the individual or entity agreeing to these terms. 84 | 85 | **your company** is any legal entity, sole proprietorship, or other kind of 86 | organization that you work for, plus all organizations that have control over, 87 | are under the control of, or are under common control with that 88 | organization. **control** means ownership of substantially all the assets of an 89 | entity, or the power to direct its management and policies by vote, contract, or 90 | otherwise. Control can be direct or indirect. 91 | 92 | **your licenses** are all the licenses granted to you for the software under 93 | these terms. 94 | 95 | **use** means anything you do with the software requiring one of your licenses. 96 | 97 | **trademark** means trademarks, service marks, and similar rights. 98 | 99 | -------------------------------------------------------------------------------- 100 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | export COMPOSE_PROJECT_NAME=supergraph-demo-fed2 3 | export SUBGRAPH_BOOT_TIME=2 4 | 5 | .PHONY: default 6 | default: deps run-supergraph 7 | 8 | # dependencies 9 | 10 | .PHONY: deps 11 | deps: 12 | @echo -------------------------------------------- 13 | curl -sSL https://rover.apollo.dev/nix/latest | sh 14 | @echo -------------------------------------------- 15 | curl -sSL https://router.apollo.dev/download/nix/latest | sh 16 | @echo -------------------------------------------- 17 | 18 | .PHONY: deps-windows 19 | deps-windows: 20 | @echo -------------------------------------------- 21 | iwr 'https://rover.apollo.dev/win/latest' | iex 22 | @echo -------------------------------------------- 23 | curl -sSL https://router.apollo.dev/download/nix/latest | sh 24 | @echo -------------------------------------------- 25 | 26 | # Standalone router with basic YAML config and Open Telemetry 27 | 28 | .PHONY: run-supergraph 29 | run-supergraph: up-subgraphs publish-subgraphs run-router 30 | 31 | .PHONY: build-subgraphs-no-cache 32 | build-subgraphs-no-cache: 33 | docker compose \ 34 | -f docker-compose.yaml \ 35 | build --no-cache 36 | 37 | .PHONY: up-subgraphs 38 | up-subgraphs: 39 | docker compose \ 40 | -f docker-compose.yaml \ 41 | -f opentelemetry/docker-compose.otel.yaml \ 42 | up -d --build 43 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 44 | docker compose logs 45 | 46 | .PHONY: publish-subgraphs 47 | publish-subgraphs: 48 | .scripts/publish.sh 49 | 50 | .PHONY: run-router 51 | run-router: 52 | @source "./.scripts/graph-api-env-export.sh" && set -x; \ 53 | ./router --version && \ 54 | ./router --dev \ 55 | -c ./supergraph/router.yaml \ 56 | --log info 57 | 58 | .PHONY: query 59 | query: 60 | @.scripts/query.sh 61 | 62 | .PHONY: smoke 63 | smoke: 64 | @.scripts/smoke.sh 65 | 66 | .PHONY: down 67 | down: 68 | docker compose down --remove-orphans 69 | 70 | # Standalone router with no --dev flag 71 | 72 | .PHONY: run-supergraph-no-dev 73 | run-supergraph-no-dev: up-subgraphs publish-subgraphs run-router-no-dev 74 | 75 | .PHONY: run-router-no-dev 76 | run-router-no-dev: 77 | @source "./.scripts/graph-api-env-export.sh" && set -x; \ 78 | ./router --version && \ 79 | ./router \ 80 | -c ./supergraph/router.yaml \ 81 | --log info 82 | 83 | # Standalone router with Rhai script 84 | 85 | .PHONY: run-supergraph-rhai 86 | run-supergraph-rhai: up-subgraphs publish-subgraphs run-router-rhai 87 | 88 | .PHONY: run-router-rhai 89 | run-router-rhai: 90 | @source "./.scripts/graph-api-env-export.sh" && set -x; \ 91 | ./router --version && \ 92 | ./router --dev \ 93 | -c ./supergraph/router-rhai-script/router.yaml \ 94 | --log info 95 | 96 | # Standalone router with Rust plugin 97 | 98 | .PHONY: run-supergraph-rust-plugin 99 | run-supergraph-rust-plugin: up-subgraphs publish-subgraphs run-router-rust-plugin 100 | 101 | .PHONY: run-router-rust-plugin 102 | run-router-rust-plugin: build-rust-plugin 103 | @source "./.scripts/graph-api-env-export.sh" && set -x; \ 104 | cd supergraph/router-rust-plugin && \ 105 | ./acme_router --version && \ 106 | ./acme_router --dev \ 107 | -c ./router.yaml \ 108 | --log info 109 | 110 | .PHONY: build-rust-plugin 111 | build-rust-plugin: 112 | cd supergraph/router-rust-plugin && cargo update && cargo build --release 113 | cd supergraph/router-rust-plugin && cp ./target/release/acme_router . 114 | 115 | .PHONY: clean-rust-plugin 116 | clean-rust-plugin: 117 | rm -rf supergraph/router-rust-plugin/target || true 118 | 119 | .PHONY: clean-cargo-cache 120 | clean-cargo-cache: 121 | rm -rf ~/.cargo/git 122 | rm -rf ~/.cargo/registry 123 | 124 | .PHONY: run-supergraph-router-dev 125 | run-supergraph-router-dev: up-subgraphs publish-subgraphs run-router-dev 126 | 127 | .PHONY: run-router-dev 128 | run-router-dev: build-router-dev 129 | @source "./.scripts/graph-api-env-export.sh" && \ 130 | cd misc/advanced/router-dev && set -x; \ 131 | ./acme_router --version && \ 132 | ./acme_router --dev \ 133 | -c ./router.yaml \ 134 | --log info 135 | 136 | .PHONY: build-router-dev 137 | build-router-dev: 138 | cd misc/advanced/router-dev && cargo update && cargo build --release 139 | cd misc/advanced/router-dev && cp ./target/release/acme_router . 140 | 141 | .PHONY: clean-router-dev 142 | clean-router-dev: 143 | rm -rf misc/advanced/router-dev/target || true 144 | 145 | # router in docker 146 | 147 | .PHONY: up-supergraph 148 | up-supergraph: publish-subgraphs-docker-compose 149 | docker compose \ 150 | -f docker-compose.yaml \ 151 | -f opentelemetry/docker-compose.otel.yaml \ 152 | -f misc/studio/docker-compose.router-no-code.yaml \ 153 | up -d --build 154 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 155 | docker compose logs 156 | docker compose logs apollo-router 157 | 158 | .PHONY: publish-subgraphs-docker-compose 159 | publish-subgraphs-docker-compose: 160 | .scripts/publish.sh "docker-compose" 161 | 162 | .PHONY: up-supergraph-rhai 163 | up-supergraph-rhai: publish-subgraphs-docker-compose 164 | docker compose \ 165 | -f docker-compose.yaml \ 166 | -f opentelemetry/docker-compose.otel.yaml \ 167 | -f misc/local/docker-compose.router-rhai.yaml \ 168 | up -d --build 169 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 170 | docker compose logs 171 | docker compose logs apollo-router-rhai 172 | 173 | .PHONY: up-supergraph-rust-plugin 174 | up-supergraph-rust-plugin: publish-subgraphs-docker-compose 175 | docker compose \ 176 | -f docker-compose.yaml \ 177 | -f opentelemetry/docker-compose.otel.yaml \ 178 | -f misc/local/docker-compose.router-rust-plugin.yaml \ 179 | up -d --build 180 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 181 | docker compose logs 182 | docker compose logs apollo-router-rust-plugin 183 | 184 | .PHONY: build-defer-apollo-client-no-cache 185 | build-defer-apollo-client-no-cache: 186 | docker compose \ 187 | -f client/defer/apollo-client/docker-compose.yaml \ 188 | build --no-cache 189 | 190 | .PHONY: up-supergraph-defer 191 | up-supergraph-defer: publish-subgraphs-docker-compose 192 | docker compose \ 193 | -f docker-compose.yaml \ 194 | -f opentelemetry/docker-compose.otel.yaml \ 195 | -f misc/studio/docker-compose.router-no-code.yaml \ 196 | -f client/defer/apollo-client/docker-compose.yaml \ 197 | up -d --build 198 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 199 | docker compose logs 200 | docker compose logs apollo-router 201 | 202 | # gateway 203 | 204 | .PHONY: up-supergraph-gateway 205 | up-supergraph-gateway: publish-subgraphs-docker-compose 206 | docker compose \ 207 | -f docker-compose.yaml \ 208 | -f opentelemetry/docker-compose.otel.yaml \ 209 | -f misc/studio/docker-compose.gateway.yaml \ 210 | up -d --build 211 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 212 | docker compose logs 213 | docker compose logs apollo-gateway 214 | 215 | # local composition 216 | 217 | .PHONY: config 218 | config: 219 | .scripts/config.sh "localhost" > ./supergraph/schema/local.yaml 2>/dev/null 220 | .scripts/config.sh "docker-compose" > ./supergraph/schema/docker.yaml 2>/dev/null 221 | 222 | .PHONY: compose 223 | compose: 224 | @set -x; cd supergraph/schema; \ 225 | rover supergraph compose --elv2-license=accept --config local.yaml > local.graphql 226 | @set -x; cd supergraph/schema; \ 227 | rover supergraph compose --elv2-license=accept --config docker.yaml > docker.graphql 228 | 229 | # standalone router with local composition 230 | 231 | .PHONY: run-supergraph-local 232 | run-supergraph-local: up-subgraphs config compose run-router-local 233 | 234 | .PHONY: run-router-local 235 | run-router-local: 236 | @set -x; \ 237 | ./router --version && \ 238 | ./router --dev \ 239 | -c ./supergraph/router.yaml \ 240 | -s ./supergraph/schema/local.graphql \ 241 | --log info 242 | 243 | # standalone router with local composition and no --dev flag 244 | 245 | .PHONY: run-supergraph-local-no-dev 246 | run-supergraph-local-no-dev: up-subgraphs config compose run-router-local-no-dev 247 | 248 | .PHONY: run-router-local-no-dev 249 | run-router-local-no-dev: 250 | @set -x; \ 251 | ./router --version && \ 252 | ./router \ 253 | -c ./supergraph/router.yaml \ 254 | -s ./supergraph/schema/local.graphql \ 255 | --log info 256 | 257 | # standalone router with local composition and rhai scripting 258 | 259 | .PHONY: run-supergraph-rhai-local 260 | run-supergraph-rhai-local: up-subgraphs config compose run-router-rhai-local 261 | 262 | .PHONY: run-router-rhai-local 263 | run-router-rhai-local: 264 | @set -x; \ 265 | ./router --version && \ 266 | ./router --dev \ 267 | -c ./supergraph/router-rhai-script/router.yaml \ 268 | -s ./supergraph/schema/local.graphql \ 269 | --log info 270 | 271 | # standalone router with local composition and rust plugin 272 | 273 | .PHONY: run-supergraph-rust-plugin-local 274 | run-supergraph-rust-plugin-local: up-subgraphs config compose run-router-rust-plugin-local 275 | 276 | .PHONY: run-router-rust-plugin-local 277 | run-router-rust-plugin-local: build-rust-plugin 278 | @cd supergraph/router-rust-plugin && set -x; \ 279 | ./acme_router --version && \ 280 | ./acme_router --dev \ 281 | -c ./router.yaml \ 282 | -s ../schema/local.graphql \ 283 | --log info 284 | 285 | # standalone router with local composition and router dev build 286 | 287 | .PHONY: run-supergraph-router-dev-local 288 | run-supergraph-router-dev-local: up-subgraphs config compose run-router-dev-local 289 | 290 | .PHONY: run-router-dev-local 291 | run-router-dev-local: build-router-dev 292 | @cd misc/advanced/router-dev && set -x; \ 293 | ./acme_router --version && \ 294 | ./acme_router --dev \ 295 | -c ./router.yaml \ 296 | -s ../../../supergraph/schema/local.graphql \ 297 | --log info 298 | 299 | # router in docker and local composition 300 | 301 | .PHONY: up-supergraph-local 302 | up-supergraph-local: config compose 303 | docker compose \ 304 | -f docker-compose.yaml \ 305 | -f opentelemetry/docker-compose.otel.yaml \ 306 | -f misc/local/docker-compose.router-no-code.yaml \ 307 | up -d --build 308 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 309 | docker compose logs 310 | docker compose logs apollo-router 311 | 312 | .PHONY: up-supergraph-rhai-local 313 | up-supergraph-rhai-local: config compose 314 | docker compose \ 315 | -f docker-compose.yaml \ 316 | -f opentelemetry/docker-compose.otel.yaml \ 317 | -f misc/local/docker-compose.router-rhai.yaml \ 318 | up -d --build 319 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 320 | docker compose logs 321 | docker compose logs apollo-router-rhai 322 | 323 | .PHONY: up-supergraph-rust-plugin-local 324 | up-supergraph-rust-plugin-local: config compose 325 | docker compose \ 326 | -f docker-compose.yaml \ 327 | -f opentelemetry/docker-compose.otel.yaml \ 328 | -f misc/local/docker-compose.router-rust-plugin.yaml \ 329 | up -d --build 330 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 331 | docker compose logs 332 | docker compose logs apollo-router-rust-plugin 333 | 334 | .PHONY: up-supergraph-local-gateway 335 | up-supergraph-local-gateway: config compose 336 | docker compose \ 337 | -f docker-compose.yaml \ 338 | -f opentelemetry/docker-compose.otel.yaml \ 339 | -f misc/local/docker-compose.gateway.yaml \ 340 | up -d --build 341 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 342 | docker compose logs 343 | docker compose logs apollo-gateway 344 | 345 | # minimal local demo 346 | 347 | .PHONY: up-supergraph-no-otel-local 348 | up-supergraph-no-otel-local: 349 | docker compose \ 350 | -f docker-compose.yaml \ 351 | -f misc/local/docker-compose.router-no-otel.yaml \ 352 | up -d --build 353 | @set -x; sleep $$SUBGRAPH_BOOT_TIME 354 | docker compose logs 355 | docker compose logs apollo-router 356 | 357 | 358 | # GitHub Actions Local Runners with ACT 359 | 360 | .PHONY: deps-act 361 | deps-act: 362 | curl https://raw.githubusercontent.com/nektos/act/master/install.sh | bash -s v0.2.31 363 | 364 | ubuntu-latest=ubuntu-latest=catthehacker/ubuntu:act-latest 365 | 366 | .PHONY: ci 367 | ci: ci-local 368 | 369 | .PHONY: ci-all 370 | ci-all: ci-local ci-studio 371 | 372 | .PHONY: ci-local 373 | ci-local: ci-local-router-no-code ci-local-router-rhai ci-local-gateway 374 | 375 | .PHONY: ci-local-rust 376 | ci-local-rust: ci-local-router-rust-plugin ci-local-router-rust-dev 377 | 378 | .PHONY: ci-local-all 379 | ci-local-all: ci-local ci-local-rust 380 | 381 | .PHONY: ci-studio 382 | ci-studio: ci-studio-router-no-code ci-studio-gateway 383 | 384 | # router 385 | 386 | .PHONY: ci-local-router-no-code 387 | ci-local-router-no-code: 388 | act -P $(ubuntu-latest) \ 389 | -W .github/workflows/local-router-no-code.yaml \ 390 | --detect-event 391 | 392 | .PHONY: ci-local-router-rhai 393 | ci-local-router-rhai: 394 | act -P $(ubuntu-latest) \ 395 | -W .github/workflows/local-router-rhai.yaml \ 396 | --detect-event 397 | 398 | .PHONY: ci-local-router-rust-plugin 399 | ci-local-router-rust-plugin: 400 | act -P $(ubuntu-latest) \ 401 | -W .github/workflows/local-router-rust-plugin.yaml \ 402 | --detect-event 403 | 404 | .PHONY: ci-local-router-rust-dev 405 | ci-local-router-rust-dev: 406 | act -P $(ubuntu-latest) \ 407 | -W .github/workflows/local-router-rust-dev.yaml \ 408 | --detect-event 409 | 410 | .PHONY: ci-studio-router-dev 411 | ci-studio-router-dev: 412 | act -P $(ubuntu-latest) \ 413 | -W .github/workflows/studio-router-rust-dev.yaml \ 414 | --secret-file graph-api.env \ 415 | -s APOLLO_GRAPH_REF_ROUTER_DEV=supergraph-router-fed2@ci-router-dev \ 416 | --detect-event 417 | 418 | .PHONY: ci-studio-router-no-code 419 | ci-studio-router-no-code: 420 | act -P $(ubuntu-latest) \ 421 | -W .github/workflows/studio-router-no-code.yaml \ 422 | --secret-file graph-api.env \ 423 | -s APOLLO_GRAPH_REF_ROUTER=supergraph-router-fed2@ci-router \ 424 | --detect-event \ 425 | -j ci-docker-managed 426 | 427 | # gateway 428 | 429 | .PHONY: ci-local-gateway 430 | ci-local-gateway: 431 | act -P $(ubuntu-latest) \ 432 | -W .github/workflows/local-gateway.yaml \ 433 | --detect-event 434 | 435 | .PHONY: ci-studio-gateway 436 | ci-studio-gateway: 437 | act -P $(ubuntu-latest) \ 438 | -W .github/workflows/studio-gateway.yaml \ 439 | --secret-file graph-api.env \ 440 | -s APOLLO_GRAPH_REF=supergraph-router-fed2@ci-gateway \ 441 | --detect-event \ 442 | -j ci-docker-managed 443 | 444 | # utilities 445 | # 446 | .PHONY: load 447 | load: load-250 448 | 449 | .PHONY: load-10 450 | load-10: 451 | @.scripts/smoke.sh 4000 10 452 | 453 | .PHONY: load-100 454 | load-100: 455 | @.scripts/smoke.sh 4000 100 456 | 457 | .PHONY: load-250 458 | load-250: 459 | @.scripts/smoke.sh 4000 250 460 | 461 | .PHONY: unpublish-subgraphs 462 | unpublish-subgraphs: 463 | .scripts/unpublish.sh 464 | 465 | .PHONY: docker-prune 466 | docker-prune: 467 | .scripts/docker-prune.sh 468 | 469 | .PHONY: deps-check 470 | deps-check: 471 | .scripts/deps-check.sh 472 | -------------------------------------------------------------------------------- /client/defer/apollo-client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /client/defer/apollo-client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bookworm-slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json . 6 | 7 | RUN npm install 8 | 9 | COPY public public 10 | COPY src src 11 | 12 | CMD [ "npm", "run", "start" ] 13 | -------------------------------------------------------------------------------- /client/defer/apollo-client/README.md: -------------------------------------------------------------------------------- 1 | 2 | # About this application 3 | 4 | - This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 5 | - This project can be used along with PR [#212](https://github.com/apollographql/supergraph-demo-fed2/pull/212) from the Fed2 Supergraph demo to run a mock Apollo Client application that uses Apollo Router with `@defer`. 6 | - To setup the Fed2 Supergraph demo please refer to the [README](https://github.com/apollographql/supergraph-demo-fed2#readme). 7 | - `Please note:` The Apollo Client doesn't yet support `@defer` and still is WIP see PR [#9598](https://github.com/apollographql/apollo-client/pull/9598) 8 | 9 | ## Available Scripts 10 | 11 | In the project directory, you can run: 12 | 13 | ### `npm start` 14 | 15 | Runs the app in the development mode.\ 16 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 17 | 18 | The page will reload when you make changes.\ 19 | You may also see any lint errors in the console. 20 | 21 | ### `npm test` 22 | 23 | Launches the test runner in the interactive watch mode.\ 24 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 25 | 26 | ### `npm run build` 27 | 28 | Builds the app for production to the `build` folder.\ 29 | It correctly bundles React in production mode and optimizes the build for the best performance. 30 | 31 | The build is minified and the filenames include the hashes.\ 32 | Your app is ready to be deployed! 33 | 34 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 35 | 36 | ### `npm run eject` 37 | 38 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 39 | 40 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 41 | 42 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 43 | 44 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 45 | 46 | ### `npm run build` fails to minify 47 | 48 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 49 | -------------------------------------------------------------------------------- /client/defer/apollo-client/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-defer' to get started 5 | 6 | version: '3' 7 | services: 8 | apollo-client-defer: 9 | container_name: apollo-client-defer 10 | build: ./client/defer/apollo-client 11 | ports: 12 | - "3000:3000" 13 | -------------------------------------------------------------------------------- /client/defer/apollo-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ac-fed2-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@apollo/client": "3.13.8", 7 | "@testing-library/jest-dom": "5.17.0", 8 | "@testing-library/react": "13.4.0", 9 | "@testing-library/user-event": "14.6.1", 10 | "graphql": "16.11.0", 11 | "react": "18.3.1", 12 | "react-dom": "18.3.1", 13 | "react-scripts": "5.0.1", 14 | "web-vitals": "2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/defer/apollo-client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/client/defer/apollo-client/public/favicon.ico -------------------------------------------------------------------------------- /client/defer/apollo-client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /client/defer/apollo-client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/client/defer/apollo-client/public/logo192.png -------------------------------------------------------------------------------- /client/defer/apollo-client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/client/defer/apollo-client/public/logo512.png -------------------------------------------------------------------------------- /client/defer/apollo-client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/defer/apollo-client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/defer/apollo-client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | .App-header { 11 | background-color: #282c34; 12 | min-height: 60vh; 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | justify-content: center; 17 | font-size: calc(10px + 2vmin); 18 | color: white; 19 | } 20 | 21 | .Grid-column { 22 | display: grid; 23 | grid-template-columns: 1fr 1fr; 24 | grid-gap: 8px; 25 | } 26 | 27 | .Test-query { 28 | background-color: #61dafb; 29 | } 30 | 31 | .Deferred-query { 32 | background-color: #61dafb; 33 | } 34 | 35 | .Nondeferred-query { 36 | background-color: #61dafb; 37 | } 38 | 39 | .Product-title { 40 | font-size: calc(3px + 2vmin); 41 | } 42 | 43 | .Delivery-text { 44 | font-size: calc(1px + 2vmin); 45 | } -------------------------------------------------------------------------------- /client/defer/apollo-client/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from "./logo.png"; 2 | import "./App.css"; 3 | import { 4 | ApolloClient, 5 | InMemoryCache, 6 | ApolloProvider, 7 | useQuery, 8 | gql, 9 | } from "@apollo/client"; 10 | 11 | const client = new ApolloClient({ 12 | uri: "http://localhost:4000/", 13 | cache: new InMemoryCache(), 14 | }); 15 | 16 | // a test query 17 | 18 | const TEST_QUERY = gql` 19 | query Query { 20 | allProducts { 21 | id 22 | delivery { 23 | estimatedDelivery 24 | fastestDelivery 25 | } 26 | } 27 | } 28 | `; 29 | 30 | // a deferred query 31 | const DEFERRED_QUERY = gql` 32 | query deferDeliveryExample { 33 | allProducts { 34 | id 35 | name 36 | ...MyFragment @defer 37 | } 38 | } 39 | fragment MyFragment on Product { 40 | delivery { 41 | estimatedDelivery 42 | fastestDelivery 43 | } 44 | } 45 | `; 46 | 47 | // a non-deferred query 48 | const NON_DEFERRED_QUERY = gql` 49 | query deferDeliveryExample { 50 | allProducts { 51 | id 52 | name 53 | ...MyFragment 54 | } 55 | } 56 | fragment MyFragment on Product { 57 | delivery { 58 | estimatedDelivery 59 | fastestDelivery 60 | } 61 | } 62 | `; 63 | 64 | function DeferredProducts() { 65 | console.log("DeferredProducts") 66 | return Render(DEFERRED_QUERY) 67 | } 68 | 69 | function NonDeferredProducts() { 70 | return Render(NON_DEFERRED_QUERY) 71 | } 72 | 73 | function Render(query) { 74 | const { loading, error, data } = useQuery( query ); 75 | 76 | console.log("Render:") 77 | console.log(loading, error, data) 78 | 79 | if (loading) return

Loading...

; 80 | if (error) { 81 | console.log(error); 82 | return

Error :( {JSON.stringify(error)}

; 83 | } 84 | 85 | if (!data) { 86 | return

Still no data :(

; 87 | } 88 | 89 | return ( 90 |
91 | 92 | {data.allProducts.map(({ id, name, delivery }) => { 93 | if(delivery) { 94 | return
95 |

{id} : {name}

96 |

Estimated Delivery : {delivery.estimatedDelivery}

97 |

Fastest Delivery : {delivery.fastestDelivery}

98 |
99 | } else { 100 | return
101 |

{id} : {name}

102 |
103 | } 104 | })} 105 |
106 | ) 107 | } 108 | 109 | function App() { 110 | return ( 111 | 112 |
113 |
114 | logo 115 |

Testing @defer with Apollo Client and Apollo Router.

116 |
117 |
118 |
119 |

A deferred query 🚀

120 | 121 |
122 |
123 |

A non-deferred query ⏲️

124 | 125 |
126 |
127 |
128 |
129 | ); 130 | } 131 | 132 | export default App; 133 | -------------------------------------------------------------------------------- /client/defer/apollo-client/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /client/defer/apollo-client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /client/defer/apollo-client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById("root")); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /client/defer/apollo-client/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/client/defer/apollo-client/src/logo.png -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # These are the subgraphs in this supergraph 2 | # They are deployed first, so they're running in the environment 3 | 4 | # When the subgraphs are ready to serve requests 5 | # we publish the subgraph schemas to the Apollo Registry 6 | 7 | # Apollo Registry then does: 8 | # - supergraph CI: composes the subgraphs into a supergraph schema 9 | # - supergraph CD: feeds new supergraph schema to the Apollo Router via Apollo Uplink polling every few seconds 10 | 11 | # Run 'make demo' to get started 12 | # then run 'make smoke' in separate window 13 | # or open http://localhost:4000 to use Apollo Sandbox to issue queries 14 | 15 | # Note this is the base docker-compose file for this project. 16 | # The paths in all other compose files are **relative to this file**, per: 17 | # https://docs.docker.com/compose/extends/#multiple-compose-files 18 | 19 | # see `./opentelemetry` `./misc/studio` and `./misc/local` for other compose files 20 | 21 | version: '3.9' 22 | services: 23 | products: 24 | container_name: products 25 | build: ./subgraphs/products 26 | ports: 27 | - "4001:4000" 28 | inventory: 29 | container_name: inventory 30 | build: ./subgraphs/inventory 31 | ports: 32 | - "4002:4000" 33 | users: 34 | container_name: users 35 | build: ./subgraphs/users 36 | ports: 37 | - "4003:4000" 38 | pandas: 39 | container_name: pandas 40 | build: ./subgraphs/pandas 41 | ports: 42 | - "4004:4000" 43 | reviews: 44 | container_name: reviews 45 | build: ./subgraphs/reviews 46 | ports: 47 | - "4005:4000" 48 | -------------------------------------------------------------------------------- /docs/media/apollo-client/defer-immediate-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/apollo-client/defer-immediate-response.png -------------------------------------------------------------------------------- /docs/media/apollo-client/deferred-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/apollo-client/deferred-response.png -------------------------------------------------------------------------------- /docs/media/apollo-kotlin/apollo-kotlin-defer-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/apollo-kotlin/apollo-kotlin-defer-demo.png -------------------------------------------------------------------------------- /docs/media/fed2/0-apollo-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/fed2/0-apollo-key.png -------------------------------------------------------------------------------- /docs/media/fed2/0-create-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/fed2/0-create-graph.png -------------------------------------------------------------------------------- /docs/media/fed2/1-publish-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/fed2/1-publish-success.png -------------------------------------------------------------------------------- /docs/media/fed2/2-studio-launches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/fed2/2-studio-launches.png -------------------------------------------------------------------------------- /docs/media/fed2/4-studio-working.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/fed2/4-studio-working.png -------------------------------------------------------------------------------- /docs/media/fed2/5-apollo-explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/fed2/5-apollo-explorer.png -------------------------------------------------------------------------------- /docs/media/honeycomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/honeycomb.png -------------------------------------------------------------------------------- /docs/media/opentelemetry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/opentelemetry.png -------------------------------------------------------------------------------- /docs/media/product-subgraph-sandbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/product-subgraph-sandbox.png -------------------------------------------------------------------------------- /docs/media/router/make_run-router-local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/router/make_run-router-local.png -------------------------------------------------------------------------------- /docs/media/router/make_run-router-rust-plugin-compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/router/make_run-router-rust-plugin-compile.png -------------------------------------------------------------------------------- /docs/media/router/make_run-router-rust-plugin-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/router/make_run-router-rust-plugin-run.png -------------------------------------------------------------------------------- /docs/media/router/make_run-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/router/make_run-router.png -------------------------------------------------------------------------------- /docs/media/router/query-plan-router-sandbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/router/query-plan-router-sandbox.png -------------------------------------------------------------------------------- /docs/media/router/rhai-custom-logging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/router/rhai-custom-logging.png -------------------------------------------------------------------------------- /docs/media/sandbox/defer-immediate-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/sandbox/defer-immediate-response.png -------------------------------------------------------------------------------- /docs/media/sandbox/deferred-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/sandbox/deferred-response.png -------------------------------------------------------------------------------- /docs/media/studio-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/studio-sign-up.png -------------------------------------------------------------------------------- /docs/media/supergraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/docs/media/supergraph.png -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Supergraph Composition 2 | 3 | The [Apollo Federation Docs](https://www.apollographql.com/docs/federation/) do a great job explaining the basics of composition. 4 | 5 | ## Composition Concepts 6 | 7 | Several [composition examples](./composition/) go into additional details about Federation 2 composition. 8 | -------------------------------------------------------------------------------- /examples/composition/README.md: -------------------------------------------------------------------------------- 1 | # Federation 2 Composition Examples 2 | 3 | ## Contents 4 | 5 | - various examples of subgraphs with supergraph.yaml config that can be used with the `rover supergraph` subcommand. See the main [README.md](/README.md#prerequisites) for rover install instructions. 6 | 7 | ## Using the `rover` CLI with the examples 8 | 9 | Run an example: 10 | 11 | ``` 12 | cd ./basic 13 | 14 | rover supergraph compose --config ./supergraph.yaml 15 | ``` 16 | 17 | Verify it composes successfully: 18 | 19 | ``` 20 | rover supergraph compose --config ./supergraph.yaml 21 | WARN: [InconsistentFieldType]: Field "A.v1" has mismatched, but compatible, types across subgraphs: will use type "Int" (from subgraph "a") in supergraph but "A.v1" has subtype "Int!" in subgraph "b" 22 | 23 | CoreSchema: 24 | 25 | schema 26 | @core(feature: "https://specs.apollo.dev/core/v0.2") 27 | @core(feature: "https://specs.apollo.dev/join/v0.2", for: EXECUTION) 28 | { 29 | query: Query 30 | } 31 | 32 | directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA 33 | 34 | directive @join__field(graph: join__Graph!, requires: join__FieldSet, provides: join__FieldSet) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION 35 | 36 | directive @join__graph(name: String!, url: String!) on ENUM_VALUE 37 | 38 | directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE 39 | 40 | directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR 41 | 42 | type A 43 | @join__type(graph: A, key: "k") 44 | @join__type(graph: B, key: "k") 45 | { 46 | k: Int 47 | v1: Int 48 | v2: String @join__field(graph: A) 49 | v3: Int @join__field(graph: B) 50 | } 51 | 52 | enum core__Purpose { 53 | """ 54 | `SECURITY` features provide metadata necessary to securely resolve fields. 55 | """ 56 | SECURITY 57 | 58 | """ 59 | `EXECUTION` features provide metadata necessary for operation execution. 60 | """ 61 | EXECUTION 62 | } 63 | 64 | scalar join__FieldSet 65 | 66 | enum join__Graph { 67 | A @join__graph(name: "a", url: "http://a:4000/graphql") 68 | B @join__graph(name: "b", url: "http://b:4000/graphql") 69 | } 70 | 71 | type Query 72 | @join__type(graph: A) 73 | @join__type(graph: B) 74 | { 75 | a: A! @join__field(graph: A) 76 | } 77 | ``` 78 | 79 | Done! 80 | 81 | ## Additional `rover` info 82 | 83 | https://www.apollographql.com/docs/rover/ -------------------------------------------------------------------------------- /examples/composition/basic/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | a: A! 3 | } 4 | 5 | type A @key(fields: "k") { 6 | k: Int 7 | v1: Int 8 | v2: String 9 | } 10 | -------------------------------------------------------------------------------- /examples/composition/basic/B.graphql: -------------------------------------------------------------------------------- 1 | type A @key(fields: "k") { 2 | k: Int 3 | v1: Int! 4 | v3: Int 5 | } 6 | -------------------------------------------------------------------------------- /examples/composition/basic/README.md: -------------------------------------------------------------------------------- 1 | A very basic example of composition/query planning. 2 | 3 | It does contain an example of a field whose type differs between subgraphs (see `v1`) but is compatible to show that 4 | some "hints" is generated. 5 | -------------------------------------------------------------------------------- /examples/composition/basic/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | a { 3 | __typename 4 | v1 5 | v2 6 | v3 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/composition/basic/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | -------------------------------------------------------------------------------- /examples/composition/complex_key/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | getE1: E1 3 | } 4 | 5 | type E1 @key(fields: "k1 { v }") { 6 | k1: E2 7 | a: Int 8 | } 9 | 10 | type E2 @key(fields: "k2") { 11 | k2: Int 12 | v: String @external 13 | } 14 | -------------------------------------------------------------------------------- /examples/composition/complex_key/B.graphql: -------------------------------------------------------------------------------- 1 | type E1 @key(fields: "k1 { v }") { 2 | k1: E2 3 | b: Int 4 | } 5 | 6 | type E2 @key(fields: "k2") { 7 | k2: Int 8 | v: String 9 | } 10 | -------------------------------------------------------------------------------- /examples/composition/complex_key/README.md: -------------------------------------------------------------------------------- 1 | Example of an entity (`E1`) whose own key requires a field (`E2.v`) of another entity (`E2`), but where that field is 2 | not available locally. 3 | -------------------------------------------------------------------------------- /examples/composition/complex_key/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | getE1 { 3 | b 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/composition/complex_key/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | t: T 3 | } 4 | 5 | type T @key(fields: "k") { 6 | k: ID! 7 | a: Int @external 8 | b: Int @external 9 | } 10 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/B.graphql: -------------------------------------------------------------------------------- 1 | type T @key(fields: "k") @key(fields: "k2") { 2 | k: ID! 3 | k2: Int 4 | a: Int 5 | b: Int 6 | } 7 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/C.graphql: -------------------------------------------------------------------------------- 1 | type T @key(fields: "k") { 2 | k: ID! 3 | a: Int 4 | b: Int 5 | } 6 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/D.graphql: -------------------------------------------------------------------------------- 1 | type T @key(fields: "k2") { 2 | k2: Int 3 | a: Int 4 | c: String 5 | } 6 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/README.md: -------------------------------------------------------------------------------- 1 | This example is artificially creating subgraphs where there is, in theory, a lot of way to plan the query (and it's not 2 | 100% trivial how to compute the best plan). This is to ensure the cost-comparison part of the query planner (the query 3 | planner essentially try to compute all the possible plan and pick the one with the lower cost; there is thus a cost 4 | function, which currently mainly tries to limit the number of fetches and the depth of the pipeline) works reasonably 5 | well. 6 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | t { 3 | a 4 | b 5 | c 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/composition/cost_handling/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | c: 12 | routing_url: http://c:4000/graphql 13 | schema: 14 | file: ./C.graphql 15 | d: 16 | routing_url: http://d:4000/graphql 17 | schema: 18 | file: ./D.graphql 19 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | t: T 3 | } 4 | 5 | type T @key(fields: "k") { 6 | k: ID! 7 | } 8 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/B.graphql: -------------------------------------------------------------------------------- 1 | type T @key(fields: "k") { 2 | k: ID! 3 | b: U 4 | } 5 | 6 | type U @key(fields: "v") { 7 | v: Int 8 | } 9 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/C.graphql: -------------------------------------------------------------------------------- 1 | type T @key(fields: "k") { 2 | k: ID! 3 | a: Int 4 | } 5 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/D.graphql: -------------------------------------------------------------------------------- 1 | type U @key(fields: "v") { 2 | v: Int 3 | w: Int 4 | } 5 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/README.md: -------------------------------------------------------------------------------- 1 | This example was mostly created to show an initial inefficiency of the process that generates the plan. 2 | It creates a case where, from subgraph A, we need to do: 1) a fetch to B followed by a fetch to D and 2) a fetch to C. 3 | Initial code was generating a plan where essentially the fetch to D had to wait on the fetch to C, this example shows 4 | it's not the case anymore. 5 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | t { 3 | a 4 | b { 5 | w 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/composition/efficient_parallels/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | c: 12 | routing_url: http://c:4000/graphql 13 | schema: 14 | file: ./C.graphql 15 | d: 16 | routing_url: http://d:4000/graphql 17 | schema: 18 | file: ./D.graphql 19 | -------------------------------------------------------------------------------- /examples/composition/entity_in_list/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | getFoo: [Foo] 3 | } 4 | 5 | type Foo { 6 | as: [A] 7 | } 8 | 9 | type A @key(fields: "k") { 10 | k: Int 11 | v1: Int 12 | v2: String 13 | } 14 | -------------------------------------------------------------------------------- /examples/composition/entity_in_list/B.graphql: -------------------------------------------------------------------------------- 1 | type A @key(fields: "k") { 2 | k: Int 3 | v3: Int 4 | } 5 | -------------------------------------------------------------------------------- /examples/composition/entity_in_list/C.graphql: -------------------------------------------------------------------------------- 1 | type A @key(fields: "k") { 2 | k: Int 3 | v4: Int 4 | } 5 | -------------------------------------------------------------------------------- /examples/composition/entity_in_list/README.md: -------------------------------------------------------------------------------- 1 | This example is mostly simple entity-fetch example but it uses fields that have list types. This is mostly just to show 2 | that the "merge path" on the query does take the lists into account (there is `@` in there). 3 | -------------------------------------------------------------------------------- /examples/composition/entity_in_list/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | getFoo { 3 | as { 4 | v2 5 | v3 6 | v4 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/composition/entity_in_list/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | c: 12 | routing_url: http://c:4000/graphql 13 | schema: 14 | file: ./C.graphql 15 | -------------------------------------------------------------------------------- /examples/composition/interface_simple/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | products: [Product]! 3 | } 4 | 5 | interface Product { 6 | upc: String! 7 | name: String 8 | price: String 9 | } 10 | 11 | type Furniture implements Product @key(fields: "upc") { 12 | upc: String! 13 | name: String 14 | price: String 15 | brand: String 16 | } 17 | 18 | type Book implements Product @key(fields: "isbn") { 19 | isbn: String! 20 | upc: String! @external 21 | name: String @external 22 | price: String #@external # can comment the external 23 | } 24 | -------------------------------------------------------------------------------- /examples/composition/interface_simple/B.graphql: -------------------------------------------------------------------------------- 1 | type Book @key(fields: "isbn") { 2 | isbn: String! 3 | upc: String! 4 | name: String 5 | price: String 6 | } 7 | -------------------------------------------------------------------------------- /examples/composition/interface_simple/README.md: -------------------------------------------------------------------------------- 1 | A example that demonstrates an interface. It has an interface `Product` with 2 implementation `Furniture` and `Book`. 2 | Both `Product` and `Furniture` are on subgraph A but most of the fields of `Book` are on subgraph B. It shows we 3 | do properly generate queries for `Furniture` and `Book` separately when we need to, but also demonstrate that if 4 | a field can be server from subgraph A for both implementations, the query plan avoid "type explosion" on that 5 | field. 6 | -------------------------------------------------------------------------------- /examples/composition/interface_simple/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | products { 3 | name 4 | price 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/composition/interface_simple/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | -------------------------------------------------------------------------------- /examples/composition/products/A.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | topProducts(first: Int = 5): [Product] 3 | highestPrice(arg: Int = 2): Price 4 | } 5 | 6 | #type Product { 7 | type Product @key(fields: "upc") { 8 | upc: String! 9 | name: String 10 | price: Price @deprecated 11 | weight: Int 12 | } 13 | 14 | type Price { 15 | amount: Int 16 | currency: String 17 | } 18 | -------------------------------------------------------------------------------- /examples/composition/products/B.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | bestReviewedProduct: Product 3 | highestPrice(arg: Int): Price 4 | } 5 | 6 | type Review @key(fields: "id") { 7 | id: ID! 8 | body: String 9 | product: Product 10 | } 11 | 12 | extend type Product @key(fields: "upc") { 13 | #extend type Product { 14 | upc: String! @external 15 | reviews: [Review] 16 | price: Price @deprecated(reason: "Because!") 17 | } 18 | 19 | type Price { 20 | amount: Int! 21 | currency: String 22 | } 23 | -------------------------------------------------------------------------------- /examples/composition/products/README.md: -------------------------------------------------------------------------------- 1 | An early example written mostly when experimenting with composition validation. It demonstrate a few of the 2 | merging/hinting behavior and by default does _not_ compose (to demonstrate a proper composition validation error; fixing 3 | it is left to the reader). 4 | -------------------------------------------------------------------------------- /examples/composition/products/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | topProducts(first: 10) { 3 | price 4 | reviews { 5 | body 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/composition/products/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | -------------------------------------------------------------------------------- /examples/composition/provides/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | t1: T 3 | t2: T @provides(fields: "a") 4 | t3: T @provides(fields: "a b") 5 | t4: T @provides(fields: "c { v }") 6 | } 7 | 8 | type T @key(fields: "k") { 9 | k: ID! 10 | a: Int @external 11 | b: Int @external 12 | c: X @external 13 | } 14 | 15 | type X @key(fields: "id") { 16 | id: Int 17 | v: Int @external 18 | v2: String 19 | } 20 | -------------------------------------------------------------------------------- /examples/composition/provides/B.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | otherT: T 3 | } 4 | 5 | type T @key(fields: "k") { 6 | k: ID! 7 | a: Int 8 | b: Int 9 | c: X 10 | } 11 | 12 | type X @key(fields: "id") { 13 | id: Int 14 | v: Int 15 | } 16 | -------------------------------------------------------------------------------- /examples/composition/provides/README.md: -------------------------------------------------------------------------------- 1 | Demonstrate the use of provides. Subgraph A declares a few operation with different provides, so it is worth updating 2 | the query to use each of those operation and see how that modifies the plan based on the provides (including a nested 3 | provides). 4 | -------------------------------------------------------------------------------- /examples/composition/provides/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | t4 { 3 | c { 4 | v 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/composition/provides/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | -------------------------------------------------------------------------------- /examples/composition/requires/A.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | t: T 3 | } 4 | 5 | type T @key(fields: "k") { 6 | k: ID! 7 | a: X @external 8 | b: Int 9 | } 10 | 11 | type X @key(fields: "id") { 12 | id: Int 13 | v: Int 14 | } 15 | -------------------------------------------------------------------------------- /examples/composition/requires/B.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | otherT: T 3 | } 4 | 5 | type T @key(fields: "k") { 6 | k: ID! 7 | a: X 8 | b: Int @external 9 | c: String @requires(fields: "a { v } b") 10 | } 11 | 12 | type X @key(fields: "id") { 13 | id: Int 14 | v: Int @external 15 | } 16 | -------------------------------------------------------------------------------- /examples/composition/requires/README.md: -------------------------------------------------------------------------------- 1 | Demonstrate the use of @requires. By default, it shows a somehow involved case of a nested @require that forces a fairly 2 | involved plan. It be changed easily to a more simple/traditional @require example by removing `a { v }` from the 3 | @requires in subgraph B (leaving only `b`). 4 | -------------------------------------------------------------------------------- /examples/composition/requires/query.graphql: -------------------------------------------------------------------------------- 1 | { 2 | t { 3 | c 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/composition/requires/supergraph.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | a: 4 | routing_url: http://a:4000/graphql 5 | schema: 6 | file: ./A.graphql 7 | b: 8 | routing_url: http://b:4000/graphql 9 | schema: 10 | file: ./B.graphql 11 | -------------------------------------------------------------------------------- /misc/README.md: -------------------------------------------------------------------------------- 1 | ## Pre-built Docker Images & Docker Compose 2 | 3 | > The `make` commands prefixed with `up-` will use `docker-compose up` with the main subgraphs [docker-compose.yaml](/docker-compose.yaml) plus one of the graph router docker-compose files in [local](./local/) or [studio](./studio/). 4 | 5 | See the [docker-compose examples](/README.md#apollo-router-pre-built-docker-images) in the main [README.md](/README.md) to get started. 6 | 7 | [Kubernetes examples](https://www.apollographql.com/docs/router/containerization/kubernetes) for use of `kustomize` and other Kubernetes tooling, plus a [Helm chart](https://www.apollographql.com/docs/router/containerization/kubernetes) are also provided. 8 | 9 | ## Run the Router directly for better local dev UX 10 | 11 | The Apollo Router [can also be downloaded](/README.md#prerequisites) and run as a single standalone binary on your laptop using `make run-supergraph` where you can see the enhanced log output formatting for a better local developer experience. See the [build your first supergraph](/README.md#build-your-first-supergraph) to get started. 12 | 13 | ## Supergraph Composition 14 | 15 | The [Apollo Federation Docs](https://www.apollographql.com/docs/federation/) do a great job explaining the basics of composition in Apollo Studio and local composition. 16 | 17 | ## Composition in Apollo Studio 18 | 19 | [Composition in Apollo Studio](https://www.apollographql.com/docs/federation/quickstart/studio-composition) enables multiple teams to independently publish their subgraph schemas after an updated subgraph has been deployed, and provides a supergraph CI/CD pipeline with and schema check that can assess the impact of a change using graph-native telemetry from your supergraph router. Composition in Apollo Studio supports [Contracts](https://www.apollographql.com/docs/studio/contracts/) which allows you to create slices of a unified graph (public API, partner API, internal API) for different consumers to use. 20 | 21 | Doing local development with your own graph variant (like a branch in GitHub) enables you to see composition errors and browse your schema in Apollo Studio before deploying them to a production supergraph and provides a great developer experience. 22 | 23 | Most of the examples in this repo use [Composition in Apollo Studio](https://www.apollographql.com/docs/federation/quickstart/studio-composition) which has some nice developer tooling that is [free for all Apollo users](https://www.apollographql.com/docs/studio/#free-for-all-apollo-users). 24 | 25 | You can even use many of Studio's dev tools without an Apollo account using [Apollo Sandbox](https://www.apollographql.com/docs/studio/explorer/sandbox/). Apollo Router provides an embedded version of Sandbox using `router --dev` mode that can even show the query plans from the Router! 26 | 27 | Get started by [downloading the rover CLI and router binary](/README.md#prerequisites) and following the steps on how to [build your first supergraph](/README.md#build-your-first-supergraph). 28 | 29 | ## Local Composition 30 | 31 | [Local composition](/README.md#local-composition) using `rover supergraph compose` can be used for simple local development use cases and air-gapped environments. 32 | 33 | However, in most cases [Composition in Apollo Studio](https://www.apollographql.com/docs/federation/quickstart/studio-composition) is preferred even for local development as newly published subgraphs are composed and automatically deployed to your local supergraph router which is listening for changes, so the supergraph automatically updates whenever you `rover subgraph publish` a new schema to the schema registry, typically after the subgraph has been updated with schema updates that need to be composed into your supergraph. 34 | -------------------------------------------------------------------------------- /misc/advanced/router-dev/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /misc/advanced/router-dev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "acme_router" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [dependencies] 8 | anyhow = "1.0.82" 9 | apollo-router = { git = "https://github.com/apollographql/router", branch = "dev" } 10 | async-trait = "0.1.80" 11 | futures = "0.3.30" 12 | http = "0.2.12" 13 | schemars = "0.9.0" 14 | serde = "1.0.200" 15 | serde_json = "1.0.116" 16 | tokio = "1.37.0" 17 | tower = { version = "0.5.0", features = ["full"] } 18 | -------------------------------------------------------------------------------- /misc/advanced/router-dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 rust:1.87 as build 2 | 3 | ENV RUST_BACKTRACE=full 4 | 5 | # create a new empty shell project 6 | RUN USER=root cargo new --bin acme_router 7 | 8 | WORKDIR /acme_router 9 | 10 | # Update our build image and install required packages 11 | RUN apt-get update -y \ 12 | && apt-get install -y \ 13 | protobuf-compiler \ 14 | cmake 15 | 16 | RUN rustup component add rustfmt 17 | 18 | # copy over your manifests 19 | COPY ./Cargo.toml ./Cargo.toml 20 | 21 | # this build step will cache your dependencies 22 | RUN cargo build --release 23 | RUN rm src/*.rs 24 | 25 | # copy your source tree 26 | COPY ./src ./src 27 | 28 | # build for release 29 | RUN rm ./target/release/deps/acme_router* 30 | RUN cargo build --release 31 | 32 | RUN mkdir -p /dist/config && mkdir -p /dist/schema 33 | 34 | # our final image uses distroless, which is more secure by default 35 | #FROM --platform=linux/amd64 gcr.io/distroless/cc-debian11 36 | 37 | # if you want bin/sh you'll want a distro that includes it instead 38 | FROM --platform=linux/amd64 debian:bookworm-slim 39 | 40 | RUN apt-get update -y \ 41 | && apt-get install -y \ 42 | ca-certificates \ 43 | curl 44 | 45 | # copy the build artifact from the build stage 46 | COPY --from=build /dist /dist 47 | COPY --from=build --chown=root:root /acme_router/target/release/acme_router /dist 48 | COPY --from=build --chown=root:root /acme_router/Cargo.lock /dist 49 | 50 | WORKDIR /dist 51 | 52 | # for faster docker shutdown 53 | STOPSIGNAL SIGINT 54 | 55 | # set the startup command to run your binary 56 | # note: if you want sh you can override the entrypoint using docker run -it --entrypoint=sh my-router-image 57 | ENTRYPOINT ["./acme_router"] 58 | -------------------------------------------------------------------------------- /misc/advanced/router-dev/router.yaml: -------------------------------------------------------------------------------- 1 | # see https://www.apollographql.com/docs/router/configuration/overview#yaml-config-file 2 | 3 | cors: 4 | allow_any_origin: true 5 | supergraph: 6 | listen: 0.0.0.0:4000 7 | introspection: true 8 | telemetry: 9 | apollo: 10 | # The percentage of requests will include HTTP request and response headers in traces sent to Apollo Studio. 11 | # This is expensive and should be left at a low value. 12 | # This cannot be higher than tracing->trace_config->sampler 13 | field_level_instrumentation_sampler: 1 # (default) 14 | tracing: 15 | trace_config: 16 | sampler: 1 # The percentage of requests that will generate traces (a rate or `always_on` or `always_off`) 17 | service_name: "router" 18 | service_namespace: "apollo" 19 | otlp: 20 | endpoint: http://${env.APOLLO_OTEL_EXPORTER_HOST:-localhost}:4317 21 | protocol: grpc 22 | batch_processor: 23 | max_export_timeout: 42s 24 | 25 | include_subgraph_errors: 26 | all: true # Propagate errors from all subgraphs 27 | -------------------------------------------------------------------------------- /misc/advanced/router-dev/src/main.rs: -------------------------------------------------------------------------------- 1 | //! curl -v \ 2 | //! --header 'content-type: application/json' \ 3 | //! --url 'http://127.0.0.1:4000' \ 4 | //! --data '{"query":"query { topProducts { reviews { author { name } } name } }"}' 5 | //! [...] 6 | //! {"data":{"topProducts":[{"reviews":[{"author":{"name":"Ada Lovelace"}},{"author":{"name":"Alan Turing"}}],"name":"Table"},{"reviews":[{"author":{"name":"Ada Lovelace"}}],"name":"Couch"},{"reviews":[{"author":{"name":"Alan Turing"}}],"name":"Chair"}]}} 7 | use anyhow::Result; 8 | 9 | // adding the module to your main.rs file 10 | // will automatically register it to the router plugin registry. 11 | // 12 | // `cargo run -- -s ../graphql/supergraph.graphql -c ./router.yaml` 13 | fn main() -> Result<()> { 14 | apollo_router::main() 15 | } 16 | -------------------------------------------------------------------------------- /misc/local/README.md: -------------------------------------------------------------------------------- 1 | # Local Composition 2 | 3 | Local composition using `rover supergraph compose` is covered here with several examples. Local composition can be useful for simple local development use cases and air-gapped environments. 4 | 5 | However, in most cases [Composition in Apollo Studio](https://www.apollographql.com/docs/federation/quickstart/studio-composition) is preferred even for local development as newly published subgraphs are composed and automatically deployed to your local supergraph router which is listening for changes, so the supergraph automatically updates whenever you `rover subgraph publish` a new schema to the schema registry. 6 | -------------------------------------------------------------------------------- /misc/local/docker-compose.gateway.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-gateway-local' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-gateway: 9 | container_name: apollo-gateway 10 | build: ./supergraph/gateway 11 | environment: 12 | - APOLLO_SCHEMA_CONFIG_EMBEDDED=true 13 | - APOLLO_OTEL_EXPORTER_TYPE=collector 14 | - APOLLO_OTEL_EXPORTER_HOST=collector 15 | - APOLLO_OTEL_EXPORTER_PORT=4318 16 | volumes: 17 | - ./supergraph/schema/docker.graphql:/etc/config/supergraph.graphql 18 | ports: 19 | - "4000:4000" -------------------------------------------------------------------------------- /misc/local/docker-compose.router-no-code.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-local' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router: 9 | container_name: apollo-router 10 | image: ghcr.io/apollographql/router:v1.61.7 11 | volumes: 12 | - ./supergraph/schema/docker.graphql:/dist/schema/supergraph.graphql 13 | - ./supergraph/router.yaml:/dist/config/router.yaml 14 | command: [ "--dev", "-c", "config/router.yaml", "-s", "schema/supergraph.graphql", "--log", "info" ] 15 | environment: 16 | - APOLLO_OTEL_EXPORTER_HOST=collector 17 | ports: 18 | - "4000:4000" 19 | -------------------------------------------------------------------------------- /misc/local/docker-compose.router-no-otel.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-no-otel-local' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router: 9 | container_name: apollo-router 10 | image: ghcr.io/apollographql/router:v1.61.7 11 | volumes: 12 | - ./supergraph/schema/docker.graphql:/dist/schema/supergraph.graphql 13 | command: [ "--dev", "-s", "schema/supergraph.graphql", "--log", "info" ] 14 | environment: 15 | - APOLLO_OTEL_EXPORTER_HOST=collector 16 | ports: 17 | - "4000:4000" 18 | -------------------------------------------------------------------------------- /misc/local/docker-compose.router-rhai.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-rhai-local' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router-rhai: 9 | container_name: apollo-router-rhai 10 | build: ./supergraph/router-rhai-script 11 | volumes: 12 | - ./supergraph/schema/docker.graphql:/dist/schema/supergraph.graphql 13 | - ./supergraph/router-rhai-script/router.yaml:/dist/config/router.yaml 14 | command: [ "--dev", "-c", "config/router.yaml", "-s", "schema/supergraph.graphql", "--log", "info" ] 15 | environment: 16 | - APOLLO_OTEL_EXPORTER_HOST=collector 17 | ports: 18 | - "4000:4000" -------------------------------------------------------------------------------- /misc/local/docker-compose.router-rust-dev.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-router-dev-local' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router-dev: 9 | container_name: apollo-router-dev 10 | build: ./misc/advanced/router-dev 11 | volumes: 12 | - ./supergraph/schema/docker.graphql:/dist/schema/supergraph.graphql 13 | - ./supergraph/router.yaml:/dist/config/router.yaml 14 | command: [ "--dev", "-c", "config/router.yaml", "-s", "schema/supergraph.graphql", "--log", "info" ] 15 | ports: 16 | - "4000:4000" -------------------------------------------------------------------------------- /misc/local/docker-compose.router-rust-plugin.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-rust-plugin-local' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router-rust-plugin: 9 | container_name: apollo-router-rust-plugin 10 | build: ./supergraph/router-rust-plugin 11 | volumes: 12 | - ./supergraph/schema/docker.graphql:/dist/schema/supergraph.graphql 13 | - ./supergraph/router-rust-plugin/router.yaml:/dist/config/router.yaml 14 | command: [ "--dev", "-c", "config/router.yaml", "-s", "schema/supergraph.graphql", "--log", "info" ] 15 | environment: 16 | - APOLLO_OTEL_EXPORTER_HOST=collector 17 | ports: 18 | - "4000:4000" 19 | -------------------------------------------------------------------------------- /misc/studio/README.md: -------------------------------------------------------------------------------- 1 | # Composition in Apollo Studio 2 | 3 | [Composition in Apollo Studio](https://www.apollographql.com/docs/federation/quickstart/studio-composition) enables multiple teams to independently publish their subgraph schemas after an updated subgraph has been deployed, and provides a supergraph CI/CD pipeline with and schema check that can assess the impact of a change using graph-native telemetry from your supergraph router. Composition in Apollo Studio supports [Contracts](https://www.apollographql.com/docs/studio/contracts/) which allows you to create slices of a unified graph (public API, partner API, internal API) for different consumers to use. 4 | 5 | Doing local development with your own graph variant (like a branch in GitHub) enables you to see composition errors and browse your schema in Apollo Studio before deploying them to a production supergraph and provides a great developer experience. 6 | 7 | Most of the examples in this repo use [Composition in Apollo Studio](https://www.apollographql.com/docs/federation/quickstart/studio-composition) which has some nice developer tooling that is [free for all Apollo users](https://www.apollographql.com/docs/studio/#free-for-all-apollo-users). 8 | 9 | You can even use many of Studio's dev tools without an Apollo account using [Apollo Sandbox](https://www.apollographql.com/docs/studio/explorer/sandbox/). Apollo Router provides an embedded version of Sandbox using `router --dev` mode that can even show the query plans from the Router! 10 | 11 | See the main [README.md](/README.md) to get started! -------------------------------------------------------------------------------- /misc/studio/docker-compose.gateway.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-gateway' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-gateway: 9 | container_name: apollo-gateway 10 | build: ./supergraph/gateway 11 | env_file: # 'make up-supergraph-gateway' saves APOLLO_KEY and APOLLO_GRAPH_REF to connect to Studio 12 | - ./graph-api.env 13 | environment: 14 | - APOLLO_OTEL_EXPORTER_TYPE=collector 15 | - APOLLO_OTEL_EXPORTER_HOST=collector 16 | - APOLLO_OTEL_EXPORTER_PORT=4318 17 | ports: 18 | - "4000:4000" -------------------------------------------------------------------------------- /misc/studio/docker-compose.router-no-code.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router: 9 | container_name: apollo-router 10 | image: ghcr.io/apollographql/router:v1.61.7 11 | volumes: 12 | - ./supergraph/router.yaml:/dist/config/router.yaml 13 | command: [ "--dev", "-c", "config/router.yaml", "--log", "info" ] 14 | env_file: # 'make up-supergraph-router' saves APOLLO_KEY and APOLLO_GRAPH_REF to connect to Studio 15 | - ./graph-api.env 16 | environment: 17 | - APOLLO_OTEL_EXPORTER_HOST=collector 18 | ports: 19 | - "4000:4000" -------------------------------------------------------------------------------- /misc/studio/docker-compose.router-rhai.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-rhai' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router-rhai: 9 | container_name: apollo-router-rhai 10 | build: ./supergraph/router-rhai-script 11 | volumes: 12 | - ./supergraph/router-rhai-script/router.yaml:/dist/config/router.yaml 13 | command: [ "--dev", "-c", "config/router.yaml", "--log", "info" ] 14 | env_file: # 'make up-supergraph-router-rhai' saves APOLLO_KEY and APOLLO_GRAPH_REF to connect to Studio 15 | - ./graph-api.env 16 | environment: 17 | - APOLLO_OTEL_EXPORTER_HOST=collector 18 | ports: 19 | - "4000:4000" -------------------------------------------------------------------------------- /misc/studio/docker-compose.router-rust-dev.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-router-dev' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router-dev: 9 | container_name: apollo-router-dev 10 | build: ./misc/advanced/router-dev 11 | volumes: 12 | - ./supergraph/router.yaml:/dist/config/router.yaml 13 | command: [ "--dev", "-c", "config/router.yaml", "--log", "info" ] 14 | env_file: # 'make up-supergraph-router-dev' saves APOLLO_KEY and APOLLO_GRAPH_REF to connect to Studio 15 | - graph-api.env 16 | ports: 17 | - "4000:4000" -------------------------------------------------------------------------------- /misc/studio/docker-compose.router-rust-plugin.yaml: -------------------------------------------------------------------------------- 1 | # extends supergraph-demo-fed2/docker-compose.yaml 2 | # https://docs.docker.com/compose/extends/#multiple-compose-files 3 | 4 | # use 'make up-supergraph-rust-plugin' to get started 5 | 6 | version: '3.9' 7 | services: 8 | apollo-router-rust-plugin: 9 | container_name: apollo-router-rust-plugin 10 | build: ./supergraph/router-rust-plugin 11 | volumes: 12 | - ./supergraph/router-rust-plugin/router.yaml:/dist/config/router.yaml 13 | command: [ "--dev", "-c", "config/router.yaml", "--log", "info" ] 14 | env_file: # 'make up-supergraph-router-rust-plugin' saves APOLLO_KEY and APOLLO_GRAPH_REF to connect to Studio 15 | - ./graph-api.env 16 | environment: 17 | - APOLLO_OTEL_EXPORTER_HOST=collector 18 | ports: 19 | - "4000:4000" -------------------------------------------------------------------------------- /opentelemetry/collector-config.yml: -------------------------------------------------------------------------------- 1 | receivers: 2 | otlp: 3 | protocols: 4 | grpc: 5 | http: 6 | cors: 7 | allowed_origins: 8 | - http://* 9 | - https://* 10 | otlp/spanmetrics: 11 | protocols: 12 | grpc: 13 | endpoint: 0.0.0.0:12346 14 | 15 | exporters: 16 | zipkin: 17 | endpoint: "http://zipkin:9411/api/v2/spans" 18 | prometheus: 19 | endpoint: "0.0.0.0:9464" 20 | 21 | processors: 22 | batch: 23 | spanmetrics: 24 | metrics_exporter: prometheus 25 | 26 | extensions: 27 | zpages: 28 | endpoint: 0.0.0.0:55679 29 | 30 | service: 31 | extensions: [zpages] 32 | pipelines: 33 | traces: 34 | receivers: [otlp] 35 | exporters: [zipkin] 36 | processors: [spanmetrics, batch] 37 | metrics: 38 | receivers: [otlp/spanmetrics] 39 | exporters: [prometheus] 40 | processors: [batch] 41 | -------------------------------------------------------------------------------- /opentelemetry/docker-compose.otel.yaml: -------------------------------------------------------------------------------- 1 | # adds open telemetry support 2 | # extends supergraph-demo-fed2/docker-compose.yaml with 3 | # https://docs.docker.com/compose/extends/#multiple-compose-files 4 | # 5 | # use 'make up-subgraphs' to get started 6 | version: "3" 7 | services: 8 | users: 9 | environment: 10 | - APOLLO_OTEL_EXPORTER_TYPE=collector 11 | - APOLLO_OTEL_EXPORTER_HOST=collector 12 | - APOLLO_OTEL_EXPORTER_PORT=4318 13 | 14 | products: 15 | environment: 16 | - APOLLO_OTEL_EXPORTER_TYPE=collector 17 | - APOLLO_OTEL_EXPORTER_HOST=collector 18 | - APOLLO_OTEL_EXPORTER_PORT=4318 19 | 20 | inventory: 21 | environment: 22 | - APOLLO_OTEL_EXPORTER_TYPE=collector 23 | - APOLLO_OTEL_EXPORTER_HOST=collector 24 | - APOLLO_OTEL_EXPORTER_PORT=4318 25 | 26 | reviews: 27 | environment: 28 | - APOLLO_OTEL_EXPORTER_TYPE=collector 29 | - APOLLO_OTEL_EXPORTER_HOST=collector 30 | - APOLLO_OTEL_EXPORTER_PORT=4318 31 | 32 | collector: 33 | container_name: collector 34 | image: otel/opentelemetry-collector-contrib:0.127.0 35 | command: ["--config=/conf/collector-config.yml"] 36 | volumes: 37 | - ./opentelemetry/collector-config.yml:/conf/collector-config.yml 38 | ports: 39 | - "9464:9464" 40 | - "4317:4317" 41 | - "4318:4318" 42 | - "55679:55679" 43 | depends_on: 44 | - zipkin 45 | 46 | zipkin: 47 | container_name: zipkin 48 | image: openzipkin/zipkin:3.5.1 49 | ports: 50 | - "9411:9411" 51 | 52 | prometheus: 53 | container_name: prometheus 54 | image: prom/prometheus:v2.55.1 55 | volumes: 56 | - ./opentelemetry/prometheus.yml:/etc/prometheus/prometheus.yml 57 | ports: 58 | - "9090:9090" 59 | -------------------------------------------------------------------------------- /opentelemetry/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 1s 3 | 4 | scrape_configs: 5 | - job_name: 'collector' 6 | static_configs: 7 | - targets: ['localhost:9464', 'collector:9464'] -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | ":dependencyDashboard", 4 | ":semanticPrefixFixDepsChoreOthers", 5 | ":ignoreModulesAndTests", 6 | ":autodetectPinVersions", 7 | ":prHourlyLimitNone", 8 | ":prConcurrentLimit20", 9 | ":separateMajorReleases", 10 | "schedule:nonOfficeHours", 11 | "group:monorepos", 12 | "group:recommended", 13 | "workarounds:all" 14 | ], 15 | "packageRules": [ 16 | { "automerge": true, "matchUpdateTypes": ["minor", "patch", "pin", "digest"]} 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/inventory/.gitignore: -------------------------------------------------------------------------------- 1 | app/bin -------------------------------------------------------------------------------- /subgraphs/inventory/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use a separate layer for the build for better caching 2 | FROM gradle:7.6.5-jdk17 AS TEMP_BUILD_IMAGE 3 | 4 | WORKDIR /usr/src/app 5 | 6 | #COPY app/build.gradle app/settings.gradle . 7 | 8 | # Only download dependencies 9 | # Eat the expected build failure since no source code has been copied yet 10 | #RUN gradle clean build --no-daemon --parallel > /dev/null 2>&1 || true 11 | 12 | # Copy all files 13 | COPY app . 14 | 15 | # Do the actual build 16 | RUN gradle clean build --no-daemon --parallel 17 | 18 | 19 | # Create a new layer with the build output to run the app 20 | FROM openjdk:22-slim-bookworm 21 | ENV ARTIFACT_NAME=app.jar 22 | 23 | WORKDIR /usr/src/app 24 | 25 | COPY --from=TEMP_BUILD_IMAGE /usr/src/app/build/libs/$ARTIFACT_NAME . 26 | 27 | ENTRYPOINT exec "java" "-jar" "-noverify" "${ARTIFACT_NAME}" 28 | -------------------------------------------------------------------------------- /subgraphs/inventory/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.springframework.boot") version "3.5.0" 3 | id("org.jetbrains.kotlin.jvm") version "1.9.25" 4 | id("org.jetbrains.kotlin.plugin.spring") version "1.9.25" 5 | } 6 | 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | java.sourceCompatibility = JavaVersion.VERSION_17 13 | 14 | dependencies { 15 | implementation(platform("org.springframework.boot:spring-boot-dependencies:3.5.0")) 16 | implementation("com.apollographql.federation:federation-graphql-java-support:4.5.0") 17 | implementation("org.jetbrains.kotlin:kotlin-reflect") 18 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 19 | implementation("org.springframework.boot:spring-boot-starter-graphql") 20 | implementation("org.springframework.boot:spring-boot-starter-web") 21 | } 22 | 23 | tasks.withType { 24 | kotlinOptions { 25 | freeCompilerArgs = listOf("-Xjsr305=strict") 26 | jvmTarget = "17" 27 | } 28 | } -------------------------------------------------------------------------------- /subgraphs/inventory/app/settings.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apollographql/supergraph-demo-fed2/d580b00641a2312d97c981bdb501c6e79879c54e/subgraphs/inventory/app/settings.gradle -------------------------------------------------------------------------------- /subgraphs/inventory/app/src/main/kotlin/inventory/Application.kt: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class Application 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /subgraphs/inventory/app/src/main/kotlin/inventory/GraphQLConfiguration.kt: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import com.apollographql.federation.graphqljava.Federation 4 | import com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation 5 | import graphql.TypeResolutionEnvironment 6 | import graphql.schema.DataFetchingEnvironment 7 | import graphql.schema.idl.RuntimeWiring 8 | import graphql.schema.idl.TypeDefinitionRegistry 9 | import inventory.model.Delivery 10 | import inventory.model.Product 11 | import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer 12 | import org.springframework.context.annotation.Bean 13 | import org.springframework.context.annotation.Configuration 14 | import org.springframework.graphql.execution.GraphQlSource.SchemaResourceBuilder 15 | import org.springframework.graphql.execution.RuntimeWiringConfigurer 16 | 17 | @Configuration 18 | class GraphQLConfiguration { 19 | 20 | private val allProducts = listOf( 21 | Product("converse-1", Delivery("6/25/2023", "6/24/2023")), 22 | Product("vans-1", Delivery("6/25/2023", "6/24/2023")), 23 | ) 24 | 25 | @Bean 26 | fun federatedTracingInstrumentation(): FederatedTracingInstrumentation { 27 | return FederatedTracingInstrumentation() 28 | } 29 | 30 | @Bean 31 | fun runtimeWiringConfigurer(): RuntimeWiringConfigurer = RuntimeWiringConfigurer { wiringBuilder -> 32 | wiringBuilder.type("ProductItf") { builder -> 33 | builder.typeResolver { environment -> 34 | environment.schema.getObjectType("Product") 35 | } 36 | } 37 | } 38 | 39 | @Bean 40 | fun federationTransform(): GraphQlSourceBuilderCustomizer { 41 | return GraphQlSourceBuilderCustomizer { builder: SchemaResourceBuilder -> 42 | builder.schemaFactory { registry: TypeDefinitionRegistry?, wiring: RuntimeWiring? -> 43 | Federation.transform(registry, wiring) 44 | .fetchEntities { env: DataFetchingEnvironment -> 45 | env.getArgument>>("representations").map { representation -> 46 | when(representation["__typename"]) { 47 | "Product" -> { 48 | val prod = allProducts.firstOrNull { it.id == representation["id"] } ?: error("Product not found: $representation") 49 | Thread.sleep(1000) 50 | prod 51 | } 52 | else -> error("Unknown type: $representation") 53 | } 54 | } 55 | } 56 | .resolveEntityType { env: TypeResolutionEnvironment -> 57 | when(env.getObject()) { 58 | is Product -> env.schema.getObjectType("Product") 59 | else -> null 60 | } 61 | } 62 | .build() 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /subgraphs/inventory/app/src/main/kotlin/inventory/WebConfiguration.kt: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import org.springframework.context.annotation.Bean 4 | import org.springframework.context.annotation.Configuration 5 | import org.springframework.graphql.server.webmvc.GraphQlHttpHandler 6 | import org.springframework.web.servlet.function.router 7 | 8 | @Configuration 9 | class WebConfiguration { 10 | 11 | @Bean 12 | fun rootGraphQLRoute(httpHandler: GraphQlHttpHandler) = router { 13 | POST("/") { request -> 14 | httpHandler.handleRequest(request) 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /subgraphs/inventory/app/src/main/kotlin/inventory/model/Product.kt: -------------------------------------------------------------------------------- 1 | package inventory.model 2 | 3 | class Product(val id: String, val delivery: Delivery) 4 | class Delivery(val estimatedDelivery: String, val fastestDelivery: String) -------------------------------------------------------------------------------- /subgraphs/inventory/app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=inventory 2 | spring.main.lazy-initialization=true 3 | server.port=4000 4 | spring.graphql.cors.allowed-origins=* 5 | -------------------------------------------------------------------------------- /subgraphs/inventory/app/src/main/resources/graphql/inventory.graphqls: -------------------------------------------------------------------------------- 1 | extend schema 2 | @link(url: "https://specs.apollo.dev/federation/v2.1", 3 | import: ["@key", "@shareable", "@external", "@requires", "@composeDirective"]) 4 | @link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@myDirective", { name: "@anotherDirective", as: "@hello" }]) 5 | @composeDirective(name: "@myDirective") 6 | @composeDirective(name: "@hello") 7 | 8 | directive @myDirective(a: String!) on FIELD_DEFINITION 9 | directive @hello on FIELD_DEFINITION 10 | 11 | type Product implements ProductItf @key(fields: "id") { 12 | id: ID! 13 | dimensions: ProductDimension @external 14 | delivery(zip: String): DeliveryEstimates @requires(fields: "dimensions { size weight }") 15 | } 16 | 17 | type ProductDimension @shareable { 18 | size: String @hello 19 | weight: Float 20 | } 21 | 22 | type DeliveryEstimates { 23 | estimatedDelivery: String 24 | fastestDelivery: String 25 | } 26 | 27 | interface ProductItf { 28 | id: ID! 29 | dimensions: ProductDimension 30 | delivery(zip: String): DeliveryEstimates 31 | } 32 | 33 | enum ShippingClass { 34 | STANDARD 35 | EXPRESS 36 | OVERNIGHT 37 | } 38 | -------------------------------------------------------------------------------- /subgraphs/pandas/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bookworm-slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json . 6 | 7 | RUN npm install 8 | 9 | COPY pandas.js . 10 | COPY pandas.graphql . 11 | 12 | CMD [ "node", "pandas.js" ] 13 | -------------------------------------------------------------------------------- /subgraphs/pandas/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subgraph-pandas", 3 | "version": "1.1.27", 4 | "description": "", 5 | "main": "pandas.js", 6 | "scripts": { 7 | "start": "node pandas.js" 8 | }, 9 | "dependencies": { 10 | "apollo-server": "3.13.0", 11 | "graphql": "16.11.0" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /subgraphs/pandas/pandas.graphql: -------------------------------------------------------------------------------- 1 | directive @tag(name: String!) repeatable on FIELD_DEFINITION 2 | 3 | type Query { 4 | allPandas: [Panda] 5 | panda(name: ID!): Panda 6 | } 7 | 8 | type Panda { 9 | name:ID! 10 | favoriteFood: String @tag(name: "nom-nom-nom") 11 | } 12 | -------------------------------------------------------------------------------- /subgraphs/pandas/pandas.js: -------------------------------------------------------------------------------- 1 | const { ApolloServer, gql } = require('apollo-server'); 2 | const { readFileSync } = require('fs'); 3 | 4 | const port = process.env.APOLLO_PORT || 4000; 5 | 6 | const pandas = [ 7 | { name: 'Basi', favoriteFood: "bamboo leaves" }, 8 | { name: 'Yun', favoriteFood: "apple" } 9 | ] 10 | 11 | const typeDefs = gql(readFileSync('./pandas.graphql', { encoding: 'utf-8' })); 12 | const resolvers = { 13 | Query: { 14 | allPandas: (_, args, context) => { 15 | return pandas; 16 | }, 17 | panda: (_, args, context) => { 18 | return pandas.find(p => p.id == args.id); 19 | } 20 | }, 21 | } 22 | const server = new ApolloServer({ typeDefs, resolvers }); 23 | server.listen( {port: port} ).then(({ url }) => { 24 | console.log(`🚀 Pandas subgraph ready at ${url}`); 25 | }).catch(err => {console.error(err)}); 26 | -------------------------------------------------------------------------------- /subgraphs/products/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bookworm-slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json . 6 | 7 | RUN npm install 8 | 9 | COPY products.js . 10 | COPY products.graphql . 11 | 12 | CMD [ "node", "products.js" ] 13 | -------------------------------------------------------------------------------- /subgraphs/products/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subgraph-products", 3 | "version": "1.1.18", 4 | "description": "", 5 | "main": "products.js", 6 | "scripts": { 7 | "start": "node products.js" 8 | }, 9 | "dependencies": { 10 | "@apollo/subgraph": "2.11.0", 11 | "apollo-server": "3.13.0", 12 | "supergraph-demo-opentelemetry": "0.2.4", 13 | "graphql": "16.11.0" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "MIT" 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/products/products.graphql: -------------------------------------------------------------------------------- 1 | extend schema 2 | @link(url: "https://specs.apollo.dev/federation/v2.1", 3 | import: ["@key", "@shareable", "@tag", "@inaccessible", "@composeDirective"]) 4 | @link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@myDirective", { name: "@anotherDirective", as: "@hello" }]) 5 | @composeDirective(name: "@myDirective") 6 | @composeDirective(name: "@hello") 7 | 8 | directive @myDirective(a: String!) on FIELD_DEFINITION 9 | directive @hello on FIELD_DEFINITION 10 | 11 | 12 | type Query { 13 | allProducts: [ProductItf] 14 | product(id: ID!): ProductItf 15 | } 16 | 17 | interface ProductItf implements SkuItf { 18 | id: ID! 19 | sku: String 20 | name: String 21 | package: String 22 | variation: ProductVariation 23 | dimensions: ProductDimension 24 | createdBy: User 25 | hidden: String @inaccessible 26 | oldField: String @deprecated(reason: "refactored out") 27 | } 28 | 29 | interface SkuItf { 30 | sku: String 31 | } 32 | 33 | type Product implements ProductItf & SkuItf @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }"){ 34 | id: ID! @tag(name: "hi-from-products") 35 | sku: String 36 | name: String @hello 37 | package: String 38 | variation: ProductVariation 39 | dimensions: ProductDimension 40 | createdBy: User 41 | hidden: String 42 | reviewsScore: Float! 43 | oldField: String 44 | } 45 | enum ShippingClass { 46 | STANDARD 47 | EXPRESS 48 | } 49 | type ProductVariation { 50 | id: ID! 51 | name: String 52 | } 53 | type ProductDimension @shareable { 54 | size: String 55 | weight: Float 56 | } 57 | type User @key(fields: "email") { 58 | email: ID! 59 | totalProductsCreated: Int @shareable 60 | } 61 | -------------------------------------------------------------------------------- /subgraphs/products/products.js: -------------------------------------------------------------------------------- 1 | // Open Telemetry (optional) 2 | const { ApolloOpenTelemetry } = require('supergraph-demo-opentelemetry'); 3 | 4 | if (process.env.APOLLO_OTEL_EXPORTER_TYPE) { 5 | new ApolloOpenTelemetry({ 6 | type: 'subgraph', 7 | name: 'products', 8 | exporter: { 9 | type: process.env.APOLLO_OTEL_EXPORTER_TYPE, // console, zipkin, collector 10 | host: process.env.APOLLO_OTEL_EXPORTER_HOST, 11 | port: process.env.APOLLO_OTEL_EXPORTER_PORT, 12 | } 13 | }).setupInstrumentation(); 14 | } 15 | 16 | const { ApolloServer, gql } = require('apollo-server'); 17 | const { buildSubgraphSchema, printSubgraphSchema } = require('@apollo/subgraph'); 18 | const { readFileSync } = require('fs'); 19 | const { printSchema } = require('graphql'); 20 | 21 | const port = process.env.APOLLO_PORT || 4000; 22 | 23 | // Data sources 24 | const products = [ 25 | { id: 'converse-1', sku: 'converse-1', package: 'converse', name: 'Converse Chuck Taylor', oldField: 'deprecated'}, 26 | { id: 'vans-1', sku: 'vans-1', package: 'vans', name: 'Vans Classic Sneaker', oldField: 'deprecated'}, 27 | ] 28 | 29 | const variationByProduct = [ 30 | { id: 'converse-1', variation: { id: 'converse-classic', name: 'Converse Chuck Taylor'}}, 31 | { id: 'vans-1', variation: { id: 'vans-classic', name: 'Vans Classic Sneaker'}}, 32 | ] 33 | 34 | const userByProduct = [ 35 | { id: 'converse-1', user: { email: 'info@converse.com', totalProductsCreated: 1099}}, 36 | { id: 'vans-1', user: { email: 'info@vans.com', totalProductsCreated: 1099}}, 37 | ] 38 | 39 | // GraphQL 40 | const typeDefs = gql(readFileSync('./products.graphql', { encoding: 'utf-8' })); 41 | const resolvers = { 42 | Query: { 43 | allProducts: (_, args, context) => { 44 | return products; 45 | }, 46 | product: (_, args, context) => { 47 | return products.find(p => p.id == args.id); 48 | } 49 | }, 50 | ProductItf: { 51 | __resolveType(obj, context, info){ 52 | return 'Product'; 53 | }, 54 | }, 55 | Product: { 56 | variation: (reference) => { 57 | return new Promise(r => setTimeout(() => { 58 | if (reference.id) { 59 | const variation = variationByProduct.find(p => p.id == reference.id).variation; 60 | r(variation); 61 | } 62 | r({ id: 'defaultVariation', name: 'default variation' }); 63 | }, 1000)); 64 | }, 65 | dimensions: () => { 66 | return { size: "1", weight: 1 } 67 | }, 68 | createdBy: (reference) => { 69 | if (reference.id) { 70 | return userByProduct.find(p => p.id == reference.id).user; 71 | } 72 | return null; 73 | }, 74 | reviewsScore: () => { 75 | return 4.5; 76 | }, 77 | __resolveReference: (reference) => { 78 | if (reference.id) return products.find(p => p.id == reference.id); 79 | else if (reference.sku && reference.package) return products.find(p => p.sku == reference.sku && p.package == reference.package); 80 | else return { id: 'rover', package: '@apollo/rover', ...reference }; 81 | } 82 | } 83 | } 84 | const schema = buildSubgraphSchema({ typeDefs, resolvers }); 85 | 86 | // console.log(`---------------------------------------`); 87 | // console.log(`🚀 subgraph-js::printSubgraphSchema 🚀`); 88 | // console.log(`---------------------------------------`); 89 | // console.log(printSubgraphSchema(schema)); 90 | // 91 | // console.log(`---------------------------------------`); 92 | // console.log(`🚀 graphql::printSchema 🚀`); 93 | // console.log(`---------------------------------------`); 94 | // console.log(printSchema(schema)); 95 | 96 | const server = new ApolloServer({ schema: schema }); 97 | server.listen( {port: port} ).then(({ url }) => { 98 | console.log(`🚀 Products subgraph ready at ${url}`); 99 | }).catch(err => {console.error(err)}); 100 | -------------------------------------------------------------------------------- /subgraphs/reviews/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | 3 | # install PDM 4 | RUN pip install -U pip setuptools wheel 5 | RUN pip install pdm 6 | 7 | # copy files 8 | COPY pyproject.toml pdm.lock /project/ 9 | COPY . /project 10 | 11 | WORKDIR /project 12 | RUN pdm install --prod --no-lock --no-editable 13 | 14 | STOPSIGNAL SIGINT 15 | 16 | CMD ["pdm", "run", "server"] 17 | -------------------------------------------------------------------------------- /subgraphs/reviews/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from starlette.applications import Starlette 4 | from strawberry.asgi import GraphQL 5 | 6 | from schema import schema 7 | 8 | graphql_app = GraphQL(schema, graphiql=False) 9 | 10 | app = Starlette() 11 | 12 | app.add_route("/", graphql_app) 13 | app.add_route("/graphql", graphql_app) 14 | 15 | 16 | if os.environ.get("APOLLO_OTEL_EXPORTER_TYPE"): 17 | from tracing import setup_tracing 18 | 19 | setup_tracing(schema, app) 20 | -------------------------------------------------------------------------------- /subgraphs/reviews/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = [ 3 | {name = "Patrick Arminio", email = "patrick.arminio@gmail.com"}, 4 | ] 5 | dependencies = [ 6 | "strawberry-graphql>=0.130.3", 7 | "uvicorn>=0.18.2", 8 | "starlette>=0.20.4", 9 | "opentelemetry-api>=1.11.1", 10 | "opentelemetry-sdk>=1.11.1", 11 | "opentelemetry-exporter-zipkin>=1.11.1", 12 | "opentelemetry-instrumentation-starlette>=0.30b1", 13 | "opentelemetry-exporter-otlp>=1.11.1", 14 | "protobuf==3.20.3", 15 | "grpcio==1.72.1", 16 | ] 17 | description = "" 18 | license = {text = "MIT"} 19 | name = "" 20 | requires-python = ">=3.10" 21 | version = "" 22 | [project.optional-dependencies] 23 | 24 | [build-system] 25 | build-backend = "pdm.pep517.api" 26 | requires = ["pdm-pep517>=1.0.0"] 27 | 28 | [tool] 29 | [tool.pdm] 30 | [tool.pdm.dev-dependencies] 31 | dev = [ 32 | "black>=22.6.0", 33 | "flake8>=5.0.4", 34 | "mypy>=0.971", 35 | ] 36 | 37 | [tool.pdm.scripts] 38 | export-schema = {shell = "strawberry export-schema schema > reviews.graphql"} 39 | server = "uvicorn app:app --host 0.0.0.0 --port 4000" 40 | -------------------------------------------------------------------------------- /subgraphs/reviews/reviews.graphql: -------------------------------------------------------------------------------- 1 | schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@override", "@shareable"]) { 2 | query: Query 3 | } 4 | 5 | type Product implements ProductItf @key(fields: "id") { 6 | id: ID! 7 | reviewsCount: Int! 8 | reviewsScore: Float! @shareable @override(from: "products") 9 | reviews: [Review!]! 10 | } 11 | 12 | interface ProductItf { 13 | id: ID! 14 | reviewsCount: Int! 15 | reviewsScore: Float! 16 | reviews: [Review!]! 17 | } 18 | 19 | type Query { 20 | review(id: Int!): Review 21 | } 22 | 23 | type Review { 24 | id: Int! 25 | body: String! 26 | } 27 | -------------------------------------------------------------------------------- /subgraphs/reviews/schema.py: -------------------------------------------------------------------------------- 1 | import strawberry 2 | 3 | 4 | @strawberry.type 5 | class Review: 6 | id: int 7 | body: str 8 | product_id: strawberry.Private[str] 9 | 10 | 11 | reviews = [ 12 | Review( 13 | id=1, body=f"A review for Apollo Federation", product_id="apollo-federation" 14 | ), 15 | Review(id=2, body=f"A review for Apollo Studio", product_id="apollo-studio"), 16 | ] 17 | 18 | 19 | def get_reviews(root: "Product") -> list[Review]: 20 | return list(filter(lambda r: r.product_id == root.id, reviews)) 21 | 22 | 23 | @strawberry.federation.interface() 24 | class ProductItf: 25 | id: strawberry.ID 26 | reviews_count: int 27 | reviews_score: float 28 | reviews: list[Review] 29 | 30 | 31 | @strawberry.federation.type(keys=["id"]) 32 | class Product(ProductItf): 33 | id: strawberry.ID 34 | reviews_count: int 35 | reviews_score: float = strawberry.federation.field(override="products") 36 | reviews: list[Review] = strawberry.field(resolver=get_reviews) 37 | 38 | @classmethod 39 | def resolve_reference(cls, id: strawberry.ID): 40 | return Product(id=id, reviews_count=3, reviews_score=4.6) 41 | 42 | 43 | @strawberry.type 44 | class Query: 45 | @strawberry.field 46 | def review(self, id: int) -> Review | None: 47 | return next((r for r in reviews if r.id == id), None) 48 | 49 | 50 | schema = strawberry.federation.Schema( 51 | query=Query, 52 | types=[Product, Review], 53 | enable_federation_2=True, 54 | extensions=[], 55 | ) 56 | -------------------------------------------------------------------------------- /subgraphs/reviews/tracing.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from opentelemetry import trace 4 | from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter 5 | from opentelemetry.exporter.zipkin.json import ZipkinExporter 6 | from opentelemetry.instrumentation.starlette import StarletteInstrumentor 7 | from opentelemetry.sdk.resources import SERVICE_NAME, Resource 8 | from opentelemetry.sdk.trace import TracerProvider 9 | from opentelemetry.sdk.trace.export import SimpleSpanProcessor, BatchSpanProcessor 10 | from strawberry.extensions.tracing import OpenTelemetryExtension 11 | 12 | 13 | def setup_tracing(schema, app): 14 | resource = Resource(attributes={SERVICE_NAME: "reviews"}) 15 | 16 | provider = TracerProvider(resource=resource) 17 | 18 | tracer_type = os.environ.get("APOLLO_OTEL_EXPORTER_TYPE") 19 | 20 | assert tracer_type in {"zipkin", "collector"} 21 | 22 | if tracer_type == "zipkin": 23 | host = os.environ.get("APOLLO_OTEL_EXPORTER_HOST", "localhost") 24 | port = os.environ.get("APOLLO_OTEL_EXPORTER_PORT", 9411) 25 | 26 | endpoint = f"http://{host}:{port}/api/v2/spans" 27 | 28 | zipkin_exporter = ZipkinExporter(endpoint=endpoint) 29 | 30 | span_processor = SimpleSpanProcessor(zipkin_exporter) 31 | provider.add_span_processor(span_processor) 32 | 33 | print("Tracing enabled with Zipkin exporter") 34 | 35 | if tracer_type == "collector": 36 | host = os.environ.get("APOLLO_OTEL_EXPORTER_HOST", "localhost") 37 | port = os.environ.get("APOLLO_OTEL_EXPORTER_PORT", 9411) 38 | 39 | endpoint = f"http://{host}:{port}/v1/traces" 40 | 41 | otlp_exporter = OTLPSpanExporter(endpoint=endpoint) 42 | 43 | span_processor = BatchSpanProcessor(otlp_exporter) 44 | 45 | provider.add_span_processor(span_processor) 46 | 47 | print("Tracing enabled with Collector exporter") 48 | 49 | trace.set_tracer_provider(provider) 50 | 51 | schema.extensions.append(OpenTelemetryExtension) 52 | StarletteInstrumentor().instrument_app(app) 53 | -------------------------------------------------------------------------------- /subgraphs/users/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bookworm-slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json . 6 | 7 | RUN npm install 8 | 9 | COPY users.js . 10 | COPY users.graphql . 11 | 12 | CMD [ "node", "users.js" ] 13 | -------------------------------------------------------------------------------- /subgraphs/users/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subgraph-users", 3 | "version": "1.1.21", 4 | "description": "", 5 | "main": "users.js", 6 | "scripts": { 7 | "start": "node users.js" 8 | }, 9 | "dependencies": { 10 | "@apollo/subgraph": "2.11.0", 11 | "apollo-server": "3.13.0", 12 | "supergraph-demo-opentelemetry": "0.2.4", 13 | "graphql": "16.11.0" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "MIT" 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/users/users.graphql: -------------------------------------------------------------------------------- 1 | directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT 2 | 3 | type User @key(fields:"email") { 4 | email:ID! @tag(name: "test-from-users") 5 | name: String 6 | totalProductsCreated: Int 7 | } 8 | -------------------------------------------------------------------------------- /subgraphs/users/users.js: -------------------------------------------------------------------------------- 1 | // Open Telemetry (optional) 2 | const { ApolloOpenTelemetry } = require('supergraph-demo-opentelemetry'); 3 | 4 | if (process.env.APOLLO_OTEL_EXPORTER_TYPE) { 5 | new ApolloOpenTelemetry({ 6 | type: 'subgraph', 7 | name: 'users', 8 | exporter: { 9 | type: process.env.APOLLO_OTEL_EXPORTER_TYPE, // console, zipkin, collector 10 | host: process.env.APOLLO_OTEL_EXPORTER_HOST, 11 | port: process.env.APOLLO_OTEL_EXPORTER_PORT, 12 | } 13 | }).setupInstrumentation(); 14 | } 15 | 16 | const { ApolloServer, gql } = require('apollo-server'); 17 | const { buildSubgraphSchema } = require('@apollo/subgraph'); 18 | const { readFileSync } = require('fs'); 19 | 20 | const port = process.env.APOLLO_PORT || 4000; 21 | 22 | const users = [ 23 | { email: 'info@converse.com', name: 'Converse', totalProductsCreated: 1100 }, 24 | { email: 'info@vans.com', name: 'Van Doren', totalProductsCreated: 1100 } 25 | ] 26 | 27 | const typeDefs = gql(readFileSync('./users.graphql', { encoding: 'utf-8' })); 28 | const resolvers = { 29 | User: { 30 | __resolveReference: (reference) => { 31 | return users.find(u => u.email == reference.email); 32 | } 33 | } 34 | } 35 | const server = new ApolloServer({ schema: buildSubgraphSchema({ typeDefs, resolvers }) }); 36 | server.listen( {port: port} ).then(({ url }) => { 37 | console.log(`🚀 Users subgraph ready at ${url}`); 38 | }).catch(err => {console.error(err)}); 39 | -------------------------------------------------------------------------------- /supergraph/README.md: -------------------------------------------------------------------------------- 1 | # Using Apollo Router 2 | 3 | ## Stock router binaries and docker images 4 | 5 | [Stock Router binaries are available to download](https://www.apollographql.com/docs/router/quickstart) for Linux, Mac, and Windows. We also ship [pre-built docker images](https://www.apollographql.com/docs/router/containerization/overview) and a [Helm chart](https://www.apollographql.com/docs/router/containerization/kubernetes) for [Kubernetes deployments](https://www.apollographql.com/docs/router/containerization/kubernetes). Kubernetes configuration examples are also provided for use of `kustomize` and other Kubernetes tooling. 6 | 7 | ## Configuration & Customization 8 | 9 | As the Router has been rolled out into more environments we’ve learned about the right integration points and customizations to make the Router work well: 10 | 11 | ### YAML configuration - no code required 12 | 13 | Apollo ships a [standalone Router binary](https://www.apollographql.com/docs/router/quickstart) that can be configured using a [YAML config file](https://www.apollographql.com/docs/router/configuration/overview#yaml-config-file) with a new stable v1 configuration schema for things like header forwarding and CORS configuration. Many new features are available like [traffic shaping](https://www.apollographql.com/docs/router/configuration/traffic-shaping/) with support for rate limiting, query deduplication, configurable timeouts and compression options. Router deployments can often be done with the stock Router binary and a minimal YAML config file. 14 | 15 | See [router.yaml](./router.yaml) 16 | 17 | ### Lightweight Rhai scripting 18 | 19 | New official support for [Rhai scripting](https://www.apollographql.com/docs/router/customizations/rhai) with a [stable v1 API](https://www.apollographql.com/docs/router/customizations/rhai-api/) offers a safe and sandboxed way to customize the Router’s request flow. Rhai is ideal for common scripting tasks like manipulating strings, processing headers, and mutating request context. Checkout the growing cookbook of [example scripts](https://github.com/apollographql/router/tree/main/examples) that can be used with the stock Router binary as a simple way of programmatically customizing the Router for your environment. 20 | 21 | See [Rhai scripting example](./rhai-scripting/) 22 | 23 | ### Native extensions 24 | 25 | Many Router features are built as native extensions that can be used via standard YAML config and Rhai scripting. Native extensions are a good choice for advanced use cases when Rhai scripting is not enough. With v1.0 we have stabilized all key extension points in the [native extension API](https://www.apollographql.com/docs/router/customizations/native) and enabled more powerful schema-driven extensions to be built using Apollo’s new [Rust tooling for GraphQL](https://www.apollographql.com/blog/announcement/tooling/apollo-rs-graphql-tools-in-rust/). 26 | 27 | See [Native extension example](./rust-plugin/) and build locally with Rust and `cargo` for the best experience. 28 | 29 | ### Adding more built-in Router functionality 30 | 31 | Our goal over time is to identify common customizations and elevate them into standard Router features, so let us know if you have a Router customization or idea that others in the community could benefit from! 32 | 33 | ## Learn more 34 | 35 | - [Docker and the router](https://www.apollographql.com/docs/router/containerization/docker) 36 | - [Rhai scripts](https://www.apollographql.com/docs/router/customizations/rhai) 37 | - [Native Rust plugins](https://www.apollographql.com/docs/router/customizations/native) -------------------------------------------------------------------------------- /supergraph/gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bookworm-slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY ./package.json . 6 | 7 | RUN npm install 8 | 9 | COPY ./gateway.js . 10 | 11 | CMD [ "node", "gateway.js" ] 12 | -------------------------------------------------------------------------------- /supergraph/gateway/gateway.js: -------------------------------------------------------------------------------- 1 | // Open Telemetry (optional) 2 | const { ApolloOpenTelemetry } = require('supergraph-demo-opentelemetry'); 3 | 4 | if (process.env.APOLLO_OTEL_EXPORTER_TYPE) { 5 | new ApolloOpenTelemetry({ 6 | type: 'router', 7 | name: 'gateway', 8 | exporter: { 9 | type: process.env.APOLLO_OTEL_EXPORTER_TYPE, // console, zipkin, collector 10 | host: process.env.APOLLO_OTEL_EXPORTER_HOST, 11 | port: process.env.APOLLO_OTEL_EXPORTER_PORT, 12 | } 13 | }).setupInstrumentation(); 14 | } 15 | 16 | // Main 17 | const { ApolloServer } = require('@apollo/server'); 18 | const { ApolloServerPluginUsageReporting } = require('@apollo/server/plugin/usageReporting'); 19 | const { startStandaloneServer } = require('@apollo/server/standalone') 20 | const { ApolloGateway } = require('@apollo/gateway'); 21 | const { readFileSync } = require('fs'); 22 | 23 | const port = process.env.APOLLO_PORT || 4000; 24 | const embeddedSchema = process.env.APOLLO_SCHEMA_CONFIG_EMBEDDED == "true" ? true : false; 25 | 26 | const config = {}; 27 | const plugins = []; 28 | 29 | if (embeddedSchema){ 30 | const supergraph = "/etc/config/supergraph.graphql" 31 | config['supergraphSdl'] = readFileSync(supergraph).toString(); 32 | console.log('Starting Apollo Gateway in local mode ...'); 33 | console.log(`Using local: ${supergraph}`) 34 | } else { 35 | console.log('Starting Apollo Gateway in managed mode ...'); 36 | plugins.push( 37 | ApolloServerPluginUsageReporting({ 38 | fieldLevelInstrumentation: 0.01 39 | })); 40 | } 41 | 42 | const gateway = new ApolloGateway(config); 43 | 44 | async function startApolloServer() { 45 | const server = new ApolloServer({ gateway, 46 | debug: true, 47 | // Subscriptions are unsupported but planned for a future Gateway version. 48 | subscriptions: false, 49 | plugins}); 50 | const { url } = await startStandaloneServer(server, { 51 | context: async ({ req }) => ({ token: req.headers.token }), 52 | listen: { port: 4000 }, 53 | }); 54 | 55 | console.log(`🚀 Server ready at ${url}`); 56 | } 57 | 58 | startApolloServer(); 59 | -------------------------------------------------------------------------------- /supergraph/gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supergraph-router", 3 | "version": "1.1.18", 4 | "description": "", 5 | "main": "gateway.js", 6 | "scripts": { 7 | "start": "node gateway.js" 8 | }, 9 | "dependencies": { 10 | "@apollo/server": "4.12.2", 11 | "@apollo/gateway": "2.11.0", 12 | "supergraph-demo-opentelemetry": "0.2.4", 13 | "graphql": "16.11.0" 14 | }, 15 | "keywords": [], 16 | "author": "" 17 | } 18 | -------------------------------------------------------------------------------- /supergraph/router-no-code/README.md: -------------------------------------------------------------------------------- 1 | # Apollo Router - no code required 2 | 3 | *No code required* - many users can migrate to the Router with the stock Router binary and a simple YAML config file 4 | 5 | *Easy to deploy/manage* - single Router binary you can run on your laptop or a pre-built docker image you can run in k8s. 6 | 7 | * Stock Router binaries are available for Linux, Mac, and Windows. 8 | * We also ship [pre-built docker images](https://www.apollographql.com/docs/router/containerization/overview) and an [updated Helm chart](https://www.apollographql.com/docs/router/containerization/kubernetes) 9 | * [Kubernetes examples](https://www.apollographql.com/docs/router/containerization/kubernetes) examples are also provided for use of `kustomize` and other Kubernetes tooling. 10 | 11 | *Run with a single command* - just use the stock Router binary 12 | 13 | *Extensive [built-in YAML configuration options](https://www.apollographql.com/docs/router/configuration/overview#yaml-config-file)* 14 | 15 | ## Download Router 16 | 17 | ``` 18 | curl -sSL https://router.apollo.dev/download/nix/latest | sh 19 | ``` 20 | 21 | ## Router with supergraph schema from Apollo Studio 22 | 23 | ``` 24 | APOLLO_KEY= \ 25 | APOLLO_GRAPH_REF=@ \ 26 | router --dev --config ./router.yaml 27 | ``` 28 | 29 | see [router.yaml](../router.yaml) 30 | 31 | ## Router with local supergraph schema 32 | 33 | ``` 34 | router --dev \ 35 | --config ./router.yaml \ 36 | --supergraph ./supergraph.graphql 37 | ``` 38 | 39 | See the [Apollo Router quickstart](https://www.apollographql.com/docs/router/quickstart) for more info! -------------------------------------------------------------------------------- /supergraph/router-rhai-script/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 debian:bookworm-slim 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | ca-certificates \ 5 | curl 6 | 7 | WORKDIR /dist 8 | 9 | COPY ./test.rhai supergraph/router-rhai-script/test.rhai 10 | 11 | RUN curl -ssL https://router.apollo.dev/download/nix/latest | sh 12 | 13 | # for faster docker shutdown 14 | STOPSIGNAL SIGINT 15 | 16 | # set the startup command to run the stock router binary 17 | ENTRYPOINT ["./router"] 18 | 19 | -------------------------------------------------------------------------------- /supergraph/router-rhai-script/router.yaml: -------------------------------------------------------------------------------- 1 | cors: 2 | allow_any_origin: true 3 | supergraph: 4 | listen: 0.0.0.0:4000 5 | introspection: true 6 | telemetry: 7 | apollo: 8 | # The percentage of requests will include HTTP request and response headers in traces sent to Apollo Studio. 9 | # This is expensive and should be left at a low value. 10 | # This cannot be higher than tracing->trace_config->sampler 11 | field_level_instrumentation_sampler: 1 # (default) 12 | tracing: 13 | trace_config: 14 | sampler: 1 # The percentage of requests that will generate traces (a rate or `always_on` or `always_off`) 15 | service_name: "router" 16 | service_namespace: "apollo" 17 | otlp: 18 | endpoint: http://${env.APOLLO_OTEL_EXPORTER_HOST:-localhost}:4317 19 | protocol: grpc 20 | timeout: 42s 21 | rhai: 22 | # Your rhai scripts are loaded from a relative directory 23 | # (relative to the directory in which your router is executing) 24 | # You can change this location by specifying scripts as below: 25 | scripts: "./supergraph/router-rhai-script" 26 | 27 | # You must have a "main" rhai file. By default this is "main.rhai" 28 | # You can change this name by specifying main: 29 | main: "test.rhai" 30 | 31 | include_subgraph_errors: 32 | all: true # Propagate errors from all subraphs 33 | -------------------------------------------------------------------------------- /supergraph/router-rhai-script/test.rhai: -------------------------------------------------------------------------------- 1 | // At the supergraph_service stage, register callbacks for processing requests 2 | fn supergraph_service(service) { 3 | const request_callback = Fn("process_request"); // This is standard Rhai functionality for creating a function pointer 4 | service.map_request(request_callback); // Register the callback 5 | } 6 | 7 | // Generate a log for each request 8 | fn process_request(request) { 9 | let start = apollo_start.elapsed; 10 | // ... Do some processing 11 | let duration = apollo_start.elapsed - start; 12 | log_info(`custom processing took: ${duration}`); 13 | } -------------------------------------------------------------------------------- /supergraph/router-rust-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | acme_router -------------------------------------------------------------------------------- /supergraph/router-rust-plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "acme_router" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.82" 8 | apollo-router = "1.45.0" 9 | async-trait = "0.1.80" 10 | futures = "0.3.30" 11 | http = "0.2.12" 12 | schemars = "0.9.0" 13 | serde = "1.0.200" 14 | serde_json = "1.0.116" 15 | tokio = "1.37.0" 16 | tower = { version = "0.5.0", features = ["full"] } 17 | tracing = "0.1.40" 18 | -------------------------------------------------------------------------------- /supergraph/router-rust-plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 rust:1.87 as build 2 | 3 | ENV RUST_BACKTRACE=full 4 | 5 | # create a new empty shell project 6 | RUN USER=root cargo new --bin acme_router 7 | 8 | WORKDIR /acme_router 9 | 10 | # Update our build image and install required packages 11 | RUN apt-get update -y \ 12 | && apt-get install -y \ 13 | protobuf-compiler \ 14 | cmake 15 | 16 | RUN rustup component add rustfmt 17 | 18 | # copy over your manifests 19 | COPY ./Cargo.toml ./Cargo.toml 20 | 21 | # this build step will cache your dependencies 22 | RUN cargo build --release 23 | RUN rm src/*.rs 24 | 25 | # copy your source tree 26 | COPY ./src ./src 27 | 28 | # build for release 29 | RUN rm ./target/release/deps/acme_router* 30 | RUN cargo build --release 31 | 32 | RUN mkdir -p /dist/config && mkdir -p /dist/schema 33 | 34 | # our final image uses distroless, which is more secure by default 35 | #FROM --platform=linux/amd64 gcr.io/distroless/cc-debian11 36 | 37 | # if you want bin/sh you'll want a distro that includes it instead 38 | FROM --platform=linux/amd64 debian:bookworm-slim 39 | 40 | RUN apt-get update -y \ 41 | && apt-get install -y \ 42 | ca-certificates \ 43 | curl 44 | 45 | # copy the build artifact from the build stage 46 | COPY --from=build /dist /dist 47 | COPY --from=build --chown=root:root /acme_router/target/release/acme_router /dist 48 | COPY --from=build --chown=root:root /acme_router/Cargo.lock /dist 49 | 50 | WORKDIR /dist 51 | 52 | # for faster docker shutdown 53 | STOPSIGNAL SIGINT 54 | 55 | # set the startup command to run your binary 56 | # note: if you want sh you can override the entrypoint using docker run -it --entrypoint=sh my-router-image 57 | ENTRYPOINT ["./acme_router"] 58 | -------------------------------------------------------------------------------- /supergraph/router-rust-plugin/router.yaml: -------------------------------------------------------------------------------- 1 | cors: 2 | allow_any_origin: true 3 | supergraph: 4 | listen: 0.0.0.0:4000 5 | introspection: true 6 | telemetry: 7 | apollo: 8 | # The percentage of requests will include HTTP request and response headers in traces sent to Apollo Studio. 9 | # This is expensive and should be left at a low value. 10 | # This cannot be higher than tracing->trace_config->sampler 11 | field_level_instrumentation_sampler: 1 # (default) 12 | tracing: 13 | trace_config: 14 | sampler: 1 # The percentage of requests that will generate traces (a rate or `always_on` or `always_off`) 15 | service_name: "router" 16 | service_namespace: "apollo" 17 | otlp: 18 | endpoint: http://${env.APOLLO_OTEL_EXPORTER_HOST:-localhost}:4317 19 | protocol: grpc 20 | timeout: 42s 21 | include_subgraph_errors: 22 | all: true # Propagate errors from all subgraphs 23 | plugins: 24 | example.hello_world: 25 | name: "Bob" 26 | -------------------------------------------------------------------------------- /supergraph/router-rust-plugin/src/hello_world.rs: -------------------------------------------------------------------------------- 1 | use apollo_router::plugin::Plugin; 2 | use apollo_router::plugin::PluginInit; 3 | use apollo_router::register_plugin; 4 | use apollo_router::services::execution; 5 | use apollo_router::services::subgraph; 6 | use apollo_router::services::supergraph; 7 | use schemars::JsonSchema; 8 | use serde::Deserialize; 9 | use tower::BoxError; 10 | use tower::ServiceBuilder; 11 | use tower::ServiceExt; 12 | 13 | #[derive(Debug)] 14 | struct HelloWorld { 15 | #[allow(dead_code)] 16 | configuration: Conf, 17 | } 18 | 19 | #[derive(Debug, Default, Deserialize, JsonSchema)] 20 | struct Conf { 21 | // Put your plugin configuration here. It will automatically be deserialized from JSON. 22 | name: String, // The name of the entity you'd like to say hello to 23 | } 24 | 25 | // This is a bare bones plugin that can be duplicated when creating your own. 26 | #[async_trait::async_trait] 27 | impl Plugin for HelloWorld { 28 | type Config = Conf; 29 | 30 | async fn new(init: PluginInit) -> Result { 31 | Ok(HelloWorld { 32 | configuration: init.config, 33 | }) 34 | } 35 | 36 | fn supergraph_service(&self, service: supergraph::BoxService) -> supergraph::BoxService { 37 | // Say hello when our service is added to the router_service 38 | // stage of the router plugin pipeline. 39 | #[cfg(test)] 40 | println!("Hello {}", self.configuration.name); 41 | #[cfg(not(test))] 42 | tracing::info!("Hello {}", self.configuration.name); 43 | // Always use service builder to compose your plugins. 44 | // It provides off the shelf building blocks for your plugin. 45 | ServiceBuilder::new() 46 | // .map_request() 47 | // .map_response() 48 | // .rate_limit() 49 | // .checkpoint() 50 | // .timeout() 51 | .service(service) 52 | .boxed() 53 | } 54 | 55 | fn execution_service(&self, service: execution::BoxService) -> execution::BoxService { 56 | //This is the default implementation and does not modify the default service. 57 | // The trait also has this implementation, and we just provide it here for illustration. 58 | service 59 | } 60 | 61 | // Called for each subgraph 62 | fn subgraph_service(&self, _name: &str, service: subgraph::BoxService) -> subgraph::BoxService { 63 | // Always use service builder to compose your plugins. 64 | // It provides off the shelf building blocks for your plugin. 65 | ServiceBuilder::new() 66 | // .map_request() 67 | // .map_response() 68 | // .rate_limit() 69 | // .checkpoint() 70 | // .timeout() 71 | .service(service) 72 | .boxed() 73 | } 74 | } 75 | 76 | // This macro allows us to use it in our plugin registry! 77 | // register_plugin takes a group name, and a plugin name. 78 | // 79 | // In order to keep the plugin names consistent, 80 | // we use using the `Reverse domain name notation` 81 | register_plugin!("example", "hello_world", HelloWorld); 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | // If we run this test as follows: cargo test -- --nocapture 86 | // we will see the message "Hello Bob" printed to standard out 87 | #[tokio::test] 88 | async fn display_message() { 89 | let config = serde_json::json!({ 90 | "plugins": { 91 | "example.hello_world": { 92 | "name": "Bob" 93 | } 94 | } 95 | }); 96 | // Build a test harness. Usually we'd use this and send requests to 97 | // it, but in this case it's enough to build the harness to see our 98 | // output when our service registers. 99 | let _test_harness = apollo_router::TestHarness::builder() 100 | .configuration_json(config) 101 | .unwrap() 102 | .build() 103 | .await 104 | .unwrap(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /supergraph/router-rust-plugin/src/main.rs: -------------------------------------------------------------------------------- 1 | //! curl -v \ 2 | //! --header 'content-type: application/json' \ 3 | //! --url 'http://127.0.0.1:4000' \ 4 | //! --data '{"query":"query { topProducts { reviews { author { name } } name } }"}' 5 | //! [...] 6 | //! {"data":{"topProducts":[{"reviews":[{"author":{"name":"Ada Lovelace"}},{"author":{"name":"Alan Turing"}}],"name":"Table"},{"reviews":[{"author":{"name":"Ada Lovelace"}}],"name":"Couch"},{"reviews":[{"author":{"name":"Alan Turing"}}],"name":"Chair"}]}} 7 | use anyhow::Result; 8 | 9 | // adding the module to your main.rs file 10 | // will automatically register it to the router plugin registry. 11 | // 12 | // you can use the plugin by adding it to `router.yml` 13 | mod hello_world; 14 | 15 | // `cargo run -- -s ../graphql/supergraph.graphql -c ./router.yaml` 16 | fn main() -> Result<()> { 17 | apollo_router::main() 18 | } 19 | -------------------------------------------------------------------------------- /supergraph/router.yaml: -------------------------------------------------------------------------------- 1 | # see https://www.apollographql.com/docs/router/configuration/overview#yaml-config-file 2 | 3 | supergraph: 4 | listen: 0.0.0.0:4000 5 | cors: 6 | allow_any_origin: true 7 | telemetry: 8 | apollo: 9 | # The percentage of requests will include HTTP request and response headers in traces sent to Apollo Studio. 10 | # This is expensive and should be left at a low value. 11 | # This cannot be higher than tracing->trace_config->sampler 12 | field_level_instrumentation_sampler: 1 # (default) 13 | tracing: 14 | trace_config: 15 | sampler: 1 # The percentage of requests that will generate traces (a rate or `always_on` or `always_off`) 16 | service_name: "router" 17 | service_namespace: "apollo" 18 | otlp: 19 | endpoint: http://${env.APOLLO_OTEL_EXPORTER_HOST:-localhost}:4317 20 | protocol: grpc 21 | batch_processor: 22 | max_export_timeout: 42s 23 | 24 | # -------------------- 25 | # note: `router --dev` has these default settings and enables the --hot-reload flag 26 | # -------------------- 27 | # supergraph: 28 | # introspection: true 29 | # sandbox: 30 | # enabled: true 31 | # homepage: 32 | # enabled: false 33 | # include_subgraph_errors: 34 | # all: true # Propagate errors from all subgraphs 35 | # plugins: 36 | # experimental.expose_query_plan: true 37 | -------------------------------------------------------------------------------- /supergraph/schema/docker.graphql: -------------------------------------------------------------------------------- 1 | schema 2 | @link(url: "https://specs.apollo.dev/link/v1.0") 3 | @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) 4 | @link(url: "https://specs.apollo.dev/tag/v0.3") 5 | @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) 6 | @link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@myDirective", {name: "@anotherDirective", as: "@hello"}]) 7 | { 8 | query: Query 9 | } 10 | 11 | directive @hello on FIELD_DEFINITION 12 | 13 | directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION 14 | 15 | directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE 16 | 17 | directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION 18 | 19 | directive @join__graph(name: String!, url: String!) on ENUM_VALUE 20 | 21 | directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE 22 | 23 | directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR 24 | 25 | directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION 26 | 27 | directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA 28 | 29 | directive @myDirective(a: String!) on FIELD_DEFINITION 30 | 31 | directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA 32 | 33 | type DeliveryEstimates 34 | @join__type(graph: INVENTORY) 35 | { 36 | estimatedDelivery: String 37 | fastestDelivery: String 38 | } 39 | 40 | scalar join__FieldSet 41 | 42 | enum join__Graph { 43 | INVENTORY @join__graph(name: "inventory", url: "http://inventory:4000/graphql") 44 | PANDAS @join__graph(name: "pandas", url: "http://pandas:4000/graphql") 45 | PRODUCTS @join__graph(name: "products", url: "http://products:4000/graphql") 46 | REVIEWS @join__graph(name: "reviews", url: "http://reviews:4000/graphql") 47 | USERS @join__graph(name: "users", url: "http://users:4000/graphql") 48 | } 49 | 50 | scalar link__Import 51 | 52 | enum link__Purpose { 53 | """ 54 | `SECURITY` features provide metadata necessary to securely resolve fields. 55 | """ 56 | SECURITY 57 | 58 | """ 59 | `EXECUTION` features provide metadata necessary for operation execution. 60 | """ 61 | EXECUTION 62 | } 63 | 64 | type Panda 65 | @join__type(graph: PANDAS) 66 | { 67 | name: ID! 68 | favoriteFood: String @tag(name: "nom-nom-nom") 69 | } 70 | 71 | type Product implements ProductItf & SkuItf 72 | @join__implements(graph: INVENTORY, interface: "ProductItf") 73 | @join__implements(graph: PRODUCTS, interface: "ProductItf") 74 | @join__implements(graph: PRODUCTS, interface: "SkuItf") 75 | @join__implements(graph: REVIEWS, interface: "ProductItf") 76 | @join__type(graph: INVENTORY, key: "id") 77 | @join__type(graph: PRODUCTS, key: "id") 78 | @join__type(graph: PRODUCTS, key: "sku package") 79 | @join__type(graph: PRODUCTS, key: "sku variation { id }") 80 | @join__type(graph: REVIEWS, key: "id") 81 | { 82 | id: ID! @tag(name: "hi-from-products") 83 | dimensions: ProductDimension @join__field(graph: INVENTORY, external: true) @join__field(graph: PRODUCTS) 84 | delivery(zip: String): DeliveryEstimates @join__field(graph: INVENTORY, requires: "dimensions { size weight }") 85 | sku: String @join__field(graph: PRODUCTS) 86 | name: String @join__field(graph: PRODUCTS) @hello 87 | package: String @join__field(graph: PRODUCTS) 88 | variation: ProductVariation @join__field(graph: PRODUCTS) 89 | createdBy: User @join__field(graph: PRODUCTS) 90 | hidden: String @join__field(graph: PRODUCTS) 91 | reviewsScore: Float! @join__field(graph: REVIEWS, override: "products") 92 | oldField: String @join__field(graph: PRODUCTS) 93 | reviewsCount: Int! @join__field(graph: REVIEWS) 94 | reviews: [Review!]! @join__field(graph: REVIEWS) 95 | } 96 | 97 | type ProductDimension 98 | @join__type(graph: INVENTORY) 99 | @join__type(graph: PRODUCTS) 100 | { 101 | size: String @hello 102 | weight: Float 103 | } 104 | 105 | interface ProductItf implements SkuItf 106 | @join__implements(graph: PRODUCTS, interface: "SkuItf") 107 | @join__type(graph: INVENTORY) 108 | @join__type(graph: PRODUCTS) 109 | @join__type(graph: REVIEWS) 110 | { 111 | id: ID! 112 | dimensions: ProductDimension @join__field(graph: INVENTORY) @join__field(graph: PRODUCTS) 113 | delivery(zip: String): DeliveryEstimates @join__field(graph: INVENTORY) 114 | sku: String @join__field(graph: PRODUCTS) 115 | name: String @join__field(graph: PRODUCTS) 116 | package: String @join__field(graph: PRODUCTS) 117 | variation: ProductVariation @join__field(graph: PRODUCTS) 118 | createdBy: User @join__field(graph: PRODUCTS) 119 | hidden: String @inaccessible @join__field(graph: PRODUCTS) 120 | oldField: String @join__field(graph: PRODUCTS) @deprecated(reason: "refactored out") 121 | reviewsCount: Int! @join__field(graph: REVIEWS) 122 | reviewsScore: Float! @join__field(graph: REVIEWS) 123 | reviews: [Review!]! @join__field(graph: REVIEWS) 124 | } 125 | 126 | type ProductVariation 127 | @join__type(graph: PRODUCTS) 128 | { 129 | id: ID! 130 | name: String 131 | } 132 | 133 | type Query 134 | @join__type(graph: INVENTORY) 135 | @join__type(graph: PANDAS) 136 | @join__type(graph: PRODUCTS) 137 | @join__type(graph: REVIEWS) 138 | @join__type(graph: USERS) 139 | { 140 | allPandas: [Panda] @join__field(graph: PANDAS) 141 | panda(name: ID!): Panda @join__field(graph: PANDAS) 142 | allProducts: [ProductItf] @join__field(graph: PRODUCTS) 143 | product(id: ID!): ProductItf @join__field(graph: PRODUCTS) 144 | review(id: Int!): Review @join__field(graph: REVIEWS) 145 | } 146 | 147 | type Review 148 | @join__type(graph: REVIEWS) 149 | { 150 | id: Int! 151 | body: String! 152 | } 153 | 154 | enum ShippingClass 155 | @join__type(graph: INVENTORY) 156 | @join__type(graph: PRODUCTS) 157 | { 158 | STANDARD @join__enumValue(graph: INVENTORY) @join__enumValue(graph: PRODUCTS) 159 | EXPRESS @join__enumValue(graph: INVENTORY) @join__enumValue(graph: PRODUCTS) 160 | OVERNIGHT @join__enumValue(graph: INVENTORY) 161 | } 162 | 163 | interface SkuItf 164 | @join__type(graph: PRODUCTS) 165 | { 166 | sku: String 167 | } 168 | 169 | type User 170 | @join__type(graph: PRODUCTS, key: "email") 171 | @join__type(graph: USERS, key: "email") 172 | { 173 | email: ID! @tag(name: "test-from-users") 174 | totalProductsCreated: Int 175 | name: String @join__field(graph: USERS) 176 | } 177 | -------------------------------------------------------------------------------- /supergraph/schema/docker.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | inventory: 4 | routing_url: http://inventory:4000/graphql 5 | schema: 6 | file: ../../subgraphs/inventory/app/src/main/resources/graphql/inventory.graphqls 7 | products: 8 | routing_url: http://products:4000/graphql 9 | schema: 10 | file: ../../subgraphs/products/products.graphql 11 | users: 12 | routing_url: http://users:4000/graphql 13 | schema: 14 | file: ../../subgraphs/users/users.graphql 15 | pandas: 16 | routing_url: http://pandas:4000/graphql 17 | schema: 18 | file: ../../subgraphs/pandas/pandas.graphql 19 | reviews: 20 | routing_url: http://reviews:4000/graphql 21 | schema: 22 | file: ../../subgraphs/reviews/reviews.graphql 23 | -------------------------------------------------------------------------------- /supergraph/schema/local.graphql: -------------------------------------------------------------------------------- 1 | schema 2 | @link(url: "https://specs.apollo.dev/link/v1.0") 3 | @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) 4 | @link(url: "https://specs.apollo.dev/tag/v0.3") 5 | @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) 6 | @link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@myDirective", {name: "@anotherDirective", as: "@hello"}]) 7 | { 8 | query: Query 9 | } 10 | 11 | directive @hello on FIELD_DEFINITION 12 | 13 | directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION 14 | 15 | directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE 16 | 17 | directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION 18 | 19 | directive @join__graph(name: String!, url: String!) on ENUM_VALUE 20 | 21 | directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE 22 | 23 | directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR 24 | 25 | directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION 26 | 27 | directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA 28 | 29 | directive @myDirective(a: String!) on FIELD_DEFINITION 30 | 31 | directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA 32 | 33 | type DeliveryEstimates 34 | @join__type(graph: INVENTORY) 35 | { 36 | estimatedDelivery: String 37 | fastestDelivery: String 38 | } 39 | 40 | scalar join__FieldSet 41 | 42 | enum join__Graph { 43 | INVENTORY @join__graph(name: "inventory", url: "http://localhost:4002/graphql") 44 | PANDAS @join__graph(name: "pandas", url: "http://localhost:4004/graphql") 45 | PRODUCTS @join__graph(name: "products", url: "http://localhost:4001/graphql") 46 | REVIEWS @join__graph(name: "reviews", url: "http://localhost:4005/graphql") 47 | USERS @join__graph(name: "users", url: "http://localhost:4003/graphql") 48 | } 49 | 50 | scalar link__Import 51 | 52 | enum link__Purpose { 53 | """ 54 | `SECURITY` features provide metadata necessary to securely resolve fields. 55 | """ 56 | SECURITY 57 | 58 | """ 59 | `EXECUTION` features provide metadata necessary for operation execution. 60 | """ 61 | EXECUTION 62 | } 63 | 64 | type Panda 65 | @join__type(graph: PANDAS) 66 | { 67 | name: ID! 68 | favoriteFood: String @tag(name: "nom-nom-nom") 69 | } 70 | 71 | type Product implements ProductItf & SkuItf 72 | @join__implements(graph: INVENTORY, interface: "ProductItf") 73 | @join__implements(graph: PRODUCTS, interface: "ProductItf") 74 | @join__implements(graph: PRODUCTS, interface: "SkuItf") 75 | @join__implements(graph: REVIEWS, interface: "ProductItf") 76 | @join__type(graph: INVENTORY, key: "id") 77 | @join__type(graph: PRODUCTS, key: "id") 78 | @join__type(graph: PRODUCTS, key: "sku package") 79 | @join__type(graph: PRODUCTS, key: "sku variation { id }") 80 | @join__type(graph: REVIEWS, key: "id") 81 | { 82 | id: ID! @tag(name: "hi-from-products") 83 | dimensions: ProductDimension @join__field(graph: INVENTORY, external: true) @join__field(graph: PRODUCTS) 84 | delivery(zip: String): DeliveryEstimates @join__field(graph: INVENTORY, requires: "dimensions { size weight }") 85 | sku: String @join__field(graph: PRODUCTS) 86 | name: String @join__field(graph: PRODUCTS) @hello 87 | package: String @join__field(graph: PRODUCTS) 88 | variation: ProductVariation @join__field(graph: PRODUCTS) 89 | createdBy: User @join__field(graph: PRODUCTS) 90 | hidden: String @join__field(graph: PRODUCTS) 91 | reviewsScore: Float! @join__field(graph: REVIEWS, override: "products") 92 | oldField: String @join__field(graph: PRODUCTS) 93 | reviewsCount: Int! @join__field(graph: REVIEWS) 94 | reviews: [Review!]! @join__field(graph: REVIEWS) 95 | } 96 | 97 | type ProductDimension 98 | @join__type(graph: INVENTORY) 99 | @join__type(graph: PRODUCTS) 100 | { 101 | size: String @hello 102 | weight: Float 103 | } 104 | 105 | interface ProductItf implements SkuItf 106 | @join__implements(graph: PRODUCTS, interface: "SkuItf") 107 | @join__type(graph: INVENTORY) 108 | @join__type(graph: PRODUCTS) 109 | @join__type(graph: REVIEWS) 110 | { 111 | id: ID! 112 | dimensions: ProductDimension @join__field(graph: INVENTORY) @join__field(graph: PRODUCTS) 113 | delivery(zip: String): DeliveryEstimates @join__field(graph: INVENTORY) 114 | sku: String @join__field(graph: PRODUCTS) 115 | name: String @join__field(graph: PRODUCTS) 116 | package: String @join__field(graph: PRODUCTS) 117 | variation: ProductVariation @join__field(graph: PRODUCTS) 118 | createdBy: User @join__field(graph: PRODUCTS) 119 | hidden: String @inaccessible @join__field(graph: PRODUCTS) 120 | oldField: String @join__field(graph: PRODUCTS) @deprecated(reason: "refactored out") 121 | reviewsCount: Int! @join__field(graph: REVIEWS) 122 | reviewsScore: Float! @join__field(graph: REVIEWS) 123 | reviews: [Review!]! @join__field(graph: REVIEWS) 124 | } 125 | 126 | type ProductVariation 127 | @join__type(graph: PRODUCTS) 128 | { 129 | id: ID! 130 | name: String 131 | } 132 | 133 | type Query 134 | @join__type(graph: INVENTORY) 135 | @join__type(graph: PANDAS) 136 | @join__type(graph: PRODUCTS) 137 | @join__type(graph: REVIEWS) 138 | @join__type(graph: USERS) 139 | { 140 | allPandas: [Panda] @join__field(graph: PANDAS) 141 | panda(name: ID!): Panda @join__field(graph: PANDAS) 142 | allProducts: [ProductItf] @join__field(graph: PRODUCTS) 143 | product(id: ID!): ProductItf @join__field(graph: PRODUCTS) 144 | review(id: Int!): Review @join__field(graph: REVIEWS) 145 | } 146 | 147 | type Review 148 | @join__type(graph: REVIEWS) 149 | { 150 | id: Int! 151 | body: String! 152 | } 153 | 154 | enum ShippingClass 155 | @join__type(graph: INVENTORY) 156 | @join__type(graph: PRODUCTS) 157 | { 158 | STANDARD @join__enumValue(graph: INVENTORY) @join__enumValue(graph: PRODUCTS) 159 | EXPRESS @join__enumValue(graph: INVENTORY) @join__enumValue(graph: PRODUCTS) 160 | OVERNIGHT @join__enumValue(graph: INVENTORY) 161 | } 162 | 163 | interface SkuItf 164 | @join__type(graph: PRODUCTS) 165 | { 166 | sku: String 167 | } 168 | 169 | type User 170 | @join__type(graph: PRODUCTS, key: "email") 171 | @join__type(graph: USERS, key: "email") 172 | { 173 | email: ID! @tag(name: "test-from-users") 174 | totalProductsCreated: Int 175 | name: String @join__field(graph: USERS) 176 | } 177 | -------------------------------------------------------------------------------- /supergraph/schema/local.yaml: -------------------------------------------------------------------------------- 1 | federation_version: 2 2 | subgraphs: 3 | products: 4 | routing_url: http://localhost:4001/graphql 5 | schema: 6 | file: ../../subgraphs/products/products.graphql 7 | inventory: 8 | routing_url: http://localhost:4002/graphql 9 | schema: 10 | file: ../../subgraphs/inventory/app/src/main/resources/graphql/inventory.graphqls 11 | users: 12 | routing_url: http://localhost:4003/graphql 13 | schema: 14 | file: ../../subgraphs/users/users.graphql 15 | pandas: 16 | routing_url: http://localhost:4004/graphql 17 | schema: 18 | file: ../../subgraphs/pandas/pandas.graphql 19 | reviews: 20 | routing_url: http://localhost:4005/graphql 21 | schema: 22 | file: ../../subgraphs/reviews/reviews.graphql 23 | --------------------------------------------------------------------------------