
Lesson Description
The "Sharing Schemas in Client & Server" Lesson is part of the full, Fullstack TypeScript, v2 (feat. Zod) course featured in this preview video. Here's what you'd learn in this lesson:
Steve refactors the client and server projects by moving the schemas into a shared package. This allows them to be imported into each project, and there is one source of truth for schema validation.
Transcript from the "Sharing Schemas in Client & Server" Lesson
[00:00:00]
>> Steve Kinney: Now the tricky part is figuring out how to share them, and it's not tricky, and so far that it's hard to do. It's tricky because the answer is, it depends. It depends on the code base that you're in, right? If you are in one true, like monorepo with folders, it's not that hard.
[00:00:22]
Just put them in the folders, right? And share them. But that wasn't your question. If you have just a bunch of folders, you already guessed that was the way. So then there are the various ways that we can do this, right? We will talk about various different API, schema, contract formats, open API, I will at one point mess up and say open AI at some point, I'm sorry.
[00:00:47]
We'll talk about that, we'll also just talk about various different monorepo or package strategies, right? If everything is in TypeScript, then life is pretty easy, cuz you can do a bunch of different things. You could have some kind of monorepo architecture, the way that we do it is like an NPM package, right?
[00:01:07]
The contracts, the Zod types, and the schemas and the types are just in an NPM package. There's good parts and bad parts to all of these things, right? Having it in an NPM package is great for the same reason that it's terrible, right? Which is, you've gotta hope that the server is using the same version of that API package as the client, right?
[00:01:32]
But if you are a large enough company, sometimes if you can have the right version control and breaking changes in the semantic versioning, then not forcing everyone to upgrade in lockstep in a 5000 person org is a feature. When there's two teams or three teams and everyone's got to coordinate, because you don't have the infrastructure for December stuff like that.
[00:01:58]
It's a bug, you know what I mean? And so I'm not gonna tell you, do it or don't do it in a given way. The answer is, it depends. It's kinda a schema migration. Yeah, a lot of things there are trade offs and that's why we get paid the dollars to figure these things out, because a lot of times there aren't a right answer.
[00:02:18]
So if they were in, these are technically in the same project, right? I could dot-dot-slash, dot-dot-slash, whatever. And honestly, you don't have to worry about versioning, you don't have to worry about keeping stuff in sync, if you can get away with that 100% do the easiest possible thing and then what happens if you can't, right?
[00:02:43]
So then the other option is, and we're simulated a little bit, is we have this shared directory here. And shared has, it's exporting everything from schemas. Guess what's in schemas? Nothing, right? So this is the kind of approach closer to what we do, but we get to cheat.
[00:03:06]
I work in an open source company with SDKs, and so our types are also in our SDK package as well. So I get some of those handled for me for free, but we could theoretically take those things that we copy and pasted in the client and the server and begin to dedupe them, right?
[00:03:25]
So we can take these same ones. So now what we do is we take this before I delete it, cuz I don't get the red squeeze lines of death, I'm just gonna copy it for a second. And we can go into our shared library, the real name of the package is the busy-bee-schema, right?
[00:03:43]
The UI is busy-bee-UI, the servers, busy-bee-API, whatever. Those felt hard to say. So the folder is named client server and shared, but it has a package name. And so we can take this and we go into schemas, pop them in here. So now they are part of this package, right?
[00:04:05]
And I will go ahead and do the inferred types as well. And so we can say export type task is inferred from the TaskSchema, export type CreateTask, export type UpdateTask, and the TaskList, cool, we can save that. And theoretically, these are now all exported from the package, right?
[00:04:36]
So instead of having to copy and paste them, and keep them in sync across two code bases, right? What I can do is basically just remove them from both. So now get rid of all this. And we'll do import. The UpdateTaskSchema, we CreateTaskSchema, the TaskSchema, and the TaskListSchema.
[00:05:21]
Yeah, used, but never read, we'll deal with that, we'll take it for now. And so we can take that from this library, and we can go back over into the client. We can even just use the ones we're using for now. I'm slightly suspicious though for a second, because I'm pretty sure.
[00:05:45]
I'm in the client. I was like, didn't I validate some request bodies earlier? So I'm in the client, so we did the client first. So there we only did the TaskList, so just to get the red squiggly lines of death out of the way. So now these are coming from that shared library.
[00:06:02]
Again, if you have to do this, do it, if you can get away without doing it, not doing stuff is way easier than doing stuff, right? Larry Wall, the guy who created Pearl, his famous quote is the virtues of a great programmer are laziness, hubris, and impatience, I believe that wholeheartedly, right?
[00:06:27]
Cool, cool, cool. So now obviously, I'm pulling this in from the shared repo. As you can see, it works, and even my fetch all tasks is the right thing, cuz I'm using a schema, I'm parsing it. It's not even in the same, quote unquote, same repo, but it's a fake monorepo.
[00:06:50]
And then we do the same thing in server, we go to server.ts. We will then delete all of this, pull this in. But what are the ones I need? We've got this CreateTaskSchema, which we're gonna import from busy-bee-schema, TaskSchema and UpdateSchema. Now my client and my server are definitely validating everything from the same schema, right?
[00:07:28]
I am cheating right now with the versioning. It's not really cheating, cuz again, you should always do the simplest thing until you can't, right? And so the one very smart, big brain cheat is in the package json. The version that we're pinning to is literally whatever the latest version is, right?
[00:07:53]
You can still run into problems where, obviously this is only checked at like NPM install time. Is where you get the newest version, which means, usually, if you're pointing to first seller, what have you or building, right? Whoever built last, you could theoretically have some drift between the two, ideally, you can imagine handle it a bunch of ways.
[00:08:17]
For right now, there's always gonna be a version of this. But at this point, if you are building them together, you will always get the same scheme as shared across both.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops