@s3 Directive

The @s3 directive resolves a field by executing an operation against an S3 or S3-compatible storage service (MinIO, Cloudflare R2, etc.). It requires a storage endpoint linked via @link(type: S3, src: "...").

Fields

FieldTypeDefaultDescription
bucketStringRequiredTarget bucket name. Supports Mustache templates.
operationS3OperationRequiredThe S3 operation to perform. See below.
keyStringnullObject key. Supports Mustache templates. Required for presigned URLs and DELETE.
prefixStringnullPrefix filter for LIST operations. Supports Mustache templates.
expirationInt3600Presigned URL expiration time in seconds.
contentTypeStringnullContent-Type for PUT presigned URLs. Supports Mustache templates.
linkIdStringnullThe @link id of the S3 connection to use. Omit for the default.
dedupeBooleanfalseDeduplicate identical in-flight S3 calls.

S3Operation

ValueDescription
GET_PRESIGNED_URLGenerate a presigned URL for downloading.
PUT_PRESIGNED_URLGenerate a presigned URL for uploading.
LISTList objects in a bucket (with optional prefix).
DELETEDelete an object from a bucket.

Examples

Generating a download URL

type Query {
  getFileUrl(key: String!): String! @s3(bucket: "my-bucket", operation: GET_PRESIGNED_URL, key: "{{.args.key}}")
}

Generating an upload URL

type Mutation {
  uploadUrl(key: String!, contentType: String): String!
  @s3(
    bucket: "my-bucket"
    operation: PUT_PRESIGNED_URL
    key: "{{.args.key}}"
    contentType: "{{.args.contentType}}"
    expiration: 3600
  )
}

Listing objects

Note: The JSON scalar is not built-in to GraphQL. You must declare scalar JSON in your schema (or use @link to import it) before using it as a return type.

type Query {
  listFiles(prefix: String): JSON! @s3(bucket: "my-bucket", operation: LIST, prefix: "{{.args.prefix}}")
}

Deleting an object

type Mutation {
  deleteFile(key: String!): Boolean! @s3(bucket: "my-bucket", operation: DELETE, key: "{{.args.key}}")
}

Using a specific S3 connection

When multiple @link(type: S3) entries are defined, use linkId to choose which one:

type Query {
  getMinioFile(key: String!): String!
  @s3(bucket: "local-bucket", operation: GET_PRESIGNED_URL, key: "{{.args.key}}", linkId: "minio")
}

Security

All dynamic values referenced by Mustache templates in bucket, key, prefix, and contentType are rendered at runtime from the GraphQL context. AWS credentials are resolved via the standard AWS credential chain (environment variables, shared config, IAM roles) and are never exposed in the GraphQL schema.