Skip to content

Commit 697f819

Browse files
committed
added objection and knex
1 parent 614924b commit 697f819

15 files changed

+1671
-28
lines changed

.env.example

+6
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
NODE_ENV=development
22
PORT=3000
3+
4+
DATABASE_URL=postgres://app:secret@localhost:5432/app
5+
DATABASE_DEBUG=true
6+
DATABASE_SSL=false
7+
DATABASE_MIN_CONNECTIONS=1
8+
DATABASE_MAX_CONNECTIONS=10

config/default.js

+12
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,16 @@ const { env } = process;
66
module.exports = {
77
env: env.NODE_ENV || 'development',
88
port: env.PORT || 3000,
9+
10+
database: {
11+
url: env.DATABASE_URL,
12+
ssl: env.DATABASE_SSL === 'true',
13+
debug: env.DATABASE_DEBUG === 'true',
14+
queryTimeout: env.DYNO && env.DYNO.startsWith('web.') ? 20000 : 300000,
15+
transactionTimeout: env.DYNO && env.DYNO.startsWith('web.') ? 20000 : 60000,
16+
connections: {
17+
min: parseInt(env.DATABASE_MIN_CONNECTIONS, 10) || 10,
18+
max: parseInt(env.DATABASE_MAX_CONNECTIONS, 10) || 100,
19+
},
20+
},
921
};

database/base/BaseModel.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const path = require('path');
2+
const { Model } = require('objection');
3+
const BaseQueryBuilder = require('./BaseQueryBuilder');
4+
5+
class BaseModel extends Model {
6+
static get modelPaths() {
7+
return [path.join(__dirname, '..', 'models')];
8+
}
9+
10+
static get QueryBuilder() {
11+
return BaseQueryBuilder;
12+
}
13+
14+
static get useLimitInFirst() {
15+
return true;
16+
}
17+
18+
$beforeInsert() {
19+
this.created_at = new Date().toISOString();
20+
this.updated_at = new Date().toISOString();
21+
}
22+
23+
$beforeUpdate() {
24+
this.updated_at = new Date().toISOString();
25+
}
26+
}
27+
28+
module.exports = BaseModel;

database/base/BaseQueryBuilder.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { QueryBuilder } = require('objection');
2+
3+
class BaseQueryBuilder extends QueryBuilder {
4+
5+
}
6+
7+
module.exports = BaseQueryBuilder;

database/index.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const Knex = require('knex');
2+
const { Model } = require('objection');
3+
const knexConfig = require('../knexfile.js');
4+
5+
// Initialize knex.
6+
const knex = Knex(knexConfig);
7+
8+
// Bind all Models to the knex instance.
9+
// This only needs to be done once before any models can be used.
10+
Model.knex(knex);
11+
12+
module.exports = knex;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
exports.up = async (knex) => {
2+
await knex.schema.createTable('items', (table) => {
3+
table.increments('id');
4+
table.string('name').notNullable().unique();
5+
table.string('name_technical').notNullable().unique();
6+
table.integer('defindex').notNullable().unique();
7+
table.string('image_url').notNullable();
8+
table.string('class').notNullable().index();
9+
table.string('type').notNullable().index();
10+
table.timestamps(false, true);
11+
});
12+
};
13+
14+
exports.down = async (knex) => {
15+
await knex.schema.dropTable('items');
16+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
exports.up = async (knex) => {
2+
await knex.schema.createTable('paintkits', (table) => {
3+
table.increments('id');
4+
table.string('name').notNullable().index();
5+
table.string('name_technical').notNullable().unique();
6+
table.integer('defindex').notNullable().unique();
7+
table.timestamps(false, true);
8+
});
9+
};
10+
11+
exports.down = async (knex) => {
12+
await knex.schema.dropTable('paintkits');
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
exports.up = async (knex) => {
2+
await knex.schema.createTable('skins', (table) => {
3+
table.increments('id');
4+
table.integer('item_id').notNullable().index();
5+
table.integer('paintkit_id').nullable().index();
6+
table.string('name').notNullable().index();
7+
table.string('name_technical').notNullable().unique();
8+
table.string('image_url').notNullable();
9+
table.timestamps(false, true);
10+
11+
table.foreign('item_id').references('id').inTable('items');
12+
table.foreign('paintkit_id').references('id').inTable('paintkits');
13+
table.unique(['item_id', 'paintkit_id']);
14+
});
15+
};
16+
17+
exports.down = async (knex) => {
18+
await knex.schema.dropTable('skins');
19+
};

database/models/Item.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const Model = require('../base/BaseModel');
2+
3+
class Item extends Model {
4+
static get tableName() {
5+
return 'items';
6+
}
7+
8+
static get idColumn() {
9+
return 'id';
10+
}
11+
12+
static get relationMappings() {
13+
return {
14+
skins: {
15+
relation: Model.HasManyRelation,
16+
modelClass: 'Skin',
17+
join: {
18+
from: 'items.id',
19+
to: 'skins.item_id',
20+
},
21+
},
22+
};
23+
}
24+
}
25+
26+
module.exports = Item;

database/models/Paintkit.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const Model = require('../base/BaseModel');
2+
3+
class Paintkit extends Model {
4+
static get tableName() {
5+
return 'paintkits';
6+
}
7+
8+
static get idColumn() {
9+
return 'id';
10+
}
11+
12+
static get relationMappings() {
13+
return {
14+
skins: {
15+
relation: Model.HasManyRelation,
16+
modelClass: 'Skin',
17+
join: {
18+
from: 'paintkits.id',
19+
to: 'skins.paintkit_id',
20+
},
21+
},
22+
};
23+
}
24+
}
25+
26+
module.exports = Paintkit;

database/models/Skin.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const Model = require('../base/BaseModel');
2+
3+
class Skin extends Model {
4+
static get tableName() {
5+
return 'skins';
6+
}
7+
8+
static get idColumn() {
9+
return 'id';
10+
}
11+
12+
static get relationMappings() {
13+
return {
14+
item: {
15+
relation: Model.BelongsToOneRelation,
16+
modelClass: 'Item',
17+
join: {
18+
from: 'skins.item_id',
19+
to: 'items.id',
20+
},
21+
},
22+
23+
paintkit: {
24+
relation: Model.BelongsToOneRelation,
25+
modelClass: 'Paintkit',
26+
join: {
27+
from: 'skins.paintkit_id',
28+
to: 'paintkits.id',
29+
},
30+
},
31+
};
32+
}
33+
}
34+
35+
module.exports = Skin;

docker-compose.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
version: '3.3'
2+
3+
services:
4+
postgres:
5+
image: postgres:12
6+
volumes:
7+
- .:/app
8+
environment:
9+
POSTGRES_PASSWORD: secret
10+
POSTGRES_USER: app
11+
POSTGRES_DB: app
12+
TZ: UTC
13+
ports:
14+
- 5432:5432

knexfile.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const config = require('config');
2+
const async = require('async');
3+
const pg = require('pg');
4+
5+
// In order to enable SSL, it seems to be enough to set `pg.defaults.ssl`
6+
// to `true` or to an empty object. With Heroku however, we also need to
7+
// explicitly allow self-signed SSL certificates. That's why we set
8+
// `rejectUnauthorized` to `false`.
9+
if (config.get('database.ssl')) {
10+
pg.defaults.ssl = {
11+
rejectUnauthorized: false,
12+
};
13+
}
14+
15+
module.exports = {
16+
client: 'pg',
17+
connection: config.get('database.url'),
18+
debug: config.get('database.debug'),
19+
migrations: {
20+
directory: './database/migrations',
21+
tableName: 'knex_migrations',
22+
},
23+
seeds: {
24+
directory: './database/seeds',
25+
},
26+
acquireConnectionTimeout: 10000,
27+
pool: {
28+
min: config.get('database.connections.min'),
29+
max: config.get('database.connections.max'),
30+
// This method can be used to execute SQL statements right after a
31+
// connection has been established.
32+
afterCreate: (conn, done) => {
33+
const queries = [
34+
// Terminate any session with an open transaction that has been idle
35+
// for longer than the specified duration in milliseconds.
36+
`set idle_in_transaction_session_timeout to ${config.get('database.transactionTimeout')};`,
37+
38+
// Abort any statement that takes more than the specified number of
39+
// milliseconds, starting from the time the command arrives at the
40+
// server from the client.
41+
`set statement_timeout to ${config.get('database.queryTimeout')};`,
42+
];
43+
44+
async.eachSeries(queries, async (query) => new Promise((resolve, reject) => {
45+
// Execute the query and wait for the callback to check if any
46+
// error occurred.
47+
conn.query(query, (error) => {
48+
if (error) {
49+
// The query returned an error, we'll have to abort the further
50+
// execution of any remaining queries and return the error.
51+
reject(error);
52+
} else {
53+
// The query successfully completed, we can execute the next one.
54+
resolve();
55+
}
56+
});
57+
}))
58+
.then(() => {
59+
// All queries have been successfully run and no error occurred.
60+
// Tell Knex that we're done with the setup of the connection.
61+
done(false, conn);
62+
})
63+
.catch((error) => {
64+
// If `error` is not falsy, the connection is discarded from the pool.
65+
// If connection acquire was triggered by a query, then the error is
66+
// passed to query promise.
67+
done(error, conn);
68+
});
69+
},
70+
},
71+
};

0 commit comments

Comments
 (0)