One-to-One Relationships

Loading "Database Design"
๐Ÿ‘จโ€๐Ÿ’ผ We still need to deal with the Note images. And we're also going to add images to the users as well. Before we do, Olivia the Owl would like to have a word with you about data modeling...
๐Ÿฆ‰ One thing you want to think about when you're building out your data model is... the future.
CAUTION: The future is impossible to predict. All you really can reasonably predict about the future is that things will change. But you don't know how. That said, it can be useful to build things in such a way that optimizes for change provided it doesn't make things sub-optimal for the present.
Consider, for example, that you are building a house. You're considering what to do about the pool you want one day. You know you don't want it now, but you may want to add one in the future. With that in mind, you may think about where the pool should go, and you decide to avoid planting trees there. Depending on how confident you are that you will get the pool you may even consider placing the plumbing for the pool in the ground before you pour the foundation so you don't have to pay the cost of digging things up later. You just need to weigh the costs of doing that now vs. the cost of doing it later and the likelihood you'll actually get the pool.
For our notes to have images, we just need a place to store the bytes for the image, its content type, and the alt text. It's not much data. But if you think about a note taking app, it's reasonable to assume folks will want to upload more than just images to their notes. Maybe they'll want to upload a csv or a pdf or any number of other types of files.
So we should think about modeling things in such a way that it makes supporting these other file types possible without complicating our data model in the present or having a difficult migration in the future.
๐Ÿ‘จโ€๐Ÿ’ผ Thanks for that Olivia.
๐Ÿฆ‰ Oh, one other thing... On the subject of images... We'll be storing our images directly in the database. Turns out, in some cases, serving files from SQLite can be 35% faster than the file system (and can be more space efficient as well)! Even in situations where that's not the case, it's certainly not much slower. So while you definitely can bring in another service to manage storing the images for you, or store it to a persistent volume, starting out with the images in the database is a perfectly reasonable approach for many applications.
๐Ÿ‘จโ€๐Ÿ’ผ Ok, great. Thanks Olivia! So, with that said, instead of making a single Image model and using that for both the User and Note models, we're going to have UserImage and NoteImage models that each hold the content type, file bytes, and alt text.
This may feel like we're duplicating code and schema, which we definitely are, but if you consider the future possibilities, it will be much easier for us to add a NotePDF model later without impacting other models than to try and get all polymorphic with a single generic File model (polymorphism and databases don't mix well).
Here's a visualized model of this:
visual representation of polymorphism as described with a ๐Ÿ‘Ž emoji
And here's what we're going to do instead:
visual representation of duplication as described with a ๐Ÿ‘ emoji
With these new models in place, we can connect them to the Note and User models. So we'll have the following relationships:
  • User -> UserImage: one-to-one
  • Note -> NoteImage: one-to-many
Don't forget that the Prisma VSCode extension, can automatically capitalize the property names it adds which is annoying. Just double check that all properties are lower-case.
The emoji should be in the schema file to help guide you through this.
๐Ÿ’ฐ Remember, if you get stuck on the syntax, remember you can check the Diff Tab to check your work against the solution.
Once you've got the model in a good place, then let's push that to the database:
npx prisma db push
And now let's check out the studio:
npx prisma studio
Now we want to create a new image/file. The tricky bit here is Prisma studio represents files as base64 encoded strings which is fine, I even prepared this base64 image for you:
data:image/jpeg;base64,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
Sadly, I couldn't find a way to create one-to-one required relationships in the Prisma studio. So it's time to move to some code!
Let's create a script to do this for us:
Go ahead and stick this in there:
import { PrismaClient } from '@prisma/client'
import fs from 'node:fs'

const prisma = new PrismaClient()

const firstNote = await prisma.note.findFirst()

if (!firstNote) {
	throw new Error('You need to have a note in the database first')
}

await prisma.note.update({
	where: { id: firstNote.id },
	data: {
		images: {
			create: [
				{
					altText: 'an adorable koala cartoon illustration',
					contentType: 'image/png',
					blob: await fs.promises.readFile(
						'./tests/fixtures/images/kody-notes/cute-koala.png',
					),
				},
				{
					altText: 'a cartoon illustration of a koala in a tree eating',
					contentType: 'image/png',
					blob: await fs.promises.readFile(
						'./tests/fixtures/images/kody-notes/koala-eating.png',
					),
				},
			],
		},
	},
})
We'll dive deeper into this later...
Next, let's run it. Because this is a TypeScript file, you'll need to use tsx instead of node:
npx tsx ./prisma/seed.ts
Great, with that done, now open up the studio:
npx prisma studio
You should have your note with the two images and two files in there now. Huzzah! We'll get further into the seeding capability later, but this is a good verification that our model is working.
Login to get access to the exclusive discord channel.
  • general
    Migration to Vite: Server-only module referenced by client
    Fabian ๐ŸŒŒ:
    Hi, I'm working on migrating to Vite following the remix docs (https://remix.run/docs/en/main/guides...
    1 ยท 20 days ago
  • ๐Ÿ’พdata
    Prisma Typed SQL
    Kent C. Dodds โ—† ๐Ÿš€๐Ÿ†๐ŸŒŒ:
    Hey <@&1123674184959148043> friends, check out the new Typed SQL feature in Prisma! I've added an ex...
    • 2
    • 2
    0 ยท 23 days ago
  • general
    Remix Vite Plugin
    Binalfew ๐Ÿš€ ๐ŸŒŒ:
    <@105755735731781632> Now that remix officially supports vite (though not stable) what does it mean...
    • โœ…1
    3 ยท a year ago
  • general
    Welcome to EpicWeb.dev! Say Hello ๐Ÿ‘‹
    Kent C. Dodds โ—† ๐Ÿš€๐Ÿ†๐ŸŒŒ:
    This is the first post of many hopefully!
    • 17
    78 ยท a year ago
  • general
    ๐Ÿ”ญfoundations
    Solutions video on localhost:5639 ?
    quang ๐Ÿš€ ๐ŸŒŒ:
    Hi, so I'm having a hard time navigating (hopefully will be better with time) The nav on epicweb.de...
    • โœ…1
    9 ยท 10 months ago
  • ๐Ÿ’พdata
    Multi-Column Index - How does it eliminate the B-tree?
    Darren ๐Ÿš€ ๐ŸŒŒ:
    So I just finished the Query Optimization - 02 Multi-Column Index lesson and was confused by the fol...
    • โœ…1
    2 ยท 2 months ago
  • general
    Epicshop is now social and mobile friendly!
    Kent C. Dodds โ—† ๐Ÿš€๐Ÿ†๐ŸŒŒ:
    I'm excited to announce that now the Epic Web workshops are mobile friendly! https://foundations.ep...
    • ๐ŸŽ‰2
    0 ยท 3 months ago
  • ๐Ÿ’พdata
    ๐Ÿ“forms
    ๐Ÿ”ญfoundations
    How can I do this?
    silvanet ๐Ÿš€ ๐ŸŒŒ:
    Viewing the Intro (from the Workshop) for Mutations, the course has an embedded video where Kent exp...
    • โœ…1
    3 ยท 4 months ago
  • ๐Ÿ’พdata
    ๐Ÿ“forms
    ๐Ÿ”ญfoundations
    Reviewing foundations, Mutations, Actions
    silvanet ๐Ÿš€ ๐ŸŒŒ:
    Forgive me for this. I went over the file size limit. I don't want to sign up for being able to exce...
    2 ยท 4 months ago
  • ๐Ÿ’พdata
    Undesired behaviors while running the data-modeling workshop
    juliano.brasil ๐ŸŒŒ:
    After pulling the latest updates I started experiencing 2 undesired behaviors while running the wor...
    2 ยท 3 months ago
  • ๐Ÿ’พdata
    Query optimization for queries generated by prisma
    Allan ๐ŸŒŒ:
    Hi everyone! I finished the data modeling workshop this week and was inspired to do some query optim...
    • โœ…1
    2 ยท 4 months ago
  • ๐Ÿ’พdata
    general
    ๐Ÿ“forms
    ๐Ÿ”ญfoundations
    double underscore?
    trendaaang ๐ŸŒŒ:
    What with the `__note-editor.tsx`? I don't see that in the Remix docs and I don't remember Kent talk...
    • โœ…1
    2 ยท 4 months ago
  • ๐Ÿ”auth
    ๐Ÿ’พdata
    08. ROLE-BASED ACCESS / 02. ROLES SEED - migration to local vs production
    Fabian ๐ŸŒŒ:
    So I'm in a bit over my head with this one, in particular with how migration works in a local env vs...
    • โœ…1
    1 ยท 4 months ago
  • ๐Ÿ’พdata
    a very large insert
    osmancakir ๐Ÿš€ ๐ŸŒŒ:
    hi all, I need to perform seeding to my deployment. I followed the steps in the database documentati...
    2 ยท 4 months ago
  • ๐Ÿ’พdata
    data modeling 06/02 is missing part of the prompt
    Allan ๐ŸŒŒ:
    In Data Modeling 06/02 the files include `server/index.ts` but there are no guiding emojis, no promp...
    • โœ…1
    2 ยท 4 months ago
  • ๐Ÿ’พdata
    Why does TS not like Buffer being used when the return type of the blob appears to be Buffer?
    OtterlyPunk:
    I'm looking at the return types from Prisma and it appears that the `petImage.blob` is a `Buffer` ty...
    • โœ…2
    5 ยท 5 months ago
  • ๐Ÿ’พdata
    ๐Ÿ”auth
    Prisma batching in the Require Authorized (07/03/solution)
    ajara ๐ŸŒŒ ๐Ÿš€:
    When I did the `requireUser` function in `auth.server.ts` I thought about using `requireUserId` func...
    • โœ…1
    4 ยท 5 months ago
  • ๐Ÿ’พdata
    ๐Ÿ”ญfoundations
    Handle Missing Data - Error when i'm using invariantResponse
    Luan.ibarra ๐ŸŒŒ:
    Hello, I'm trying to use the invariantResponse utility from // "#app/utils/misc.ts" to do this in a...
    • โœ…2
    2 ยท 5 months ago
  • ๐Ÿ’พdata
    Data Modeling: a real world scenario
    trendaaang ๐ŸŒŒ:
    Kent, In the data modeling workshop, you mentioned that polymorphism is generally a bad idea in dat...
    • โœ…1
    3 ยท 5 months ago
  • ๐Ÿ”ญfoundations
    ๐Ÿ’พdata
    general
    ๐Ÿ“forms
    ๐Ÿ”auth
    Native Logging
    trendaaang ๐ŸŒŒ:
    I was thinking that it could be useful to log every CRUD operation to help track down errors. Is tha...
    • โœ…1
    6 ยท 5 months ago
  • ๐Ÿ’พdata
    ๐Ÿ“forms
    Issues with child routes when loading a modal with actions (server/client)
    OtterlyPunk:
    I've set up a mini-project to go through the first set of videos and I'm having issues with the form...
    • โœ…1
    7 ยท 6 months ago
  • general
    The video play is pretty laggy currently
    QzCurious ๐ŸŒŒ:
    I thought I should tag you for this <@105755735731781632>. Please take a look if something wrong.
    • โœ…2
    9 ยท 6 months ago
  • general
    New Workshop Scheduled
    Kent C. Dodds โ—† ๐Ÿš€๐Ÿ†๐ŸŒŒ:
    Hey Epic Web devs! I wanted to let you know before everyone else on here: https://www.epicweb.dev/ev...
    • 2
    0 ยท 6 months ago
  • general
    Deploying an exercise
    Khoi ๐Ÿš€ ๐ŸŒŒ:
    Dear <@105755735731781632> , First of all, I really appreciate your effort in building this EPIC cou...
    • โœ…1
    1 ยท 6 months ago
  • general
    "Start App" throws error: Error: Cannot add empty string to PrefixLookupTrie
    Martin ๐ŸŒŒ:
    โœ— npm run start > start > kcdshop start [playground:4000] [playground:4000] > dev [playground:4000...
    • โœ…1
    7 ยท 10 months ago
  • general
    ๐Ÿ“forms
    Can't start the playground
    trendaaang ๐ŸŒŒ:
    Been a minute since I last worked on this course. Just tried running the app and was notified that t...
    • โœ…1
    3 ยท 7 months ago
  • ๐Ÿ’พdata
    ๐Ÿ“forms
    Getting a TS error that is not present in the course files
    OtterlyPunk:
    So I'm working in parallel and I'm feeling the problem is I'm using a new version of something in my...
    • โœ…1
    12 ยท 6 months ago
  • general
    Question about the Workshop App tabs
    sjollivier ๐ŸŒŒ:
    Just started the course. I might have missed this in the Getting Started video, but how should I be ...
    • โœ…1
    1 ยท 7 months ago