tauri
tauri copied to clipboard
[Proposal]: Modular core APIs
This idea is in a very early stage, please contribute by leaving questions and ideas!
Is your feature request related to a problem? Please describe. The backend currently has two different ways of exposing ApIs to the frontend: Commands (and Plugins) as well as the core APIs. Commands are very modular while the core APIs are not.
Describe the solution you'd like Modularizing the core api using a similar system to #2959, inspired by deno's extensions. So a tauri app could look like this:
let plugins = vec![
tauri::api::fs::init(),
tauri::api::path::init()
];
tauri::Builder::default()
.plugins(plugins)
.build(tauri::generate_context!())
.expect("error while building tauri application");
Why?:
- In-code configuration.
When reading the code it's immediately obvious what APIs are available to the frontend.
The current allowlist system split's feature configuration between the app, the
Cargo.tomlfile and thetauri.conf.jsonfile. There is also no opportunity to add comments in thetauri.conf.jsonfile, where dev could explain why an API is enabled. - No feature flags. Tauri apps would be as slim as possible without the copious amounts of feature flags we employ right now. (Of course more involved features like systray would still use them on top) This makes the cli waay simpler and less error prone.
- Capabilities.
The
initfunction is a very natural way to pass capabilities to APIs enabling capability oriented design. - A clear path for new APIs. @amrbashir pioneered this with the vibrancy plugin. Similar to rust itself, we could develop new APIs in separately versioned plugins, that can be iterated upon more easily. Once they reached sufficient stability can be merged into the core. This is maybe the most compelling reason IMO.
- A clear path for old APIs.
Core APIs can be deprecated and still be accessible to users who didn't upgrade yet, while not taking up space for users who did. (I could see
withGlobalTaurigoing this route for example. It would still be there for users who need it, but would never end up in the binary for users who don't)
Open Questions
- Would the name plugin still be applicable to this new system?
- How do users tell the
tauri::Builderabout plugins? - Does
tauri::Builder::default()come with default plugins? - Are there core plugins that will not be modular (i.e. enabled by default)?
There's one downside going with this approach and removing the allowlist entirely: people need to touch the Rust code to enable APIs, and a lot of users are scared of it.
But turning the APIs into plugins is something I have in mind for quite some time, specially since it's the only way to have an allowlist when we have bindings (each core plugin could have its own binding, then e.g. a Deno user can install each plugin individually).
There's one downside going with this approach and removing the allowlist entirely: people need to touch the Rust code to enable APIs, and a lot of users are scared of it.
can't the allowlist include the plugins init calls ? like have a top-level tauri::include_apis!() macro ? kinda like how windows-rs used a similar macro to generate and include the apis
There's one downside going with this approach and removing the allowlist entirely: people need to touch the Rust code to enable APIs, and a lot of users are scared of it.
Do you think it is that bad? Adding plugins to a vec doesn't touch lifetimes or any other part of rust that people find scary 🤔 But I get the concern, maybe a macro could help here, yes!
Or: tauri init adds the vec snippet to the main function with all the apis "pre-filled" so rust-newcomers would just need to remove lines they don't want 🤔
If there are no feature flags, will Rust skip the unused dependencies when compiling?
Definitely would like to specify the allowlist in code, and avoid tauri writing feature flags to Cargo.toml
If there are no feature flags, will Rust skip the unused dependencies when compiling?
Yes, of course! Rust binaries are large, but without dead code elimination and other optimizations the size would be measured in gigabytes 😉 I will do a mvp of this next week and we can go from there!
Definitely would like to specify the allowlist in code, and avoid tauri writing feature flags to Cargo.toml
I think this is actually an antipattern to be honest. Having your configuration in exactly one place makes it auditable. If you can set this kind of thing anywhere in code, then where do you look to know what you have set?
I think this is actually an antipattern to be honest. Having your configuration in exactly one place makes it auditable. If you can set this kind of thing anywhere in code, then where do you look to know what you have set?
Totally agree, having the config in one place is good and the closer we can get to that the better.
That said, configuration of a tauri app is already split between main.rs and tauri.conf.json right now and as a user I'd rather have all configuration in rust code where I can add comments and the like at the expense of simplicity 🤔
But I know that a lot of people interested in/using tauri don't know rust and making the project accessible is important. The question is how do we archive this; and imo we should provide easy-to-use APIs, so on-ramps into rust rather than avoiding rust through custom config tooling
I think this is actually an antipattern to be honest. Having your configuration in exactly one place makes it auditable. If you can set this kind of thing anywhere in code, then where do you look to know what you have set?
Actually it can be in one place in rust, with the proposed API, all plugins (user defined) and core plugins (our plugins proposed in this issue) can only be passed through tauri::Builder::plugins() in main.rs.
I think the point brought up about users having to touch Rust code is very daunting, even if it a very simple operation. I think the option for users to choose one system over another is preferable. So for example, have the project when initialized, include the allowList in the config file. In the main code, we could have a macro for the plugins portion, something link include_from_config!(),
This way, if a user would rather use the init pattern, they can just remove the macro call and go from there.
Another important note, if we wanted to go with the above route, of allowing users to choose, it would be a very good idea to emit an info message to the console so the user knows which of the two methods are being used, and thus avoid confusion. This or just emit a warning, if for example, the user opts out of using the config file, but still has the allowList key in their config file.
But I 100% love the idea of modularizing the core API's.
As a notice to anyone coming along, work on this will continue after the release of v1.
But just for context (and so I can remember later) I like the idea of a macro that pulls in the tauri.conf.json and compiles down to calls to the modular APIs under the hood.
configuration of a tauri app is already split between
main.rsandtauri.conf.jsonright now and as a user I'd rather have all configuration in rust code where I can add comments and the like
I like the idea of a macro that pulls in the
tauri.conf.jsonand compiles down to calls to the modular APIs under the hood
@JonasKruckenberg If comments are a desirable feature, is JSONC an option for the config file?
rc.0 introduced JSON5 support for the conf file in February :)
With the migration of the API endpoints to separated plugins this has been implemented (though there's a lot more we can do, which are tracked in other issues).