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!
Top comments (0)