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

lookahead

v1.0.0

Published

Look Ahead GraphQL fields with recursive joins/lookups using Mongo aggregation pipelines for Apollo queries.

Downloads

1

Readme

Lookahead

Look Ahead GraphQL fields with recursive joins ($lookup) using Mongo aggregation pipelines for Apollo queries.

Future

I am building Version 2 of this library which would allow you to recursively convert Nested GraphQL request into N Level Nested Lookups Queries, Along with support for different stages such as Match, Limit & Sort. You may wanna look into mongo-aggregation-builder, an easier and readable way of building mongodb aggregation pipelines.

Overview

A Apollo/MongoDB based project uses GraphQL resolvers to recursively fetch fields from Mongo collections. This approach is often sufficient in most of the cases, however it suffers from a major issue.

  • GraphQL attempts to fetch fields recursively due to which single request can lead to many database requests. It's easy to see how we can quickly reach hundreds of lookups for a single Apollo query. This issue can be solved by performing a single Mongo aggregation that fetches all the data in one go, performing lookups on the related collections, so that we can then sort or filter on any field in the result.

lookahead does all the heavy lifting for you:

  1. It analyses the resolveInfo data passed to the top-level resolver in order to extract the hierarchy of fields that have been requested. It does this to ensure that it only performs the joins required for the actual query.

  2. From this information it builds a single Mongo aggregation pipeline that recursively performs lookups for the other collections used in the request.

    You can then include the pipeline as part of a larger aggregation pipeline that sorts and filters the result.

Installation

npm install lookahead

You'll also need to include lookahead's type definition and directive when calling Apollo's makeExecutableSchema:

import { mergeTypes } from 'merge-graphql-schemas';
import { lookAheadDirective } from 'lookahead';

...


const { lookAheadDirectiveTypeDefs, lookAheadDirectiveTransformer } = lookAheadDirective('lookahead');

let schema = makeExecutableSchema({
  typeDefs: mergeTypes([lookAheadDirectiveTypeDefs, ...yourTypes]),
  resolvers,
});

schema = lookAheadDirectiveTransformer(schema)

Specifying the Joins

lookahead needs to know which fields are joins, and how to join them. In order to make this both easy to specify and declarative, a custom GraphQL directive, @lookahead, is used to specify this information directly in the types declaration. Here's an example:

type Company {
  ...
  user: User @lookahead(lookup: { collection: "users", localField: "userId", foreignField: "_id" })
}

type Query {
  ...
  companies: [Company!]!
}

Writing the Resolvers

In your resolvers you'll call createPipeline to create the aggregation pipeline:

import { createPipeline } from 'lookahead';

...

const companies = (_, { limit = 20 }, context, resolveInfo) => {
  // Create a pipeline to first perform any initial matching, then do the lookups and finally fetch the results
  const pipeline = [
    // Perform any initial matching that you need.
    // This would typically depend on the parameters passed to the query.
    { $match: { type: 'b2b' } }

    // Include all the pipeline stages generated by lookahead to do the lookups
    // We pass `null` since the `users` query is mapped directly to the result
    // of an aggregation on the Users collection.
    ...createPipeline(null, resolveInfo, context),

    // Filter, sort or limit the result.
    { $limit: limit },
  ];

  // How you call Mongo will depend on your code base. You'll need to pass your pipeline to Mongo's aggregate.
  // This is how you'd do it using `mongoose`
  return CompanyCollection.aggregate(pipeline);
});