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.
Related
I'm trying to setup MongoDB connection library function. I know this function is solid, its used in a whole lot of places (search for Global is used here to maintain a cached connection across hot reloads) and you'll find a whole lot of uses including next.js releases. Note, the purpose of global storage for the database connection is to reduce the overall # of db connections in use at any one time.
What I'm not understanding is the error I'm getting when I import this library via import { connectToDatabase } from '$lib/database';
database.js
// https://github.com/mongodb-developer/mongodb-next-todo/blob/main/util/mongodb.js
import { ENV_OBJ } from "$lib/env";
import { MongoClient } from "mongodb";
const uri = ENV_OBJ.MONGODB_URI;
if (!uri) {
throw new Error("Please define the Mongodb uri environment variable inside .env");
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
let cached = global.mongo
if (!cached) {
cached = global.mongo = { conn: null, promise: null }
}
export const connectToDatabase = async() => {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const options = {
useNewUrlParser: true,
useUnifiedTopology: true
};
cached.promise = MongoClient.connect(MONGODB_URI, opts).then((client) => {
return {
client,
db: client.db(MONGODB_DB),
}
})
}
cached.conn = await cached.promise;
return cached.conn;
}
The errors:
global is not defined
ReferenceError: global is not defined
at node_modules/mongodb/lib/promise_provider.js (http://localhost:3000/node_modules/.vite/mongodb.js?v=3885e04e:548:25)
at __require2 (http://localhost:3000/node_modules/.vite/chunk-6ODJH7E3.js?v=3885e04e:10:44)
at node_modules/mongodb/lib/utils.js (http://localhost:3000/node_modules/.vite/mongodb.js?v=3885e04e:6524:30)
at __require2 (http://localhost:3000/node_modules/.vite/chunk-6ODJH7E3.js?v=3885e04e:10:44)
at node_modules/mongodb/lib/cursor/abstract_cursor.js (http://localhost:3000/node_modules/.vite/mongodb.js?v=3885e04e:10873:19)
at __require2 (http://localhost:3000/node_modules/.vite/chunk-6ODJH7E3.js?v=3885e04e:10:44)
at node_modules/mongodb/lib/index.js (http://localhost:3000/node_modules/.vite/mongodb.js?v=3885e04e:25281:29)
at __require2 (http://localhost:3000/node_modules/.vite/chunk-6ODJH7E3.js?v=3885e04e:10:44)
at http://localhost:3000/node_modules/.vite/mongodb.js?v=3885e04e:25616:23
Note, I do see a file in my generated minimal sveltekit repo called global.d.ts I'm not sure of its purpose. It contains only:
/// <reference types="#sveltejs/kit" />
Any ideas on what's causing the error?
Reference: "#sveltejs/kit": "version": "1.0.0-next.118",
Edit: After spending a whole lot of time on this issue, the global not defined error seems to come from import { MongoClient } from "mongodb"; If I add appropriate console.logs, I can see that the MongoClient function works fine on the server, but then I get the global error on the client. The server indicates no errors at all.
So it turns out I was calling import { connectToDatabase } from '$lib/database' not in a .js helper file or api style (.js) endpoints. I was attempting to use that import and make a database call directly from the <script> portion of a xxx.svelte file.
Definite no go. That generates an immediate global not defined error.
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
We're using HashiCorp's Vault to store database connection credentials, then constructing the connection string for pg-promise from those. The 'catch' is that the Vault details are provided from a Promise wrapper, due to request callbacks to the Vault API.
Example database.js module:
const pgp = require('pg-promise')(/* options obj */);
const getDbo = () => {
return new Promise( (resolve, reject) => {
vault.init().then(secrets => {
let credentials = secrets.dbUser + ':' + secrets.dbPass
let connStr = 'postgres://' + credentials + '<#endpoint/db>'
let dbo = pgp(connStr, (err) => {
reject(err)
})
resolve(dbo);
})
}
module.exports = { get: getDbo }
This is being imported in multiple routes. With this we are seeing the warning "WARNING: Creating a duplicate database object for the same connection." Is there a better way to resolve this so there is only one object per connection details?
Creating and initializing a connection for pg-promise is a completely synchronous operation, as per the API, so there is no point using promises for that.
For initializing the library see Where should I initialize pg-promise.
See also:
Verify database connection with pg-promise when starting an app.
I'm porting the ember portion of an ember-rails app to Ember CLI. So far so good, but I'm having trouble with a get request that's blowing up in RESTSerializer.
The Application Controller attempts to grab a list of widgets
`import Ember from 'ember'`
ApplicationController = Ember.Controller.extend
widgets: (->
#store.find('unfinishedWidgets', id: #get('currentId'))
).property()
`export default ApplicationController`
I can see the API Request fire off, the JSON comes back, everything looks just like it did in the Ember Rails stack. Except then instead of updating the property and displaying in the view, it blows up:
TypeError: undefined is not a function
at Object.func (http://localhost:4200/myapp/assets/vendor.js:49473:18)
at Object.Cache.get (http://localhost:4200/myapp/assets/vendor.js:25091:38)
at decamelize (http://localhost:4200/myapp/assets/vendor.js:49515:31)
at RESTSerializer.extend.keyForAttribute (http://localhost:4200/myapp/assets/vendor.js:66565:16)
at apply (http://localhost:4200/myapp/assets/vendor.js:32821:27)
at superWrapper [as keyForAttribute] (http://localhost:4200/myapp/assets/vendor.js:32393:15)
at null.<anonymous> (http://localhost:4200/myapp/assets/vendor.js:69024:31)
at null.<anonymous> (http://localhost:4200/myapp/assets/vendor.js:71513:20)
at cb (http://localhost:4200/myapp/assets/vendor.js:29067:22)
at OrderedSet.forEach (http://localhost:4200/myapp/assets/vendor.js:28865:13) vendor.js:28532logToConsole vendor.js:28532RSVP.onerrorDefault vendor.js:42608__exports__.default.trigger vendor.js:61072Promise._onerror vendor.js:62084publishRejection vendor.js:60315(anonymous function) vendor.js:42583DeferredActionQueues.invoke vendor.js:13853DeferredActionQueues.flush vendor.js:13923Backburner.end vendor.js:13309Backburner.run vendor.js:13364run vendor.js:31375hash.success vendor.js:68006fire vendor.js:3237self.fireWith vendor.js:3349done vendor.js:9393callback
I put in a breakpoint at decamelize, which stopped so I could inspect what was going on:
function decamelize(str) {
return DECAMELIZE_CACHE.get(str);
}
str at this point isn't a string, it's:
Object {type: undefined, isAttribute: true, options: Object, parentType: function, name: "bundleId"}
isAttribute: true
name: "otherId"
options: Object
parentType: (subclass of DS.Model)
type: undefined__proto__: Object
So this is the first DS.attr() in my Model:
`import DS from 'ember-data'`
unfinishedWidgets = DS.Model.extend
otherId: DS.attr()
# other attrs
`export default UnsubmittedRequest`
I'm using a ActiveModelAdapter by default, and I've also made an empty ActiveModelSerializer.
`import DS from 'ember-data'`
ApplicationAdapter = DS.ActiveModelAdapter.extend
namespace: 'api/myapp/v1'
`export default ApplicationAdapter`
`import DS from 'ember-data'`
ApplicationSerializer = DS.ActiveModelSerializer.extend()
`export default ApplicationSerializer`
EDIT:
I ended up fixing it with:
ApplicationSerializer = DS.ActiveModelSerializer.extend
keyForAttribute: (type, name) ->
name
Though I'm still not clear on why it was necessary in Ember CLI when it was fine in Ember-rails
This happens when your server returns an invalid json response. In this case it's not returning with a root object.
You're server is likely returning something like this
{
other_id: 1
// ...
}
and it needs to be
{
unfinished_widget: {
other_id: 1
// ...
}
}
The ActiveModelAdapter/Serializer expects it in that style. Honestly this error is terrible and should be reported as a bug in ember-data. I've hit it before and it's super hard to debug and track down.
So, as it turns out, it was simply a bug in EmberData 1.0.0.beta.10. I updated to beta 12 and everything worked.
I have a grails application in which Im using db.authenticate for a login page but I understand this method has been deprecated and therefore I would like to upgrade my application to using the MongoCredential object for authentication. However, unlike the db.authenticate which nicely returns a boolean to get authentication done, the MongoCredential doesn't return a boolean so how can I go about accomplishing the code replacement with minimal headache. Ideally, I'd like to derive some kind of a boolean to tell me if authentication was achieved. Thanks for your patience. Im a newbie with Mongodb.
This is part of the code I need to replace which currently makes use of the deprecated method "authenticate":
MongoClient mongoClient = new MongoClient("localhost", 27017)
DB db = mongoClient.getDB("twcdb");
def userName = params.username
def passWord = params.password
//deprecated method being used in the line below.
boolean auth = db.authenticate(userName, passWord.toCharArray())
if (auth) {
userloggedin = params.username
render(contentType: 'text/json') {
[success: true, url: createLink(controller: 'admin', action: 'loggedin')]
}
}
else {
render(contentType: 'text/json') {
["success": false, "message": 'Login or Password is incorrect.']
}
Edit: I know that the answer must lie in testing a property of the MongoClient object somehow to see if it contains a valid authenticated connection but I am still stuck on how to do this. Given I knowingly feed the MongoClient constructor with a bogus MongoCredential, it still creates an object that isn't null. I was betting on the null test but no joy. So, how do I replace the deprecated db.authenticate?