question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

TypeScript plugin or runtime tracing to allow query composition?

See original GitHub issue

https://github.com/xialvjun/ts-sql-plugin is an interesting project that uses a TS plugin to typecheck sql query strings. https://github.com/mmkal/slonik-tools/tree/master/packages/typegen#readme is also an interesting project that uses runtime tracing to detect query types on first run.

Both should allow you to do something roughly like this:

const notDeleted = (asOf = null) => sql`
  deleted_at is null or deleted_at > ${asOf || sql`now()`}
`

const myQuery = (bar) => sql`
  select * from mytable
  where ${notDeleted()}
  and foo = ${bar}
`

// result is fully typed
const result = await db.query(myQuery(bar))

Is pgTyped interested in experimenting with such techniques to enable query composition, for convenient sharing of common conditions etc?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:19 (8 by maintainers)

github_iconTop GitHub Comments

14reactions
maxpaincommented, Jun 16, 2021

@adelsz what do you think about this concept?

We could generate one pgtyped.d.ts for the entire project with the generic function query and mappings, where all our queries are typescript literals and mapped to corresponding params and results.

Example:

declare module '@pgtyped/query' {
  export type Mappings = {
    '\n			SELECT cluster_id\n			FROM cluster_subnets\n			WHERE $clientIP <<= subnet\n		': {
      params: {
        clientIP: string | null | void
      }
      result: {
        cluster_id: number
      }
    }
  }

  export function query<T extends keyof Mappings>(
    ...args: Mappings[T]['params'] extends void
      ? [T]
      : [T, Mappings[T]['params']]
  ): Promise<Mappings[T]['result'][]>
}

And use like this:

import query from '@pgtyped/query' 

// both result and params are strictly typed
const [subnet] = await query(`
  SELECT cluster_id
  FROM cluster_subnets
  WHERE $clientIP <<= subnet
`, { clientIP })

query function implementation:

import pg from 'postgres' // for example
import { parseTSQuery } from './loader/typescript'
import { processTSQueryAST } from './preprocessor-ts'

export default function query(...args) {
    const [query, params] = args

    // Actually, we should cache queryAst.
    const { query: queryAst } = parseTSQuery(query)

    const { query: processedQuery, bindings } = processTSQueryAST(
        queryAst,
        params
    )

    return pg.unsafe(processedQuery, bindings)
}

We also would make a type-safe generic es6 template tag (even with strictly typed params), but there is a problem in typescript itself

3reactions
maxpaincommented, Jun 22, 2021

In an ideal world, we would use TypeScript Type Providers, but this issue is unresolved since 2015 and we need something useful right now.

At the moment using pgtyped on big projects is pretty painful.

For example, at my projects, I have tons of imports at the beginning of the files. image
Moreover, we have to place all our queries at the beginning of the files to speed-up performance image

Yes, we tried to use SQL files and use imports like this:

import * as queries from './src/books/queries';

await queries.findBookById.run(
    {
      bookId: 42,
    },
    client,
  );

But it is very uncomfortable to scroll a file (or open a separate .sql file) every time you create a new query. Moreover, you have to think about unique names of new SQL queries and when using separate SQL files you have to write all the annotations stuff:

/*
  @name selectSomeUsers
  @param ages -> (...)
  @param names -> (...)
*/
SELECT FROM users WHERE age in :ages or name in :names;

Someone may get benefits of reusing the same SQL queries, but in practice in different places you use different columns, joins, WHERE conditions, etc.

Also, we like to save context and write SQL queries in the same place we call/use them.

Read more comments on GitHub >

github_iconTop Results From Across the Web

TSConfig Reference - Docs on every TSConfig option
From allowJs to useDefineForClassFields the TSConfig reference includes information about all of the active compiler flags setting up a TypeScript project.
Read more >
clean-code-typescript - GitHub Pages
Clean Code concepts adapted for TypeScript. ... For now, let these guidelines serve as a touchstone by which to assess the quality of...
Read more >
Apollo Federation subgraph specification
Enhanced introspection with Query._service. Some federated graph routers can compose their supergraph schema dynamically at runtime. To do so, a graph router ...
Read more >
Configuring and deploying distributed tracing
The main backend components, Agent, Collector, and Query service, are all packaged into a single executable which is configured, by default. to use ......
Read more >
inversify-express-utils - npm
TypeScript icon, indicating that this package has built-in type declarations. 6.4.2 • Public • Published a year ago. Readme · Explore BETA ·...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found