Vapor 3 PostgreSQL CRUD without requests http
Based on this question and answers (Is is possible to use Vapor 3 Postgres Fluent in a standalone script?) I realized CRUD like this:
import Vapor
import FluentPostgreSQL
final class Device: PostgreSQLModel {
var id: Int?
var isWorking: Bool
var serial: Int
init(isWorking: Bool, serial: Int) {
self.isWorking = isWorking
self.serial = serial
}
}
extension Device: Content {}
extension Device: Migration {}
extension Device: Parameter {}
final class WorkWithPostgres {
let databaseConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "username", database: "testestest", password: nil)
let database: PostgreSQLDatabase
static let shared = WorkWithPostgres()
private init() {
database = PostgreSQLDatabase(config: databaseConfig)
}
func readAll<T: PostgreSQLModel>(postgreSQLModel: T.Type, completion: (([T]) -> Void)?) {
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let conn = database.newConnection(on: worker)
let _ = conn.map { connection in
postgreSQLModel.query(on: connection).all().map { databaseData in
worker.shutdownGracefully { _ in
}
completion?(databaseData)
}
}
}
func create<T: PostgreSQLModel>(postgreSQLModel: T) {
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let conn = database.newConnection(on: worker)
let _ = conn.map { connection in
let _ = postgreSQLModel.save(on: connection).whenComplete {
worker.shutdownGracefully { _ in
}
}
}
}
}
final class DeviceController {
func readAll(completion: (([Device]) -> Void)?) {
WorkWithPostgres.shared.readAll(postgreSQLModel: Device.self) { devices in
completion?(devices)
}
}
func create(isWorking: Bool, serial: Int) {
let device = Device(isWorking: isWorking, serial: serial)
WorkWithPostgres.shared.create(postgreSQLModel: device)
}
}
It is working, but I am not sure is it good way to do this. Does somebody know?
Vapor 4 PostgreSQL CRUD without http requests
So, correct idea not to work with database like I did with Vapor 3. Now, in Vapor 4, you should use Database
instance. Where to get it? I can suggest 2 options for you. First is boot(_ app: Application)
in boot.swift
or in configure(_ app: Application)
in configure.swift
. So app
will have options to work with database like this: <Model>.query(on: app.db)
. Second option is req
in any request. req
should be of type Request
, like this: <Model>.query(on: req.db)
. By Model
, you can take any class inherited of class Model
, for example: final class User: Model, Content {
You should organise your code to be called from boot.swift
or configure.swift
or from any request, for example, in routes.swift
Is is possible to use Vapor 3 Postgres Fluent in a standalone script?
You will need to create an event loop group to be able to make database requests. SwiftNIO's MultiThreadedEventLoopGroup
is good for this:
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 2)
You can change the number of threads used as you need.
Now you can create a connection to the database with that worker:
let conn = try database.newConnection(on: worker)
The connection is in a future, so you can map
it and pass the connection in your query:
conn.flatMap { connection in
return Product.query(on: connection)...
}
Make sure you shutdown your worker when you are done with it using shutdownGracefully(queue:_:)
Can Vapor 4 serve both HTTP and HTTPS requests in the same application?
No Vapor 4 cannot serve different protocols on the same port/application. The easiest way around is to run two instances of the application, one for each. Or just get the internal traffic to use HTTPS.
Another option would be to stick Nginx in front or similar
Swift backend using vapor framework postgresql
The Vapor app will live in a separate Xcode project as it's a separate app. In terms of connecting everything together, it's just another API to the client, so you call it like you would any other
Multiple Databases in Vapor
The magic of multiple databases lies with the DatabaseID
. You can define a new instance of DatabaseID
, and register a connection with it.
extension DatabaseID {
static let readOnly = DatabaseID("readOnly")
}
static func configureDatabase(_ app: Application) throws {
try app.databases.use(.postgres(url: "postgresql://user@localhost:5432/user"), as: .psql)
try app.databases.use(.postgres(url: "postgresql://user@localhost:5432/read_only"), as: .readOnly)
}
Then, when you want to run a query, instead of using the request.db
database, use the .db(_:)
method and pass in your identifier:
User.query(on: request.db(.readOnly))
how to build a vapor app from an already existing database?
This is somewhere Vapor (or more correctly Fluent, which is the database level of Vapor) is a bit limited.
Yes, you can use your existing tables. In your prepare(_:)
method, you can simply leave the implementation empty without creating a table at all. You should also leave revert(_:)
empty as well.
In your init(node:in:)
initialiser and makeNode(context:)
method, you will need to map between the column names and types in your table and the property types in your Swift model.
Vapor + PostgreSQL + Nginx build on Docker not operation properly
To connect to database inside container dont use localhost as a db host but your db container name. So in your case host is psql. Here your docker compose is not well formatted psql and nginx must have one tab more. But maybe its just SO formatting wrong.
How to store Swift types (Date, Dictionary and others) in PostgreSQL with Vapor 3?
- for date
timestamp without time zone
- for
[String: Any]
should bejsonb
vapor 3 fluent-mysql join query
You could do it with FluentMySQL like this
func something(_ req: Request) throws -> Future<HTTPStatus> {
return User.query(on: req)
// this is how you can join anything
.join(\Token.userId, to: \User.id)
// this is how you can filter values
.filter(\Token.createdAt, .lessThan, Date())
// this is how to apply LIMIT and OFFSET
.range(lower: 0, upper: 10)
// this is how to decode joined model
.alsoDecode(Token.self)
// requests an array of results (or use .first if you need only one first row)
.all().map { results in
for (user, token) in results {
print("user: \(user.firstName) token: \(token.token)")
}
return .ok
}
}
Or you could build a raw query using SwifQL library and execute it like this
func something2(_ req: Request) throws -> Future<HTTPStatus> {
// build your query like you do in raw SQL
let query = SwifQLSelectBuilder().select(User.table.*)
.from(User.table)
.join(.left, Token.table, on: \Token.userId == \User.id)
.where(\Token.createdAt < Fn.now())
.offset(0)
.limit(10)
.build()
// this way you could print raw query to execute it in mysql manually for debugging purposes
print("raw query: " + query.prepare(.mysql).plain)
// executes query with mysql dialect
return query.execute(on: req, as: .mysql)
// requests an array of results (or use .first if you need only one first row)
// You also could decode query results into the custom struct
.all(decoding: User.self).map { users in
return .ok
}
}
Related Topics
Iterating Through an Array of Strings, Fetched from Mongodb
Error: 'string' Is Not Convertible to 'string!'
Nsmanagedobjectcontext's Propagatesdeletesatendofevent Set to False Causes Error on Save
Carthage Update Error: "Github API Request Failed: Bad Credentials"
Bundle.Main.Path(Forresource... Always Returns Nil When Looking for Xml File
Load Viewcontroller Swift - Black Screen
Swift Cannot Convert The Expression's Type 'Void' to Type 'string!'
Different UIfont Sizes for Different iOS Devices in Swift
How to Mutate an Array in a Dictionary
+' Is Deprecated: Mixed-Type Addition Is Deprecated in Swift 3.1
Issue with Returning a Directory Enumerator from Nsfilemanager Using Enumeratoraturl in Swift
Member Operator '==' Must Have at Least One Argument of Type
Close UIdatepicker After Selection When Style Is .Compact
How to Get The Coordinates of The Point on a Line That Has The Smallest Distance from Another Point