Uploading a file to ASP.Net Core via GraphQL
Tags: graphql, apollo, hotchocolate, aspnetcore
Published: 2022-02-09 19:56:09

In a recent internal application, I was asked to provide a way to upload a backup to the application. This is a fairly isolated implementation that may useful for others.

Our tech stack looks like:
_Server:_
- ASP.Net [Core] 5.0
- HotChocolate for GraphQL

_Client:_
- React with TypeScript
- Material UI components
- Apollo JS for GraphQL

## Server GraphQL Endpoint
The first step is to put the GraphQL endpoint in place on the server.
```
public async Task<CompanyBackup> UploadCompanyBackupAsync(
        [Service] IHost host,
        [GraphQLType(typeof(NonNullType<UploadType>))] IFile file) =>
        new CompanyBackup
        {
            Id = "backup"
        };
```

If you've haven't already been using GraphQL in your project, you will need to register the containing class as a mutational entity. (in this case, `Mutations`) This registers the GraphQL endpoints and tells HotChocolate which type contains our endpoints. It also tells GraphQL that we would like to post an object of type UploadType to the endpoint.

`services.AddGraphQLServer().AddMutationType<Mutations>().AddType<UploadType>(); `

## Configuration Apollo
On the client-side, Apollo needs a bit of configuration to function correctly. A new link must be provided for the binary upload capabilities. To install the required package which contains the new link, run:

`npm i apollo-upload-client`

and, if you're using TypeScript:

`npm i --save-dev @types/apollo-upload-client`

Then in your Apollo config file, add these snippets of code:
```
const uploadLink = createUploadLink({
  uri: '/graphql/'
});
```
This upload link comes from the package you just installed, and enables Apollo to post binary data to the server.
```
const uploadVsNonUploadLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition!.name!.value === 'uploadCompanyBackup'
    );
  },
  uploadLink,
  httpLink,
);
```
This next link determines which link should be used for each transaction. Replace the `httpLink` variable near the end with your actual `HttpLink` variable name.
Finally, to activate the split function, provide `uploadVsNonUploadLink` in your ApolloClient initialization statement for `link` instead of the `httpClient` implementation.

## Query
Below is the query we'll use to upload the file.
```
import { gql } from '@apollo/client';

export const UploadBackupMutation = gql`
  mutation uploadCompanyBackup($file: Upload!) {
    uploadCompanyBackup(file: $file) {
      id
    }
  }
`;
```

## The Real Code (JSX)
The mutation hook uses the GraphQL query we just wrote:
```
const [uploadBackup] = useMutation(UploadBackupMutation);
```
Below is the UI code that actually allows the user to do the upload. It calls `onChange` when the input value changes.
```
<Button component="label" color="primary">
  <AddIcon /> Upload a Backup
  <input type="file" hidden onChange={onChange} />
</Button>
```
Below is detailed the `onChange` method. It basically calls the mutation with the variables set. (the file selected)
```
function onChange({
  target: {
    validity,
    files: [file],
  },
}: any) {
  if (validity.valid)
    uploadBackup({
      variables: {
        file,
        companyId: company.id
      } 
  });
}
```

For me this took a little to get everything pieced together. If it helped your project I'd appreciate a 👍.

Cheers till next time!