Skip to content

feat!: migrate to Nitro V1 - #453

Open
chrispader wants to merge 2 commits into
nitro-v1/example-bufferfrom
@chrispader/feat/nitro-v1
Open

chrispader wants to merge 2 commits into
nitro-v1/example-bufferfrom
@chrispader/feat/nitro-v1

Conversation

@chrispader

@chrispader chrispader commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Nitro SQLite still uses Nitro 0.37, so apps that move to the Nitro V1 alpha can't build it. V1 declares specs as decorated classes, generates the JavaScript runtime modules, and requires React Native 0.88. This PR ports the specs, the C++ hybrid objects, and the Android code to Nitro 1.0.0-alpha.nightly.86.g15e19db42041. The JavaScript code imports the hybrid classes from the specs, which re-export the runtime modules that nitrogen generates under #nitro/*.

A few changes need a closer look:

  • React Native macOS and visionOS aren't supported anymore, because Nitro V1 only supports iOS among Apple platforms for now. The macOS example, docs, and CI jobs stay in the repository, but CI skips the macOS build and lockfile checks until that changes.
  • Nitro V1 builds iOS dependencies with Swift Package Manager, which build(ios)!: build with Swift Package Manager #458 sets up, so the iOS CI job is paused in this PR.
  • NitroSQLite no longer types close, drop, and the other native methods that its object spread never copied at runtime. They stay available on NitroSQLite.native.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
react-native-nitro-sqlite Ready Ready Preview Oct 23, 2026 12:19am UTC

Request Review

@mrousavy mrousavy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice stuff so far - did you make use of the new types already? Like AnyJSON?

Requesting changes because babel definitely has to work somehow.

Comment on lines +34 to 51
export declare class NitroSQLite extends HybridObject {
/** Create a root with its own default and independent connections. */
constructor()

/** Open a name-based default connection, creating the database unless `readOnly` is true.
* @param dbName Database file name and default connection key.
* @param location Directory relative to the platform database directory.
* @param readOnly Open an existing database without write access.
* @param encryptionKey Optional key for a licensed SEE build. Without SEE, a
* supplied key throws `EncryptionNotEnabled`. An invalid or wrong key or an
* existing plaintext database throws `DatabaseCannotBeDecrypted`.
*/
open(
dbName: string,
location?: string,
readOnly?: boolean,
encryptionKey?: string,
): void

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you pretty much just migrated this 1:1 to how it was before, but Nitro V1 unlocks some new patterns that make development much more elegant.

For example, I don't see why the NitroSQLite Hybrid Object has to be an instance object (aka one with a constructor()), why isn't it simply a static one where e.g. open(..) would be static open(..)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chris's friendly neighborhood AI agent here. 🤖

It's an instance on purpose. Each NitroSQLite instance owns its connection registry and closes those connections in its destructor, so open databases live exactly as long as the JS runtime that opened them. With static state they would survive a reload, and reopening a database by name would then throw DatabaseAlreadyOpen. The constructor now also takes the directories from the new PlatformContext (#462). If you'd still rather have statics, we can change it.

Comment on lines +5 to 13
/** Android startup hook that configures the native database directories. */
@Android({
language: 'kotlin',
implementation: { className: 'HybridNitroSQLiteOnLoad' },
})
export declare class NitroSQLiteOnLoad extends HybridObject {
/** Pass the app's files and cache directories from the application context to SQLite. */
static init(): void
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if that should be moved into NitroSQLite too? Or not called *OnLoad but instead something like PlatformDirectoryHelper or something idk? Then that returns a directory or whatever and can be passed into NitroSQLite? No idea what it does, just some thoughts.

I think react-native-mmkv has a similar pattern with PlatformObject or whatever it is called, this creates the cache directory on Kotlin/Swift while the other object was in C++.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chris's AI wingmate checking in. 🤖

Good idea. NitroSQLiteOnLoad only told C++ where to store databases on Android, and iOS did the same through an Objective-C +load. #462 replaces both with a PlatformContext hybrid object, implemented in Kotlin on Android and Objective-C++ on iOS, that returns the database, temporary, and legacy database directories. JavaScript passes them into the NitroSQLite constructor, so the JNI setter and the +load hook are gone.

Comment on lines +1 to +9
import { NitroModules } from 'react-native-nitro-modules'
import type { NitroSQLite as NitroSQLiteSpec } from './specs/NitroSQLite.nitro'
import type { NitroSQLite } from './specs/NitroSQLite.nitro'

export const HybridNitroSQLite =
NitroModules.createHybridObject<NitroSQLiteSpec>('NitroSQLite')
// Look the class up by name so Metro never loads the spec, whose decorators need a Babel parser plugin.
const NitroSQLiteClass = NitroModules.getHybridClass<typeof NitroSQLite>(
'rnnitrosqlite.NitroSQLite',
)

export const HybridNitroSQLite = new NitroSQLiteClass()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not good - you should get the babel to work. I think its just a simple boolean switch to allow .cjs exports in babel. Nitro generates those .cjs classes for you which essentially do exactly what you just wrote here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chris's AI sidekick here. 🤖

Agreed, and it's fixed now. #453 imports the classes from the specs, so src/nitro.ts uses the generated #nitro/* modules instead of its own getHybridClass lookup. The other half is in #457. React Native's default Babel preset can't parse the spec decorators, and our react-native field pointed at src, so apps would have compiled the specs themselves. The entry points now point at the built output, and bob parses the decorators with a Babel config for each target.

Comment on lines +44 to +46
"imports": {
"#nitro/*": "./nitrogen/generated/shared/javascript/*.cjs"
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct

@chrispader
chrispader changed the base branch from main to nitro-v1/example-buffer October 22, 2026 18:18
@chrispader
chrispader added this pull request to stack #461 October 22, 2026 18:19
@chrispader
chrispader force-pushed the @chrispader/feat/nitro-v1 branch from a405776 to c02fcda Compare October 22, 2026 18:28
@chrispader

Copy link
Copy Markdown
Member Author

Chris's AI understudy, reporting for duty. 🤖

AnyJSON doesn't fit here, because SQLite BLOBs arrive as ArrayBuffers and AnyJSON can't hold them. Query parameters use a typed variant instead, and result rows go through a custom C++ converter for speed.

I also split this PR into a stack so each part can be reviewed on its own:

The stack builds on #455, which upgrades the example to React Native 0.88.

chrispader and others added 2 commits October 23, 2026 01:16
Rewrite the Nitro specs as decorated classes, regenerate the bindings,
and port the C++ hybrid objects to V1's value-type Promise, ArrayBuffer
and Callback APIs. JavaScript imports the hybrid classes from the specs,
which re-export the runtime modules that nitrogen generates under

Nitro V1 doesn't support CocoaPods, so the iOS, macOS and native
lockfile jobs are paused. The Nitro 0.37 patch moves into the macOS
example because the root install can't apply it to Nitro V1.

BREAKING CHANGE: Requires React Native 0.88 and Nitro V1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

Co-authored-by: Codex <[email protected]>
The podspec loads CocoaPods files that nitrogen V1 no longer generates,
so the test can't run until iOS builds with Swift Package Manager.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@chrispader
chrispader removed this pull request from stack #464 October 23, 2026 00:18
@chrispader
chrispader force-pushed the @chrispader/feat/nitro-v1 branch 2 times, most recently from 0a20e65 to 3bee6c9 Compare October 23, 2026 00:19
@chrispader
chrispader added this pull request to stack #466 October 23, 2026 00:19

This branch was successfully deployed

1 active deployment
Preview — 3bee6c99 Deployed Oct 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants