Application development front-end feature access (API, UI...)

Is there somewhere a technical design or architectural decision document about how application developers are supposed to interact with the platform ?

I have the feeling that everything is done to push features out of the platform into each and every application and that those have to be implemented with Vue or you’ll suffer more.

I think forcing into Vue is a bad move and bring nothing to the user.

I know that some features (toast, dialogs, old search…) where historically made available through a variable on window. I see that those things are being removed little by little… Someone said here that OC should not be used and I wonder why this is the case.

Things I believe should remain in some exposed variable by the platform:

  • core APIs (e.g. provisioning api, search) – eventual password request should be handled by that method
  • Toast
  • Dialogs
  • translation (I see t(...) is there but the documentation states that we should used @nextcloud/l10n).

This would result in less burden on the application developers, no forced path to Vue and reduce bundle sizes (e.g. if I integrate the @nextcloud/request-password module in my app, it grows by half a megabyte)

So back to my original question: is this documented somewhere so that I can try to understand the goal/vision or reasons behind what I feel is bad design?

Hello @callmemagnus.

You are by far not forced to use Vue in the development. It is just convenient as many UI items can be prepared as custom elements for the app devs. But you can still create dynamic pages (in the sense that these are generated in PHP only, no JS/Vue needed).

The thing is that global variables such as the window.OC to access the said functions is in general a bad idea and a code smell.

  • You cannot check if the variable is really existing, causing trouble for IDEs
  • If something in the API changes, you are restricted to the globally available version. You have to do polyfills and the like to be compatible to multiple versions.
  • The loading time increases as always all JS functions need to be ready for calling, even if not needed at that point in time/app.

Your misunderstanding is that these functionalities are removed. In fact, these are just moved to other locations. For example, the dialoga are now residing in the @ nextcloud/dialogs package. This allows to selectively add functionality to the apps as needed.

The only exception is the t and n from the translation setup. These are (according to the docs) still officially sopported. The referenced npm package is there to use the translations in test environments and outside the NC core.

What do you mean by “this”? The fact the legacy access to OC is reduced in favor of other access methods?

Yeah, I have seen similar effects at various locations. This is partially due to a bug in webpack (tree shaking is not working 100%), not completely ported code (feel free to contribute), and other effects. Additionally, you have to keep the overall size of the downloads in mind: You will just get more prominently be notified of this. In fact, it was always used and downloaded but not prominently to the app devs.

Christian

Hello @christianlupus,

Thanks for that long answer. It confirms what I was guessing about the move towards @nextcloud/xxx module set.

I don’t agree with that statement because of this specific use-case of providing core features to “plug-in” developers. Hence my request to see some document explaining such decisions. Something being a bad idea is heavily dependent on the context of use.

For IDE, using @nextcloud typings makes it perfectly usable is up to date and configured correctly and using some sort of typings.

This is half true. Just looking at the @nextcloud/dialogs npmjs page I see that if I want to use those and support Nextcloud 27 and 28 I would have to deliver 2 different versions of my app on 2 different branches in order to cope with the package change. For global variables, that would be the same problem but I could tackle it with the same version.

Change of contracts could be handled the same way it is already “solved” for API contracts, with the version, window.NC.provisioning.v1.set('yyy','yyy'). If new version is not mergeable with previous, create a new version.

Maybe true. But I would challenge that by the fact that most of these provided core methods I’m talking about would always have to be present anyway to be used by some core app. Network download would happen once and not per app.

I haven’t misunderstood, I wanted to get to the behinds of why everything is moved out of the core into, for now, heavy packages.

t and n

Great that they are still supported, that will remove ~20k (mostly dompurify) from my build. I didn’t find it in the typings package so I wondered if it was a waiting-to-die feature.

Exactly that yes.

I wonder about that. My app (prod mode) goes from 83.76 kB to 638.39 kB if I switch from OC.PasswordConfirmation to that module.

Examining a little further the bundle I see:

  • the code of the password module: ~31%
  • Vue: ~20%
  • l10n related lib (dompurify, gettext) ~ 10%
  • floating-vue: ~7.5%
  • other libs: focus-trap, lodash tabbable, semver: ~8.2%

There is room for improvement but not that much as if it was a bug related to tree shacking as far as I can guess.

My source (the column on the left and me darker part in the second column is my app):

For this version I removed my usage of translate and switched to t. Reverting that would make my app share part of the @nextcloud/l10n package.

I would guess that this specific functionality will only be made available in my admin page so not on the main page which is a good thing but I wonder how it would grow with the dialog module.
And dynamic loading seems to be out of the question for what I have read and tried.

@susnux or @Daphne, maybe you could forward this to the appropriate person? I am a bit lost, to be honest here.

This might be related/helpful: How to deal with @nextcloud/vue bloat? - #3 by mdw

That’s how i dealt with the bloat coming from the @nextcloud packages.

sure, which part would you like me to forward? is there one specific question that needs answering or is it about the whole thread?

1 Like

Basically anything OC is private API and should not be used by apps directly. That applies for frontend as well as backend (OCP is the API to be used by apps there)
It is still used in couple/many locations, but we try to remove it and bring it to proper states.
For the frontend the way forward is the @nextcloud/vue library as well as all the other packages in our GitHub org nextcloud-libraries · GitHub and on NPM @nextcloud - npm search

While I agree this is bad and should be solved ultimately in the library and with some roll-ups, that is the way to go because as per above OC.PasswordConfirmation starts with OC and is therefore private and can change/be removed at any time.

2 Likes

As it was already said that OC is private (as the PHP API) and only OCP is public, I will come to your other point:

Using @nextcloud/l10n will not increase you bundle size significantly:

The problem you see with the huge new bundle size is a combination of multiple problems:

  1. Your app does not use Vue, so in this case the upper right square can not prevented / is only used by your dependencies (thus if you use vue you already have that in).
  2. Currently translations of components of the @nextcloud/vue library are not split, meaning the huge dark square on the lower right is not fully needed as it also contains translations for components you not use. This is known and on the roadmap for v9. (do not mixup this is about @nextcloud/vue and completely unrelated to @nextcloud/l10n).
  3. Using webpack. Webpack is pretty bad at tree shaking, terser helps but still it is not perfect as specially as tree shaking only really works with ESM.

In general huge bundle sizes are not nice, but also not really a problem as they are only loaded once and then the browser cache kicks in.
So performance wise it is not important if you bundle is huge, but if the individual files are small (reducing JS parsing time).

Thus you can improve performance by splitting and dynamiccally import stuff you do not need on the average / current use case.

So not speaking for Nextcloud but for me personally: I fully agree on deprecating and removing global API. But I think we should share more dependencies to reduce duplication. For this I can imagine we provide our @nextcloud/ dependencies as modules, so users can import and use them.

3 Likes

Does it make sense to “quckly” introduce proxy packages to cover the current OC implementation and then later on replace these by better implementations?

Thanks all!

The more we dig into this, the more I try to understand the path to Vue. As corporate experienced developer I understand the need to enforce some sort of unity for the end-use and for fellow developer. In that universe, a Nextcloud app dev you can jump from one codebase to another without an issue. Although it sounds for me as a corporate trap, I get how that kind of conclusion can be reached.

But this reasoning is harder to grasp in the context of Nextcloud application development.

Regarding simple interaction elements, like dialogs, buttons, I can try to mimick the ones of the Vue provaided library. But when it comes to integrate into processes like “should-i-get-the-password-of-the-user-before-calling-the-endpoint” I cannot do anything else than integrate the 500 kB… And that make me sad…

Yes, I could develop my app in Vue (I currently do that for a living) but no. I feel that our user deserves a better and lighter web and I want to have fun writing my Nextcloud App.

My reactions/comments to statments above:

I will look into that.

I then think OC should not be part of the @nextcloud/typings packages or there should be one for application developer and one for core.

So Nextcloud IS indirectly trying to push into Vue (for a better developer experience). For some packages, I have a real effort to make an abstraction (like the dialog or password request packages) to allow a usage as a function although I still have to include some Vue related things in my build And live with the sad result of growing the bundle size as I obviously don’t use Vue.

Another issue I see is that as the UI components are provided as Vue components, it is not as straight forward to copy them onto another framework that would have a smaller foot-print (vs. an HTML/CSS library).

That depends on the definition of significatly, including l10n also includes its dependencies like dom purify.

None of the builds shown in my picture are using Webpack. Both are using Vite if I read correctly the password-request package so the problem is not only webpack and/or terser.

Big bundle size might not only be annoying for the first page load but also fills memory with things the engine has to parse and which might, in the end, impact performance. This is especially true for either old devices or low-spec phones (or just shitty browser implementations).

My main issue is not duplication but the fact that the core Nextcloud team has decided that exposing things on window is bad and with that goal in mind moves little by little those functionalities into modules for application to include in their code. Even if that makes each and every application over 500 kB (and probably to more).

I’m not the owner of the platform nor a stakeholder, just a guy, developer and user of Nextcloud as a product expressing my concerns about what I now know is the envisioned echo-system Nextcloud wants for its application developer. I’m day-to-day Jira (yes the bloated thingy) application developer so I have some grounds for comparing and what I saw here itches me (not that Jira echo-system is way better but it has some advantages with regards with integrations).

This is not the main argument for vue components in my opinion. The reason we have vue components is that we want to unify the user experience among all nextcloud app. Users should not feel surprised by unusual interface interaction design but should feel familiar with a new app just by virtue of the interaction design resembling other apps they have used before. This is why we have components that are shared among apps.

In order to realize shared components, the nature of technology forces (or forced) us at some point to choose a framework for these components, though. In this course, some people then made the decision to adopt vue, for better or for worse.

So Nextcloud IS indirectly trying to push into Vue (for a better developer experience).

From where I am sitting, noone is pushing for anything. We really try hard to keep our packages framework agnostic. You can use most of the frontend packages without using nextcloud/vue. We don’t want people to have to use vue, or to have to use webpack, but we are also interested in improving the development experience and thus make available packages that streamline development for people that align with our default choices of frameworks and tools.

Unfortunately, having default choices for such things usually means that life gets harder for people who do not or cannot adopt these default choices. We are not infallible and there likely are spots where we could improve the lives of developers in this category, like yourself, as you have noticed. Keep in mind that even if Nextcloud is a company and we have money and we do compete with Google, we are not Google. We are trying hard but sometimes things are neglected, not out of bad will but because of a lack of resources. We certainly value people pointing out where we can improve, and even more people taking it upon themselves to help improve things.

My main issue is not duplication but the fact that the core Nextcloud team has decided that exposing things on window is bad and with that goal in mind moves little by little those functionalities into modules for application to include in their code.

The problem I see with exposing things on window is that it represents an API that we need to maintain and cannot break easily. If we exposed all vue components as properties of window a lot of apps would break much more regularly or we wouldn’t be able to break the components’ APIs at all in the interest of compatibility. By providing this API surface as a library with a version that apps depend on on their own terms, we decouple this frontend API from the Nextcloud server version. Apps’ developers don’t need to be afraid if their app frontend will still work in the new nextcloud version because they specify the version of the libraries they depend on on their own. That’s, I think, the reasoning for moving away from exposing things on window. It allows our frontend engineers to innovate on new user interactions without breaking apps all the time in the process.

I can understand that from your perspective it seems like we are taking away useful APIs from you. In the end, though, I think, we care a lot about our developer community and try to avoid choices that cause hardship for app developers. We are definitely not perfect yet, but thanks to people like you we can venture to improve. Thank you for bearing with us. :slight_smile:

Disclaimer: I don’t work for Nextcloud, just built a few apps over it. Opinions are personal.

So Nextcloud IS indirectly trying to push into Vue

Honestly I think this is a good thing. Consistency makes overall management much easier. For instance, having a consistent framework provides a clear and tested migration path for apps when there are breaking changes in the core. In an ideal world there would be enough time to maintain an equivalent component library for each framework (say including pure-js), but well it’s not.

I still have to include some Vue related things in my build And live with the sad result of growing the bundle size as I obviously don’t use Vue

I agree, but then this is the cost all frameworks must pay. And developing any non-trivial application without a framework is recipe for maintenance disaster. Now it could be argued that the UX for a particular app is trivial and framework is bloat, but in that case why care about bundle size to begin with? Premature optimization?

I’ve extensively worked on frontend performance optimization, especially in Nextcloud’s context. As it is, the major overhead is not even your app but the common JS components (by a factor of several times, even for complex apps). Nextcloud 28 significantly improves handling of these components (disclaimer: I worked on this but no warranties!!) so things should already be much faster from a loading-bloat POV.

Besides, Vue itself is very lean and fast so the cost is largely only at app load / parse / compile (again, which largely isn’t the app itself but the common core)

Another issue I see is that as the UI components are provided as Vue components, it is not as straight forward to copy them onto another framework that would have a smaller foot-print

The question I’d ask is, is it worth having a smaller footprint over maintainability? That’s coming from the developer of an app that’s primarily focused on performance. If you’re worried about bundle size and memory usage, I also suggest actually profiling performance. From my experience, memory rarely bottlenecks; even for a massive app like Memories, memory usage is ~100MB which is peanuts for modern hardware.

My main issue is not duplication but the fact that the core Nextcloud team has decided that exposing things on window is bad and with that goal in mind moves little by little those functionalities into modules for application to include in their code.

This is more of a philosophical question IMO. The reliance on globals defined outside your app makes it a “plugin”. Bundling everything inside your app makes it a standalone “app”. Standalone applications are much less brittle and maintainable.

That and IMO globals are bad, which is why we created ES6 modules to start with.

tl;dr or summary: bundle size optimizations inherently run into some tradeoffs at some point. In most cases these optimizations aren’t worth the cost, especially unless a profile shows realistic slowdowns in parsing. Even on a 5-year old $150 phone, JS parsing for large Vue apps like Memories is less than half a second, which makes the goal of further optimization questionable to me.

3 Likes