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

better-sqs

v0.1.1

Published

Type-safe queues for AWS SQS

Readme

Better-SQS

Type-safe queues for AWS SQS. A simple, developer-friendly API with full TypeScript support.

Features

  • Developer-friendly API: send(topic, payload) instead of raw SQS SDK calls
  • Type-safe: Full TypeScript support with generics
  • Handler routing: Automatically routes messages to handlers based on queue name
  • FIFO support: Built-in message grouping, deduplication, and batch failure handling
  • Serverless-first: Factory pattern with no global state - perfect for Lambda
  • Simple API: Familiar send(topic, payload) pattern
  • Framework-agnostic: Works with any AWS Lambda setup (SST, CDK, Serverless, etc.)
  • Optional logger: Accepts any logger interface (pino, winston, console, etc.)

Installation

npm install better-sqs
# or
bun add better-sqs
# or
pnpm add better-sqs

Quick Start

1. Sending Messages

Option A: Standalone send() (simplest)

import { send } from "better-sqs";

// Simple send - uses env vars automatically
await send("user-notifications", {
  userId: "123",
  type: "welcome"
});

// With options
await send("order-processing", { orderId: "456" }, {
  idempotencyKey: "unique-key",
  delaySeconds: 60,
  groupId: "order-456", // Required for FIFO queues
});

Option B: Client Instance (more control)

import { createClient } from "better-sqs";

// Create a configured client
const queue = createClient({
  region: "us-west-2",
  // Optional: logger, sqsClient, queueUrlResolver
});

// Use the client
await queue.send("user-notifications", {
  userId: "123",
  type: "welcome"
});

2. Creating Handlers

import { createHandler } from "better-sqs";
import type { SQSEvent } from "aws-lambda";

// Define your topics and payload types
type Topics = "user-notifications" | "order-processing";
type Payloads = {
  "user-notifications": { userId: string; type: string };
  "order-processing": { orderId: string; action: string };
};

// Create handler with type safety
export const handler = async (event: SQSEvent) => {
  return createHandler<Topics, Payloads>({
    "user-notifications": async (payload, metadata) => {
      // payload is typed as { userId: string; type: string }
      console.log("Processing notification:", payload);
      await sendWelcomeEmail(payload.userId);
    },
    "order-processing": async (payload, metadata) => {
      // payload is typed as { orderId: string; action: string }
      console.log("Processing order:", payload);
      await processOrder(payload.orderId);
    }
  })(event);
};

Configuration

Environment Variables

Better-SQS automatically looks for queue URLs in environment variables:

# Format: SQS_<TOPIC>_URL or SQS_<TOPIC_UPPERCASE>_URL
SQS_USER_NOTIFICATIONS_URL=https://sqs.us-east-1.amazonaws.com/123456789/user-notifications.fifo
SQS_ORDER_PROCESSING_URL=https://sqs.us-east-1.amazonaws.com/123456789/order-processing.fifo

The standalone send() function uses these environment variables automatically - no configuration needed!

Creating a Client Instance

For more control, create a configured client instance using the factory pattern:

import { createClient } from "better-sqs";
import pino from "pino";

// Create a configured client
const queue = createClient({
  logger: pino(),
  region: "us-west-2",
  queueUrlResolver: (topic) => {
    // Custom resolver logic
    return `https://sqs.us-west-2.amazonaws.com/123456789/${topic}.fifo`;
  },
});

// Use the client
await queue.send("user-notifications", { userId: "123", type: "welcome" });

Using Custom SQS Client

import { createClient } from "better-sqs";
import { SQSClient } from "@aws-sdk/client-sqs";

const sqsClient = new SQSClient({
  region: "us-west-2",
  // ... custom config
});

const queue = createClient({
  sqsClient,
});

await queue.send("my-topic", { data: "example" });

Client with Defaults

You can also create a client that uses environment variables and defaults:

import { createClient } from "better-sqs";

// Uses env vars automatically - no config needed
const queue = createClient();

await queue.send("my-topic", { data: "example" });

FIFO Queues

Better-SQS fully supports SQS FIFO queues with message grouping and deduplication:

import { send } from "better-sqs";

// FIFO queue requires groupId
await send("order-processing", { orderId: "123" }, {
  groupId: "order-123", // Messages with same groupId are processed in order
  idempotencyKey: "unique-key", // Prevents duplicates
  delaySeconds: 30,
});

FIFO Queue Requirements

  • groupId: Required for FIFO queues. Messages with the same groupId are processed in order.
  • deduplicationId: Required unless content-based deduplication is enabled. If not provided, idempotencyKey or a hash of the payload will be used.

Handler Retry Logic

Handlers can return a retry result to control visibility timeout (similar to other queue libraries' retry patterns):

export const handler = createHandler({
  "user-notifications": async (payload, metadata) => {
    if (!isSystemReady()) {
      // Request retry after 5 minutes
      return { retryAfterSeconds: 300 };
    }
    
    await processNotification(payload);
  }
});

Note: SQS doesn't support direct retry timing control from handlers. The retryAfterSeconds is informational for logging/monitoring. Actual retries follow the queue's visibility timeout configuration.

Error Handling

Better-SQS handles errors gracefully:

  • Failed messages: Automatically added to batchItemFailures for partial batch failure handling
  • Unknown queues: Logged as warnings and marked as successful to prevent infinite retries
  • Parse errors: Caught and logged, message added to failures for retry
export const handler = createHandler({
  "my-topic": async (payload, metadata) => {
    try {
      await processMessage(payload);
    } catch (error) {
      // Error is automatically caught and message is added to batchItemFailures
      // Message will be retried according to queue configuration
      throw error; // Re-throw if you want to ensure it's logged
    }
  }
});

Type Safety

Better-SQS provides full type safety through TypeScript generics:

// Define your topics
type Topics = "user-notifications" | "order-processing" | "email-sending";

// Define payload types for each topic
type Payloads = {
  "user-notifications": { userId: string; type: string };
  "order-processing": { orderId: string; action: string };
  "email-sending": { to: string; subject: string; body: string };
};

// Type-safe send
await send<"user-notifications", Payloads["user-notifications"]>(
  "user-notifications",
  { userId: "123", type: "welcome" } // TypeScript ensures correct shape
);

// Type-safe handler
export const handler = createHandler<Topics, Payloads>({
  "user-notifications": async (payload, metadata) => {
    // payload is typed as { userId: string; type: string }
    console.log(payload.userId); // ✅ Type-safe
    console.log(payload.invalid); // ❌ TypeScript error
  }
});

Advanced Usage

Custom Message Attributes

import { send } from "better-sqs";

await send("my-topic", { data: "example" }, {
  messageAttributes: {
    priority: {
      StringValue: "high",
      DataType: "String",
    },
    timestamp: {
      StringValue: Date.now().toString(),
      DataType: "Number",
    },
  },
});

Per-Call Configuration

The standalone send() function supports per-call configuration overrides:

import { send } from "better-sqs";

await send(
  "my-topic",
  { data: "example" },
  { delaySeconds: 60 }, // Send options
  { region: "us-west-2" } // Optional per-call config override
);

For more control, create a client instance:

import { createClient } from "better-sqs";
import { SQSClient } from "@aws-sdk/client-sqs";

const customClient = new SQSClient({ region: "us-west-2" });

const queue = createClient({ sqsClient: customClient });

await queue.send("my-topic", { data: "example" }, { delaySeconds: 60 });

Limitations

  • Consumer Groups: SQS doesn't support consumer groups natively. Use separate queues or SQS message filtering if needed.
  • Retry Timing: SQS retry timing is controlled by queue configuration, not handler return values.
  • Message Size: SQS has a 256KB message size limit (payload must be serializable to JSON).

Acknowledgments

Better-SQS was inspired by @vercel/queue's developer-friendly API design. We've adapted the pattern for AWS SQS with a focus on type safety and serverless-first architecture.

License

MIT