Can`t connect my MySQL database in Vapor project - swift

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

Related

Is it possible to pull data from Heroku Postgresql?

Currently I am working on a telegram bot using Heroku and Flask to deploy it. I wanted to store some data into a database then I came across Heroku PostgreSQL. I managed to store data into it but I am having trouble retrieving these data from Heroku PostgreSQL. I have research quite a bit but couldn't find much information about the process on pulling the data. Can someone enlighten me or suggest what are the steps to retrieve/pull these data from Heroku PostgreSQL with Flask?
The Telegram Bot is basically a random food generator. I have stored a list of food into the data and wanted to pull these data and random generator whenever user trigger the bot/action.
Screenshot of the current heroku postgresql database
from os import environ
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from invokes import invoke_http
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = environ.get("DB_URL") or "postgres://database-url"
app.config["SQLALCHEMY_TRACK_MODIFICATION"] = false
db = SQLAlchemy(app)
CORS(app)
class Cusine(db.Model):
__tablename__ = "cusines"
id = db.Column(db.String(255), primary_key =True)
cusine = db.Column(db.String(255), nullable=False)
def __init__(self, id, cusine):
self.id = id
self.cusine = cusine
def getId(self):
return self.id
def getCusine(self):
return self.cusine
def json(self):
return {"id": self.id,"cusine":self.cusine}
db.create_all()
#app.route("/getAllCusine")#
def getAllCusine():
try:
all_cusine = Cusine.query.all()
cusines = []
for row in all_cusine:
cusines.append(row.cusine)
return jsonify(
{
"code": 200,
"message": cusines
}
),200
except:
return jsonify(
{
"code": 500,
"message": "error in getting all course"
}
),500
So, how can I pull the data from Heroku PostgreSQL using Flask?
Thank you in advance.
I have managed to extract the data from Heroku PostgreSQL.
I have changed the above code with the following.
import os
import psycopg2
DATABASE_URL = os.environ['DATABASE_URL']
def retrieveData():
try:
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
cursor = conn.cursor()
cursor.execute("select * from table") #some sql statement
conn.commit()
result = cursor.fetchall()
cursor.close()
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return result
It will return a list of tuple.
Remember to pip install psycopg2-binary
It is my very first time working with Heroku and PostgreSQL, which took me quite awhile to figure out. But thanks guy for teaching me how to properly use stackoverflow, appreciate it. Hopefully this can help someone out there (like me), who is new to PostgreSQL. Cheers :)

Having Trouble Initializing Postgresql Database Through PG-Promise

I'm completely new to Express/Postgresql and I'm trying to learn them to create a web application. After some poking around, I decided that I wanted to develop my back-end with TypeScript. I successfully converted all my other files from JavaScript to TypeScript, but I still can't figure out how to initialize the pg-promise connection in TypeScript!
I've been trying to follow the TypeScript guidelines in this link here. https://github.com/vitaly-t/pg-promise/tree/master/typescript
// Initialize the PostGres database conneciton for use throughout
the entire application
import {IMain, IDatabase} from 'pg-promise';
import * as pgPromise from 'pg-promise';
const pgp: IMain = pgPromise({
query(e: any) {
console.log('QUERY RESULT:', e.query);
},
receive(data: any, result: any, e: any) {
console.log(`DATA FROM QUERY ${e.query} WAS RECEIVED.`);
}
});
const connection: any = {
host: 'localhost',
port: 5432,
database: 'RushHub',
user: 'RyanArifin',
password: null
}
const db: IDatabase<any> = pgp(connection);
export {
db
};
I currently am getting the error "TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof pgPromise' has no compatible call signatures." This error is coming from when I try to set my initialization options. Any help would be appreciated, thanks!
This is standard TypeScript configuration flag - esModuleInterop. When set to true, the import syntax is import pgPromise from 'pg-promise';, and when false, which is the default, the syntax is import * as pgPromise from 'pg-promise';.
The library gives you example for the default TypeScript configuration.

How do I log SQL statements in Vapor 3/Fluent?

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.

Setting up database connection using Vapor framework

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"))

Setting up MongoLab or MongoHQ with Play Framework on Heroku

I can't seem to find any instructions for setting up either MongoHQ or MongoLab with Play Framework on Heroku.
Is there something similar to this which is for the Spring Framework (I believe) https://devcenter.heroku.com/articles/mongohq#use-with-java that I can follow?
Or does anyone have any instructions that they can provide.
MongoHQ adds a MONGOHQ_URL to your Heroku config environment
heroku config
Which returns something like
MONGOHQ_URL: mongodb://heroku:1234#someserver.mongohq.com:10071/app1234
Use this envronment variable inside your application to fetch which URI to connect Mongo to. I have an overriden property that can be specified in the Procfile
web: target/start -Dhttp.port=$PORT -Dmongodb.uri=$MONGOHQ_URL
And in a Play model class I connect and query Mongo with:
val mongoUri = MongoUri(Play.configuration.getString("mongodb.uri").get)
val mongoCollection = mongoUri.connectDB match {
case Left(thrown) => throw thrown
case Right(database) => {
mongoUri.username.map { username =>
mongoUri.password.map { password =>
database.underlying.authenticate(username,password)
}
}
database.apply("mycollection")
}
}
def findSomething(something:String) = {
val searchTerm = MongoDBObject("something" -> something)
mongoCollection.findOne(searchTerm) map { mongoObject =>
Logger.info("Found %s".format(mongoObject.getAs[String]("somethingElse")))
}
}
Assumptions made: Play 2.0, Scala, Casbah
Here is an example app that uses MongoHQ, Play 2, Scala, and works on Heroku:
https://github.com/jamesward/play2bars/tree/scala-mongodb
There aren't really instructions, just some simple code.