npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

knex-gql

v0.0.7-dev

Published

A Schema First GraphQL Query Builder for Knex.js

Downloads

10

Readme

knex-gql

[experimental] A Schema First GraphQL Query Builder for Knex.js

Why

Creating a GraphQL service with a Relational Database is a hard thing. We should take care of:

  • Performance. N+1 problem will happen if you don't use Dataloader.
  • Pagination. Dataloader pattern is hard to implement pagination without a hacky union or window functions.
  • Security. Keeping private fields needs much more work.
  • Code Reusability. GraphQL frameworks provides the Middleware function, but it is often not enough.
  • Realtime. Using GraphQL subscription is a challenging task.
  • Type Safety. How to type GraphQL result without relying on Codegen?

So, why not integrate Knex with GraphQL directly?

Getting Started

Step 1. Install

yarn add knex-gql

Step 2. Define GraphQL schema using SDL

# Schema.gql

type User @table(name: "users") {
  id: ID!
  name: String!
  posts: [Post!]! @hasMany(foreignKey: "user_id")
  pageinatedPosts: [Post!]!
    @hasMany(foreignKey: "user_id", type: PAGINATOR, limit: 7)
}

input UserInput {
  name: String!
}

type Post @table(name: "posts") {
  id: ID!
  user_id: ID!
  title: String!
  user: User! @belongsTo(foreignKey: "user_id")
}

type Query {
  user(
    id: ID @where(operator: "=")
    name: String @where(operator: "LIKE")
  ): User @first
}

type Mutation {
  createUser(input: UserInput!): User! @insert
}
  • @table defines which table is associated with that type.
  • @hasMany and belongsTo define table relations. Relations are automatically batch loaded when executed, meaning that no N+1 problem happens.
  • @where filters rows when these arguments are passed.
  • @first will returns the first matching row.

Step 3. Create KnexGql instance

import { KnexGql } from 'knex-gql'
import Knex from 'knex'
import fs from 'fs/promises'

export async function getKnexGql() {
  const typeDefs = await fs.readFile('./schema.gql', 'utf8')

  const knex = Knex({
    client: 'pg',
    connection: 'postgres://postgres:[email protected]:11155/postgres',
  })

  const knexGql = new KnexGql({ knex, typeDefs })

  await knexGql.prepareTableColumnsMap() // This loads column info before execution

  return knexGql
}

Step 4. Run Query

import { gql } from 'knex-gql'

getKnexGql().then(async (knexGql) => {
  await knexGql
    .query(
      gql`
        mutation CreateUser {
          createUser(input: { name: "Kay" }) {
            id
            name
          }
        }
      `,
    )
    .then(console.log)
  // Output:
  // {
  //   data: {
  //     createUser: {
  //       id: 'f8f37213-060b-4b6d-843c-5fc498bcbc08',
  //       name: 'Kay'
  //     }
  //   }
  // }

  await knexGql
    .query(
      gql`
        query GetUser {
          user(name: "Kay") {
            id
            name
          }
        }
      `,
    )
    .then(console.log)
  // Output:
  // {
  //   data: {
  //     user: {
  //       id: 'f8f37213-060b-4b6d-843c-5fc498bcbc08',
  //       name: 'Kay'
  //     }
  //   }
  // }
})

Features

  • Automatically batch loads to avoid N+1
  • Automatically select required columns based on fields set
  • Middleware and custom resolver available

// Documentation todo

Directives

| name | description | | ---------- | ------------------------------------- | | @all | Get all matching records | | @belongsTo | Get the parent record of given type | | @first | Get the first matching record | | @hasMany | Get child records with pagination | | @insert | Insert a record into the type's table | | @paginate | Paginate matching records | | @table | Map table and type | | @where | Add where clause for current query |

For more details, please see Sample app. https://github.com/acro5piano/knex-gql-sample-app