@kipon.io/transformers
v0.0.3
Published
Transform columnar SQL results into complex nested JSON objects with custom transformations
Maintainers
Readme
Transformers (JSON Mapping)
Package to transform columnar results from SQL queries (with joins) into complex JSON objects with nested relationships, with support for custom transformations.
Table of Contents
- Installation
- Features
- Basic Usage
- Value Transformations
- Nested Relationships
- Using TransformFactory
- Creating Reusable Transformers
- API Reference
- Integration with SQL Libraries
- Why Use This Library
- Contributing
- License
Installation
# Using npm
npm install @kipon.io/transformers
# Using yarn
yarn add @kipon.io/transformers
# Using pnpm
pnpm add @kipon.io/transformersFeatures
- Easily transform SQL query results into nested JSON structures
- Support for one-to-one and one-to-many relationships
- Value transformations (convert strings to Date, Number, Boolean)
- Field mapping (aliases)
- Support for deeply nested relationships
- Factory for creating reusable transformers
- Written in TypeScript with full type definitions
- Zero dependencies (except es-toolkit for utility functions)
Basic Usage
import { transformResult } from '@kipon.io/transformers';
// Result from a SQL query with joins
const queryResults = [
{
id: 101,
name: 'John Doe',
department_id: 5,
department_name: 'Engineering',
project_id: 301,
project_name: 'API Redesign'
}
];
// Transform into structured object
const result = transformResult(queryResults, {
one: ['department'], // One-to-one relationship
many: ['project'] // One-to-many relationship
});
console.log(result);
/* Output:
{
id: 101,
name: 'John Doe',
department: {
id: 5,
name: 'Engineering'
},
project: [
{
id: 301,
name: 'API Redesign'
}
]
}
*/Value Transformations
import { transformResult } from '@kipon.io/transformers';
const result = transformResult(queryResults, {
transforms: {
// Convert string to Date
created_at: (value) => new Date(value),
// Map field to another
updated_at: 'updated_time',
// Convert string to Boolean
is_active: (value) => value === 'true',
// Convert string to Number
count: (value) => parseInt(value, 10)
},
one: ['department'],
many: ['project']
});Nested Relationships
import { transformResult } from '@kipon.io/transformers';
const result = transformResult(queryResults, {
one: [
{
prefix: 'department',
one: ['manager'] // Nested relationship: department -> manager
}
],
many: [
{
prefix: 'project',
many: ['task'] // Nested relationship: project -> task
}
]
});
/* Output:
{
id: 101,
name: 'John Doe',
department: {
id: 5,
name: 'Engineering',
manager: {
id: 201,
name: 'Jane Smith'
}
},
project: [
{
id: 301,
name: 'API Redesign',
task: [
{ id: 401, name: 'Database Migration' },
{ id: 402, name: 'API Documentation' }
]
}
]
}
*/Using TransformFactory
import { TransformFactory } from '@kipon.io/transformers';
// Create specific transformations
const dateTransforms = TransformFactory.datesToDate('created_at', 'updated_at');
const fieldMappings = TransformFactory.mapFields({ display_name: 'full_name' });
const booleanTransforms = TransformFactory.stringsToBooleans('is_active', 'is_admin');
const numberTransforms = TransformFactory.stringToNumbers('count', 'price');
// Merge schemas
const schema = TransformFactory.mergeSchemas(
dateTransforms,
fieldMappings,
booleanTransforms,
numberTransforms,
{
one: ['department'],
many: ['project']
}
);
// Apply the schema
const result = transformResult(queryResults, schema);Creating Reusable Transformers
import { TransformFactory } from '@kipon.io/transformers';
// Create a reusable transformer
const userTransformer = TransformFactory.createTransformer({
transforms: {
created_at: (value) => new Date(value),
updated_at: 'updated_time',
is_active: (value) => value === 'true'
},
one: ['department'],
many: ['project']
});
// Use the transformer in different places
const users1 = userTransformer(queryResults1);
const users2 = userTransformer(queryResults2);API Reference
transformResult(rows, schema)
Transforms SQL result rows into structured objects.
- rows: Array of objects returned by the SQL query
- schema: Transformation configuration
Transformation Schema
interface TransformSchema {
prefix?: string; // Column prefix (optional for root)
key?: string; // Key field for identification (default: "id")
one?: string[] | TransformSchema | (string | TransformSchema)[]; // One-to-one relationships
many?: string[] | TransformSchema | (string | TransformSchema)[]; // One-to-many relationships
transforms?: { // Custom transformations
[key: string]: string | ((value: any) => any)
}
}TransformFactory
Utility for creating and combining transformations.
Basic Transformations
datesToDate(...fieldNames): TransformSchemaCreates transformations to convert strings to Date objects.// Example const schema = TransformFactory.datesToDate('created_at', 'updated_at');mapFields(mappings): TransformSchemaCreates transformations to map fields from one name to another.// Example const schema = TransformFactory.mapFields({ 'updated_at': 'updated_time', 'display_name': 'full_name' });stringsToBooleans(...fieldNames): TransformSchemaCreates transformations to convert strings to boolean values.// Example const schema = TransformFactory.stringsToBooleans('is_active', 'is_admin');stringToNumbers(...fieldNames): TransformSchemaCreates transformations to convert strings to numbers.// Example const schema = TransformFactory.stringToNumbers('count', 'price');
Advanced Transformations
formatStrings(mappings): TransformSchemaCreates transformations to format strings (uppercase, lowercase, etc).// Example const schema = TransformFactory.formatStrings({ 'username': 'lowercase', 'title': 'capitalize', 'code': 'uppercase' });customTransforms(mappings): TransformSchemaCreates custom transformations with user-defined functions.// Example const schema = TransformFactory.customTransforms({ 'full_name': (value) => `${value.first_name} ${value.last_name}`, 'age': (value) => new Date().getFullYear() - value.birth_year });
Relations Configuration
oneToOne(...relations): TransformSchemaCreates a one-to-one relationship configuration.// Example const schema = TransformFactory.oneToOne('profile', 'address');oneToMany(...relations): TransformSchemaCreates a one-to-many relationship configuration.// Example const schema = TransformFactory.oneToMany('posts', 'comments');nestedRelation(prefix, key, config): TransformSchemaCreates a nested relationship configuration.// Example const schema = TransformFactory.nestedRelation('department', 'id', { one: ['manager'], many: ['employees'] });
Utility Methods
identity(): TransformSchemaReturns an empty schema (useful as placeholder).mergeSchemas(...schemas): TransformSchemaCombines multiple schemas into one.// Example const schema = TransformFactory.mergeSchemas( schema1, schema2, schema3 );conditional(condition, trueSchema, falseSchema): TransformSchemaReturns different schemas based on a condition.// Example const schema = TransformFactory.conditional( includeDetails, detailedSchema, simpleSchema );createTransformer(schema): FunctionCreates a reusable transformer function from a schema.// Example const transformer = TransformFactory.createTransformer(schema);createNamedTransformer(name, schema): ObjectCreates a named transformer function for better traceability.// Example const { userTransformer } = TransformFactory.createNamedTransformer('userTransformer', schema);
Integration with SQL Libraries
PostgreSQL (pg)
import { Pool } from 'pg';
import { transformResult } from '@kipon.io/transformers';
const pool = new Pool();
async function getUserWithRelations(userId) {
const { rows } = await pool.query(`
SELECT
u.id, u.name, u.email, u.created_at,
d.id as department_id, d.name as department_name,
p.id as project_id, p.name as project_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN user_projects up ON u.id = up.user_id
LEFT JOIN projects p ON up.project_id = p.id
WHERE u.id = $1
`, [userId]);
return transformResult(rows, {
transforms: {
created_at: (value) => new Date(value)
},
one: ['department'],
many: ['project']
});
}With Squel-ts
import * as squel from 'squel-ts';
import { transformResult } from '@kipon.io/transformers';
async function getUsersWithRelations(db) {
const query = squel.select()
.from('users', 'u')
.field('u.id').field('u.name').field('u.email')
.field('d.id', 'department_id').field('d.name', 'department_name')
.field('p.id', 'project_id').field('p.name', 'project_name')
.left_join('departments', 'd', 'u.department_id = d.id')
.left_join('user_projects', 'up', 'u.id = up.user_id')
.left_join('projects', 'p', 'up.project_id = p.id')
.toString();
const { rows } = await db.query(query);
return transformResult(rows, {
one: ['department'],
many: ['project']
});
}With Serverless-postgres
import { Client } from 'serverless-postgres';
import { transformResult, TransformFactory } from '@kipon.io/transformers';
const pgClient = new Client({
connectionString: process.env.DATABASE_URL
});
// Create a reusable transformer
const userTransformer = TransformFactory.createTransformer({
transforms: {
created_at: (value) => new Date(value),
updated_at: 'updated_time'
},
one: ['department'],
many: ['project']
});
async function getUsers() {
await pgClient.connect();
try {
const { rows } = await pgClient.query(`
SELECT
u.id, u.name, u.email, u.created_at, u.updated_time,
d.id as department_id, d.name as department_name,
p.id as project_id, p.name as project_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN user_projects up ON u.id = up.user_id
LEFT JOIN projects p ON up.project_id = p.id
`);
return userTransformer(rows);
} finally {
await pgClient.clean();
}
}Why Use This Library
- Simplifies code: Eliminates the need for complex loops to structure data
- Improves readability: Separates query logic from transformation logic
- Avoids common mistakes: Automatically manages uniqueness of records in relationships
- Supports deep nesting: Facilitates creation of complex data structures
- TypeScript typing: Offers type safety and autocompletion
- Flexible transformations: Allows formatting data during transformation
Contributing
Contributions are welcome! Please feel free to submit a PR.
- Fork the repository
- Create your feature branch:
git checkout -b my-feature - Commit your changes:
git commit -m 'Add some feature' - Push to the branch:
git push origin my-feature - Submit a pull request
