stripe-node: no runtime dependencies, generated types, and a minor bump that can add type errors
Node.js library for the Stripe API.
At a glance
- What is it?
- The Stripe Node library wraps the Stripe API for server-side JavaScript with an empty dependency list and TypeScript definitions generated from an OpenAPI version. Its versioning policy then does something unusual: it accepts that a minor release can break your type check, and explains why.
- Who is it for?
- stripe-node suits a server-side TypeScript team that can live with types pinned to the newest API version and treats the SDK as a generated artifact rather than a hand-maintained one. It is a poor fit if you are stuck on an old API version and want accurate types without suppression comments, or if a minor upgrade adding type errors would break your build.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- stripe/stripe-node4,569 ★ · +60
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| stripe/stripe-node | 4.5k | 4.6k |
60 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The dependency list is empty, and Node 20 is the stated floor
The manifest is the first place to look, and the entry that matters is `"dependencies": {}`. The library ships no runtime dependencies at all.
What is pinned instead lives in other places. `engines` asks for Node >=20, and the README states the policy in words: all LTS versions of Node.js 20 or later are supported, with the full schedule linked in the documentation. A separate optional peer dependency on `@types/node` >=20 keeps type definitions out of the install unless you want them. Two `resolutions` entries pin `minimist` and `nanoid`, which belong to the development tree rather than to your runtime.
Installation is the familiar two lines:
npm install stripe
# or
yarn add stripeRelease cadence is fast and visible: v23.1.0 shipped on 2026-10-29 alongside v23.2.0-beta.1 the same day, v23.2.0-alpha.1 followed on 2026-10-30, and the last push was on 2026-11-02. The client itself is constructed with your account secret key, either as a default import with `new Stripe('sk_test_...')` or through CommonJS with `require`.
The types are generated, and they only describe the newest API version
Two files at the repository root explain where the definitions come from: CODEGEN_VERSION and OPENAPI_VERSION. Their names point to definitions generated from a versioned OpenAPI description.
That mechanism has one consequence that shapes everything else. The types reflect the latest API version only, which the README states plainly, and it gives the reason: types can change between API versions, for instance when a field moves from a string to a hash. If you are on an older version such as `2019-10-17`, the types will not match it.
The documented workaround is deliberately blunt. Pass the version you are on and suppress the differences with a `// @ts-ignore stripe-version-2019-10-17` comment, repeated anywhere the two versions disagree, with the instruction to remove those comments when you upgrade. The project states a preference instead: upgrading your API version so the definitions apply. The same `// @ts-ignore` advice is repeated for beta features whose parameters are not in the type definitions yet.
A minor upgrade can add type errors, and that is the policy talking
The versioning section is the most useful paragraph in the repository for anyone deciding when to upgrade. The types always reflect the latest shape of the API, and a backwards-incompatible API change produces a new API version plus a new major of the library. That part is ordinary.
The unusual part is what happens when the API changes in a way that weakens a type guarantee without breaking anything at runtime. The worked example is a new value added to a response enum together with a new request parameter: if the new value only ever comes back when the new parameter is sent, no existing usage breaks, so it is not treated as a breaking change. Under this policy such a change ships in a minor release, which means:
import Stripe from 'stripe';
const stripeClient = new Stripe('sk_test_...');Your type check can therefore fail after a minor bump, and the suggested fix is additional type guards. The stated reasoning is a trade: they judge this better than outdated, inaccurate types, and better than vastly more frequent major releases that would distract from real breaking changes with worse runtime consequences. Invitations to argue the policy in an issue are open.
Open enums widen with OtherString, closed ones are the small exception
The enum handling is the mechanism behind those type errors, and it is described directly. Many Stripe API enums are open, meaning new values can be added even on older API versions. To stay honest about that, open enum fields in TypeScript include a string type, provided through `OtherString`, in addition to the known literal values.
The effect is that the field type is correct both for values known when the SDK was released and for values that only appear later. You get a union rather than an exhaustive list, and a comparison against an unlisted value narrows instead of failing to compile.
A small number of enums are closed, and for those Stripe guarantees that no new value will be added without an API version change. That guarantee is what lets those fields stay as narrow literal unions.
So the same `switch` over a status field can be exhaustive for one enum and open for another, with nothing in your code to tell you which is which. The API reference is where you check.
The missing key error is usually a build step importing the client too early
There is a warning block in the README and it earns its place. If you are on v17.x.x or later and get an error about a missing API key while you are certain the key is available, the likeliest cause is that the module instantiating `Stripe` was imported at a moment when the key was not present, a build step being the classic case.
Two fixes are offered. Instantiate the client lazily, so the constructor runs on first use rather than at import time. Or keep the eager construction and supply a placeholder, which the README says is enough to get the code through a build step.
CommonJS is still documented alongside the default import, with the promise style kept explicit:
const Stripe = require('stripe');
const stripeClient = Stripe('sk_test_...');
stripeClient.customers
.create({
email: '[email protected]',
})
.then((customer) => console.log(customer.id))
.catch((error) => console.error(error));One boundary is worth stating: this library is for server-side JavaScript. Collecting customer and payment information in the browser is explicitly pointed at Stripe.js instead.
Expandable fields arrive as string | Foo, so you cast or you write getId
Expanding a field changes its type at the call site, which is where TypeScript users hit the first surprise. Expandable fields are typed as `string | Foo`, so after passing `expand` you must cast to reach the properties of the object form:
const customerEmail: string = (paymentIntent.customer as Stripe.Customer).email;
function getId(stripeObject: {id: string} | string) {
return typeof stripeObject === 'string' ? stripeObject : stripeObject.id;
}The README shows `expand: ['customer']` on a payment intent retrieve call and then defines the helper, with the note that you should define and use it if you extract `id` often. That pattern is the practical advice: pick one of the two forms once, and stop casting.
Since the types only ever describe the latest API version, this is also the place where an old API version hurts most, because the expandable field's shape may itself have moved between versions.
The `examples/` directory carries snippets and webhook signing samples, and a full server example lives in the separate stripe-samples repository.
The Makefile is on its way out, and justfile owns the build and the type tests
The repository is mid-migration between two task runners, and the Makefile says so in its first line: it is deprecated and slated for deletion, with the equivalent `just` commands preferred.
What the old Makefile did is still instructive. `update-version` writes the VERSION file and then rewrites the version in package.json and the `Stripe.PACKAGE_VERSION` constant in `src/stripe.core.ts` with in-place perl edits. Three files hold the same value.
The justfile takes over. It prepends `node_modules/.bin` to `PATH` so webhook tests can call `eslint` directly, and it defines `test` as install plus build plus `mocha`, plus `types-test`, which installs and compiles three separate test projects (`types`, `types-cjs`, `types-cjs-node16`) with `tsc --build` to prove the published types work under each module setup. Integration tests are separate and slow, gated behind `RUN_INTEGRATION_TESTS=1`, and `ci-test` runs install, test, types-test and integrations-test in sequence.
Build output is generated twice, as ESM and CommonJS, with three tsconfig files behind it, and the manifest entry points at the CommonJS build.
Editorial conclusion
stripe-node suits a server-side TypeScript team that can live with types pinned to the newest API version and treats the SDK as a generated artifact rather than a hand-maintained one. It is a poor fit if you are stuck on an old API version and want accurate types without suppression comments, or if a minor upgrade adding type errors would break your build. Before pinning, decide whether your CI treats type errors as blocking before you take a minor bump.
Frequently asked questions
How do I install the Stripe Node library?
Run npm install stripe, or yarn add stripe. The package declares engines node >=20 and the project states it supports all LTS versions of Node.js 20 or later.
Why does the Stripe client report a missing API key when I know it is set?
The module that instantiates Stripe was probably imported while the key was not present, for example during a build step. The documented fixes are to instantiate the client lazily or to pass a placeholder value through the build.
Why do TypeScript types break after a stripe-node minor upgrade?
Under the stated versioning policy, a change that adds a response enum value together with a new request parameter is not treated as breaking, so new type errors can appear in minor versions and are meant to be resolved with additional type guards.
How do I handle expandable fields in stripe-node with TypeScript?
Expandable fields are typed as string | Foo, so cast the object form to reach its properties, or define a helper such as getId that returns the id whether the field is a string or an object.
Which API version do the stripe-node types match?
Only the latest. On an older version such as 2019-10-17 you can pass that version and silence differences with // @ts-ignore stripe-version-2019-10-17 comments, which should be removed when you upgrade.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/stripe-stripe-node)Star history chart for your README
[](https://hysenlabs.com/projects/stripe-stripe-node)Was this useful?