10

Example:

  • upload file to server and save resulting path to the database, only authenticated users should be able to upload files

How to implement this?

srghma
  • 4,770
  • 2
  • 38
  • 54

2 Answers2

10

to summarize we have 3 ways:

  • client uploads to s3 (or similar service), get's file url, then makes insert/update mutation to the right table
  • custom uploader - write application/server that uploads files and mutates db and use nginx routing to redirect some requests to it
  • custom resolver using schema stitching (example)
srghma
  • 4,770
  • 2
  • 38
  • 54
0

If you are uploading files to AWS S3, there is a simple way that you don't have to launch another server to process file upload or create a handler for hasura action.

Basically, when you upload files to S3, it's better to get signed url from backend and upload to s3 directly. BTW, for multiple image sizes hosting, this approach is easy and painless.

The critical point is how to get s3 signed url to upload.
In node.js, you can do

const AWS = require("aws-sdk");
const s3 = new AWS.S3({ apiVersion: "2006-03-01" });
const signedUrl = s3.getSignedUrl("putObject", {
  Bucket: "my-bucket",
  Key: "path/to/file.jpg",
  Expires: 600,
});
console.log("signedUrl", signedUrl);

A signedUrl example is like https://my-bucket.s3.amazonaws.com/path/to/file.jpg?AWSAccessKeyId=AKISE362FGWH263SG&Expires=1621134177&Signature=oa%2FeRF36DSfgYwFdC%2BRVrs3sAnGA%3D.
Normally, you will put the above code to a handler hosted in AWS Lambda or glitch, and add some logic for authorization and even add a row to table.

You can see that the most important part is Signature=oa%2FeRF36DSfgYwFdC%2BRVrs3sAnGA%3D. How can we make it easier to get Signature?

After digging into AWS JS SDK, we can find signature is computed here.

return util.crypto.lib.createHmac(fn, key).update(string).digest(digest);

fn = 'sha1'
string = 'PUT\n\n\n1621135558\b/my-bucket/path/to/file.jpg'
digest = 'base64'

It's just sha1 a certain format of string. This means we can just use hasura computed fields and Postgres crypto function to achieve the same results.

So if you have a table "files"

CREATE TABLE files (
   id SERIAL,
   created_at timestamps,
   filename text,
   user_id integer
);

you can create a SQL function

CREATE OR REPLACE FUNCTION public.file_signed_url(file_row files)
 RETURNS text
 LANGUAGE sql
 STABLE
AS $function$
  SELECT ENCODE( HMAC(
  
  'PUT' ||E'\n'||E'\n'||E'\n'|| 
  (cast(extract(epoch from file_row.created_at) as integer) + 600)
  ||E'\n'|| '/my-bucket/' || file_row.filename
  
  , 'AWS_SECRET', 'SHA1'), 'BASE64')
$function$

Finally, follow this to expose this computed field to Hasura.

This way allows you to be able to not add any backend stuff and handle permission all in Hasura.

Howard
  • 601
  • 1
  • 11
  • 15
  • 1
    While this looks appealing, this signature is the v2 one, which has been deprecated since 2018. The current v4 signature is significantly harder to compute in SQL. – Krisztián Szabó Aug 06 '21 at 08:08