JoinJS
JoinJS is a JavaScript library designed to transform flat result sets from database queries, typically involving complex joins, into deeply nested JavaScript objects. It functions as a lightweight alternative to full-blown Object-Relational Mappers (ORMs), giving developers precise control over their SQL queries while simplifying the object mapping layer. The current stable version is 1.1.2, last updated in 2019, suggesting an irregular or halted release cadence. Key differentiators include its "no-nonsense" approach to database interaction, inspired by the Java MyBatis framework, which encourages writing raw SQL or using existing query builders like Knex.js, and then leveraging JoinJS solely for the post-query data structuring. It ships with TypeScript definitions, enhancing developer experience for type-safe applications.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'map') OR TypeError: joinjs.map is not a function
cause Attempting to call `map` on an undefined or incorrectly imported `joinjs` object, often due to incorrect CommonJS `require` or ESM `import` syntax (e.g., trying to destructure `map` directly if it's not a named export).fixEnsure `joinjs` is correctly imported as the default export (`import joinjs from 'join-js';` for ESM, `const joinjs = require('join-js');` for CommonJS) before calling `joinjs.map()` or `joinjs.mapOne()`. -
NotFoundError: Mapped object not found
cause The `mapOne` method was called, but the provided `resultSet` did not contain data that could be mapped to a single object based on the given `resultMaps` and `mapId`.fixVerify that your SQL query returns at least one row corresponding to the expected primary entity. If it's an expected scenario for no data to be found, wrap the call to `mapOne` in a `try-catch` block to handle the `NotFoundError` gracefully. -
Mapped result is empty or incorrect data structure.
cause Misconfiguration in `resultMaps`, particularly incorrect `idProperty`, `properties`, `collections` definitions, or `columnPrefix` values that do not align with the actual column names in the `resultSet`.fixCarefully review `resultMaps` to ensure `mapId`, `idProperty`, `properties`, and `collections` (including `name`, `mapId`, and `columnPrefix`) accurately reflect the structure of your desired objects and the column names from your database query.
Warnings
- breaking The `mapOne()` method now throws a `NotFoundError` if no matching object is found in the result set, instead of returning `null` or `undefined`. Applications relying on the previous behavior must implement `try-catch` blocks.
- gotcha The library has not been actively maintained since its last release in 2019. While functional, it may not receive updates for new JavaScript features, bug fixes, or security patches, which could pose long-term maintenance risks.
- gotcha While JoinJS simplifies mapping, it does not provide features for query building, migrations, or schema management. Developers must use separate tools (e.g., Knex.js, raw SQL) for database interactions.
Install
-
npm install join-js -
yarn add join-js -
pnpm add join-js
Imports
- joinjs (main object)
import { joinjs } from 'join-js';import joinjs from 'join-js';
- map
import { map } from 'join-js';import joinjs from 'join-js'; joinjs.map(resultSet, resultMaps, 'teamMap', 'team_');
- mapOne
import { mapOne } from 'join-js';import joinjs from 'join-js'; joinjs.mapOne(resultSet, resultMaps, 'teamMap', 'team_');
Quickstart
import joinjs from 'join-js';
const resultSet = [
{ team_id: 1, team_name: 'New England Patriots', player_id: 1, player_name: 'Tom Brady' },
{ team_id: 1, team_name: 'New England Patriots', player_id: 2, player_name: 'Rob Gronkowski' },
{ team_id: 2, team_name: 'New York Jets', player_id: 3, player_name: 'Geno Smith' },
{ team_id: 2, team_name: 'New York Jets', player_id: 4, player_name: 'Darrelle Revis' }
];
const resultMaps = [
{
mapId: 'teamMap',
idProperty: 'id',
properties: ['name'],
collections: [
{name: 'players', mapId: 'playerMap', columnPrefix: 'player_'}
]
},
{
mapId: 'playerMap',
idProperty: 'id',
properties: ['name']
}
];
try {
const mappedResult = joinjs.map(resultSet, resultMaps, 'teamMap', 'team_');
console.log('Mapped Result:', JSON.stringify(mappedResult, null, 2));
const mappedOneTeam = joinjs.mapOne(resultSet, resultMaps, 'teamMap', 'team_');
console.log('\nMapped One Team:', JSON.stringify(mappedOneTeam, null, 2));
// Demonstrate NotFoundError for mapOne with an empty set
const emptyResultSet = [];
try {
joinjs.mapOne(emptyResultSet, resultMaps, 'teamMap', 'team_');
} catch (e) {
if (e.name === 'NotFoundError') {
console.log('\nCaught expected NotFoundError for mapOne with empty result set.');
} else {
throw e;
}
}
} catch (error) {
console.error('An error occurred:', error);
}