I'm using React and Graphql on the frontend and Django and Graphene on the backend.
I want to be able to download a pdf file of a report. I try to do it using mutation as follows:
const [createPdf, {loading: createPdfLoading, error: createPdfError}] = useMutation(CREATE_PDF)
const handleCreatePDF = async (reportId) => {
const res = await createPdf({variables: {reportId: parseInt(reportId) }})
debugger;
};
export const CREATE_PDF = gql`
mutation ($reportId: Int!) {
createPdf (reportId: $reportId){
reportId
}
}
`;
On the backend I have something like this:
class CreatePDFFromReport(graphene.Mutation):
report_id = graphene.Int()
class Arguments:
report_id = graphene.Int(required=True)
def mutate(self, info, report_id):
user = info.context.user
if user.is_anonymous:
raise GraphQLError("You are not logged in!")
report = Report.objects.get(id=report_id)
if not report:
raise GraphQLError("Report not found!")
if user != report.posted_by:
raise GraphQLError("You are not permitted to do that!")
html_string = render_to_string('report.html', {'report_id': 1})
pdf_file = HTML(string=html_string)
response = HttpResponse(pdf_file, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="rapport_{}"'.format(report_id)
return response
# return CreatePDFFromReport(report_id=report_id)
When I uncomment return CreatePDFFromReport(report_id=report_id)
it works fine.
But I want to return pdf file.
Is there any possibility to do that?
Thanks.
You can't return file(binary)/headers(mime)/etc (needed to be handled as download request behaviour by browser) using json'ed communication. GraphQL request and response are in json format.
Workflow:
id
to be a part of download url or full url
(string) to target fileid
or url
in this case should be a part of required mutation response
mutation ($reportId: Int!) {
createPdf (reportId: $reportId){
reportDownloadId
}
}
reportDownloadId
) is available in data
data
(result/response) from mutation can be accessed in two ways:
... by { data }
:
const [addTodo, { data }] = useMutation(ADD_TODO);
... or by onCompleted
handler:
addTodo({
variables: { type: input.value },
onCompleted = {(data) => {
// some action with 'data'
// f.e. setDownloadUrl(data.reportDownloadId)
// from 'const [downloadUrl, setDownloadUrl] = useState(null)'
}}
});
Both methods are described in docs
Both methods will allow you to render (conditionally) a download button/link/component, f.e.
{downloadUrl && <DownloadReport url={downloadUrl}/>}
{downloadUrl && <a href={downloadUrl}>Report ready - download it</a>}
// render download, share, save in cloud, etc.
Handler method can be used to automatically invoke downloading file request, without rendering additional button. This will look like one action while technically there are two following requests (graphql mutation and download). This thread describes how to handle downloads using js.