It looks like in Vapor 2 you could do something like:
let query = <some fluent query object>
logger?.debug(query)
and it would print out the full SQL statement, but I'm not seeing any documentation of how to do that now in Vapor 3.
How can I see what SQL is being generated by my QueryBuilder?
Thanks to Nick in the comments, who pointed me to the right set of docs. This can be accomplished by using the enableLogging method. So now my configure.swift includes this code:
let dbConfig: PostgreSQLDatabaseConfig
if let url = Environment.get("DATABASE_URL"), let psqlConfig = PostgreSQLDatabaseConfig(url: url, transport: .unverifiedTLS) {
dbConfig = psqlConfig
} else {
dbConfig = ...something for the local db...
}
let postgresql = PostgreSQLDatabase(config: dbConfig)
/// Register the configured SQLite database to the database config.
var databases = DatabasesConfig()
databases.enableLogging(on: .psql)
databases.add(database: postgresql, as: .psql)
services.register(databases)
The important line being the third from the bottom. For a while I was trying to enable debugging on PostgreSQLDatabaseConfig, so to anyone in the future, take note that you're enabling it on the DatabasesConfig object instead.
Related
I'm new at Vapor and I wanted to try to connect my MySQL database. I found on official documentation how to do it but when I try to send a query it throws an error:
No services are available for 'DatabaseConnectionPoolCache'. (Container.swift:112)
I just copy-paste the code from documentation and it doesn`t work. Could someone help me to found out why?
I have MySQL#5.7 on Mojave. Actually simple requests work well, for example :
router.get("hey") { req in
return "Stas, hey"
}
Code in configure.swift :
import FluentSQLite
import MySQL
import Vapor
/// Called before your application initializes.
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
/// Register providers first
try services.register(FluentSQLiteProvider())
try services.register(MySQLProvider())
/// Register routes to the router
let router = EngineRouter.default()
try routes(router)
services.register(router, as: Router.self)
/// Register middleware
var middlewares = MiddlewareConfig() // Create _empty_ middleware config
/// middlewares.use(FileMiddleware.self) // Serves files from `Public/` directory
middlewares.use(ErrorMiddleware.self) // Catches errors and converts to HTTP response
services.register(middlewares)
// Configure a SQLite database
let sqlite = try SQLiteDatabase(storage: .memory)
/// Register the configured SQLite database to the database config.
var databases = DatabasesConfig()
databases.add(database: sqlite, as: .sqlite)
services.register(databases)
/// Configure migrations
var migrations = MigrationConfig()
migrations.add(model: Todo.self, database: .sqlite)
services.register(migrations)
//Configure a MySQL database
let mysql = try MySQLDatabase(config: MySQLDatabaseConfig(
hostname: "127.0.0.1",
port: 3306,
username: "root",
password: "7374",
database: "WORK_TIME"))
///Register to the congig
var mysqlDatabases = DatabasesConfig()
mysqlDatabases.add(database: mysql, as: .mysql)
services.register(mysqlDatabases)
}
My query in main.swift :
public struct MySQLVersion: Codable {
let version: String
}
router.get("sql") { req in
return req.withPooledConnection(to: .mysql) {conn in
return conn.raw("SELECT ##version as version")
.all(decoding: MySQLVersion.self)
}.map { rows in
return rows[0].version
}
}
It should return the version of my MySQL but it throws a strange error.
Your issue is that there is some leftover SQLite stuff from the original project template.
Start by removing the fluent-sqlite dependency from your Package.swift file and the FluentSQLite target from any target dependencies. Then run swift package update (and vapor xcode if you use Xcode) in your terminal.
Now that you have removed the FluentSQLite dependency from your project, you should be able to follow to compiler errors to fix your issue. Here are the ones I found:
import FluentSQLite
import MySQL
Should be:
import FluentMySQL
try services.register(FluentSQLiteProvider())
try services.register(MySQLProvider())
Should be
try services.register(FluentMySQLProvider())
Delete this, since you are using MySQL instead of SQLite:
// Configure a SQLite database
let sqlite = try SQLiteDatabase(storage: .memory)
/// Register the configured SQLite database to the database config.
var databases = DatabasesConfig()
databases.add(database: sqlite, as: .sqlite)
services.register(databases)
migrations.add(model: Todo.self, database: .sqlite)
Should be
migrations.add(model: Todo.self, database: .mysql)
I think that covers everything. Your /sql route should work now.
To solve this problem you should get rid of all leftover SQLite stuff from the original project and have to write your routes in routes.swift
I'm trying to build APIs using Swift and I've chosen to use Vapor.
I've created a SQLite database and am able to connect to it using a DB client.
Now I want my Swift Vapor project to connect to it as well using the FluentSQLite package.
I've created my database in the root folder of my project:
/Users/rutgerhuijsmans/Documents/runk-3.0
My database is called runk-3.0-database
The folder looks like this:
I try to connect to my DB using the following configuration:
import FluentSQLite
import Vapor
/// Called before your application initializes.
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
/// Register providers first
try services.register(FluentSQLiteProvider())
/// Register routes to the router
let router = EngineRouter.default()
try routes(router)
services.register(router, as: Router.self)
/// Register middleware
var middlewares = MiddlewareConfig() // Create _empty_ middleware config
/// middlewares.use(FileMiddleware.self) // Serves files from `Public/` directory
middlewares.use(ErrorMiddleware.self) // Catches errors and converts to HTTP response
services.register(middlewares)
let sqlite: SQLiteDatabase?
do {
sqlite = try SQLiteDatabase(storage: .file(path: "runk-3.0-database"))
print("data base connected") // This gets printed
/// Register the configured SQLite database to the database config.
var databases = DatabasesConfig()
databases.add(database: sqlite!, as: .sqlite)
services.register(databases)
/// Configure migrations
var migrations = MigrationConfig()
migrations.add(model: User.self, database: .sqlite)
services.register(migrations)
} catch {
print("couldn't connect") // This doesn't get printed
}
}
What am I doing wrong?
As IMike17 explained, your code just creates the new DB file into the Build/Products/Debug or release folder. You have to set full path dynamically as below:
do {
let directory = DirectoryConfig.detect()
let filePath = directory.workDir + "runk-3.0-database"
sqlite = try SQLiteDatabase(storage: .file(path: filePath))
......
Using the .file(path: "runk-3.0-database") the method, if you specify only the name, creates a database file with the specified name in the Derived Data folder. If the file exists in the Derived Data folder, SQLiteDatabase uses it. So the DB is erased when cleaning the build folder.
The console prints out the path of the Derived Data where you can find the DB:
Running default command: /Users/username/Library/Developer/Xcode/DerivedData/SQLiteDB-xxxxxxxxxxxxxxxxxxxxxxx/Build/Products/Debug/
If you use a full path to the DB in your project then the file is used.
Change your init method as follows and you should be good to go for a local environment:
sqlite = try SQLiteDatabase(storage: .file(path: "/Users/rutgerhuijsmans/Documents/runk-3.0/runk-3.0-database"))
I have a problem creating a unit test with EF Core (2.0.1).
I create my db with the following options:
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.ConfigureWarnings((b) =>
{
b.Ignore(InMemoryEventId.TransactionIgnoredWarning);
})
.Options;
The code I want to test looks something like this:
using (IDbContextTransaction transaction = await context.Database.BeginTransactionAsync())
{
await context.Database.ExecuteSqlCommandAsync("DELETE FROM fooSchema.Customers WHERE ID = {0}", id);
await context.SaveChangesAsync();
// Other stuff...
context.Customers.Add(fooCustomer);
await context.SaveChangesAsync();
}
First I had the issue with InMemory not supporting transactions. I solved it using ConfigureWarnings as shown in the code. But then it turns out InMemory doesn't handle ExecuteSqlCommandAsync. So then I tried SQLLite, but it doesn't handle custom schemas instead.
How do I create a DbContext, without any "real" DB, that handles transactions, schema and ExecuteSqlCommandAsync?
It is OK to suppress the error from ExecuteSqlCommandAsync. But I cannot find the EventId for it. In reality it works great, this is just for the unit test.
Connects without a hitch, but on insert() throws me this error.
var MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
var url = 'mongodb://____:____#ds125565.mlab.com:25565/heroku_w268n9pj';
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
db = client.db('temp');
console.log("connected!");
const collection = db.collection('temp');
collection.insert([{
something: please
}
});
I saw some other answers regarding mLab accounts and credentials, but I just created a new admin account for this. Frustrating because it was working previously with v2.3.
When attempting to connect to an mlab database, you have to correctly specify the client. It's located at the end of your connection string, just after the final forward slash.
mlab_url = "mongodb://db_user_name:db_password#ds139725.mlab.com:39725/heroku_sd1fp182?retryWrites=false"
client = MongoClient(url)
db = client["heroku_sd1fp182"]
collection = db["coinHack"]
You may also get the error:
This MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string.
Just add "?retryWrites=false" to your connection string, as shown above.
I have set up auth in my MongoDB as mentioned here. Initially, in my project I was accessing a single database say, firstdb from mongoose using
let url = "mongodb://localhost:27017/firstdb";
let options = {
server:{
socketOptions:{
keepAlive:120
}
},
user:"username1",
pass:"mypassword1"
};
mongoose.connect(url,options,callback);
The user with username and mypassword was created in the firstdb itself giving it readWrite perms. I did this while logged in with my admin user.
Things were working smoothly. Then I had a requirement of connecting to a second database. So I changed my code as such
let url1 = "mongodb://localhost:27017/firstdb";
let options1 = {
server:{
socketOptions:{
keepAlive:120
}
},
user:"username1",
pass:"mypassword1",
auth:{
authdb:"firstdb"
}
};
let connection1 = mongoose.createConnection(url1,options1);
let url2 = "mongodb://localhost:27017/seconddb";
let options2 = {
server:{
socketOptions:{
keepAlive:120
}
},
user:"username2",
pass:"mypassword2",
auth:{
authdb:"seconddb"
}
};
let connection2 = mongoose.createConnection(url2,options2);
This time I created user username2 the same way in the seconddb database. But now mongoose is unable to perform any operation and is failing with Not authorized to execute command. I can access the db through mongo shell though. I also spun up the code in my local system which doesn't have mongodb auth enabled and it works fine there. Please help
So, after shooting around in the blind, found something that works.
Use the username and password in the url itself like
let connection1 = mongoose.createConnection("mongodb://username1:password1#localhost:27017/firstdb");
Still, I would like to know why passing the parameters as options doesn't work.