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

aws-service-stack

v0.18.383

Published

chinggis core backend package

Readme

AWS Service Stack

npm version License: ISC TypeScript

📦 NPM Package: aws-service-stack

A comprehensive TypeScript backend framework for AWS serverless applications, providing enterprise-grade abstractions for DynamoDB, OpenSearch, API Gateway, Lambda, and Cognito integration.

🚀 Features

Core Infrastructure

  • 🏗️ Base Controllers - Abstract AWS Lambda event handling for API Gateway, SQS, DynamoDB Streams, and WebSocket events
  • 🗄️ Repository Pattern - Unified interfaces for DynamoDB and OpenSearch with transaction support
  • 🔐 Authentication & Authorization - Built-in Cognito integration with role-based permissions
  • 📊 Search & Analytics - Full-text search with OpenSearch integration
  • ✅ Validation - Support for both Zod and Yup schemas with type-safe validation
  • ✅ Permission - Item owner logic with built-in access control and parent child user management

AWS Services Integration

  • DynamoDB - Complete CRUD operations, batch processing, transactions, and advanced querying
  • OpenSearch - Full-text search, faceted search, and analytics
  • API Gateway - RESTful API handling with automatic request/response parsing
  • Lambda - Event-driven architecture with SQS, DynamoDB Streams, and scheduled events
  • Cognito - User authentication, authorization, and group management
  • Secrets Manager - Secure credential management

Developer Experience

  • 🔧 TypeScript First - Full type safety with comprehensive interfaces
  • 🏛️ Dependency Injection - TypeDI integration for clean architecture
  • 📝 Comprehensive Logging - Structured logging with different levels
  • 🧪 Testing Ready - Jest configuration with TypeScript support
  • 📦 Modular Design - Tree-shakable modules for optimal bundle size

📦 Installation

npm install aws-service-stack

Peer Dependencies

This package requires the following AWS SDK v3 packages as peer dependencies:

npm install
@aws-sdk/client-dynamodb
@aws-sdk/client-cognito-identity-provider
@aws-sdk/client-secrets-manager
@aws-sdk/util-dynamodb
@opensearch-project/opensearch

🏁 Quick Start

1. Basic Controller Setup

import Container, { Service } from "typedi";
import { CONFIG_Product, path } from "./Product-config";

import { ProductService } from "../../service/product/product-service-crud.interface";
import "../../service/product/product-service-crud";
import { ControllerApi, HttpRequest } from "@chinggis/core";
import { Product } from "src/model-shared/product.model";

@Service("ProductControllerApi")
export class ProductControllerApi extends ControllerApi<Product, ProductService> {
  constructor() {
    const service: ProductService = Container.get("ProductCrudService");
    super(service, CONFIG_PRODUCT.toObject());
  }

  /** Additional custom endpoints */
  protected async processCrudRequest(req: HttpRequest): Promise<any> {
    if (req.resourcePath === `GET ${path}/list-non-level-one`) {
      return await this.service.ListNonLevelOne(req.filter);
    }
  }
}

2. Entity Configuration

import { Access as a, DynamoIndexMap, EndpointPolicy, EntityConfigImpl, HttpMethod as m } from "@chinggis/core";
import {
  CREATE,
  REPLACE,
  RESPONSE_FIELDS_DETAILS as FIELDS_D,
  RESPONSE_FIELDS_LIST as FIELDS_L,
  UPDATE,
} from "../../model-shared/product.model";

export const openSearch_Product = {
  domain: "https://search-amplify-opense-abcd.ap-southeast-1.es.amazonaws.com",
  index: "product",
};
export const path = "/products"; // url path base

// Product configuration
export class ProductConfig extends EntityConfigImpl {
  constructor() {
    // DYNAMODB
    const tableName = "product-dev";
    const ownerFieldName = "owner";
    const indexMap = new DynamoIndexMap()
      .setFields("id")
      .set("byAccountType", { field: "accountType", rFields: ["accountType"] });

    // PERMISSIONS
    const adminGroupName = ["adminUsers"];
    const policyList: EndpointPolicy[] = [
      { method: m.GET, path: `${path}`, access: [a.ADMIN], response: FIELDS_L },
      { method: m.GET, path: `${path}/search`, access: [a.ADMIN], response: FIELDS_L },
      { method: m.GET, path: `${path}/list-non-level-one`, access: [a.ADMIN], response: FIELDS_L },
      { method: m.GET, path: `${path}/{id}`, access: [a.OWNER, a.ADMIN], response: FIELDS_D },
      { method: m.POST, path: `${path}`, access: [a.ADMIN], validator: CREATE, response: FIELDS_D },
      { method: m.PUT, path: `${path}/{id}`, access: [a.ADMIN], validator: REPLACE, response: FIELDS_D },
      { method: m.PATCH, path: `${path}/{id}`, access: [a.ADMIN], validator: UPDATE, response: FIELDS_D },
      { method: m.DELETE, path: `${path}/{id}`, access: [a.ADMIN] },
    ];

    // INIT
    super(path, adminGroupName);
    this.setDynamoDB(tableName, ownerFieldName, indexMap)
      .setOpenSearch(openSearch_Product.domain, openSearch_Product.index)
      .setPolicies(policyList);
  }
}

// Export default Product configuration
export const CONFIG_PRODUCT = new ProductConfig();

3. Repository Definition - DynamoDB

import { Service } from "typedi";
import { BaseRepoDBImpl } from "@chinggis/core";
import { Product } from "../../model-shared/product.model";
import { ProductRepoDB } from "./product-repo-db.interface";
import "./product-repo-db";

@Service("ProductRepoDB")
export class ProductRepoDBImpl extends BaseRepoDBImpl<Product> implements ProductRepoDB {}

/** additional custom repo methode for dynamodb integration**/
//...

4. Repository Definition - OpenSearch

import { Service } from "typedi";
import { BaseRepoESImpl } from "@chinggis/core";
import { Product } from "../../model-shared/product.model";
import { ProductRepoES } from "./product-repo-es.interface";
import "./product-repo-es";

@Service("ProductRepoES")
export class ProductRepoESImpl extends BaseRepoESImpl<Product> implements ProductRepoES {}

/** additional custom repo methode for openSearch integration**/
//...

5. Service Definition

import { Container, Service } from "typedi";
import { ProfileService } from "./Product-service.interface";
import "./Product-service";
import { ProfileRepoDB } from "../repositories/Product/Product-repo-db.interface";
import "../repositories/Product/Product-repo-db";

import { ErrorHttp } from "../../exception/errors";
import { CrudServiceImpl } from "@chinggis/core";
import { Product } from "../model-shared/example.model";

@Service("ProfileService")
export class ProductServiceImpl
  extends CrudServiceImpl<Product, ProductRepoDB, ProductRepoES>
  implements ProductService
{
  constructor() {
    super(Container.get("ProductRepoDB"), Container.get("ProductRepoES"));
  }

  /** Custom service methode **/
  //...
}

🛠️ Built-in API Endpoints

Your AWS Service Stack automatically provides these RESTful endpoints for any entity:

Standard CRUD Operations

POST    /products               # Insert new product (create)
GET     /products/{id}          # Get single product by ID
GET     /products               # List products via DynamoDB Query (by categoryId, ProductId, status, …)
GET     /products/search        # Search products via OpenSearch (full-text, sort, pagination)
GET     /products/search/query  # Search Query products via OpenSearch (aggregations, multi-filters, full-text, sort, pagination)
GET     /products/scan          # Scan all products (Admin only, expensive in DynamoDB)
PUT     /products/{id}          # Update full product (replace all fields)
PATCH   /products/{id}          # Update partial product (only specific fields)
DELETE  /products/{id}          # Delete product by ID

List Operation - URL Filter

index filter, combined index filter, range filter for date or number field, sort, search, paged

openSearch: .../products/search?category=Toys&color=red&age_from=5&age_to=8&page=5&size=50
dynamoDB:   .../products/?category=Toys&color=red&age_from=5&age_to=8&size=50&lastKey=dfashdfjkasd89843dfa
  • order=ASC
  • oderBy=createdDate
  • searchKeyword=marvel
  • searchBy=name,manufacture,description
  • [customFieldName]=[matchValue]
  • size=20
  • page=3 or lastKey=...
  • [dateOrNumberFieldName]_from=[Value]
  • [dateOrNumberFieldName]_to=[Value]
  • begin_[dateOrNumberFieldName]=[Value]
  • end_[dateOrNumberFieldName]=[Value]

OpenSearch V2 List Operation - URL Filter

multi-filter, aggregations, search keyword, multi-sort

openSearch v2: .../products/search/query?category__eq=Toys&color__in=red,yellow&age__gte=5&age__lte=8&page=5&size=50&agg__terms=status&agg__terms__sub=customerId
  • field__in=red,yellow

  • field__eq=Toys

  • field__gte=5

  • field__lte=8

  • field__ne=Book

  • field__exists=true

  • field__exists=false

  • field__regex=^Foo.*

  • agg__terms=status

  • agg__terms__sub=customerId

  • agg__sum=price

  • agg__avg=price

  • agg__min=price

  • agg__max=price

  • search=marvel

  • searchFields=name,manufacture,description

  • searchOperator=or

  • searchOperator=and

  • sort=desc,asc

  • sortField=price,category

  • color__in=red,yellow

  • category__eq=Toys

  • status__exists=true

  • age__gte=5

  • age__lte=8

  • size=20

  • page=3

Advanced Features

  • 🔍 Smart Filtering - DynamoDB queries with complex conditions
  • 🔎 Full-Text Search - OpenSearch integration with faceted search
  • 📄 Pagination - Built-in pagination with lastKey support
  • 🔐 Authorization - Role-based access control per endpoint
  • ✅ Validation - Automatic request/response validation
  • 📊 Analytics - Built-in logging and monitoring

🏗️ Architecture

Layered Architecture

┌─────────────────┐
│   Controllers   │ ← API Gateway, Lambda Events
├─────────────────┤
│    Services     │ ← Business Logic
├─────────────────┤
│  Repositories   │ ← Data Access (DynamoDB, OpenSearch)
├─────────────────┤
│   Providers     │ ← AWS SDK Clients
└─────────────────┘

Event Handling

  • API Gateway - RESTful API endpoints
  • SQS - Message queue processing
  • DynamoDB Streams - Real-time data processing
  • WebSocket - Real-time communication
  • Scheduled Events - Cron-based tasks

🔧 Configuration

Environment Variables

# AWS Configuration
AWS_REGION=us-east-1
IAM_ACCESS_KEY=your-access-key
IAM_SECRET_ACCESS_KEY=your-secret-key

# OpenSearch
OPENSEARCH_ENDPOINT=https://your-domain.us-east-1.es.amazonaws.com

# API Gateway
WS_ENDPOINT=wss://your-websocket-api.execute-api.us-east-1.amazonaws.com/prod

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions"
:
  {
    "baseUrl"
  :
    "./",
      "paths"
  :
    {
      "@chinggis/core"
    :
      ["node_modules/aws-service-stack/dist/index.d.ts"],
        "@chinggis/types"
    :
      ["node_modules/aws-service-stack/dist/model/index.d.ts"]
    }
  }
}

📚 API Reference

BaseController

  • handleRequest(event) - Main Lambda handler
  • handleSQS(event) - SQS message processing
  • handleWebSocket(event) - WebSocket connection handling

BaseService

  • save(entity, ownerId) - Create/update entity
  • findById(id, userId) - Find entity by ID
  • find(filter: Filter, userId) - Find entity by filter
  • delete(id, userId) - Delete entity
  • ... - Usefully crud methods

Repository Interfaces

  • CoreRepo<T> - Generic CRUD operations
  • BaseRepoDB<T> - DynamoDB-specific operations
  • BaseRepoES<T> - OpenSearch-specific operations

🧪 Testing

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run specific test file
npm test -- user.test.ts

🚀 Example Deployment

Serverless Framework - For Example

# serverless.yml
service: my-aws-service
provider:
  name: aws
  runtime: nodejs18.x
  region: us-east-1

functions:
  api:
    handler: dist/handler.handler
    events:
      - http:
          path: /{proxy+}
          method: ANY

AWS CDK

import { Function, Runtime } from "aws-cdk-lib/aws-lambda";

new Function(this, "MyFunction", {
  runtime: Runtime.NODEJS_18_X,
  handler: "dist/handler.handler",
  code: Code.fromAsset("dist"),
});

🔐 Role-Based Access Control (RBAC)

The framework provides a centralized RBAC system through ControllerRole, which manages Cognito user pool groups, DynamoDB-backed permissions, and scope-based data filtering — all integrated directly into ControllerApi.

Architecture

┌──────────────────┐
│  ControllerApi   │ ← Delegates RBAC check automatically
├──────────────────┤
│  ControllerRole  │ ← Permission CRUD, Cognito groups, scope enforcement
├──────────────────┤
│PermissionService │ ← Read-through cache (15-min TTL) + orchestration
├──────────────────┤
│  PermissionRepo  │ ← DynamoDB via BaseRepoDBImpl
└──────────────────┘

Permission Model

Each permission is stored in DynamoDB with the composite key role#resource#scope:

interface Permission {
  id: string;
  role: string; // Cognito group name: "Manager", "Agent"
  resource: string; // API resource: "property", "order"
  scope: string; // Data scope: "Organization", "Branch", "Agent"
  method: {
    get?: boolean;
    post?: boolean;
    patch?: boolean;
    put?: boolean;
    delete?: boolean;
  };
  permissionKey: string; // "Manager#property#Branch"
}

Configuration

Enable RBAC by adding ROLE_TABLE, ROLE_PATH, and SCOPE_MAP to your entity config:

import { EntityConfigImpl, ScopeMap } from "@chinggis/core";

const scopeMap: ScopeMap = new Map([
  ["Organization", { filterField: "orgId", claimKey: "custom:orgId" }],
  ["Branch", { filterField: "branchId", claimKey: "custom:branch" }],
  ["Agent", { filterField: "agentId", claimKey: "custom:agent" }],
]);

export class PropertyConfig extends EntityConfigImpl {
  constructor() {
    super("/properties", ["adminUsers"]);

    this.setDynamoDB("property-dev", "owner", indexMap)
      .setOpenSearch(domain, "property")
      .setPolicies(policyList)
      .setScopes(scopeMap)
      .setRoleTable("role-permissions-dev")
      .setRolePath("/properties/role");
  }
}

When both ROLE_TABLE and SCOPE_MAP are configured, ControllerApi automatically creates a ControllerRole instance and enforces RBAC on every request.

How It Works

  1. Request arrives at ControllerApi.resolveCrudRequest()
  2. If RBAC is configured, ControllerRole.checkRbacAccess() is called
  3. User's role is extracted from JWT groups[0]
  4. Scope is read from ?scope=Branch query parameter and validated against ScopeMap
  5. Permission is checked via PermissionService (with in-memory cache)
  6. On success, scope filter is applied (e.g., filter.branchId = identity["custom:branch"])
  7. On failure, 403 PermissionDenied is thrown

Permission CRUD Endpoints

When ROLE_PATH is configured, permission management endpoints are automatically available:

GET     /properties/role               # List all permissions
POST    /properties/role               # Create a permission
PATCH   /properties/role               # Update a permission
DELETE  /properties/role               # Delete a permission
POST    /properties/role/add-role      # Create a Cognito user pool group
POST    /properties/role/add-user-role # Add a User to cognito group

Create a Permission

POST /properties/role
{
  "role": "Manager",
  "resource": "property",
  "scope": "Branch",
  "method": { "get": true, "post": true, "patch": true, "delete": false }
}

Create a Cognito Group (Role)

POST /properties/role/add-role
{
  "groupName": "Manager",
  "description": "Branch-level managers"
}

Programmatic Usage

ControllerRole can also be used directly in custom controllers or services:

import { ControllerRole } from "@chinggis/core";

const roleController = new ControllerRole("role-permissions-dev");

// Permission CRUD
await roleController.addPermission({
  role: "Manager",
  resource: "property",
  scope: "Branch",
  method: { get: true, post: true, patch: true },
});

await roleController.listPermissions();
await roleController.updatePermission("perm-id", { method: { delete: true } });
await roleController.deletePermission("perm-id");

// Permission check
const allowed = await roleController.hasPermission("Manager", "property", "Branch", "GET");

// Cognito group management
await roleController.addRole("us-east-1_PoolId", "Manager", "Branch-level managers");
await roleController.assignRole("us-east-1_PoolId", "[email protected]", "Manager");

Caching

PermissionService uses a read-through cache with 15-minute TTL optimized for Lambda warm invocations:

  • Cache hits return instantly without DB calls
  • Concurrent requests for the same key are deduplicated (single in-flight fetch)
  • Cache is automatically invalidated on create, update, and delete operations

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat(core): add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# Clone the repository
git clone https://github.com/chinggis-systems/aws-service-stack.git

# Install dependencies
npm install

# Run tests
npm test

# Build the project
npm run build

📄 License

This project is licensed under the ISC License - see the LICENSE file for details.

🏢 About Chinggis Systems

Built by Chinggis Systems - Enterprise software solutions for modern cloud architectures.

📞 Support


Made with ❤️ for the AWS community