Storing files on S3
Beside operations for your data, there are mutations related to S3 compatible storage. Contember itself doesn't store your files, but it can help you with signing URLs for uploading (or reading) those files.
S3 server
In Contember development stack, there is bundled Minio server, which is S3 compatible storage server, therefore you don't have to setup anything on localhost as it is running out of box. For production see our guide
Signing upload URL
Use GraphQL generateUploadUrl
mutation to generate a presigned S3 upload URL. This is a secure way how to access your S3 bucket without exposing credentials to a client application, because only Contember server knows a secret key, using which it can sign one time URL.
Execute following mutation in your application:
mutation {
signedUpload: generateUploadUrl(contentType: "image/jpeg") {
url
publicUrl
method
headers {
key
value
}
}
}
The url
and other fields can be used to construct a request, which uploads a file directly to S3 storage from an application:
await fetch(
signedUpload.url,
{
method: signedUpload.method,
headers: Object.fromEntries(signedUpload.headers.map(({ key, value }) => [key, value])),
body: content,
}
)
generateUploadUrl
mutation has few optional arguments
- expiration (default 3600) - URL expiration in seconds
- acl (default corresponds S3 provider, usually it is "PUBLIC_READ") - object ACL
- PUBLIC_READ: anyone who knows public URL is allowed to read an object
- PRIVATE: object is not accessible using its public URL
- NONE: explicit object ACL is not set, leaving ACL to bucket policies
Some S3 providers does not support object level ACL, so this argument will not be available at all.
Signing read url
When you set an object ACL to PRIVATE, you can use this mutation to sign a read URL:
mutation {
generateReadUrl(objectKey: "images/5b934fe1-0e30-4761-9e00-d4bfabbddf34.png") {
url
}
}
If you store public URL instead of object key, don't worry. Contember will recognize it and parses an object key from it.
You can also optionally set an expiration of the URL (default 3600 seconds)
Configuring S3 ACL
To use S3 functionality, each role in the ACL schema must have defined ACL rules. This is the most simple rule, which allows both operations on any key:
const adminRole = {
s3: {
'**': {
read: true,
upload: true,
}
},
// ... variables, stage, entities...
}
In a key you define a glob-like pattern for an S3 object key and in a value you define allowed operations (read
and upload
). We use picomatch library for a pattern matching.
Example patterns
**
- matches anythingimages/**
- matches any object with animages
key prefix**.jpg
- matches any object with ajpg
extension
ACL evaluation
Contember will try to find any rule for an object key, which allows given operation. This means that if you first define a specific rule for private/**
, which does NOT allow an upload, and later you define a generic rule **
, which allows upload, then this rule will override previous one.
read
option only affects generateReadUrl
mutation and does not affect upload ACL (PUBLIC_READ
, PRIVATE
) nor public read URL in any way.