Posting multiple items at once

Hello!

I’m following the book and there is the post method where I can post an acronym at a time. But, is there a way to create a method to post many at one? How would be this method?

Thank you.

@0xtim Can you please help with this when you get a chance? Thank you - much appreciated! :]

@macbieoo you can set the incoming type to [Acronym].self and you’ll get an array of acronyms. For saving - I can’t remember if the convenience function exists yet for a batch save, otherwise you’ll need to loop through and save each one

1 Like

I’m not sure this is the best solution but worked for me.

Inside the boot method in the controller: acronyms.post[[Acronym].self, use: createHandler]

And the createHandler method:

func createHandler(_ req: Request, acronyms: [Acronym]) throws -> Future<HTTPStatus> {
var arr: [EventLoopFuture< Acronym >] = []
for i in acronyms {
arr.append(i.save(on: req])
}
return arr.flatten(on: req).transform(to: .created]
}

I would have done it this way too. But I wonder if, in the event one of the Futures fails and throws, would all transactions be rolled back…

No, if one future fails, any futures that have succeeded up to that point won’t be cancelled or rolled back. You can wrap the block in a transaction if you need that

@0xtim I suppose this is how you can wrap the database inserts in a transaction that can be rolled back in the event one of the futures fails and throws. The code below assumes a PostgreSQL database and a response of HTTPStatus type:

struct AcronymsController: RouteCollection {
  func boot(router: Router) throws {
    ...
    let tokenAuthMiddleware = User.tokenAuthMiddleware()
    let guardAuthMiddleware = User.guardAuthMiddleware()
    let tokenAuthGroup = acronymsRoutes.grouped(tokenAuthMiddleware, guardAuthMiddleware)
    tokenAuthGroup.post(AcronymCreateData.self, use: createHandler)
    tokenAuthGroup.post([AcronymCreateData].self, use: createAcronymsHandler)
    ...
  }

  func createAcronymsHandler(_ req: Request, acronymsData: [AcronymCreateData]) throws -> Future<HTTPStatus> {
    let user = try req.requireAuthenticated(User.self)
    var acronymSaveResults: [Future<Acronym>] = []
    return req.transaction(on: .psql) { conn in
      for acronymData in acronymsData {
        let acronym = try Acronym(short: acronymData.short, long: acronymData.long, userID: user.requireID())
        acronymSaveResults.append(acronym.save(on: conn))
      }
      return acronymSaveResults.flatten(on: conn)
        .transform(to: .created)
    }
  }

  ...
}

struct AcronymCreateData: Content {
  let short: String
  let long: String
}
1 Like

@michaeltansg Thank you for sharing this - much appreciated! :]