HTTP JSON API & gRPC-web?

I have a few questions about using the HTTP JSON API to interact with a ledger and gRPC-web

The docs state that the HTTP JSON API is distinct from the gRPC API in that certain functionalities are limited

This also makes sense, as invoking gRPC methods requires an HTTP/2 proxy for REST-full interactions

I also understand that the HTTP JSON API ultimately translates and forwards these requests into gRPC calls

  • What exactly is the HTTP JSON API?
  • Is there a gRPC-web proxy available for more direct interaction with the API?
  • Is the HTTP JSON API an implementation of a proper gRPC-web proxy? (as opposed to a more specialized or custom built service?)
1 Like

No, it’s a custom-built service (more on that on the following answer).

It’s a custom-built service that sits in front a participant and exposes a slightly different interface that is more familiar to developers who are not accustomed to gRPC. In particular, it uses its own specific encoding for Daml values which is more lightweight to interpret on the client than what you would get natively out of gRPC.

The gRPC-Web tutorial uses Envoy. I would assume that’s a safe choice. Unfortunately I’m not aware of others.

A word of caution

The Ledger API is meant for fast, payload-agnostic transaction processing, not analytical workloads. As such, its querying capabilities are limited. Querying directly the ledger comes at the expense of flexibility. It also means that you are going to be bound 1:1 to the Daml models on the ledger and any upgrade will be breaking. Personally, I generally recommend having a service-specific backend that mediates between the ledger and downstream consumers. Of course, YMMV and you know your specific context best.

2 Likes

Thank you for taking the time to answer each of my questions, @stefanobaghino-da

This is unsupported, but we’ve managed to use envoy for a web-grpc interface, using this config:

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 8080 }
      filter_chains:
      - filters:
        - name: envoy.filters.network.http_connection_manager
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            stat_prefix: edge
            http_filters:
            - name: envoy.filters.http.router
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
            route_config:
              virtual_hosts:
              - name: direct_response_service
                domains: ["*"]
                routes:
                - match:
                    prefix: "/"
                  direct_response:
                    status: 200
                    body:
                      filename: "./core/js/src/test/html/index.html"
    - name: listener_1
      address:
        socket_address: { address: 0.0.0.0, port_value: 6865 }
      filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              codec_type: auto
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                  - name: local_service
                    domains: ["*"]
                    routes:
                      - match: { prefix: "/" }
                        route:
                          cluster: ledger_api
                          timeout: 0s
                          max_stream_duration:
                            grpc_timeout_header_max: 0s
                    cors:
                      allow_origin_string_match:
                        - prefix: "*"
                      allow_methods: GET, PUT, DELETE, POST, OPTIONS
                      allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                      max_age: "1728000"
                      expose_headers: custom-header-1,grpc-status,grpc-message
              http_filters:
                - name: envoy.filters.http.grpc_web
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
                - name: envoy.filters.http.cors
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
                - name: envoy.filters.http.router
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          transport_socket:
            name: envoy.transport_sockets.tls
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
              common_tls_context:
                alpn_protocols: "h2"
                tls_certificate_sds_secret_configs:
                  - name : participant_secrets

  clusters:
    - name: ledger_api
      connect_timeout: 0.25s
      http2_protocol_options: {}
      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 127.0.0.1
                    port_value: 5041
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
          common_tls_context:
            tls_certificate_sds_secret_configs:
              - name : participant_secrets

  secrets:
    - name: participant_secrets
      tls_certificate:
        certificate_chain:
          filename: "/path/to/participant.crt"
        private_key:
          filename: "/path/to/participant.pem"

One annoying this is that you need to have TLS support, so to test all this you also need to configure key certificates, which is an extra burden during initial development.

1 Like