How a Collaborative App Builder Handles Permissions
(152 chars):** How RapidNative built team permissions, three access levels, and a two-layer project-sharing model into its collaborative app builder for React Native apps.
By Riya
21st May 2026
Last updated: 20th May 2026
Most AI app builders are designed for one person. You type a prompt, a screen appears, you tweak it, you ship it. The entire loop assumes a single set of hands on a single keyboard — which is fine, right up until a teammate asks for access. The moment a second person needs into your project, a solo tool runs out of answers to questions it never had to ask: who can open this? Who can change it? Who can only look?
This post is a behind-the-scenes look at how we built the permissions and sharing layer inside RapidNative, a collaborative app builder that turns plain English into real React Native and Expo apps. It is not the flashiest part of the product — nobody signs up because of an access-control table — but it is the part that decides whether a designer, a product manager, and a developer can actually work on the same app without stepping on each other.
A real team has roles, not just users — and a collaborative app builder has to model both. — Photo via Unsplash
Why a collaborative app builder needs a real permissions model
A collaborative app builder is a tool where multiple people — designers, PMs, engineers, stakeholders — design, generate, and refine the same application together, instead of one person building in isolation and emailing files around. That sounds simple, but it forces three hard requirements: every resource must belong to a team, every team member must have a defined level of access, and every action must be checked against that access before it runs.
This is the same problem a shared document editor solves, just applied to mobile apps. If two people can touch the same artifact, you need a model for who is allowed to do what — and the collaboration software market, which grew to roughly $8.4 billion in 2025, exists almost entirely because teams got tired of "send me the latest version" workflows.
We had a choice early on: bolt sharing onto a single-user product later, or make the team the foundation from day one. We chose the foundation. General-purpose AI builders like v0 optimize for the solo prompt-to-output loop; we optimized for the team. Here is how that decision plays out across the stack.
The team is the unit of everything
In RapidNative, you do not own projects — your team does. When you sign up, we create a personal team for you automatically and make you its owner. Everything you build belongs to a team, and every collaboration decision starts there.
The data model is deliberately small. A teams table holds the workspace itself: a name, an owner_id, a description. A team_users join table records membership — one row per person per team — and carries a single but important role column with three values:
| Role | What it means |
|---|---|
| owner | Created the team. Full control, including billing and membership. |
| admin | Sees and manages every project in the team. |
| member | Works on the projects that have been shared with them. |
Membership is many-to-many on purpose. One person can belong to several teams — your own personal workspace, your startup's team, a client's team — and one team can have many people. That is what makes RapidNative feel like a workspace rather than a folder.
Crucially, the team is not just a billing or login concept. It scopes data. Every projects row carries a team_id. So do credits, and so do AI request logs. When the dashboard lists your projects, the query filters on team_id for the team you currently have selected. When you generate a screen, the credits come from the team's pool, not a personal one. The team boundary is a real wall in the database — backed by Postgres row-level security — not a label in the UI.
Three access levels: restricted, view, and edit
Belonging to a team gets you in the door. It does not automatically get you into every project. For that, RapidNative uses a small enum — team_access_level — with exactly three values:
| Access level | What the person can do |
|---|---|
| restricted | No access. The project is invisible to them. |
| view | Open the project, see every screen, leave comments — but change nothing. |
| edit | Full building rights: prompt the AI, edit components, change layouts. |
Three levels is a deliberate constraint. We considered finer-grained permissions — comment-only, preview-only, export-only — and decided against them. Most real teams only need three answers: keep them out, let them look, or let them build. Every extra level is another branch to test and another decision to explain to a user. Three covers the cases that actually come up, whether the app started life from a sketch, a PRD, or a screenshot.
Access levels are only as good as the checks that enforce them on every read and write. — Photo via Unsplash
Two layers of sharing: a team default and per-person overrides
Here is the part most "we added collaboration" write-ups skip, and it is the most interesting design decision in the system.
A naive sharing model has one setting per project per person: a big list of who can do what. That works, but it is tedious. If you want your whole team to see a project by default, you have to add every single member by hand — and then remember to add the next hire.
RapidNative splits sharing into two layers:
- A team-wide default. Each project has one
project_sharerow. It holds ateam_access_levelthat applies to everyone on the team who is not specifically overridden, plus anis_publicflag for link sharing. Set the default toviewand the entire team — including people who join next month — can see the project automatically. - Per-person overrides. A second table,
project_share_team_user, links specific team members to a project with their ownaccess_level. This is where you say "the whole team can view, but Priya and Sam can edit." Each row overrides the team default for one person.
So a permission resolves in two steps: start with the project's team default, then check whether this specific person has an override. The override wins. When a project is first created, RapidNative seeds this automatically — a project_share row with a sensible default, plus a project_share_team_user row giving the creator edit. The creator never has to "share with themselves."
This two-table split is the difference between sharing that scales and sharing that doesn't. The default handles the common case ("my team can see my work") with zero clicks. The override table handles the specific case ("but only these two can change it") without forcing you to enumerate everyone. It is a small schema decision that quietly removes a whole category of busywork.
How a permission check actually runs
Storing access levels is the easy half. The system only works if every sensitive action checks them. In RapidNative, two functions guard each project — one for reading, one for writing.
When someone opens a project, a canViewProjectShare check runs. Its logic is short and ordered for speed:
- Is this person the project's owner? If yes, allow immediately — no lookups needed.
- Otherwise, resolve their effective access (team default, plus any per-person override). If it is
vieworedit, allow. If it isrestricted, deny.
When someone tries to change a project — send a prompt to the AI, edit a component, rename a screen — a stricter canUpdateProject check runs. Same shape, but only edit passes. view is not enough.
Above the application layer, Next.js middleware does a coarser pass first. It runs on every request and decides, before any route code executes, whether a request even deserves to be processed. API routes under /api/user/ require a valid Auth.js session; admin routes are gated to a fixed allow-list; and — importantly for collaboration — public and shared project routes are explicitly allowed through without a login, because that is the entire point of a share link.
The result is layered: middleware filters obviously-unauthorized traffic, then the per-action gates make the fine-grained call. A view-only teammate can load a project all day and never accidentally mutate it, because the write path checks edit independently of the read path. Reading and writing are separate doors with separate locks.
Inviting people without the friction
A permissions model is useless if getting people into it is painful. RapidNative has two on-ramps, depending on whether the person needs to build or just needs to look.
For teammates who build, there is a proper invite. From the dashboard, an invite modal opens, you type an email, and a request to the add-member endpoint does the rest. On the server, a TeamService looks the email up: if that person already has a RapidNative account, they are added to the team immediately; if not, an account is created for them on the spot. Either way, they receive a branded invitation email — rendered from a React email template — with a login link already scoped to the right team. There is no "create an account, then find the team, then ask to join" maze. Inviting teammates is a feature of RapidNative's paid plans; you can see how seats work on the pricing page.
For stakeholders who only need to look, an account is overkill. This is what the is_public flag and share links are for. Flip a project to public, and anyone with the link — or a QR code pointing at it — can open the running app on their own phone. No App Store, no TestFlight, no download. A founder can text a link to an investor and have them tapping through a real React Native app thirty seconds later. That path deliberately skips the whole permissions system, because the permission is "has the link."
A public share link turns "review my app" into a thirty-second action — no account required. — Photo via Unsplash
Switching between teams without losing your place
Because one person can belong to many teams, RapidNative has to answer a subtle question on every page load: which team are you currently working in? Get this wrong and you show someone the wrong projects — or worse, spend the wrong team's credits.
The obvious answer — store the current team in the database on the user row — is also the wrong one as a source of truth. Our internal engineering guide is blunt about it: the selected_team_id column on the users table is only a fallback for the very first load. Here is why.
If the database row were the source of truth, you could not have two browser tabs open on two different teams. Tab A switches teams, writes the database, and tab B silently changes underneath you. So RapidNative uses a three-layer chain instead:
- Session storage — per browser tab. This is the real answer for "which team is this tab in." Two tabs, two teams, no interference.
- Redux — the in-memory mirror the UI reads from while the tab is alive.
- The database column — consulted only when a brand-new session has no session-storage value yet.
Switching teams runs through a switchTeam action that updates session storage and Redux immediately — the UI changes instantly — and then fires a request to update the database in the background, without waiting on it. The UI never blocks on that write, because the UI has already moved on. It is a fire-and-forget sync that exists purely so the next fresh login starts in a sensible place.
This is the kind of detail you only get right by having lived with the bug. Tab isolation is invisible when it works and infuriating when it doesn't.
What we haven't built into our collaborative app builder yet
Time for an honest section. Today, a RapidNative team shares one workspace: the same projects, the same credit pool, the same comment threads. Comments are genuinely flexible — they use a polymorphic model (a commentable_type plus a commentable_id) so a thread can hang off a whole project, a single file, or one layer, with nested replies and soft deletes.
What we have not shipped is Figma-style live presence: cursors with names, "Sam is editing this screen right now" badges, real-time multiplayer on a single canvas. That is a deliberate scoping call, not an oversight. Live presence needs a persistent connection layer and conflict resolution on every keystroke, and for the way teams actually use an app builder today — one person prompting, others reviewing and commenting — a shared workspace with fast reloads covers the real workflow. We would rather ship a permissions model that is correct than a cursor that is cute. Live presence is on the roadmap; it is simply behind the things that mattered more.
Common questions about team permissions
Can multiple people edit the same app?
Yes. Any team member with edit access can open a shared project and change it — prompt the AI, edit components, adjust layouts. Access is set per project, so a teammate can have edit on one app and view on another, all controlled through that project's sharing settings.
What is the difference between view and edit access?
view access lets someone open a project, see every screen, and leave comments, but not change anything. edit access adds full building rights: sending prompts, editing components, and modifying layouts. A third level, restricted, hides the project from that person entirely.
Do people I share an app with need an account?
Only if they are building. Teammates who edit get a RapidNative account, created automatically when they are invited. Stakeholders who just need to try the app can open a public share link or scan a QR code on their phone — no account and no download required.
The unglamorous layer that makes collaboration real
Team collaboration in an app builder is not one feature. It is a stack: a team that owns resources, roles that rank members, access levels that gate projects, a two-layer sharing model that scales without busywork, permission checks on every read and write, and a team-switching system that survives multiple browser tabs. None of it shows up in a demo GIF. All of it is the difference between a tool one person uses and a tool a team trusts.
That is the bet behind RapidNative as a collaborative app builder: the way to help teams ship mobile apps faster is not just a better prompt box, but a permissions model solid enough that a designer, a PM, and a developer can all work in the same place without a meeting about who broke what. If you want the rest of the picture, our deep dives on real-time collaboration and how a chat prompt becomes production React Native code pick up where this one stops, and you can browse the rest on the RapidNative blog.
The fastest way to understand it, though, is to use it. Start a project and invite a teammate — or send a public link to someone and watch them open your app on their phone in seconds. Either way, you are using the layer this post is about.
Ready to Build Your App?
Turn your idea into a production-ready React Native app in minutes.
Free tools to get you started
Free AI PRD Generator
Generate a professional product requirements document in seconds. Describe your product idea and get a complete, structured PRD instantly.
Try it freeFree AI App Name Generator
Generate unique, brandable app name ideas with AI. Get creative name suggestions with taglines, brand colors, and monogram previews.
Try it freeFree AI App Icon Generator
Generate beautiful, professional app icons with AI. Describe your app and get multiple icon variations in different styles, ready for App Store and Google Play.
Try it freeFrequently Asked Questions
RapidNative is an AI-powered mobile app builder. Describe the app you want in plain English and RapidNative generates real, production-ready React Native screens you can preview, edit, and publish to the App Store or Google Play.