
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@potygen/potygen
Advanced tools
Main readme at potygen
yarn add @potygen/potygen
The main interface to potygen is the sql string template literal.
import { sql } from '@potygen/potygen';
import { Client } from 'pg';
const db = new Client(process.env.POSTGRES_CONNECTION);
async function main() {
await db.connect();
const productsQuery = sql`SELECT product FROM orders WHERE region = $region`;
const data = await productsQuery(db, { region: 'Sofia' });
console.log(data);
await db.end();
}
main();
This is paired with @potygen/cli to generate types for statically typed queries.
You can also get the raw query config object that would be passed to pg query call.
import { sql, toQueryConfig } from '@potygen/potygen';
const productsQuery = sql`SELECT product FROM orders WHERE region = $region`;
const queryConfig = toQueryConfig(productsQuery, { region: 'Sofia' });
console.log(queryConfig);
You can map the result of the query, and keep the mapping as part of the query itself, using mapResult helper.
The resulting mapped query is still a query and can be also mapped with the mapResult.
const productsQuery = sql<MyQuery>`SELECT product FROM orders WHERE region = $region`;
const mappedProductsQuery = mapResult(
(rows) => rows.map((row) => ({ ...row, productLength: row.product.length })),
productsQuery,
);
const secondMappedProductsQuery = mapResult(
(rows) => rows.map((row) => ({ ...row, productLengthSquare: Math.pow(row.productLength, 2) })),
mappedProductsQuery,
);
console.log(await productsQuery(db, { region: 'Sofia' }));
console.log(await mappedProductsQuery(db, { region: 'Sofia' }));
console.log(await secondMappedProductsQuery(db, { region: 'Sofia' }));
const oneProductQuery = oneResult(sql<MyQuery>`SELECT product FROM orders WHERE region = $region LIMIT 1`);
console.log(await oneProductQuery(db, { region: 'Sofia' }));
maybeOneResult() - Return the first element, after the query is run, returns undefined if result is emptyoneResult() - Return the first element, useful for queries where we always expect at least one resultatLeastOneResult - Return the rows but throw an error if no rows have been returnedgraph LR
SQL --> | Parse | AST(Abstract Syntax Tree)
AST --> | Plan | QI(Query Interface)
QI --> | Load | T(Type)
SQL is processed through several stages.
With the parse function we process the raw sql into an abstract syntax tree (AST) that is used throughout the various components.
Note All of the tokens are numbers, to make sence of them you'll need to reference SqlName
The ast is also heavily typed with a lot of docs and diagrams of what they represent, for example the SelectTag. To help with working with the ast, every tag's type also has a type guard for it in grammar.guards.ts
import { parser } from '@potygen/potygen';
const sql = `SELECT * FROM users`;
const { ast } = parser(sql);
console.log(ast);
The AST is later used by @potygen/prettier-plugin-pgsql, @potygen/typescript-pgsql-plugin as well as the typescript generation from @potygen/cli
Using the AST we can create an "interface" for a specific SQL - what parameters are required by it an the type of its response. If no information from the database is needed (no tables / views / functions etc. were used in the query), the parsing can end here and we could use the result to generate typescrint types.
import { parser, toQueryInterface } from '@potygen/potygen';
const sql = `SELECT 123 as "col1"`;
const { ast } = parser(sql);
const queryInterface = toQueryInterface(ast);
console.log(queryInterface.results);
If types require data to be loaded, then a plan (Load Type) will be returned instead. This will later be used to construct queries to get the data from postgres.
import { parser, toQueryInterface } from '@potygen/potygen';
const sql = `SELECT name FROM users WHERE email = $email`;
const { ast } = parser(sql);
const queryInterface = toQueryInterface(ast);
console.log(JSON.stringify(queryInterface, null, 2));
With loadQueryInterfacesData you can load the data, required to generate the types for a given query. Since this could be done in bulk, or incrementally, by keeping an reusing loaded data, its a separate function.
import {
parser,
toQueryInterface,
loadQueryInterfacesData,
toLoadedQueryInterface,
LoadedData,
} from '@potygen/potygen';
import { Client } from 'pg';
/**
* Log all operation details to the console
*/
const logger = console;
const db = new Client(process.env.POSTGRES_CONNECTION);
const context = { db, logger };
/**
* A reusable cache of already laoded data
*/
let loadedData: LoadedData[] = [];
async function main() {
await db.connect();
const sql = `SELECT product FROM orders WHERE region = $region`;
const { ast } = parser(sql);
const queryInterface = toQueryInterface(ast);
/**
* If the data is already present in loadedData, it will not be loaded again
*/
loadedData = await loadQueryInterfacesData(context, [queryInterface], loadedData);
const loadedQueryInterface = toLoadedQueryInterface(loadedData)(queryInterface);
console.log(JSON.stringify(loadedQueryInterface, null, 2));
await db.end();
}
main();
The loadedQueryInterface will now have all the data needed for various typescript generation purposes or similar.
Since the type data required for most databases is not that big, we can actually load all of it once, and then be able to resolve the types of any query, as long as the tables / views / enums / functions of that database have not been altered.
import { parser, toQueryInterface, toLoadedQueryInterface, LoadedData, loadAllData } from '@potygen/potygen';
import { Client } from 'pg';
/**
* Log all operation details to the console
*/
const logger = console;
const db = new Client(process.env.POSTGRES_CONNECTION);
const context = { db, logger };
/**
* A reusable cache of already laoded data
*/
let loadedData: LoadedData[] = [];
async function main() {
await db.connect();
/**
* Load _all_ data from the given database, all the table, view, type, enum and function data.
*/
loadedData = await loadAllData(context, loadedData);
const sql = `SELECT product FROM orders WHERE region = $region`;
const { ast } = parser(sql);
const queryInterface = toQueryInterface(ast);
const loadedQueryInterface = toLoadedQueryInterface(loadedData)(queryInterface);
console.log(JSON.stringify(loadedQueryInterface, null, 2));
await db.end();
}
main();
Potygen also includes logic for inspecting and auto-compliting queries that is used in typescript language extensions.
import { toInfoContext, loadAllData, completionAtOffset, quickInfoAtOffset } from '@potygen/potygen';
import { Client } from 'pg';
/**
* Log all operation details to the console
*/
const logger = console;
const db = new Client(process.env.POSTGRES_CONNECTION);
const context = { db, logger };
async function main() {
await db.connect();
const data = await loadAllData(context, []);
const infoContext = toInfoContext(data, logger);
const sql = `SELECT product FROM orders WHERE region = $region`;
// ^
const completion = completionAtOffset(infoContext, sql, 7);
console.log(JSON.stringify(completion, null, 2));
const quickInfo = quickInfoAtOffset(infoContext, sql, 7);
console.log(JSON.stringify(quickInfo, null, 2));
await db.end();
}
main();
With the sql query you can wrap
FAQs
Postgres Typescript Generator
We found that @potygen/potygen demonstrated a not healthy version release cadence and project activity because the last version was released 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
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.