Skip to content

TypeScript SDK

The official Node.js/TypeScript SDK for JSONQL. One line of configuration gives your API dynamic filtering, sorting, pagination, field selection, and relationships — no custom endpoints needed.

Package@jsonql-standard/jsonql-ts
Version1.1.0
LicenseMIT
RuntimeNode.js 18+
Terminal window
npm install @jsonql-standard/jsonql-ts

Pick your framework — one mount gives you a complete JSONQL API:

import express from 'express';
import { jsonqlExpress } from '@jsonql-standard/jsonql-ts';
import { PostgresDriver } from '@jsonql-standard/jsonql-ts/drivers/postgres';
const app = express();
app.use(express.json());
const driver = new PostgresDriver('postgres://localhost/mydb');
// One line of config — dynamic query API ready
app.use('/api', jsonqlExpress({ driver }));
app.listen(3000);
import Fastify from 'fastify';
import { jsonqlFastify } from '@jsonql-standard/jsonql-ts';
import { PostgresDriver } from '@jsonql-standard/jsonql-ts/drivers/postgres';
const fastify = Fastify();
const driver = new PostgresDriver('postgres://localhost/mydb');
// Auto-registers /:table routes
fastify.register(jsonqlFastify, { driver });
fastify.listen({ port: 3000 });
import { Module } from '@nestjs/common';
import { JsonqlModule } from '@jsonql-standard/jsonql-ts';
@Module({
imports: [
JsonqlModule.forRoot({
driver: new PostgresDriver('postgres://localhost/mydb'),
}),
],
})
export class AppModule {}

That’s it. No query controllers, no endpoint-per-filter, no route boilerplate. Your clients can now query any table dynamically.

For MongoDB-backed APIs, use the Mongo adapter variants. They take a database (a connected mongodb Db) instead of a SQL driver and produce the same { meta, data } response contract:

import express from 'express';
import { jsonqlExpressMongo, connectMongo } from '@jsonql-standard/jsonql-ts';
const app = express();
app.use(express.json());
const { db } = await connectMongo({ uri: 'mongodb://localhost:27017', dbName: 'mydb' });
app.use('/api', jsonqlExpressMongo({ database: db }));
app.listen(3000);

The same family is available for every framework:

FrameworkSQL adapterMongoDB adapter
ExpressjsonqlExpressjsonqlExpressMongo
FastifyjsonqlFastifyjsonqlFastifyMongo
NestJSJsonqlServiceJsonqlMongoService

Every query is a JSON POST to /api/{table}:

Terminal window
# Select specific fields, filter, sort, paginate
curl -X POST http://localhost:3000/api/users -H 'Content-Type: application/json' -d '{
"fields": ["id", "name", "email"],
"where": { "status": { "eq": "active" } },
"sort": ["-created_at"],
"limit": 20
}'
# → { "data": [{ "id": 1, "name": "Alice", "email": "alice@co.com" }, ...] }
Terminal window
# Complex filters with AND/OR
curl -X POST http://localhost:3000/api/products -d '{
"where": {
"and": [
{ "price": { "gte": 10, "lte": 100 } },
{ "category": { "in": ["electronics", "books"] } }
]
},
"sort": ["price"],
"limit": 50
}'
Terminal window
# Include related data — joins resolved automatically
curl -X POST http://localhost:3000/api/users -d '{
"fields": ["id", "name"],
"include": {
"posts": { "fields": ["title", "created_at"], "limit": 5 }
}
}'
# → { "data": [{ "id": 1, "name": "Alice", "posts": [{ "title": "Hello", ... }] }] }
Terminal window
# Aggregation & groupBy
curl -X POST http://localhost:3000/api/orders -d '{
"aggregate": { "total": { "fn": "sum", "field": "amount" } },
"groupBy": ["status"]
}'

CRUD mutations work too:

Terminal window
# Create (POST with "data")
curl -X POST http://localhost:3000/api/users \
-d '{ "data": { "name": "Bob", "email": "bob@co.com" } }'
# Update (PATCH)
curl -X PATCH http://localhost:3000/api/users \
-d '{ "patch": { "status": "inactive" }, "where": { "id": { "eq": 1 } } }'
# Delete
curl -X DELETE http://localhost:3000/api/users \
-d '{ "where": { "id": { "eq": 1 } } }'

Without schema, all columns are queryable. With schema, you control which fields are exposed, hide sensitive columns, and enable relationship resolution:

app.use('/api', jsonqlExpress({
driver,
schema: {
tables: {
users: {
fields: {
id: { type: 'number' },
name: { type: 'string' },
email: { type: 'string' },
secret: { type: 'string', allowSelect: false }, // hidden from queries
},
relations: {
posts: { type: 'hasMany', table: 'posts', field: 'user_id' },
},
},
posts: {
fields: {
id: { type: 'number' },
title: { type: 'string' },
user_id: { type: 'number' },
},
},
},
},
}));

Inject logic at any point in the pipeline — tenant isolation, audit logging, RLS:

app.use('/api', jsonqlExpress({
driver,
hooks: {
beforeQuery: (query, table) => {
query.where = { ...query.where, tenant_id: { eq: currentTenantId } };
},
afterQuery: (results) => results,
},
}));
DatabaseDriver importPackage
PostgreSQLdrivers/postgrespg
MySQLdrivers/mysqlmysql2
SQLitedrivers/sqlitesqlite3
MSSQLdrivers/mssqlmssql
MongoDBdrivers/mongodbmongodb

All errors include a machine-readable error_code:

graph TD
E["JsonQLError"] --> V["JsonQLValidationError<br/>(VALIDATION_ERROR)"]
E --> T["JsonQLTranspileError<br/>(TRANSPILE_ERROR)"]
E --> X["JsonQLExecutionError<br/>(EXECUTION_ERROR)"]
{ "error": "Field 'secret' is not allowed", "error_code": "VALIDATION_ERROR" }

For server-side programmatic query construction:

import { JSONQLQueryBuilder } from '@jsonql-standard/jsonql-ts';
const query = new JSONQLQueryBuilder()
.from('users')
.select('id', 'name')
.where({ status: { eq: 'active' } })
.orderBy('-created_at')
.limit(10)
.build();

When you want the full pipeline (parse → validate → transpile → execute → hydrate) without wiring a framework adapter, use the JSONQLEngine facade. It mirrors the Engine builder shipped by the Go, Python, and Java SDKs, so the high-level API is consistent across the ecosystem.

import { JSONQLEngine } from '@jsonql-standard/jsonql-ts';
const engine = JSONQLEngine.builder()
.postgres()
.schema(schema)
.driver(driver) // or .executor(async (sql, params) => db.query(sql, params))
.build();
const result = await engine.execute({ where: { age: { gt: 18 } } }, 'users');
console.log(result.data); // hydrated rows
console.log(result.isMutation); // false

Builder methods: .postgres() / .mysql() / .sqlite() / .mssql() (or .dialect(name)), .schema(), .driver(), .executor(), .logger(), .parserOptions(), .debug(), then .build(). When a .driver() is set and no dialect was chosen, the dialect is inferred from the driver.

Use the transpiler directly for custom pipelines:

import { SQLTranspiler } from '@jsonql-standard/jsonql-ts';
const transpiler = new SQLTranspiler('postgres');
const { sql, parameters } = transpiler.transpile(query, 'users');
// → SELECT "users"."id", "users"."name" FROM "users" WHERE "users"."status" = $1
ExportPurpose
jsonqlExpress()Express middleware — mount and go
jsonqlFastifyFastify plugin with auto-routing
JsonqlModuleNestJS module with injectable service
JSONQLParserParse & validate incoming JSON
JSONQLEngineHigh-level pipeline facade (parse→validate→transpile→execute→hydrate)
SQLTranspilerConvert parsed query → SQL + params
ResultHydratorFlatten SQL joins → nested JSON
JSONQLQueryBuilderFluent query construction (advanced)
JSONQLMutationBuilderFluent mutation construction (advanced)
SchemaManagerLoad schemas from introspection + JSON

135/135 tests passing across all configurations:

AdapterPostgreSQLMySQLSQLiteMSSQL
Express
Fastify
NestJS
Terminal window
npm install && npm test # Install + test
npx prettier --check . # Format check (CI enforced)

The SDK is organised into 12 canonical modules. A request flows through them in order:

parser → validator → transpiler → driver → drivers → hydrator
↑ factory wires it together
↑ builder is an alternative input
ModuleFilePurpose
parsersrc/parser/Tokenise & validate incoming JSON, produce an AST
validatorsrc/validator/Schema & permission checks against the AST
transpilersrc/transpiler/AST → parameterised SQL for the active dialect
mongo_transpilersrc/transpiler/mongo.tsAST → MongoDB filter & aggregation pipelines
dialectsrc/transpiler/dialect.tsPer-flavour quoting & parameter markers (Postgres $1, MySQL/SQLite ?, MSSQL @p1)
driversrc/driver.tsOrchestrates execute → retry → diagnostics
driverssrc/drivers/Concrete backends: postgres, mysql, sqlite, mssql, mongodb
hydratorsrc/hydrator.tsFlatten join rows back into nested JSON
enginesrc/engine.tsHigh-level facade running the full pipeline via a fluent builder
factorysrc/core.ts (createJsonql() helper)One-call wiring of parser + transpiler + driver + hydrator
buildersrc/builder/Fluent JSONQLQueryBuilder / JSONQLMutationBuilder
schemasrc/schema/Optional schema loader (introspection + JSON)
adapterssrc/adapters/Framework integrations: express, fastify, nestjs
errorssrc/errors.tsJsonQLError hierarchy with error_code

For most apps you only touch adapters at install time and the factory for advanced wiring. Everything else is automatic.

github.com/jsonql-standard/jsonql-ts