In this chapter we're going to add the projects entity to our application. So this chapter will be quite similar to the previous one where we introduced the message model. So in this chapter we're going to add the project schema, we're going to add message relations to that project, and then we're going to create a new project on user prompt. And the last thing we have to do is preserve project ID in background jobs, so we know where to store that AI result. Basically, each message needs to belong to a project so we can keep track of all of our AI generations.
So let's go ahead and start by adding a new Prisma schema. As always, confirm you are on your main branch and if you haven't synchronized your changes, you should have 08 messages as your last merge. So I'm gonna go inside of Prisma. I have some migrations here because we added them last time. And now let's go above the message here.
And actually, let's go above the message type and above message role. And let's add a model project. The ID will be the same as the message, so you can add it here. The name will be a string. And then we're just going to have created at and updated at so we can add this and then down here add messages which will be a type of message like this and now we have to create an equal relation in the message model.
So let's go inside of the message model here. And let's go ahead and add project ID to be a type of string. And below that project to be a type of project, give it a relation decorator with fields project ID, references ID and on delete cascade. So exactly the same as the message relation in the fragment, right? We are aiming for project id field, referencing the id field in the project and if the project gets deleted the message gets deleted as well and then the fragment gets deleted as well.
Perfect! So now that we have this we have to push that to our database. So I recommend shutting down both of your servers here and let's first do npx prisma migrate reset simply so we remove everything from our database because we have invalid data at the moment. And once this is deleted, let's go ahead and do npx prisma migrate dev and once it connects to the database, let's go ahead and call this migration project. There we go.
So I'm going to call this projects like this and that should apply the migration. Perfect. Now let's go ahead and start this server and let's start the Ingest server here. There we go. So now what we are going to do is the following.
We're gonna go inside of source, inside of modules, and let's copy the messages and paste it here and let's rename it to projects. Let's go inside of server procedures, make sure you are inside of projects here. We're going to change this from messages router to projects router, like this. And then we're going to modify how this works as well. So for the getMany, change this to be projects.
And then in here, await Prisma.project findMany. So that's the getMany procedure for the projects router. For the create here, the value will also be the message, right? And actually I'm not even, yeah. So we are going to create a project by entering a prompt, right?
So we are not going to create a new project and then give the project a name. Instead, we're going to have a big landing page like this, and we will simply say, hey, enter something, like create a Netflix clone, and then we're going to click Create. And this will create both the message and the project at the same time. So in the create, we actually only have the value, right? The prompt.
So that's going to be this. So we're going to do the following. Const createdProject, await Prisma project create. And then for the data, we have to give a project a name so for this we're going to add a generator package to our project So let's go ahead and let's quickly do npm install random words Slugs random word slugs. You can of course use a billion other Generators, but this is the one I found that looks the most like all the other apps I can find.
So this is the version 0.1.7 in case you're interested. And let's go ahead and use it now. So let me just add it here, generate slug from random words slug. And then in here, in here, the name will be generate slug and pass in two words. And let's go ahead and open the settings and pass in the format to be kebab, like this.
So that will be it for the name. And now we have to immediately create the message. So we can do that either separately like this, or we can just pass in the messages here, and then open the create inside, and you can just copy this. Exactly like this and then you can remove this. And then we start the ingest here and besides sending the value we will also send the project id to be createdProject.id and then in here you will have createdProject as the return.
And that's it. That is our create method for the projects router. So in here it would be a good idea to limit the length as our CodeRabbit suggested previously. So let me add maximum here. And let's add, I don't know, 10, 000 maybe.
That could be the good upper limit. Message is too long. This is not actually the message. This is prompt, right? Or value, since this is called value.
So yeah, it's either going to be required or if it's longer than 10 000 characters we're gonna say okay that's too long you can of course modify this later to however you like perfect so you can of course also play around with this right it even has some more options which you can do, but I found this to be sufficient. And also in our Prisma schema, the project's name is not unique. So it doesn't matter if there are conflicts with this. Great. So now that we have this, we also have to modify our messages procedures here, because right now they are not exactly working.
So let's go inside of the create base procedure here. And for the value, well, we can just copy this, just so we're on the same page here so either min or max and then let's also add project ID here which will be a type of string with a minimum value of one and a message. Project ID is required like this. And then in here, when we create a new message, we will also assign project ID to the input project ID. So each message will be stored in an individual agent, in an individual project, right.
And now what we have to do is also modify the ingest send to also accept project ID from input project ID. Like this. There we go. And now what we have to do is we have to modify our ingest functions to accept the project ID. So let's go all the way down here to when we actually save the result.
And you can see we have an error here. That's because this message is missing the project ID. So project ID will be input. My apologies, it is event. Let me just find it.
How do I do this? Just a second. Event data value. So this will be event data project ID, like this, and do the same thing here. So basically you have to make sure that anytime a message is created you add the project ID So you can highlight this part and use command shift F to search it through your entire project and basically every place that you find this, it should include project ID.
So just be extra careful in the functions of the ingest here. So you don't forget to, So you don't accidentally misspell this, right? Because there are no strict typings here. We can improve this later on, but for now just make sure you didn't misspell project ID when you extract it from event.data. Perfect!
And now let's also do ingest.send. Simply so we see that we are sending project ID in all places that we need. Great. So in here we are extracting in from created project, but in here it is from input project ID perfect so now what we have to do is we have to go inside of source app folder page dot TSX and we have to modify this so this will no longer be creating messages and we no longer have to query messages We only did that before because we were interested in seeing them. So we can remove this.
And instead, we can do create project. And this will be trpc projects, which doesn't exist. The reason it doesn't exist is because we forgot to add it. So inside of the trpc folder, routers app, add projects, projects router. And you can import it from modules, projects, server, procedures.
Basically this thing we just created. And now that we have that, we have a proper working projects create. We can remove onSuccess and instead we can add onError here. And you can do toast.error, error.message. Like that.
And now that we have the create project, let's go ahead and let's do create project is pending and create project.mutate. And this will be submit. Like this. And let's go ahead and just modify this slightly by adding height screen with screen flex item center and justify center. And inside of here, Let's do this.
Let's give this a class name maximum with seven Excel MX auto flex item center. Let's do flex call and gap y or an items and justify center. And now when I refresh this, there we go. It looks like a centered little prompt. We can maybe expand this, let's see, maximum width.
Okay, screen. Let's just keep it at seven Excel like this. And when you write test now and click Submit, it should say, well, nothing, nothing for the success message. But now what should happen is the following. It should create, well, I have no idea what it's going to create now because I just typed test.
So let's actually see. OK, so it returns the error. Something went wrong. Please try again. Even though it generated something in the sandbox, I have no idea what that is.
I think not a single file was modified yet. So it's just an empty Next.js page. But if you look at your Prisma Studio now, and if you actually start it, so let's do npx Prisma Studio, you should now have a project. And inside of that project, you should have, There we go, I have a name, uninterested plastic. So a new project was generated and I have two messages inside.
The first is the message from the user who asked the test and then a response from the assistant, which is a type of error because something went wrong because this is clearly not something the AI can generate, right? So let's try build a landing page and let's click submit. What should happen now in the Prisma Studio here, this one, is that we should have a new project now, Modern London, with one message, as you can see. Let's just refresh. There we go.
So build a landing page by user. We are running this and now we should have a successful example and all the messages for this project will be stored in that project. So you can see how our submit data was a project ID and the value build a landing page. So there we go. Now, when I refresh this again, I should get another message from the assistant with the task summary.
And this message also has a proper fragment. And inside of here, we should be able to see... Let's open in new tab this fragment. And in here I have the sandbox URL. And I should now see the landing page.
There we go. Perfect. So now that we have this, let's go ahead and just do one more thing so we can start building the UI for these messages. So let's go inside of source app folder. Let's create a new folder called projects.
And in here, open project ID. So this is basically a dynamic URL part. It's important how you write this. So curly brackets, square brackets are extremely important. And then how you type inside is exactly how you're going to extract this value.
So be mindful of casing, right? Now add page.tsx here. And export do a page export like this and they div like this and this will be project ID and then to extract the project ID you simply create an interface props with params which are a type of promise and inside project ID which is a type of string. And then in here you can extract the props, you can extract the params, And since this is a server component, you can make this an asynchronous component and extract the project ID from await params. And then you can set the project ID to be project ID.
As simple as this. So how do I know that it is project ID? How do I know it's not project ID 123? Because of how we named the folder. So if you named this with a lowercase letter I, then you need to change this to a lowercase letter I.
So be mindful of how you name this dynamic folder. And once you've done that, go back to your page here and go ahead and add router from useRouter from next navigation like this. And then add the onSuccess here, which I'm going to transform into an arrow function simply because I prefer them, no other reason. We only need the data here. And let's do router.push forward slash projects and then data.id.
So how come that we have the data ID available for us? Because in the create procedure, we return the created project. So this new project that was just created, we have its ID right here. So now, if I do build a blue landing page and click submit right here, there we go, I'm redirected to project ID and that new project. And now in here, I will load only the messages for that project.
So in the next chapter we will go inside of our modules, messages, procedures, and we will modify the getMany to accept a specific project ID and then query by project ID instead of loading all of them. But that is for the next chapter. Amazing, amazing job! So you just added project schema. In our entire application we will have one more model in the database but this is pretty much it.
Amazing! So we added the project schema, message relations, new project on user prompt and we preserve the project ID in background jobs. Now let's go ahead and commit this. So I'm going to open a new branch, 09 projects. I'm going to stage all of my changes.
09 projects, and I'm going to click commit. And let's go ahead and publish the branch. And then let's go ahead and open a pull request, just like that. And let's see the summary of this chapter. And here we have the CodeRabbit summary.
We introduced support for projects, allowing users to create and view projects, each with an associated initial message. We also added a dedicated project page displaying the project ID for now. This will later be the actual interface where you will chat with an AI and see the preview of your work. And in here we have a couple of recommended changes. So in here it recommends throwing an error in the background job if it cannot find the project ID.
And this is definitely a good idea, but I would rather we don't even invoke a background job if we don't have a project ID. Because where do we even save this message then, right? So we have to think of a different way to improve this, but a good suggestion nevertheless. And another suggestion regarding the migration, since this is just, you know, development migration, I really don't care about this one since it's not really dangerous for our use case. So I'm going to merge this pull request.
And that marks the end of this chapter. As always, make sure you go back to your main branch and make sure you click on synchronize changes so you pull that new merge. And once that is done, you can go inside of your source control button here, go inside of graph and you should see that we just merged projects. Amazing! See you in the next chapter!