ORT-NODEJS(1) | General Commands Manual | ORT-NODEJS(1) |
ort-nodejs
—
generate node.js module
ort-nodejs |
[-ev ] [-N
db] [config...] |
Accepts ort(5) config files, defaulting to standard input, and generates a Node module in TypeScript. Its arguments are as follows:
-e
import
statements,
nor are top-level namespaces, classes, and functions marked as
export
. This is useful for embedding instead of
using as a module. This flag may be deprecated in the future.-v
-N
bdThe output requires only the "typescript", "better-sqlite3", "bcrypt", and optionally "validator" dependencies. The output is documented using the typedoc(1) language.
An application context is initialised with the top-level function. It throws an exception on database error.
ort
(dbname:
string, args?: ortargs):
ortdbThe ortdb object has the following properties and methods:
connect
():
ortctxort
(),
is set to the default values.ort-nodejs
used to create the
module.ort-nodejs
used to create
the module.Each sequence of database interaction begins with
the
connect
()
method from the application-wide instance. The resulting
ortctx object has the following role methods:
db_role
(newrole:
string): voidprocess.abort
().db_role_current
():
stringdb_role
() hasn't yet been called, this will be
"default".The following ortctx methods should be used for transaction management. These throw an exception on database error.
db_trans_open_immediate
(id:
number): voiddb_trans_open_deferred
(id:
number): voiddb_trans_open_exclusive
(id:
number): voiddb_trans_commit
(id:
number): voiddb_trans_open_immediate
(),
db_trans_open_deferred
(), or
db_trans_open_exclusive
() with identifier
id.db_trans_rollback
(id:
number): voiddb_trans_open_immediate
(),
db_trans_open_deferred
(), or
db_trans_open_exclusive
() with identifier
id.Any insert
statements are output as
follows. Let "foo" be the name of the exemplar structure in this
and subsequent prototypes. Throws an exception on database error.
db_foo_insert
(ARGS):
bigintrowid
, which is automatically set by the
database. If any fields are specified as null
,
they may be passed as null
values.Query statements count
,
iterate
, list
,
search
are output as follows. These throw an
exception on database error.
db_foo_count
():
bigintdb_foo_count_xxxx
()
but returning a count of all rows.db_foo_count_xxxx
(ARGS):
bigintdb_foo_get_xxxx
(),
but returning a count of responses.db_foo_count_by_xxxx_op1_yy_zz_op2
(ARGS):
bigintdb_foo_get_by_xxxx_op1_yy_zz_op2
(),
but returning a count of responses.db_foo_get_xxxx
(ARGS):
ortns.foo|nullsearch
statement named "xxxx". The
function accepts variables for all binary-operator fields to check (i.e.,
all except for those checking for null).db_foo_get_by_xxxx_op1_yy_zz_op2
(ARGS):
ortns.foo|nulldb_foo_get_xxxx
(), but for (possibly-nested)
structures. In the given example, "xxxx" is a field in the given
structure with operation "op1" and "yy_zz" means a
field "zz" in the nested structure "yy" with operation
"op2".db_foo_iterate
(ARGS,
cb): voiddb_foo_iterate_xxxx
()
but iterating over all rows.db_foo_iterate_xxxx
(ARGS,
cb): voiddb_foo_get_xxxx
(), but invoking a function
callback per row. The cb callback accepts a single
parameter of type ortns.foo and does not have a
return value.db_foo_iterate_by_xxxx_op1_yy_zz_op2
(ARGS,
cb): voiddb_foo_get_by_xxxx_op1_yy_zz_op2
(), but
invoking a function callback for each retrieved result.db_foo_list
():
ortns.foo[]db_foo_list_xxxx
()
but allocating and filling a queue of all rows.db_foo_list_xxxx
(ARGS):
ortns.foo[]db_foo_get_xxxx
(), but producing an array of
responses.db_foo_list_by_xxxx_op1_yy_zz_op2
(ARGS):
ortns.foo[]db_foo_get_by_xxxx_op1_yy_zz_op2
(), but
producing a queue of responses.Any update
statements in the configuration
are output as the following methods on ortctx. These
throw an exception on database error.
db_foo_update_xxxx
(ARGS):
booleanupdate
statements. The parameters
passed to this function are first the fields to modify, then the fields
that constrain which rows are updated. Update fields are only specified
for operations for binary-operator constraints, i.e., those not checking
for null status. Returns true on success, false on constraint
failure.db_foo_update_xx_mod_by_yy_op
(ARGS):
booleandb_foo_update_xxxx
(), but using an un-named
update statement modifying "xx" with modifier "mod"
constrained by "yy" with operation "op". Either or
both modifiers and constraints may be empty. If modifiers are empty, all
fields are modified by setting. If constraints are empty, they and the
preceding "by" are omitted.Any delete
statements in the configuration
are output as the following methods on ortctx. These
throw an exception on database error.
db_foo_delete_xxxx
(ARGS):
voiddelete
function "xxxx".
The ARGS passed to this function are the fields that
constrain which rows are deleted. Parameters are only specified for
operations for binary-operator constraints, i.e., those not checking for
null status.db_foo_delete_by_yy_op
(ARGS):
voiddb_foo_delete_xxxx
(), but using an un-named
delete
statement constrained by "yy"
with operation "op".All database functions will throw an exception if errors occur in talking to the underlying database. The exception object returned is of type SqliteError, inheriting from the generic Error. It is documented in the "better-sqlite3", but consists of two pertinent members:
Any enumerations or bitfields (enum
,
bitf
) in the configuration are output in the
ortns namespace and named as themselves. Each
enumeration item is serialised as a string property.
Bitfields have two property per item: one for the bit index, one for the
produced mask. These are prefixed by "BITI" and "BITF",
respectively.
namespace ortns { enum anenum { item2 = '3' } enum abitf { BITI_item2 = '3', BITF_item2 = '8', } }
Each struct
is output as an interface in
the ortns namespace and named as itself followed by
"Data", such as ortns.fooData. All items of
the structure are mapped to properties with the following types:
bit |
bigint |
bits |
bigint |
blob |
Buffer |
date |
bigint |
email |
string |
enum |
ortns.enum |
epoch |
bigint |
int |
bigint |
password |
string |
real |
number |
text |
string |
Since bitfields are combinations of bits in their respective enumerations, they are represented by bigint and not the enumeration type.
If a field is marked as null
, it will also
be given the null type, such as
bigint|null.
namespace ortns { interface barData { anint: bigint; astring: string; anulldate: bigint|null; } }
Data objects returned by query methods are output as classes in the ortns namespace and named as in the configuration. Letting "foo" be an exemplar structure name, ortns.foo, the object consists of the following.
export
():
anyJSON.stringify
()
to output JSON objects.The exported object, when converted into a string, is readable by applications using the ort-javascript(1) tool.
If run with -v
,
ort-nodejs
outputs validation functions for each
native field type in an object ortvalid.ortValids,
with a validator for each field. The fields (object properties) are named
struct-field.
Validator functions are typed according to their mapped field
types as described in Data
structures: (value?: any) => TYPE|null, and
accept the value (which may be undefined) of the request input. These return
null
when the input is undefined,
undefined
, null
, fails any
user-defined validation, or the following:
bit |
not in 0–63 |
date |
not ISO-8601 format |
epoch |
not 64-bit signed integer |
int |
not 64-bit signed integer |
real |
not 64-bit decimal number |
email |
not valid e-mail |
bits |
not contained in 64 bits |
The ort-nodejs
utility exits 0 on
success, and >0 if an error occurs.
The following example is a full web-server running on port 3000 using the Node framework. It uses the "express", framework for web requests, "validator" for input validation, "bcrypt" for passwords, and "better-sqlite3" for the database. It mandates the use of TypeScript instead of JavaScript. It needs only the npm(1) system installed and (depending on the operating system) a C/C++ compiler for native packages.
Begin a project (if not already begun) as follows:
% cd myproject % npm init -y % npm install typescript better-sqlite3 express bcrypt % npm install @types/express @types/bcrypt @types/better-sqlite3 % npx tsc --init
For validation:
% npm install validator @types/validator
When installing "better-sqlite3" on OpenBSD, you may need to specify an alternate Python interpreter:
% PYTHON=/usr/local/bin/python2.7 \ npm install better-sqlite3
Older versions of OpenBSD required
overriding CXX
and CC
with
ports-installed versions for both "better-sqlite3" and
"bcrypt".
Modify package.json to mandate the use of TypeScript instead of JavaScript:
[...] "main": "index.ts", "scripts": { "tsc": "tsc" } [...]
Next, modify tsconfig.json to use a more up-to-date output type for JavaScript, otherwise many TypeScript security idioms will not be available.
"target": "es2015",
Now use the following toy ort(5) configuration installed as myproject.ort:
roles { role user; }; struct user { field name text limit gt 0; field id int rowid; insert; search id: name id; roles default { all; }; };
Compile the configuration as a module. This assumes that validation is also required.
% mkdir modules % ort-nodejs -v myproject.ort > modules/ort.ts
Use the following simple application:
import express from 'express'; import { ort, ortns, ortctx, ortdb, ortvalid } from './modules/ort'; const app: express.Application = express(); const db: ortdb = ort('test.db'); app.get("/put", function(req: express.Request, res: express.Response) { const ctx: ortctx = db.connect(); const name: string|null = ortvalid.ortValids ['user-name'](req.query['user-name']); if (name === null) return res.status(400).send('bad'); const id: bigint = ctx.db_user_insert(name); return res.send(id.toString()); } ); app.get("/get", function(req: express.Request, res: express.Response) { const ctx: ortctx = db.connect(); const id: bigint|null = ortvalid.ortValids ['user-id'](req.query['user-id']); if (id === null) return res.status(400).send('bad'); const obj: ortns.user|null = ctx.db_user_get_id(id); if (obj === null) return res.status(404).send('not found'); return res.send(JSON.stringify(obj.export())); } ); app.listen(3000, function() { console.log('Server is running.'); });
Compile the application. This will create index.js.
% npm run tsc
Make sure that the database exists. This should only be run once.
% ort-sql db.ort | sqlite3 test.db
Lastly, run the project itself:
% node index.js Server is running.
Making an HTTP request to "localhost:3000/get?user-id=nnn" will result in a display of the created user's identifier, while "localhost:3000/put?user-name=xxx" will create one.
October 25, 2021 | OpenBSD 6.7 |