Skip to content

Instantly share code, notes, and snippets.

@adam-fowler
Last active July 20, 2020 17:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adam-fowler/2f6a2d7412ec122d4355d0fa956ea985 to your computer and use it in GitHub Desktop.
Save adam-fowler/2f6a2d7412ec122d4355d0fa956ea985 to your computer and use it in GitHub Desktop.

Extending Vapor Application and Request to support AWS SDK Swift

import AWSS3
import Vapor

public extension Application {
    var aws: AWS {
        .init(application: self)
    }

    struct AWS {
        struct ClientKey: StorageKey {
            typealias Value = AWSClient
        }

        public var client: AWSClient {
            get {
                guard let client = self.application.storage[ClientKey.self] else {
                    fatalError("AWSClient not setup. Use application.aws.client = ...")
                }
                return client
            }
            nonmutating set {
                self.application.storage.set(ClientKey.self, to: newValue) {
                    try $0.syncShutdown()
                }            
            }
        }

        let application: Application
    }
}

public extension Request {

    var aws: AWS {
        .init(request: self)
    }

    struct AWS {
        var client: AWSClient {
            return request.application.aws.client
        }
        
        let request: Request
    }
}

Then in configuration.swift

app.aws.client = AWSClient(httpClientProvider: .shared(app.http.client.shared))

In your controller you can access the AWSClient as follows

func myRoute(req: Request) -> EventLoopFuture<> {
    let client = req.aws.client
    let s3 = S3(client: client, region: .useast1)
}

Alternatively you could also include your service structs in the Application as well.

extension Application.AWS {
    struct S3Key: StorageKey {
        typealias Value = S3
    }

    public var s3: S3 {
        get {
            guard let s3 = self.application.storage[S3Key.self] else {
                fatalError("S3 not setup. Use application.aws.s3 = ...")
            }
            return s3
        }
        nonmutating set {
            self.application.storage[S3Key.self] = newValue
        }
    }
}

public extension Request.AWS {
    var s3: S3 {
        return request.application.aws.s3
    }
}
@hiimtmac
Copy link

hiimtmac commented Jul 6, 2020

Should the nonmutating set for client maybe do this instead?

self.application.storage.set(ClientKey.self, to: newValue) {
    try $0.syncShutdown()
}

I found an example in Vapor/APNS which did something similar. Otherwise in all my tests I have to do the following to avoid fatal error warning about shutting down the client.

let app = Application(.testing)
defer {
    try! app.aws.client.syncShutdown() // <-- this line in particular
    app.shutdown()
}

@adam-fowler
Copy link
Author

@hiimtmac good point

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment