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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@composurecdk/neptune

v0.11.2

Published

Composable Amazon Neptune cluster builder with well-architected defaults

Readme

@composurecdk/neptune

Amazon Neptune cluster builder for ComposureCDK.

This package provides a fluent builder for Amazon Neptune clusters with secure, AWS-recommended defaults. It wraps the CDK alpha DatabaseCluster L2 construct — refer to the CDK documentation for the full set of configurable properties.

Alpha dependency. Neptune's CDK L2 lives in @aws-cdk/aws-neptune-alpha, which is production-usable but semver-unstable and version-locked to its matching aws-cdk-lib release. It is a peer dependency of this package — install it alongside aws-cdk-lib in your app, at 2.190.0-alpha.0 or later and matching your aws-cdk-lib release. The peer range is *, so npm does not enforce that minimum.

npm install @composurecdk/neptune @aws-cdk/aws-neptune-alpha aws-cdk-lib

Cluster Builder

A Neptune cluster owns its writer/reader instances — the CDK L2 creates them from the instance type and instance count — so a single cluster builder covers both serverless and provisioned topologies. There is intentionally no separate instance builder: in-region read replicas are added via the instance count (.instances(n)), and cross-region scale-out is a Neptune Global Database (separate read-only clusters per region, future work), not extra instances on one cluster.

Serverless

import { compose, ref } from "@composurecdk/core";
import { createVpcBuilder, type VpcBuilderResult } from "@composurecdk/ec2";
import { createClusterBuilder } from "@composurecdk/neptune";
import { InstanceType } from "@aws-cdk/aws-neptune-alpha";

const system = compose(
  {
    network: createVpcBuilder().maxAzs(2),
    graph: createClusterBuilder()
      .vpc(ref<VpcBuilderResult>("network").get("vpc"))
      .instanceType(InstanceType.SERVERLESS)
      .serverlessScalingConfiguration({ minCapacity: 1, maxCapacity: 8 }),
  },
  { network: [], graph: ["network"] },
);

Provisioned

const graph = createClusterBuilder()
  .vpc(ref<VpcBuilderResult>("network").get("vpc"))
  .instanceType(InstanceType.R6G_LARGE)
  .instances(2); // one writer + one reader

The vpc is set via the dedicated .vpc() method (it is required and accepts a Ref for cross-component wiring). securityGroups likewise accept Resolvable values. Every other DatabaseClusterProps property is available as a fluent setter. The instanceType is required — defaulting it would create surprise cost.

Build result

build() returns every construct the builder creates, per the build-results-must-be-complete rule:

interface ClusterBuilderResult {
  cluster: DatabaseCluster;
  subnetGroup: ISubnetGroup;
  clusterParameterGroup: IClusterParameterGroup;
  alarms: Record<string, Alarm>;
}

Secure Defaults

createClusterBuilder applies the following defaults. Each can be overridden via the builder's fluent API. Defaults are anchored to the AWS Well-Architected Framework — Neptune lens.

| Property | Default | Rationale | | ------------------------- | ------------------------- | --------------------------------------------------------------------- | | storageEncrypted | true | Encryption at rest; supply a CMK via .kmsKey(). | | iamAuthentication | true | Removes long-lived static credentials. | | removalPolicy | RETAIN | Protects graph data from an errant cdk destroy. | | deletionProtection | true | Blocks accidental deletion of the cluster. | | backupRetention | Duration.days(7) | Production-grade window (CDK default is 1 day). | | cloudwatchLogsExports | [LogType.AUDIT] | Exports audit logs (the only CloudWatch-exportable Neptune log type). | | cloudwatchLogsRetention | RetentionDays.ONE_MONTH | Bounds log storage cost. | | copyTagsToSnapshot | true | Preserves cost-allocation tags on backups. | | autoMinorVersionUpgrade | true | Stays on patched engine versions. |

The defaults are exported as CLUSTER_DEFAULTS for visibility and testing.

Encryption at rest

storageEncrypted is on by default, using the AWS-managed Neptune key. .kmsKey(...) selects a customer-managed key instead — for independent rotation, a key policy you control, and CloudTrail visibility of decrypt calls. It accepts a concrete key or a Resolvable, so a key built by @composurecdk/kms can be a component of the same system rather than a construct created before compose:

import { compose, ref } from "@composurecdk/core";
import { createKeyBuilder, type KeyBuilderResult } from "@composurecdk/kms";

compose(
  {
    graphKey: createKeyBuilder().description("Encrypts the knowledge-graph cluster at rest."),
    graph: createClusterBuilder()
      .vpc(vpc)
      .instanceType(InstanceType.SERVERLESS)
      .serverlessScalingConfiguration({ minCapacity: 1, maxCapacity: 8 })
      .kmsKey(ref<KeyBuilderResult>("graphKey").get("key")),
  },
  { graphKey: [], graph: ["graphKey"] },
).build(stack, "Knowledge");

Both the key and the encryption setting are fixed at creation: Neptune cannot encrypt an existing unencrypted cluster, nor move to a different key, without restoring from a snapshot. Setting .storageEncrypted(false) alongside a key is a contradiction CDK rejects at synth — the builder does not silently reconcile it.

Audit-log parameter group

Audit log export only emits data once audit logging is enabled in the cluster parameter group. So the builder auto-creates a cluster parameter group with neptune_enable_audit_log = "1" (parallel to how createVpcBuilder auto-creates a flow-log group), with the family derived from the configured engine version. Add or override parameters with .clusterParameters({...}), or supply your own group with .clusterParameterGroup(myGroup) (mutually exclusive with .clusterParameters()).

Granting access

Because IAM authentication is on by default, a principal needs two things to reach the cluster, and each is declared where its dependency already points (ADR-0013). The network path is a rule in the cluster's own security group, so it stays on the cluster — allowDefaultPortFrom(peer), the same method createInterfaceEndpointBuilder exposes. The IAM grant lands on the consumer's principal, so it is declared on the grantee builder, pointing back at the cluster via a ref:

import { clusterGrants, type ClusterBuilderResult } from "@composurecdk/neptune";

compose(
  {
    graph: createClusterBuilder()
      .vpc(ref<VpcBuilderResult>("network").get("vpc"))
      .instanceType(InstanceType.SERVERLESS)
      .serverlessScalingConfiguration({ minCapacity: 1, maxCapacity: 8 })
      .allowDefaultPortFrom(
        ref<SecurityGroupBuilderResult>("bastionSg").get("securityGroup"),
        "Bastion to Neptune",
      ),

    bastion: createInstanceBuilder()
      .vpc(ref<VpcBuilderResult>("network").get("vpc"))
      .securityGroup(ref<SecurityGroupBuilderResult>("bastionSg").get("securityGroup"))
      .grant(clusterGrants.connect(ref<ClusterBuilderResult>("graph").get("cluster"))),
  },
  {
    graph: ["network", "bastionSg"], // cluster → the SG its ingress rule names
    bastion: ["network", "bastionSg", "graph"], // consumer → resource; no reverse edge, no cycle
  },
);

clusterGrants.connect(cluster) delegates to the cluster's native grantConnect, which grants the whole neptune-db:* namespace on the cluster's ARN. A principal that needs less wants a narrower policy of its own over the data-plane actions.

The grant goes on any builder that accepts one — an InstanceBuilder, a FunctionBuilder, or a RoleBuilder where the principal is a role you own. Each routes the policy onto whichever role the component runs as, so no separate role component is needed to hold it.

allowDefaultPortFrom(peer) takes any IConnectable — a security group, an EC2 instance, a VPC-attached Lambda function — and applies cluster.connections.allowDefaultPortFrom(peer, description), opening ingress on the cluster's SG and the matching egress on the peer's. Prefer the peer's security group over the peer itself: naming a compute component makes the cluster depend on it, the reverse edge consumer-side grants exist to avoid.

If you disable IAM authentication (.iamAuthentication(false)), the network path is the whole grant — the alpha L2 then rejects a data-plane grant at synth rather than emitting an inert policy, so drop the clusterGrants call along with IAM auth.

For a worked example, see the Neptune graph stack.

Recommended Alarms

The builder creates recommended CloudWatch alarms by default. Neptune is not yet covered by the CloudWatch out-of-the-box alarm recommendations, so thresholds follow the Neptune metrics guidance. No alarm actions are configured — access alarms from the build result to add actions.

| Alarm | Metric | Default threshold | Created when | | --------------------------------- | -------------------------------------------- | -------------------- | ------------------------ | | cpuUtilization | CPUUtilization (Average, 1 min) | >= 80% | Always | | mainRequestQueuePendingRequests | MainRequestQueuePendingRequests (Avg, 1 min) | > 100 | Always | | bufferCacheHitRatio | BufferCacheHitRatio (Average, 1 min) | < 99.9% | Always | | clusterReplicaLag | ClusterReplicaLag (Average, 1 min) | > 30000 ms | Always[^lag] | | serverlessDatabaseCapacity | ServerlessDatabaseCapacity (Average, 1 min) | 90% of maxCapacity | Serverless clusters only |

[^lag]: Only emits data when the cluster has a read replica. TreatMissingData defaults to notBreaching, so it stays quiet on a single-instance cluster.

The defaults are exported as CLUSTER_ALARM_DEFAULTS for visibility and testing.

Customizing and disabling alarms

createClusterBuilder()
  // tune one alarm, disable another
  .recommendedAlarms({ cpuUtilization: { threshold: 90 }, bufferCacheHitRatio: false })
  // add a custom alarm
  .addAlarm("gremlinErrors", (a) =>
    a
      .metric((cluster) => cluster.metric("NumGremlinErrorsPerSec"))
      .threshold(0)
      .greaterThan(),
  );

// disable all recommended alarms
createClusterBuilder().recommendedAlarms(false);