Search code examples
typescriptmappingapi-designtypeorm

Typeorm: Return only one property from loaded relation


Usecase: We have files in an S3 bucket with the respective information in our database. I would like to provide only an url for the frontend. Is this even possible?

At the moment it loads the whole object:

"descriptionFile": {
    "id": 99,
    "createdAt": "2020-07-23T11:58:59.510Z",
    "updatedAt": "2020-07-23T11:58:59.510Z",
    "s3Identifier": "asdfasdf-2we3123r99"
},

Goal: To only have an url in the response:

"descriptionFileUrl": "https://myapi.com/api/media/asdfasdf-2we3123r99"

These are my entities:

// category.entity.ts
@OneToOne(type => S3File)
@JoinColumn({ name: 'description_file_id' })
public descriptionFile: S3File;

// s3-file.entity.ts
@Column({ name: 's3_identifier', nullable: false })
public s3Identifier: string;

public get fullUrl() {
  return 'https://' + this.s3Identifier;
}

Service function that load the data:

this.categoryRepository.find({
  relations: [..., 'descriptionFile'],
});

Solution

  • I just stumpled upon my own question two years later. In the mean time I know that Presigned URLs are the best solution for this question. The (imo) easiest way to use them in a node setup is the official package @aws-sdk/client-s3: https://www.npmjs.com/package/@aws-sdk/client-s3. Note: this also works with minio. :)

    Example:

    // constructor of service class
    constructor(...) {
        this.s3Client = new S3Client({
            credentials: {
                accessKeyId: this.configService.s3AccessKey,
                secretAccessKey: this.configService.s3SharedSecret,
            },
            forcePathStyle: true,
            endpoint: this.configService.s3Host,
        });
    }
    
    public getPresignedUrl(bucket: string, fileName: string): Promise<string> {
        const signedUrlExpireSeconds = 60 * 60; // 1h
    
        const command = new GetObjectCommand({
          Bucket: bucket,
          Key: fileName,
        });
        return getSignedUrl(this.s3Client, command, { expiresIn: signedUrlExpireSeconds });
      }
    

    Then you can use the getPresignedUrl wherever you like in your project.