grpc_graphql_gateway 1.2.4

A Rust implementation of gRPC-GraphQL gateway - generates GraphQL execution code from gRPC services
# File Uploads

The gateway automatically supports GraphQL file uploads via multipart requests, following the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec).

## Proto Definition

Map `bytes` fields to handle file uploads:

```protobuf
message UploadAvatarRequest {
  string user_id = 1;
  bytes avatar = 2;  // Maps to Upload scalar in GraphQL
}

message UploadAvatarResponse {
  string user_id = 1;
  int64 size = 2;
}

service UserService {
  rpc UploadAvatar(UploadAvatarRequest) returns (UploadAvatarResponse) {
    option (graphql.schema) = {
      type: MUTATION
      name: "uploadAvatar"
      request { name: "input" }
    };
  }
}
```

## GraphQL Mutation

The generated GraphQL schema includes an `Upload` scalar:

```graphql
mutation UploadAvatar($file: Upload!) {
  uploadAvatar(input: { userId: "123", avatar: $file }) {
    userId
    size
  }
}
```

## Using curl

```bash
curl http://localhost:8888/graphql \
  --form 'operations={"query": "mutation($file: Upload!) { uploadAvatar(input:{userId:\"123\", avatar:$file}) { userId size } }", "variables": {"file": null}}' \
  --form 'map={"0": ["variables.file"]}' \
  --form '0=@avatar.png'
```

### Request Format

1. **operations** - JSON containing the query and variables
2. **map** - Maps file indices to variable paths
3. **0, 1, ...** - The actual file content

## JavaScript Client

Using Apollo Client with `apollo-upload-client`:

```javascript
import { createUploadLink } from 'apollo-upload-client';
import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  link: createUploadLink({ uri: 'http://localhost:8888/graphql' }),
  cache: new InMemoryCache(),
});

// Upload mutation
const UPLOAD_AVATAR = gql`
  mutation UploadAvatar($file: Upload!) {
    uploadAvatar(input: { userId: "123", avatar: $file }) {
      userId
      size
    }
  }
`;

// Trigger upload
const file = document.querySelector('input[type="file"]').files[0];
client.mutate({
  mutation: UPLOAD_AVATAR,
  variables: { file },
});
```

## Multiple Files

Upload multiple files by adding more entries to the map:

```bash
curl http://localhost:8888/graphql \
  --form 'operations={"query": "mutation($files: [Upload!]!) { uploadFiles(files: $files) { count } }", "variables": {"files": [null, null]}}' \
  --form 'map={"0": ["variables.files.0"], "1": ["variables.files.1"]}' \
  --form '0=@file1.pdf' \
  --form '1=@file2.pdf'
```

## File Size Limits

By default, uploads are limited by your web server configuration. For large files, consider:

1. Streaming uploads to avoid memory pressure
2. Setting appropriate timeouts
3. Using a CDN or object storage for very large files

## Backend Handling

On the gRPC backend, the file is received as `bytes`. Example in Rust:

```rust
async fn upload_avatar(
    &self,
    request: Request<UploadAvatarRequest>,
) -> Result<Response<UploadAvatarResponse>, Status> {
    let req = request.into_inner();
    let file_data = req.avatar;  // Vec<u8>
    let size = file_data.len() as i64;
    
    // Save file, upload to S3, etc.
    
    Ok(Response::new(UploadAvatarResponse {
        user_id: req.user_id,
        size,
    }))
}
```