Buda Lightning Invoice follows a standard Express layered architecture where every inbound HTTP request moves through a predictable pipeline: a route matches the URL and triggers input validation, a middleware gates invalid payloads early, a controller owns the HTTP response and orchestrates business logic, and helpers handle the single-responsibility work of talking to the Buda.com API client. Keeping these concerns in separate layers makes the code easy to test in isolation — each layer can be exercised with its dependencies mocked — and straightforward to extend when new endpoints are needed.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nicosaporiti/buda-lightning-invoice/llms.txt
Use this file to discover all available pages before exploring further.
Directory layout
Request pipeline
The following steps trace the journey of aPOST /newinvoice request from the network to the final JSON response.
index.js — Bootstrap
Express receives the incoming request.
app.set('trust proxy', true) is configured first so the app correctly reads the originating IP when running behind a reverse proxy. cors() middleware then adds the appropriate CORS headers so browser-based clients are allowed. express.json() parses the JSON request body. The request is then handed off to the router registered at '/'.routes/buda.js — Route matching and validation
The router matches
POST /newinvoice and runs the express-validator check chain before the controller. Two fields are validated: amount must be a non-empty integer ≥ 1, and msg must be a non-empty string of at least one character.middlewares/fields-validator.js — Gate invalid requests
fieldsValidation calls validationResult(req). If there are any validation errors it immediately returns 400 with a structured { ok: false, errors: {...} } payload and the chain stops — the controller is never reached. If validation passes, next() is called.controllers/buda.js → newInvoice() — Business orchestration
The
newInvoice controller extracts amount and msg from req.body, delegates the actual invoice creation to getInvoice(amount, msg), and either returns the BOLT11 invoice in a 200 response or catches any thrown error and returns 400.helpers/getInvoice.js — Invoice creation
getInvoice reads BUDA_API_KEY and BUDA_API_SECRET from environment variables, instantiates the Buda API client, and calls lightning_network_invoices(amount, 'BTC', msg, false). It extracts encoded_payment_request from the response and returns it as a plain string.buda-promise/buda.js — Signed HTTP request
The Buda API client signs the outbound request using HMAC-SHA384, attaches the
X-SBTC-APIKEY, X-SBTC-NONCE, and X-SBTC-SIGNATURE headers, and dispatches the authenticated HTTP call to Buda.com. The signature is computed over the concatenation of method, path, the base64-encoded request body, and the nonce. The resulting data travels back up the promise chain as the BOLT11 invoice string.Key modules in detail
routes/buda.js — Route definitions
The router file is the single source of truth for URL structure and validation rules. Every route declares its validation checks inline, then threads fieldsValidation before the controller. This keeps validation co-located with the route definition and out of the controller.
controllers/buda.js — HTTP handlers
Controllers own the HTTP contract. They read inputs from req, call helpers, and write the final response. They never call the Buda API directly — all Buda interaction is delegated to helpers. This keeps controllers thin and easy to unit-test with mocked helpers.
helpers/getInvoice.js — Lightning invoice creation
This helper has one job: given an amount in satoshis and a msg, return a BOLT11 encoded payment request string. It reads credentials from the environment at call time, so it works correctly in both production and test environments (the test environment sets env vars explicitly in beforeEach).
helpers/getPaymentConfirmation.js — Payment verification
This helper fetches the full BTC deposit history from Buda and searches for a deposit whose encoded_payment_request matches the provided invoice string and whose state is 'confirmed'. It returns a boolean — true when a confirmed match is found, false otherwise.
middlewares/fields-validator.js — Validation gate
The middleware wraps express-validator’s validationResult to produce a consistent 400 error shape. When validation fails, it calls errors.mapped() which produces a keyed object where each key is the failing field name — this makes it easy for clients to map error messages back to specific form fields.
Adding a new endpoint
Follow these steps to add a new endpoint to the service:Define the route and validation in routes/buda.js
Add a new
router.get() or router.post() call. Specify all check() rules inline, then add fieldsValidation as the penultimate middleware, followed by your new controller function.Add the handler to controllers/buda.js
Write an
async function that reads from req.body or req.query, calls a helper, and returns the appropriate JSON response. Export the function and import it at the top of routes/buda.js.Add a helper in helpers/ if needed
If your endpoint interacts with the Buda API, create a new file in
helpers/. Instantiate the Buda client with env credentials, call the appropriate API method, and return a clean value to the controller.Write unit and integration tests
- Add unit tests in
tests/unit/helpers/andtests/unit/controllers/for the new helper and handler, mockingbuda-promiseusing the sharedMockBuda. - Add integration test cases in
tests/integration/routes.test.jscovering at least: a valid request returning200, and each invalid input combination returning400.
Coverage thresholds are enforced — adding a new endpoint without tests may cause
npm run test:coverage to fail if function or branch coverage drops below the minimums defined in jest.config.js.