grpc / grpc/grpc-web

Metadata is empty, but Network tab shows it

Open
#1,376 2 comments 0 reactions 0 assignees View on GitHub
pending-clarification
Dominant language
JavaScript
Stars
9.3k
Forks
802
Avg merge
1d 7h
Merged PRs (30d)
5

Description

In short, the metadata of my web-grpc client is always empty.

### Proto definition
Let's say my proto file looks like:
```proto
// some/path/myservice.proto
syntax = "proto3";

import "google/protobuf/empty.proto";

package some.path.myservice;

service MyService {
rpc RespondMetadata(google.protobuf.Empty) returns (google.protobuf.Empty) {}
}
```

### Code generation
I generated code for my proto files using `"grpc-web": "^1.4.2"`.

And protoc:
```shell
$ protoc --version
libprotoc 24.3
```

The command is:
```shell
protoc -I=. --js_out=import_style=commonjs,binary:. \
--grpc-web_out=import_style=typescript,mode=grpcweb:. \
some/path/myservice.proto
```

### Server-side
The server of this gRPC sets the metadata header `some-header` in Golang:

```go
func (s myServer) RespondMetadata(ctx context.Context, request *emptypb.Empty) (*emptypb.Empty, error) {
// ... Do some other stuff
// All side-effects resulting from this call happen sucessfully in the server. e.g. printing to console

// Set the metadata key value pair as header
header := metadata.New(map[string]string{"some-header": "some-value"})
err := grpc.SetHeader(ctx, header)
if err != nil {
return nil, fmt.Errorf("Failed to set header: %w", err)
}

return &emptypb.Empty{}, nil
}
```

I know this is working fine because I can create a Golang client for this server and successfully read the headers, both by dialing into the grpc port directly, or by dialing an Envoy proxy that I have already set up.

### Metadata transformed into HTTP headers
If I create my grpc client for the browser using grpc-web, I can see the metadata present in the HTTP Response Headers of the browser Network tab:

image

### Issue within browser (grpc-web) typescript code
However, in the code the metadata is always empty.

I initialize my client in the following way:

```typescript
import { MyServiceClient } "some/path/MyServiceClientPb";

const client = new MyServiceClient(serverUrl, null, null)
```

And I have tried calling the method and reading metadata in the following ways:
```typescript
const grpcRequest = new google_protobuf_empty_pb.Empty();

const call = client.respondMetadata(grpcRequest, {}, (err, response) => {
if (err) {
console.log("There was an error", err);
} else {
// response is of type google.protobuf.Empty
// So I can't read metadata from here
console.log("Successfull response: ", response)
}
});

call.on('status', (status) => {
console.log("Status metadata: ", status.metadata) // This is empty: {}
console.log("Status!:", status)
});
call.on('metadata', (metadata) => {
console.log("Received metadata: ", metadata); // This is empty: {}
const value = metadata['some-header'];

console.log('The received header is: ', value); // this is undefined
})
```

Any ideas how can I read **response** metadata? Or why are these approaches not working?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.