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.

Please set the playground first

Loading "One-to-One Relationships"
Loading "One-to-One Relationships"
Login to get access to the exclusive discord channel.
  • general
    Modals / Dialogs
    Lucas Wargha πŸš€ 🌌:
    It seems like modals and dialogs are becoming a hot topic on my team lately. I haven’t found a solid...
    3 Β· 4 days ago
  • general
    epic stack website initial load at home page is unstyled (sometimes)
    osmancakir πŸš€ 🌌:
    Sometimes (especially when it is loaded first time on a new browser etc.) I see this unstyled versio...
    • βœ…1
    10 Β· 3 months ago
  • general
    Welcome to EpicWeb.dev! Say Hello πŸ‘‹
    Kent C. Dodds β—† πŸš€πŸ†πŸŒŒ:
    This is the first post of many hopefully!
    • 18
    86 Β· 2 years ago
  • general
    Resource / Api endpoints on epic stack / RR7
    Lucas Wargha πŸš€ 🌌:
    Hi everyone! Quick question for those using the Epic Stack: How are you handling resource routes ...
    • βœ…1
    2 Β· 2 months ago
  • general
    Epic stack using tanstack form
    Lucas Wargha πŸš€ 🌌:
    https://github.com/epicweb-dev/epic-stack/compare/epicweb-dev:main...wargha:feature/tanstack-form-ex...
    • βœ…1
    3 Β· 2 months ago
  • general
    Init command outdated on the EpicWeb website
    Virgile πŸ† 🌌:
    Hi everyone. I've initialized a new epic-stack project yesterday. Following instructions from http...
    • βœ…1
    3 Β· 2 months ago
  • general
    Mark as complete, resets the first time you click it.
    Daniel V.C πŸš€ 🌌:
    Not sure if anyone else has had this issue, as i've not seen anyone else talk about it, but I find ...
    • βœ…1
    8 Β· 3 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 Β· a year ago
  • general
    Keeping Epic Stack Projects Free on Fly – Any Tips?
    Lucas Wargha πŸš€ 🌌:
    I’ve been experimenting with the Epic Stack and deploying some dummy projects on Fly. I noticed that...
    • βœ…1
    0 Β· 3 months ago
  • πŸ’Ύdata
    general
    πŸ“forms
    πŸ”­foundations
    Creating Notes
    Scott 🌌 πŸ†:
    Does anybody know in what workshop we create notes? I would like to see the routing structure. So fa...
    • βœ…1
    2 Β· 5 months ago
  • πŸ’Ύdata
    Prisma extension not showing my db
    oaguinaga 🌌 πŸš€:
    I noticed that in the data modeling module, Kent can visualize Prisma tables directly inside the edi...
    • βœ…1
    2 Β· 5 months ago
  • πŸ”­foundations
    πŸ’Ύdata
    general
    πŸ“forms
    πŸ”auth
    Thank you for the inspiration
    Binalfew πŸš€ 🌌:
    <@105755735731781632> I wanted to thank you for the incredible knowledge I gained from your Epic Web...
    • ❀️1
    1 Β· 5 months ago
  • πŸ’Ύdata
    How often do you need to run a migration?
    Scott 🌌 πŸ†:
    I just finished the Data Migrations workshop (I come from a No-SQL background). Let's say I add a n...
    • βœ…1
    1 Β· 7 months ago
  • general
    npm install everytime I setup a new playground
    Duki 🌌:
    Is it normal that I have to run `npm install` in my playground directory, everytime I setup the play...
    • βœ…1
    2 Β· 8 months ago
  • πŸ’Ύdata
    Code snippets in workshop app
    remich 🌌:
    What is used to include the code snippets in the workshop app? I'd like to have syntax-highlighted P...
    • βœ…1
    1 Β· 9 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...
    • βœ…1
    2 Β· a year 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...
    • βœ…1
    2 Β· a year ago
  • 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
    1 Β· 10 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...
    • βœ…1
    2 Β· a year 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...
    • βœ…2
    12 Β· a year ago
  • general
    Remix Vite Plugin
    Binalfew πŸš€ 🌌:
    <@105755735731781632> Now that remix officially supports vite (though not stable) what does it mean...
    • βœ…1
    3 Β· 2 years 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 Β· 10 months 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 Β· 2 years 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 Β· a year 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 Β· a year 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 Β· a year ago