Skip to main content

Command Palette

Search for a command to run...

Stop Over-Engineering Your Lightning Wallet Integration

Updated
4 min read
Stop Over-Engineering Your Lightning Wallet Integration

Meet nostr-core: a dead-simple NWC client that does exactly what you need - and nothing more.

If you've ever tried to add Lightning payments to a JavaScript app, you've probably hit a familiar wall: bloated SDKs, vendor lock-in, and dependency trees that could fill a phone book.

I want to introduce you to nostr-core - a minimal, vendor-neutral Nostr Wallet Connect (NWC) client that strips away the noise and lets you focus on building.


The Problem with Existing Solutions

Most Lightning wallet SDKs come with baggage. You want to pay an invoice - simple enough, right? But the SDK you install drags in 400+ packages, couples you to a specific provider's OAuth flow, and requires you to learn an API surface with five different classes before you can send a single satoshi.

There's a better way.

One Connection String. That's It.

Here's what it looks like to connect to a wallet and check your balance with nostr-core:

import { NWC } from 'nostr-core'

const nwc = new NWC('nostr+walletconnect://...')
await nwc.connect()

const { balance } = await nwc.getBalance()
console.log(`Balance: ${balance} msats`)

No OAuth. No configuration objects. No provider-specific setup. You pass in a standard NWC connection URI, and you're ready to go.

Pay an Invoice in Three Lines

const { preimage } = await nwc.payInvoice('lnbc...')
console.log('Paid! Preimage:', preimage)
nwc.close()

That's a complete payment flow. Connect, pay, done.

Lightning Address Payments - Built In

One of the standout features is native Lightning Address support. No extra dependencies, no separate libraries:

const { preimage } = await nwc.payLightningAddress('hello@getalby.com', 100)
console.log('Paid 100 sats!')

Need fiat conversion? Also built in:

const { preimage, sats, rate } = await nwc.payLightningAddressFiat(
  'hello@getalby.com', 5, 'usd'
)
console.log(`Paid \({sats} sats (\)5 at $${rate}/BTC)`)

Error Handling That Actually Helps

Instead of catching generic errors and guessing what went wrong, nostr-core gives you a typed error hierarchy:

import { NWCWalletError, NWCTimeoutError, NWCConnectionError } from 'nostr-core'

try {
  await nwc.payInvoice('lnbc...')
} catch (err) {
  if (err instanceof NWCWalletError) {
    // The wallet said no maybe insufficient balance
    console.error(`Wallet rejected [\({err.code}]: \){err.message}`)
  } else if (err instanceof NWCTimeoutError) {
    // Relay or wallet didn't respond in time
    console.error('Timed out:', err.code)
  } else if (err instanceof NWCConnectionError) {
    // WebSocket dropped
    console.error('Connection lost')
  }
}

Eight specific error classes cover every failure mode: wallet rejections, publish failures, relay timeouts, decryption issues, Lightning Address resolution errors, and fiat conversion problems. No more guesswork.

What's Under the Hood

nostr-core is built on top of the audited noble cryptography libraries - the same ones used across the broader Nostr ecosystem. It auto-detects whether your wallet supports NIP-04 or NIP-44 encryption and handles everything transparently.

The full NIP-47 spec is covered: pay_invoice, get_balance, make_invoice, list_transactions, pay_keysend, sign_message, and more.

And it runs everywhere -Node.js 18+, Deno, Bun, Cloudflare Workers. Anywhere with Web Crypto and WebSocket support.

The Numbers Don't Lie

Here's how nostr-core stacks up against the most common alternative:

Metric

nostr-core

Alternative

Install size

118 MB

159 MB

Packages installed

79

436

Total dependency tree

132

698

That's 26% smaller, with 82% fewer packages. Fewer dependencies means fewer supply chain risks, faster installs, and less surface area for things to break.

Who Should Use This?

If you're building anything that needs Lightning payments in JavaScript or TypeScript a web app, a bot, a CLI tool, a backend service and you don't need provider-specific features like OAuth flows, nostr-core is almost certainly the right choice.

It's ESM-only, tree-shakeable, and exposes a single NWC class. The learning curve is measured in minutes, not hours.

Get Started

npm install nostr-core

Check out the demo and the GitHub repository for full documentation.


nostr-core is MIT licensed and built on audited noble cryptography libraries. No vendor lock-in, no framework dependencies, no surprises.