![require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages](https://cdn.sanity.io/images/cgdhsj6q/production/be8ab80c8efa5907bc341c6fefe9aa20d239d890-1600x1097.png?w=400&fit=max&auto=format)
Security News
require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
lambdaorm
Advanced tools
IMPORTANT: the library is in an Alpha version!!!
LambdaORM is an intermediary between the business model and the persistence of the data. Completely decoupling the business model from the data layer.
It is the link between the business model and data persistence.
The classes that represent the business model are completely clean, without any attribute that binds them to persistence.
All the configuration required to resolve the relationship between the business model and persistence is done in the schema, which is configuration.
This configuration can be done in a yaml, json file or passed as a parameter when initializing the ORM.
This configuration contains the following sections.
This example poses a stage where two dataSources are accessed. Data source 1 is mysql and contains the Countries table and dataSource 2 is postgres contains the States table.
In the case of the Countries entity, both the name of the table and the fields coincide with the name of the entity and the name of the properties, so the mapping is transparent.
But in the case of the States entity, the name of the table and its fields differ, so the mapping defines the mapping.
entities:
- name: Positions
abstract: true
properties:
- name: latitude
length: 16
- name: longitude
length: 16
- name: Countries
extends: Positions
primaryKey: ["iso3"]
uniqueKey: ["name"]
properties:
- name: name
nullable: false
- name: iso3
length: 3
nullable: false
- name: region
- name: subregion
relations:
- name: states
type: manyToOne
composite: true
from: iso3
entity: States
to: countryCode
- name: States
extends: Positions
primaryKey: ["id"]
uniqueKey: ["countryCode", "name"]
properties:
- name: id
type: integer
nullable: false
- name: name
nullable: false
- name: countryCode
nullable: false
length: 3
relations:
- name: country
from: countryCode
entity: Countries
to: iso3
dataSources:
- name: dataSource1
dialect: mysql
mapping: mapping1
connection: $CNN_MYDB
- name: dataSource2
dialect: postgres
mapping: mapping2
connection: $CNN_MYDB2
mappings:
- name: mapping1
- name: mapping2
entities:
- name: States
mapping: TBL_STATES
properties:
- name: id
mapping: ID
- name: name
mapping: NAME
- name: countryCode
mapping: COUNTRY_CODE
- name: latitude
mapping: LATITUDE
- name: longitude
mapping: LONGITUDE
stages:
- name: stage1
dataSources:
- name: dataSource2
condition: entity == "States"
- name: dataSource1
The query language is based on javascript lambda expression. These expressions can be written as javascript code by browsing the business model entities.
Expressions can also be sent as a string
LambdaOrm translates the expression into the language corresponding to each database engine.
Javascript lambda expression:
Countries.filter(p=> p.region == region).page(1,3).map(p=> [p.name,p.subregion,p.latitude,p.longitude]).include(p => p.states.filter(p=> substr(p.name,1,1)=="F").map(p=> [p.name,p.latitude,p.longitude]))
Javascript lambda expression as string
'Countries.filter(p=> p.region == region).page(1,3).map(p=> [p.name,p.subregion,p.latitude,p.longitude]).include(p => p.states.filter(p=> substr(p.name,1,1)=="F").map(p=> [p.name,p.latitude,p.longitude]))'
where the SQL equivalent of the expression is:
SELECT c.name AS `name`, c.subregion AS `subregion`, c.latitude AS `latitude`, c.longitude AS `longitude`, c.iso3 AS `__iso3`
FROM Countries c
WHERE c.region = ?
LIMIT 0,3
SELECT s.NAME AS "name", s.LATITUDE AS "latitude", s.LONGITUDE AS "longitude", s.COUNTRY_CODE AS "__parentId"
FROM TBL_STATES s
WHERE SUBSTR(s.NAME,1,1) = 'F'
To work with the orm we can do it using the singleton object called "orm" or using repositories.
This orm object acts as a facade and from this we access all the methods.
When the orm.init() method is called, the orm initialization will be executed from the configuration.
This method receives the expression as a javascript lambda function or a string.
The advantage of writing the expression as a javascript lambda function is that this way we will have the help of intellisense and we will make sure that the expression has no syntax errors.
import { orm } from 'lambdaorm'
(async () => {
await orm.init()
const query = (region:string) => Countries.filter(p=> p.region == region)
.page(1,3)
.map(p=> [p.name,p.subregion,p.latitude,p.longitude])
.include(p => p.states.filter(p=> substr(p.name,1,1)=="F")
.map(p=> [p.name,p.latitude,p.longitude])
)
const result = await orm.execute(query, { region: 'Asia' })
console.log(JSON.stringify(result, null, 2))
await orm.end()
})()
The advantage of writing the expression in a string is that we can receive it from outside, example UI, CLI command, stored, etc.
import { orm } from 'lambdaorm'
(async () => {
await orm.init()
const query = 'Countries.filter(p=> p.region == region).page(1,3).map(p=> [p.name,p.subregion,p.latitude,p.longitude]).include(p => p.states.filter(p=> substr(p.name,1,1)=="F").map(p=> [p.name,p.latitude,p.longitude]))'
)
const result = await orm.execute(query, { region: 'Asia' })
console.log(JSON.stringify(result, null, 2))
await orm.end()
})()
[
{
"name": "Afghanistan",
"subregion": "Southern Asia",
"latitude": "33.00000000",
"longitude": "65.00000000",
"states": [ { "name": "Farah", "latitude": "32.49532800", "longitude": "62.26266270" },
{ "name": "Faryab", "latitude": "36.07956130","longitude": "64.90595500" }]
},
{
"name": "United Arab Emirates",
"subregion": "Western Asia",
"latitude": "24.00000000",
"longitude": "54.00000000",
"states": [ { "name": "Fujairah","latitude": "25.12880990","longitude": "56.32648490" }]
},
{
"name": "Armenia",
"subregion": "Western Asia",
"latitude": "40.00000000",
"longitude": "45.00000000",
"states": []
}
]
Repositories are associated with an entity and have various methods to interact with it.
import { orm } from 'lambdaorm'
import { CountryRespository } from './models/country'
(async () => {
await orm.init()
const countryRespository = new CountryRespository('mydb')
const result = await countryRespository.query().filter(p=> p.region == region)
.page(1,3)
.map(p=> [p.name,p.subregion,p.latitude,p.longitude])
.include(p => p.states.filter(p=> substr(p.name,1,1)=="F")
.map(p=> [p.name,p.latitude,p.longitude])
).execute({ region: 'Asia' })
console.log(JSON.stringify(result, null, 2))
await orm.end()
})()
LambdaORM includes the include method to load related entities, both for OnetoMany, manyToOne, and oneToOne relationships.
We can also apply filters or bring us some fields from related entities.
For each include, a statement is executed that fetches all the necessary records, then the objects with relationships are assembled in memory. In this way, multiple executions are avoided, considerably improving performance.
Includes can be used in selects, inserts, updates, deletes, and bulckinserts.
import { orm } from 'lambdaorm'
(async () => {
await orm.init()
const query = (id:number) => Orders
.filter(p => p.id === id)
.include(p => [p.customer.map(p => ({ name: p.name, address: concat(p.address, ', ', p.city, ' (', p.postalCode, ') ', p.country) })),
p.details.include(p => p.product
.include(p => p.category.map(p => p.name))
.map(p => p.name))
.map(p => [p.quantity, p.unitPrice])])
.map(p => p.orderDate)
const result = await orm.execute(query)
console.log(JSON.stringify(result, null, 2))
await orm.end()
})()
The previous sentence will bring us the following result:
[[
{
"orderDate": "1996-07-03T22:00:00.000Z",
"customer": { "name": "Vins et alcools Chevalier", "address": "59 rue de l'Abbaye, Reims (51100) France"
},
"details": [
{
"quantity": 12, "unitPrice": 14,
"product": { "name": "Queso Cabrales", "category": { "name": "Dairy Products"}
}
},
{
"quantity": 10, "unitPrice": 9.8,
"product": { "name": "Singaporean Hokkien Fried Mee", "category": { "name": "Grains/Cereals" }}
},
{
"quantity": 5, "unitPrice": 34.8,
"product": { "name": "Mozzarella di Giovanni", "category": { "name": "Dairy Products" } }
}
]
}
]]
To work with transactions use the orm.transaction method.
This method receives the name of the database as its first argument and as its second is a callback function that does not pass a Transaction object, in the example we call it tr.
We use the lambda or expression method to execute the sentence (as we find it written).
When we reach the end and return the callback, the orm will internally execute the COMMIT, if there is an exception, internally the ROLLBACK will be executed
import { orm } from 'lambdaorm'
(async () => {
const order={customerId:"VINET",employeeId:5,orderDate:"1996-07-03T22:00:00.000Z",requiredDate:"1996-07-31T22:00:00.000Z",shippedDate:"1996-07-15T22:00:00.000Z",shipViaId:3,freight:32.38,name:"Vins et alcools Chevalier",address:"59 rue de l-Abbaye",city:"Reims",region:null,postalCode:"51100",country:"France",details:[{productId:11,unitPrice:14,quantity:12,discount:!1},{productId:42,unitPrice:9.8,quantity:10,discount:!1},{productId:72,unitPrice:34.8,quantity:5,discount:!1}]};
try {
orm.transaction({}, 'stage', async (tr) => {
// create order
const orderId = await tr.execute(() => Orders.insert().include(p => p.details), order)
// get order
const result = await tr.execute((id:number) => Orders.filter(p => p.id === id).include(p => p.details), { id: orderId })
const order2 = result[0]
// updated order
order2.address = 'changed 59 rue de l-Abbaye'
order2.details[0].discount = true
order2.details[1].unitPrice = 10
order2.details[2].quantity = 7
const updateCount = await tr.execute(() => Orders.update().include(p => p.details), order2)
console.log(updateCount)
// get order
const order3 = await tr.execute((id:number) => Orders.filter(p => p.id === id).include(p => p.details), { id: orderId })
console.log(JSON.stringify(order3))
// delete
const deleteCount = await tr.execute(() => Orders.delete().include(p => p.details), order3[0])
console.log(deleteCount)
// get order
const order4 = await tr.execute((id:number) => Orders.filter(p => p.id === id).include(p => p.details), { id: orderId })
console.log(JSON.stringify(order4))
})
} catch (error) {
console.log(error)
}
})()
Lambda ORM has the following methods to extract metadata information from expressions.
To execute these methods it is not necessary to connect to the database.
method | Description | Path |
---|---|---|
parameters | returns the list of parameters in the expression | orm.parameters(query) |
model | returns the model of the result in an execution | orm.model(query) |
metadata | returns the metadata of the expression | orm.metadata(query) |
sentence | returns the sentence in the specified dialect | orm.sentence(query) |
npm install lambdaorm
Install the package globally to use the CLI commands to help you create and maintain projects
npm install lambdaorm-cli -g
In this laboratory we will see:
Creating the northwind sample database tables and loading it with sample data. This database presents several non-standard cases such as: - Name of tables and fields with spaces - Tables with composite primary keys - Tables with autonumeric ids and others with ids strings
Since this is the database that was used for many examples and unit tests, you can test the example queries that are in the documentation. We will also see some example queries to execute from CLI
In this laboratory we will see:
In this laboratory we will see:
In this laboratory we will see:
In this laboratory we will see:
FAQs
ORM
The npm package lambdaorm receives a total of 72 weekly downloads. As such, lambdaorm popularity was classified as not popular.
We found that lambdaorm demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
Security News
PyPI now supports iOS and Android wheels, making it easier for Python developers to distribute mobile packages.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.