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

appsync-local-server

v1.3.1

Published

A tool for running and testing AppSync JavaScript resolvers locally

Readme

appsync-local

CI npm version npm downloads codecov License: MIT

Run AWS AppSync JavaScript resolvers locally. No VTL, just JavaScript.

Install

npm install -g appsync-local-server

Usage

appsync-local start -c appsync-config.json -p 4000

Server starts at http://localhost:4000/

Config File

{
  "schema": "./schema.graphql",
  "apiConfig": {
    "auth": [{ "type": "API_KEY", "key": "dev-key" }]
  },
  "dataSources": [
    { "type": "NONE", "name": "LocalDS" }
  ],
  "resolvers": [
    {
      "type": "Query",
      "field": "getUser",
      "kind": "Unit",
      "dataSource": "LocalDS",
      "file": "./resolvers/getUser.js"
    }
  ]
}

Resolver Format

Resolvers export request and response functions:

// resolvers/getUser.js
export function request(ctx) {
  return { id: ctx.arguments.id };
}

export function response(ctx) {
  return { id: ctx.prev.result.id, name: 'Alice' };
}

Context (ctx) includes:

  • ctx.arguments - GraphQL arguments
  • ctx.prev.result - Result from request function (or previous pipeline function)
  • ctx.stash - Shared data across pipeline functions
  • ctx.source - Parent resolver result
  • ctx.identity - Auth identity info
  • ctx.request.headers - HTTP headers

Using @aws-appsync/utils

Write your resolvers exactly as you would for AWS AppSync. Standard imports work seamlessly:

// resolvers/createUser.js
import { util } from '@aws-appsync/utils';
import { put } from '@aws-appsync/utils/dynamodb';

export function request(ctx) {
  return put({
    key: { id: util.autoId() },
    item: {
      ...ctx.arguments.input,
      createdAt: util.time.nowISO8601()
    }
  });
}

export function response(ctx) {
  return ctx.prev.result;
}

The same resolver code works locally and when deployed to AWS AppSync.

Available Utilities

Globals (util, runtime, extensions) are also available without imports:

export function request(ctx) {
  // util is a global - no import needed
  return { id: util.autoId(), timestamp: util.time.nowEpochSeconds() };
}

DynamoDB helpers from @aws-appsync/utils/dynamodb:

  • get(), put(), update(), remove() - Single item operations
  • query(), scan() - Query and scan operations
  • batchGet(), batchPut(), batchDelete() - Batch operations
  • transactGet(), transactWrite() - Transactions
  • operations - Update expression builders (increment, append, etc.)

TypeScript Support

Install @aws-appsync/utils for TypeScript types:

npm install --save-dev @aws-appsync/utils

Data Sources

NONE

For pure JavaScript logic, no external calls:

{ "type": "NONE", "name": "LocalDS" }

DYNAMODB

Supports both local DynamoDB and real AWS DynamoDB.

Local DynamoDB:

{
  "type": "DYNAMODB",
  "name": "UsersTable",
  "config": {
    "tableName": "users",
    "region": "us-east-1",
    "endpoint": "http://localhost:8000",
    "accessKeyId": "fakeId",
    "secretAccessKey": "fakeSecret"
  }
}

Start local DynamoDB:

docker run -p 8000:8000 amazon/dynamodb-local

Real AWS DynamoDB:

{
  "type": "DYNAMODB",
  "name": "UsersTable",
  "config": {
    "tableName": "users",
    "region": "us-east-1"
  }
}

Omit endpoint to connect to real AWS. Credentials are loaded from the default AWS credential chain (env vars, ~/.aws/credentials, IAM role).

LAMBDA

Execute local JavaScript as Lambda:

{
  "type": "LAMBDA",
  "name": "MyLambda",
  "config": {
    "functionName": "processor",
    "file": "./lambdas/processor.js"
  }
}

Lambda file exports a handler:

export async function handler(event, context) {
  return { result: event.input * 2 };
}

HTTP

Call external HTTP APIs:

{
  "type": "HTTP",
  "name": "RestAPI",
  "config": {
    "endpoint": "https://api.example.com",
    "defaultHeaders": { "Authorization": "Bearer token" }
  }
}

Resolver builds HTTP request:

export function request(ctx) {
  return {
    method: 'GET',
    resourcePath: `/users/${ctx.arguments.id}`,
    params: {
      headers: { 'Accept': 'application/json' }
    }
  };
}

export function response(ctx) {
  return ctx.prev.result.body;
}

RDS

Supports both direct database connections and AWS RDS Data API.

Local/Direct Connection:

{
  "type": "RDS",
  "name": "Database",
  "config": {
    "engine": "postgresql",
    "databaseName": "mydb",
    "mode": "local",
    "host": "localhost",
    "port": 5432,
    "user": "postgres",
    "password": "password"
  }
}

AWS RDS Data API:

{
  "type": "RDS",
  "name": "Database",
  "config": {
    "engine": "postgresql",
    "databaseName": "mydb",
    "mode": "aws",
    "region": "us-east-1",
    "resourceArn": "arn:aws:rds:us-east-1:123456789:cluster:my-cluster",
    "awsSecretStoreArn": "arn:aws:secretsmanager:us-east-1:123456789:secret:my-secret"
  }
}

Resolver executes SQL:

export function request(ctx) {
  return {
    operation: 'executeStatement',
    sql: 'SELECT * FROM users WHERE id = :id',
    variableMap: { id: ctx.arguments.id }
  };
}

export function response(ctx) {
  return ctx.prev.result.records[0];
}

Pipeline Resolvers

Chain multiple functions:

{
  "type": "Mutation",
  "field": "createUser",
  "kind": "Pipeline",
  "file": "./resolvers/createUser.js",
  "pipelineFunctions": [
    { "file": "./functions/validate.js", "dataSource": "LocalDS" },
    { "file": "./functions/save.js", "dataSource": "UsersTable" }
  ]
}

Main resolver wraps the pipeline:

// resolvers/createUser.js
export function request(ctx) {
  ctx.stash.input = ctx.arguments.input;
  return {};
}

export function response(ctx) {
  return ctx.stash.result;
}

Each function in the pipeline:

// functions/validate.js
export function request(ctx) {
  if (!ctx.stash.input.email) {
    throw new Error('Email required');
  }
  return {};
}

export function response(ctx) {
  return ctx.prev.result;
}

Authentication

API Key

{
  "type": "API_KEY",
  "key": "your-dev-key"
}

Lambda Authorizer

With local Lambda file:

{
  "type": "AWS_LAMBDA",
  "lambdaFunction": "./auth/authorizer.js"
}

With mock identity (for local development without a Lambda):

{
  "type": "AWS_LAMBDA",
  "identity": {
    "sub": "user-123",
    "username": "testuser",
    "groups": ["admin"]
  },
  "resolverContext": {
    "tenantId": "tenant-abc",
    "role": "admin"
  }
}

The identity fields are available in resolvers via ctx.identity:

  • ctx.identity.sub - User ID
  • ctx.identity.username - Username
  • ctx.identity.groups - User groups array

The resolverContext is available via ctx.identity.resolverContext and merged into ctx.identity.claims.

Cognito User Pools

{
  "type": "AMAZON_COGNITO_USER_POOLS",
  "userPoolId": "us-east-1_xxxxx"
}

OpenID Connect

{
  "type": "OPENID_CONNECT",
  "issuer": "https://your-issuer.com",
  "clientId": "your-client-id"
}

IAM

{
  "type": "AWS_IAM"
}

Multiple Auth Methods

You can configure multiple auth methods. The first one that succeeds will be used:

{
  "apiConfig": {
    "auth": [
      { "type": "AWS_LAMBDA", "identity": { "sub": "dev-user" } },
      { "type": "API_KEY", "key": "fallback-key" }
    ]
  }
}

Full Example

See examples/basic/ for a working setup.

Run it:

npx appsync-local start -c examples/basic/appsync-config.json -p 4000

Query:

curl -X POST http://localhost:4000/ \
  -H "Content-Type: application/json" \
  -d '{"query": "{ listUsers { id name email } }"}'

Limitations

  • JavaScript resolvers only (no VTL)
  • Local simulation, not real AWS services
  • No subscriptions

Development

npm install
npm run dev -- -c examples/basic/appsync-config.json -p 4000
npm test
npm run test:e2e