👉 Back to contents | 🚀 Getting started | 🥞 Application server layers | 🧩 Application server features
Install npm install pg
and see docs: https://node-postgres.com/
Put connection options to application/config/database.js
, for example:
({
host: process.env.DB_HOST || '127.0.0.1',
port: 5432,
database: 'application',
user: 'marcus',
password: 'marcus',
});
Example with Client
class:
const client = new npm.pg.Client(config.database);
await client.connect();
const res = await client.query('SELECT * from CITIES where ID = $1', [25]);
console.log(res.rows[0]);
await client.end();
Example with Pool
class:
You can initialize connection on start from application/db/geo/start.js
:
async () => {
db.geo.pg = new npm.pg.Pool(config.database);
};
After that you can use Pool from anywhere like:
const res = await db.geo.pg.query('SELECT * from CITIES where ID = $1', [25]);
console.log(res.rows[0]);
Install npm install redis
and see docs: https://www.npmjs.com/package/redis
Initialize connection from application/db/redis/start.js
:
async () => {
db.redis.client = await npm.redis.createClient().connect();
};
After that use db.redis.client
from anywhere:
await db.redis.client.set('key', 'value');
const value = await db.redis.client.get('key');
Let's look at a more complex endpoint: application/api/geo/citiesByCountry.js
async ({ countryId }) => {
const fields = ['cityId', 'name'];
const where = { countryId };
const data = await db.pg.select('City', fields, where);
return { result: 'success', data };
};
We need Postgres server running and dependencies installed: npm i pg metasql
.
To initialize connection to the database add following code to file application/db/pg/start.js
:
async () => {
const options = { ...config.database, console };
db.pg = new metarhia.metasql.Database(options);
};
Also we need configuration in application/config/database.js
:
({
host: process.env.DB_HOST || '127.0.0.1',
port: 5432,
database: 'application',
user: 'marcus',
password: 'marcus',
});
Then need to have a tables in database. To generate it just add following schemas.
Schema application/schemas/Country.js
:
({
Entity: {},
name: { type: 'string', unique: true },
});
Schema application/schemas/City.js
:
({
Entity: {},
name: { type: 'string', unique: true },
country: 'Country',
});
Add database schemas configuration application/schemas/.database.js
:
({
name: 'example',
version: 1,
driver: 'pg',
});
Then generate SQL script by: npx metasql c
After that we will get application/schemas/database.sql
with two tables:
CREATE TABLE "Country" (
"countryId" bigint generated always as identity,
"name" varchar NOT NULL
);
ALTER TABLE "Country" ADD CONSTRAINT "pkCountry" PRIMARY KEY ("countryId");
CREATE UNIQUE INDEX "akCountryName" ON "Country" ("name");
CREATE TABLE "City" (
"cityId" bigint generated always as identity,
"name" varchar NOT NULL,
"countryId" bigint NOT NULL
);
ALTER TABLE "City" ADD CONSTRAINT "pkCity" PRIMARY KEY ("cityId");
CREATE UNIQUE INDEX "akCityName" ON "City" ("name");
ALTER TABLE "City" ADD CONSTRAINT "fkCityCountry" FOREIGN KEY ("countryId") REFERENCES "Country" ("countryId");
To put those tables in postgre server first run two SQL commands (you may use psql
or any other tool to do that):
CREATE USER marcus WITH PASSWORD 'marcus';
CREATE DATABASE application OWNER marcus;
Then execute database.sql
with psql
:
PGPASSWORD=marcus psql -d application -f application/schemas/database.sql -U marcus
Then we need a few records in each table:
INSERT INTO "Country" ("name") VALUES
('Italy'),
('Cuba');
INSERT INTO "City" ("name", "countryId") VALUES
('Rome', 1),
('Havana', 2),
('Livorno', 1);
const query = db.pg.sql`
SELECT * FROM "City"
WHERE "cityId" < ${5} AND "name" <> 'La Haye-en-Touraine'
ORDER BY name
LIMIT 3
`;
See more examples: https://github.com/metarhia/metasql/blob/master/test/sql.js
Put start
hook to application/db/pg/start.js
:
async () => {
db.pg = npm.knex({
client: 'pg',
connection: config.database
});
};
See docs here: https://knexjs.org/
Directory with database schema should contain following files:
File | Description |
---|---|
.database.js |
Schema parameters |
.types.js |
Custom types |
<EntityName>.js |
Entity definition |
Schema parameters .database.js
({
name: 'example',
description: 'Example database schema',
version: 3,
driver: 'pg',
authors: [
{ name: 'Timur Shemsedinov', email: '[email protected]' },
],
extensions: [
'hstore',
'postgis',
'postgis_topology',
'pg_trgm',
],
connection: {
host: '127.0.0.1',
port: 5432,
database: 'application',
user: 'postgres',
password: 'postgres',
},
});
Custom types .types.js
({
point: 'geometry(Point, 4326)',
});
Entity definition <EntityName>.js
Where <EntityName>
is a name of certain Entity (Class) of subject domain. For example, City.js
definition may looks like:
({
Registry: { realm: 'application', storage: 'append' },
country: 'Country',
name: { type: 'string', unique: true },
location: { type: 'point', required: false },
population: { type: 'number', default: 0 },
area: { // group of fields
total: '?number', // nullable field (shorthand for required: false)
water: '?number',
},
});
Here is Registry
is a metadata record. Other allowed kinkds:
Dictionary
- lookup table, have own id for primary key;Registry
- registry, uses global identifier for primary key;Entity
- entity, has own id for primary key;Journal
- access logs and other logs, have own id for primary key);Details
- details for registry or entity table, have own id for primary key;Relation
- detailes attached to the intersection of multiple entities;View
- named query database allow to select from as from regular table;Struct
- structure (in-memory, API contract or db complex data type);Scalar
- scalar value;Form
- user interface form;Projection
- schema projection;
Realm:
global
- universal and globally used data;application
- application specific data;local
- data stored on this certain server;
Storage: enum: ['persistent', 'append', 'view', 'memory'] }
.
Define custom primary key:
({
company: 'Company',
city: 'City',
companyCity: { primary: ['Company', 'City'] },
});
// This will generate pkCompanyCity
Many-to-many relation (Company-to-City):
// File: Company.js
({
name: 'string',
cities: { many: 'City' },
});
// This will generate CompanyCities cross-reference table
// with structure: { company, city }
Unique composite index:
({
country: 'Country',
name: 'string',
nameByCountry: { unique: ['country', 'name'] },
});
Not unique composite index:
({
street: 'string',
building: 'string',
apartment: 'string',
natural: { index: ['street', 'building', 'apartment'] },
});
Custom indexes (gin, gist):
({
name: 'string',
location: 'point',
akName: { index: 'gin (name gin_trgm_ops)' },
akLocation: { index: 'gist (location)' },
});
Put schemas to application/schemas
and run npx metasql c
after that you will find SQL DDL in application/schemas/database.sql
and TypeScript typings in application/schemas/database.d.ts
.
To be implemented in 3-6 months.
👉 Back to contents | 🚀 Getting started | 🥞 Application server layers | 🧩 Application server features