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.

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();

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
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)

github.com/jsonql-standard/jsonql-ts