Failing to run migration Postgres on Heroku - node.js

Well, when I enter heroku bash and try to run npx typeorm migration:run it just throws me an error:
What's weird is that locally it works when the DATABASE is on localhost like this in .env file:
DATABASE_URL=postgres://postgres:docker#localhost:5432/gittin
This is my ormconfig.js:
module.exports = {
"type": "postgres",
"url": process.env.DATABASE_URL,
"entities": ["dist/entities/*.js"],
"cli": {
"migrationsDir": "src/database/migrations",
"entitiesDir": "src/entities"
}
}
Yes, I added the heroku postgres addon to the app.
PS: If needed, this is the repo of the project: https://github.com/joaocasarin/gittin

As I was discussing with Carlo in the comments, I had to add the ssl property in the ormconfig.js, but not only setting it to true when the environment was production. So according to this, I had to put { rejectUnauthorized: false } when production mode, and just false when not.
So the ormconfig.js is like this right now:
module.exports = {
"type": "postgres",
"ssl": process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
"url": process.env.DATABASE_URL,
"entities": ["dist/entities/*.js"],
"cli": {
"migrationsDir": "src/database/migrations",
"entitiesDir": "src/entities"
}
}

Related

Node, typeorm - I need to connect to Oracle without credentials

I am working with typeorm and Oracle with typescript, but I have the following problem when I want to connect.
Basically I want to use externalAuth but it tells me that the variable is read only, how can I solve it??
Cannot assign to 'externalAuth' because it is a read-only
import { DataSource } from "typeorm"
import { Empleado } from "./entity/Empleado"
import * as oracledb from "oracledb";
oracledb.externalAuth = true;
oracledb.initOracleClient({ libDir: 'C:\\instantclient_19_12' });
export const AppDataSource = new DataSource({
type: "oracle",
connectString: "xxxxx",
database: "xxxx",
synchronize: false,
logging: true,
entities: [Empleado],
migrations: [],
subscribers: [],
})```
From a quick test with the TypeORM sample app, setting externalAuth using the extra attribute in the project's ormconfig.json file like this seems to pass through the correct settings to node-oracledb:
{
"type": "oracle",
"connectString": "localhost/orclpdb1",
"synchronize": true,
"logging": false,
"extra" : {
"externalAuth": true
},
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migration/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
],
"cli": {
"entitiesDir": "src/entity",
"migrationsDir": "src/migration",
"subscribersDir": "src/subscriber"
}
}

Typeorm: When i restart server, drop BLOB column from database

I use Node Js with Typeorm and Mysql.
When i restart the server the BLOB img disappear from database, because the the server always drop the column ¿How can avoid this?
The drop:
query: ALTER TABLE `products_images` DROP COLUMN `img`
query: ALTER TABLE `products_images` ADD `img` blob NULL
Example of my code:
#Entity('products_images')
export class ProductsImages extends Base {
#Column("blob", { nullable: true })
img: Buffer;
}
you can made change in ormconfig.json file "synchronize" : false
and serve again
might be your problem is solved
its work for me
{
"type": "mysql",
"host": "",
"port": 3306,
"username": "root",
"password": "",
"database": "money",
"synchronize": false,
"logging": true,
"entities": ["dist/entity/**/*.js"],
"migrations": ["dist/migration/**/*.js"],
"subscribers": ["dist/subscriber/**/*.js"],
"cli": {
"entitiesDir": "./src/entity",
"migrationsDir": "./src/migration",
"subscribersDir": "./src/subscriber"
}
}
Remove the dist folder in your project and rerun the app.
This helps me.

typeorm config with .env variables

i have this ormconfig.json:
{
"type": "postgres",
"host": "db-pg",
"port": 5432,
"username": "spirit",
"password": "api",
"database": "emasa_ci",
"synchronize": true,
"logging": false,
"entities": ["dist/src/entity/**/*.js"],
"migrations": ["dist/src/migration/**/*.js"],
"subscribers": ["dist/src/subscriber/**/*.js"],
"cli": {
"entitiesDir": "dist/src/entity",
"migrationsDir": "dist/src/migration",
"subscribersDir": "dist/src/subscriber"
}
}
and have this env:
SERVER_PORT=4000
DB_HOST=db-pg
DB_PORT=5432
DB_USER=spirit
DB_PASS=api
DB_NAME=emasa_ci
but .env doesn't work in .json and so I don't know how I'm going to use my enviroment variables in my config orm
There is a good documentation.
If you want to dig into the source code - there is a class ConnectionOptionReader, which is looking for file ormconfig (with extensions env, js, cjs, ts, json, yml, yaml, xml) or for file .env. See the load function for more details.
1 So the easiest way is to add a line in your .env file, like this:
TYPEORM_URL=postgres://user:pass#host:port/dbname
Or use this sample. TypeORM will parse .env file using dotenv.
Here you can find all available env varibales.
2 If you read your .env file before TypeORM initialization, you can already use your env variables. For example in a Javascript file, instead ormconfig.json. Just export object like this from the file ormconfig.js:
module.exports = {
"type": "postgres",
"host": process.env.DB_HOST,
"port": process.env.DB_PORT,
"username": process.env.DB_USER,
"password": process.env.DB_PASS,
"database": process.env.DB_NAME,
"synchronize": true,
"logging": false,
"entities": ["dist/src/entity/**/*.js"],
"migrations": ["dist/src/migration/**/*.js"],
"subscribers": ["dist/src/subscriber/**/*.js"],
"cli": {
"entitiesDir": "dist/src/entity",
"migrationsDir": "dist/src/migration",
"subscribersDir": "dist/src/subscriber"
}
};
Another example
Since ormconfig is deprecated, I suggest another approach using TypeORM DataSource.
I'm using Heroku to deploy my server, so I created an environment file containing the same variable as created in Heroku Dynos, named .development.env:
DATABASE_URL="postgres://user:password#localhost:5432/main"
Note that you can place this file anywhere in your project tree.
Then I created a datasource file:
import dotenv from 'dotenv';
import { DataSource } from 'typeorm';
// Load env file
dotenv.config({ path: '../api/.development.env' });
DataSource definition
const AppDataSource = new DataSource({
type: 'postgres',
url: process.env.DATABASE_URL,
logging: true,
entities: ['../api/dist/**/*.entity.js'],
migrations: ['./migrations/*.js'],
subscribers: [],
});
export default AppDataSource;
This way, you can store in your environment your database connection settings.
You could hide ormconfig.json and put your secrets directly in there, or viceversa, load TypeORM's configuration from your .env file. Is there a precise reason why you need them to be separated? If yes, then we can work out a solution.

How configure TypeORM ormconfig.json file to parse Entities from js dist folder or ts src folder?

I set my TypeORM config entities path like:
"entities": ["src/entities/**/*.ts"]
This works good when I use ts-node. ts-node src/main.ts
After compile typescripts using tsc, I got a dist folder with the compiled application:
However, typeORM still tries to get entities from the src folder instead of dist. throwing a lot of unexpectec syntax errors for parsing a TS file instead of a JS. So I change the fallowing string to the entities condiguration:
"entities": ["dist/entities/**/*.js"]
It works with node node dist/main.js but it does not works with ts-node src/main.ts
How can I configure ormconfig.json to be able to work with both (node at dist folder and ts-node at src folder)?
I'd suggest using an ormconfig.js instead of the JSON version and use an environment variable or similar to switch between the two configs. eg; something like the following stripped down example.
const srcConfig = {
"entities": [
"src/entities/**/*.ts"
],
}
const distConfig = {
"entities": [
"dist/entities/**/*.js"
],
}
module.exports = process.env.TS_NODE ? srcConfig : distConfig;
Note you'll need to set the TS_NODE env var somewhere; I did notice there's a PR not yet merged that will do it.
In addition to the existing answer:
I didn't like the idea to much to always work on the dist folder. Why? Because when I run commands from the command line I don't want to check whether dist is up-to-date, meaning was recently compiled.
So, I like my my typeorm commands like typeorm schema:sync work on the src! You can do that by running them via ts-node.
So, instead of
typeorm schema:sync
use
// Linux
ts-node ./node_modules/.bin/typeorm schema:sync
// Windows
ts-node ./node_modules/typeorm/cli.js schema:sync
Background
typeorm cli uses node, which relies on the files being compiled to javascript. So, that would only work on the /dist folder, which is the compiled version. However, that requires a watcher or similar running to capture changes of your ORM files. The described way here compiles typescript on-the-fly making a compilation not required. Source: https://github.com/typeorm/typeorm/blob/master/docs/faq.md#how-to-use-typeorm-with-ts-node
Based upon lock answer:
My full ormconfig.js
//npm install --save "detect-ts-node"
const detectTSNode = require('detect-ts-node');
const commonConfig = {
"type": "mssql",
"host": "127.0.0.1",
"port": 1433,
"username": "sa",
"password": "$$$$$$",
"database": "$$$$$$",
"synchronize": true,
"logging": false,
"options": {
"encrypt": false,
"enableArithAbort": false
}
};
const srcConfig = {
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migration/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
],
"cli": {
"entitiesDir": "src/entity",
"migrationsDir": "src/migration",
"subscribersDir": "src/subscriber"
}
};
const distConfig = {
"entities": [
__dirname + "/dist/entity/**/*.js"
],
"migrations": [
__dirname + "/dist/migration/**/*.js"
],
"subscribers": [
__dirname + "/dist/subscriber/**/*.js"
],
"cli": {
"entitiesDir": __dirname + "/dist/entity",
"migrationsDir": __dirname + "/dist/migration",
"subscribersDir": __dirname + "/dist/subscriber"
}
};
const result = {};
let key;
// Append common configs to final object
for (key in commonConfig) {
if (commonConfig.hasOwnProperty(key)) {
result[key] = commonConfig[key];
}
}
if (detectTSNode) {
// if ts-node append src configuration
for (key in srcConfig) {
if (srcConfig.hasOwnProperty(key)) {
result[key] = srcConfig[key];
}
}
} else {
// else append dist configuration
for (key in distConfig) {
if (distConfig.hasOwnProperty(key)) {
result[key] = distConfig[key];
}
}
}
module.exports = result;
Usage in console.ts (my main file name)
import { createConnection } from "typeorm";
const conf = require('../ormconfig.js');
// Print the result for debuggin purposes
console.log(conf);
createConnection(conf).then(async connection => {
console.log("do your job here")
}).catch(error => {
console.log(error)
});

Issue using Ghost with Google Cloud SQL

I'm following the instructions here to use Ghost as an NPM module, and attempting to setup Ghost for production.
I'm running Google cloud sql proxy locally. When I run NODE_ENV=production knex-migrator init --mgpath node_modules/ghost I get this error message:
NAME: RollbackError
CODE: ER_ACCESS_DENIED_ERROR
MESSAGE: ER_ACCESS_DENIED_ERROR: Access denied for user 'root'#'cloudsqlproxy~[SOME_IP_ADDRESS]' (using password: NO)
Running knex-migrator init --mgpath node_modules/ghost works just fine, and I can launch the app locally with no problems. It's only the I try to setup the app for production that I get problems.
EDIT: I can connect to the db via MySQL Workbench, using the same credentials I'm using in the config below
Here's my config.production.json (with private data removed):
{
"production": {
"url": "https://MY_PROJECT_ID.appspot.com",
"fileStorage": false,
"mail": {},
"database": {
"client": "mysql",
"connection": {
"socketPath": "/cloudsql/MY_INSTANCE_CONNECTION_NAME",
"user": "USER",
"password": "PASSWORD",
"database": "DATABASE_NAME",
"charset": "utf8"
},
"debug": false
},
"server": {
"host": "0.0.0.0",
"port": "2368"
},
"paths": {
"contentPath": "content/"
}
}
}
And app.yaml:
runtime: nodejs
env: flex
manual_scaling:
instances: 1
env_variables:
MYSQL_USER: ******
MYSQL_PASSWORD: ******
MYSQL_DATABASE: ******
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance-name
INSTANCE_CONNECTION_NAME: ******
beta_settings:
# The connection name of your instance on its Overview page in the Google
# Cloud Platform Console, or use `YOUR_PROJECT_ID:YOUR_REGION:YOUR_INSTANCE_NAME`
cloud_sql_instances: ******
# Setting to keep gcloud from uploading not required files for deployment
skip_files:
- ^(.*/)?#.*#$
- ^(.*/)?.*~$
- ^(.*/)?.*\.py[co]$
- ^(.*/)?.*/RCS/.*$
- ^(.*/)?\..*$
- ^(.*/)?.*\.ts$
- ^(.*/)?config\.development\.json$
The file ghost.prod.config.js isn't something Ghost recognises - I'm not sure where that file name came from, but Ghost < 1.0 used config.js with all environments in one file, and Ghost >= 1.0 uses config.<env>.json with each environment in its own file.
Your config.production.json file doesn't contain your MySQL connection info, and therefore the knex-migrator tool is not able to connect to your DB.
If you merge the contents of ghost.prod.config.js into config.producton.json this should work fine.
Your config.production.json should look something like this:
{
"url": "https://something.appspot.com",
"database": {
"client": "mysql",
"connection": {
"socketPath": "path",
"user": "user",
"password": "password",
"database": "dbname",
"charset": "utf8"
}
}
}
The caveat here is that the new JSON format cannot contain code or logic, only explicit values, e.g. process.env.PORT || "2368" is no longer permitted.
Instead, you'll need to use either arguments or environment variables to provide dynamic configuration. Documentation for how to use environment variables is here: https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
E.g. NODE_ENV=production port=[your port] database__connection__user=[your user] ...etc... knex-migrator init --mgpath node_modules/ghost
You'd need to add an environment variable for every dynamic variable in the config.
I figured out the problem.
My config file shouldn't have the "production" property. My config should look like this:
{
"url": "https://MY_PROJECT_ID.appspot.com",
"fileStorage": false,
"mail": {},
"database": {
"client": "mysql",
"connection": {
"socketPath": "/cloudsql/MY_INSTANCE_CONNECTION_NAME",
"user": "USER",
"password": "PASSWORD",
"database": "DATABASE_NAME",
"charset": "utf8"
},
"debug": false
},
"server": {
"host": "0.0.0.0",
"port": "8080"
},
"paths": {
"contentPath": "content/"
}
}
It now overrides the default config.
The only issue is that you can't use knex-migrator with the "socketPath" property set, but this is needed to run the app in the cloud.

Resources