Bootcamp
A simple approach to data modelling
Photo by MARIOLA GROBELSKA on Unsplash
Most Bubble tutorials start with the interface: drag on a repeating group, style a card, wire up a workflow. But every one of those elements sits on top of a decision you may not remember making, which is the structure of your database. Get that structure right and everything downstream gets easier: searches are simple, filters work, pages stay fast. Get it wrong and you’ll spend weeks building workarounds for a problem that took thirty seconds to create.
The good news is that designing a schema doesn’t need a computer science degree. It needs a short, repeatable method, plus a working grasp of a few database ideas that transfer to every platform you’ll ever touch. Here’s both.
Data modelling is the process of defining the structure of your data: which data types (tables) your app needs, which fields each one holds, and how they relate to one another. We model data because almost every app needs more than one data type, and documenting the model stops us getting lost.
Relational databases in plain terms
Bubble is built on PostgreSQL, one of the oldest and most widely used open-source relational databases, hosted on AWS in the US West 2 (Oregon) region. A relational database stores information in tables (in Bubble terms, data types). You can picture each one as a spreadsheet tab with rows and columns. The important part is the word relational: these tables have predefined relationships with each other. In a CRM, the things you’re tracking (contact, deal, mailing list, score) all connect.
The mechanism that makes those connections work is the key. Every table has a key field, sometimes called a unique ID or reference, and it’s assigned automatically to every row as it’s created. When you hold the key for a row, you can retrieve that row quickly, efficiently and accurately.
You can see this in Bubble. Open App Data, click any record, and you’ll find its unique ID. Copy that ID into the search box and the exact record comes straight back; add a few extra characters and nothing matches. Compare that with finding someone by typing part of their name or email domain: it’s slower, it consumes more resources, and it may return many results or the wrong one. The key gives the database a direct route to one specific row, and that’s why relational databases are fast.
Two pieces of terminology are worth knowing, even though Bubble hides them well. The unique ID of a row in its own table is the primary key. When a different data type stores that ID, as when a deal holds the ID of its contact, the stored reference is a foreign key. You don’t need to think about this daily, but it explains what Bubble is actually doing under the hood.
Creating a relationship takes one field
Here’s the punchline early, because in Bubble it’s genuinely simple. Suppose the CRM has a deals screen full of deal cards, and each deal belongs to a particular contact. How do we link them?
- Create a new data type called Deal.
- On Deal, create a new field named contact, and set the field type to Contact, your existing custom data type.
- That’s it. Every deal now has a field defining which contact it belongs to.
If you come from a database background you might wonder where the contact ID field is. This is the contact ID field: Bubble stores the contact’s unique ID in it and just gives it a friendlier name. When you create a deal in App Data and assign it a contact, you can even switch the primary display field back to unique ID and watch the raw reference appear.
The payoff comes when you read data. Once the relationship exists, an expression like Do a search for Deals's Contact's Name reaches through the link and pulls fields from the related record. In effect, every field on Contact becomes available anywhere you have a Deal. Because the reference is a key rather than stored text, the lookup is fast; storing the name as plain text and searching for it later would be nowhere near as efficient.
Data types versus fields
Before modelling anything, know your building blocks. Fields on a data type come from a set of basic types, and each has a purpose:
- Text: strings, from short names to long paragraphs and blog articles. Also phone numbers (they contain spaces, brackets and plus signs, so they’re not really numbers) and email addresses.
- Number: integers or decimals, positive or negative.
- Date: date and time data. Even if you only need a time, store it in a date field; you get access to Bubble’s powerful date functions.
- Yes/no: boolean data, true or false. Great for evaluating conditions and for flag fields such as
deletedorarchived. - Image and file: each stores the URL of the upload on Bubble’s file system.
- Geographic address: stored as latitude and longitude, then displayed as an address via the Google Maps geocode API.
- Number range and date range: two values stored as a range. Underused, but their comparison operators (does this range contain that point? do these ranges overlap?) let you do powerful checks in one or two steps.
- Date interval: the difference between two dates, e.g. current date/time minus a birthday gives an age.
If you’re unsure which type fits, look at the operators it offers. Type Current date/time in an expression and the dropdown shows everything a date can do: +months, change seconds to, ranges. That list tells you how the type is meant to be used. Should months live in a date field or an option set? It depends what you’ll do with them. Picking the wrong type is how hacky, inefficient workarounds are born.
Beyond the basic types, fields can also hold custom data types (a deal storing a contact), option sets, and API response types. A field can even reference its own data type, such as a Contact with a field of type Contact. That’s called a recursive field, and it’s worth pondering what you’d use it for: think about relationships between records of the same kind.
One reminder on option sets: they suit short, static lists that appear across your app. Only the developer can change them, users can’t, and changes take effect when you deploy. Update the label once and it updates everywhere, which is a maintainability win.
The method: four steps from idea to schema
Now the approach itself. It’s deliberately simple, and it works for complex applications.
Step 1: identify the entities. An entity is a concept within your app: a thing, something you would name in ordinary language. Depending on the app, an entity could be a person, customer, contact, product, order, invoice, profile, FAQ, log entry or report. Look at your designs and your brief and pull out the nouns.
The thinking matters more than the listing. On one project we worked on, a device gets installed on a vehicle, and each installation generates a report. Is the entity the report? The device? The vehicle it’s installed on? Are those one entity or three? Choosing the installation report as your entity when what the business really needs to track is the vehicle will bend the whole app out of shape. Ask what each thing means and how you see the data playing out before you commit.
Step 2: list them. For the CRM project, the list looks like this: Contact, Deal, Deal Stage (to do, doing, done), Deal Status (open, won, lost), Contact Status (hot, cold, archived), Tags, and User. Notice that a stage and a status are different concepts even though both describe a deal; one is where it sits in your process, the other is how it ended up.
Step 3: map how the entities relate. Draw a diagram. Our team uses a UML-style notation, but boxes and lines are fine. The test for each link is a plain-language sentence: a deal needs to know who its contact is, so there must be a link there. For the CRM: a contact has deals, a deal has a deal stage, a deal has a deal status, a contact has a contact status, a contact has tags. The user doesn’t tie to anything yet; it’s simply the account accessing the app, and relationships to it come later.
Step 4: decide the fields. The best way to find fields is to wireframe your screens. Grab a piece of paper and sketch the screens you think the app needs (a profile page, a product form) and the fields reveal themselves: a picture, a name, a description, some tags. Then record it: contact has a name, an email, a phone, an address, and those become the fields on the Contact data type. An entity relationship diagram is the ideal way to capture the result, and we cover that later in the module.
That’s the whole method: entities, list, relationships, fields. It front-loads the thinking that most builders skip, and it produces a data structure customised to what your app actually is rather than what the first screen happened to need.
Try it yourself
Take an app you’re building (or the CRM if you’re following the bootcamp) and run the four steps on paper before touching the editor. List the entities, write one plain-language sentence per relationship, wireframe two screens, and derive the fields from the wireframes. Thirty minutes of this consistently saves days of restructuring later.
This skill in the AI era
Of everything in this course, data modelling may have aged the best. AI coding tools will happily generate migrations, models and CRUD screens in seconds, but they generate them for whatever entities you name, and the question of whether the entity is the vehicle or the installation report is still yours to answer. A builder who can hand Claude Code a clean entity list and relationship map gets a clean system back; a vague prompt gets a plausible-looking schema with the wrong bones. It’s a core module in our Claude Code training, and one of the clearest examples of why no-code skills transfer straight into AI-assisted development.
If this sparked something, let's talk.
No pitch, no pressure — just a conversation about what you're working on.
Let's talkRelated posts
Bootcamp
Module 5 — Application architecture: pages, states and reusable pieces
Single-page vs multi-page apps, URL parameters, custom states, reusable elements — and a big practical building a main template with an expanding navigation drawer.
Bootcamp
API calls as actions vs data (and the planning checklist)
When to use Bubble API calls as actions versus data sources, a one-to-many auth pattern, and the planning checklist that keeps integrations from failing silently.
Bootcamp
Updating data in Bubble: auto-binding vs workflows
When to update Bubble records with Make changes to a thing workflows, when auto-binding is the better fit, the privacy rule it needs, and safe deleting.