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

apollo-datasource-cosmosdb

v1.1.0

Published

An Apollo data source for Azure CosmosDB

Downloads

587

Readme

Apollo DataSource for CosmosDB

This is a CosmosDB DataSource for the Apollo GraphQL Server. It was adapted from the MongoDB Data Source project.

Usage

Use by creating a new class, inheriting from CosmosDataSource passing in the CosmosDb container instance (created from the CosmosDB Javascript API). Use a separate DataSource for each data type.

Example:

data-sources/Users.ts

export interface UserDoc {
  id: string; // a string id value is required for entities using this library
  name: string;
}

export class UserDataSource extends CosmosDataSource<UserDoc> {}
export class PostDataSource extends CosmosDataSource<PostDoc> {}

server.ts

import { CosmosClient } from "@azure/cosmos";
import { CosmosDataSource } from "apollo-datasource-cosmosdb";

const cosmosClient = new CosmosClient({
  endpoint: "https://my-cosmos-db.documents.azure.com:443/",
  key: "--------key-goes-here---==",
});
const cosmosContainer = cosmosClient.database("MyDatabase").container("Items");

import UserDataSource from "./data-sources/Users.js";
import PostDataSource from "./data-sources/Users.js";

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources: () => ({
    users: new UserDataSource(cosmosContainer),
    posts: new PostDataSource(cosmosContainer),
  }),
});

Context

It is often useful to define a context. See Apollo docs on context To make this strongly typed, there is a second type paramater on the CosmosDbDataSource:

interface MyQueryContext {
  currentUserId: string
}

/////

const userDataSource extends CosmosDataSource<UserDoc, MyQueryContext> {}

Custom Queries

CosmosDataSource exposes a findManyByQuery method that accepts a ComosDB SQL query either as a string or a SqlQuerySpec object containing the query and a parameter collection. This can be used directly in the resolvers, but probably better to create wrappers that hide the query details.

Creating a derived class with custom query methods, you can hide all of your query logic in the DataSource class:

export class UserDataSource extends CosmosDataSource<UserDoc, ApolloContext> {
  findManyByGroupID = async (group_id, args: FindArgs = {}) => {
    log.debug(`UserDataSource: getGroupUsers ${group_id}`);
    const query = `SELECT * FROM c where c.type = "User" and exists(select * from g in c.groups where g = @group_id) `;
    const results = await this.findManyByQuery(
      {
        query,
        parameters: [{ name: "@group_id", value: group_id }],
      },
      args
    );
    log.debug(`Result count ${results.resources.length}`);
    return results.resources;
  };
  findOneByUserName = async (userName: string, args: FindArgs = {}) => {
    const results = await this.findManyByQuery(
      {
        query: `SELECT * FROM c WHERE c.userName = @userName AND c.type = 'User'`,
        parameters: [{ name: "@userName", value: userName }],
      },
      args
    );
    if (results.resources && results.resources.length)
      return results.resources[0];
    return undefined;
  };
}

Write Operations

This DataSource has some built in mutation methods to create, update and delete items. They can be used directly in resolvers or wrapped with custom methods.

await context.dataSources.users.createOne(userDoc);

await context.dataSources.users.updateOne(userDoc);

await context.dataSources.users.updateOnePartial(user_id, { name: "Bob" });

await context.dataSources.users.deleteOne(userId);

The data loader (and cache, if used) are updated after mutation operations.

Batching

Batching is provided on all queries using the DataLoader library.

Caching

Caching is available on an opt-in basis by passing a ttl option on queries.

Typescript

This library is written in Typescript and exports full type definitions, but usable in pure Javascript as well. This works really well with GraphQL Codegen's typed resolvers.

API

const thisUser = await users.findOneById(id: string, {ttl})  // => Promise<T | undefined>

const userPair = await users.findManyByIds([id1, id2], {ttl}) // => Promise<(T | undefined)[]>

await users.deleteFromCacheById(id}) // => Promise<void>

// query method; note that this returns the CosmosDB FeedResponse object because sometimes this extra information is useful
const userListResponse = await users.findManyByQuery('SELECT * from c where c.type="User"', {ttl, requestOptions}) // => Promise<FeedResponse<T>>
console.log(userListResponse.resources) // user array from query

// create method returns the CosmosDB ItemResponse object
const createResponse = await users.createOne(newUser) // => Promise<ItemResponse<T>>
console.log(createResponse.resource)  // user object returned from create, with CosmosDB-added values

const updateUserResponse = await users.updateOne(thisUser) // => Promise<ItemResponse<T>>

const updatePartialResponse = await users.updateOnePartial(id, {firstName: "Bob"}) // => Promise<ItemResponse<T>>
console.log(updatePartialResponse.resource) // full user object from DB after updates