Introduction & Installation
In this video, you're going to learn how to use Prisma with SvelteKit by building a simple CRUD application. This video isn't going to be a deep dive on Prisma itself, but more so how you can set it up and use it with SvelteKit. If you are interested in more videos about Prisma in the future and some of the cool things that you can do with it, let me know in the comments down below and I will certainly consider for future videos.
So I want to keep this intro as short as possible, so let's get into it. Now, I went ahead and set up the markdown for the application, just some basic forms and some cards here using Pico CSS. But before we can do anything with Prisma, we need to install the Prisma and Prisma client packages. So let's head into our VS Code here, and that's the first thing that we're going to install.
Initializing Prisma & Defining the Schema
And then once those are installed, we can actually initialize Prisma within our project by running npx prisma init.
And you'll see here now a Prisma folder was created with a schema.prisma file inside of it. Now to keep this video short and sweet, we'll be using SQLite. However, the beauty of Prisma is that you can pretty much use whatever database you'd like later on with only minor adjustments needed, depending on what type of database you choose to use. So we're going to be using SQLite, so we'll change our provider to sql-lite. And then for the URL, we'll just set this to file:./dev.sqlite. So it's going to put the database inside of this folder here.
Now we can define a model. So we can say model, let me close my terminal here, article, and we'll give it an ID, which is going to be an integer, and it's going to be default autoincrement(). So it's going to increase by one every single time we create a new article. Now I'll have a title, which is going to be of type string, and content, which can be of type string. Now to get our database up and running with this new table we have defined here with an article, we can open up our terminal again and say npx prisma db push.
Now this command should only be used during development because it doesn't produce migrations or a record of your database's history. So when you're at a point where you want to deploy this application to production, you want to look into how to handle migrations with Prisma and manage your migrations that way.
Verifying the Database Schema
So as we can see now, we have this dev.sqlite file that was created. And the reason I can actually see this inside of VS Code like this is because I have an extension called SQLite Viewer, and it only lets me see files that are named .sqlite, but I'll leave a link to that extension in the video description if you want to check it out. But now we can see that we have an article table with our three different columns here. We have ID, title, and content, which is exactly what we would expect. We can see that ID is a number, title is a string, and content is a string.
Configuring the Prisma Client
So now that our database is up and running, it's time to interact with it. And in order to do so, we're going to have to initialize the Prisma client. So I like to actually set up my Prisma client inside of a directory lib/server and then do prisma.ts. And by placing it in the directory server, SvelteKit will make sure that this file cannot be run on a client, which is exactly what you want, right? Because all direct communications with our database should only be happening on our server side, right?
So this is where we will define our Prisma client. But we have to do something else first. Because SvelteKit hot reloads whenever we make changes to our files during development, we want to make sure that we're not creating a new instance of the Prisma client that we're going to set up on every hot reload, which would then create an additional connection to our database on every reload. And this can cause some unexpected and annoying errors depending on what database platform you're using. So to get around that, we actually need to store the Prisma client as a global variable whenever we're running in development mode, because global variables aren't reloaded with those hot reloads.
So to do that, we'll first come into our app.d.ts here and I'm just going to set up a new variable and I'm going to call it prisma and it's going to be a type PrismaClient. And if you're just using JavaScript, you can skip this step here. And then inside of our prisma.ts file, what we want to do is we want to say import { PrismaClient } from '@prisma/client'. And then we'll say const prisma = global.prisma || new PrismaClient(). So basically, if global.prisma exists, it's going to use that, otherwise we'll instantiate a new client here. And then we can set up some logic to essentially set the global prisma variable to prisma if we're in a development environment. Like this, we can say if (process.env.NODE_ENV === 'development'), then we want to set global.prisma = prisma. And then we'll export Prisma.
And if you're using JavaScript, you can pause the video in just a second when I show a little snapshot of what it would look like for JavaScript. So now the first time our application runs in development mode, global.prisma will not be set here, right? So therefore, it's going to set a new Prisma client to prisma, right? And then if we're in process.env.NODE_ENV of development, it's going to then set global.prisma equal to this prisma here, right? And then we export that. So then the next time it reloads, it's going to check, and then that time, global.prisma will be set. Therefore, we're still using that global.prisma rather than creating a new one every single time we hot reload.
Create: Adding Articles via Form Actions
Okay, so now that we have our client set up, we can actually use it now by adding the ability to add new articles to our database. So you can see this is where our application is as it stands right now. We have this form here, and this form already exists. That means we can go ahead and just start to add the form actions. So I'm going to create a new +page.server.ts file, and then I'm going to set up my actions with SvelteKit actions here.
And we'll set up an action called createArticle. It's going to be asynchronous and it's going to take in a request. And then we're going to take out the title and content. It's going to be Object.fromEntries(await request.formData()). And then we can actually say as { title: string, content: string }. This is because since we're doing form data here, and Object.fromEntries, title is going to be a FormDataEntryValue type rather than being a string, which is what Prisma is expecting. But we know it is a string.
So then because Prisma returns promises when we're creating a new article or creating something inside of our database, we need to set up a try-catch block here. So we can say await prisma.article.create({ data: { title, content } }). And then we'll catch any errors, and then if we do have an error, we'll just return fail(). And then we can obviously console.error(this.error) as well, just so we can see it while we're in development. And then if everything is good to go, we will just return a status of 201. And then we need to update our form's action to actually hit that action. So we can say ?/createArticle, method post, everything was good to go here.
And now let's try to actually create an article. So we can say 'my first article', 'this is a test article'. Say Add. Okay, we didn't get any errors, so it looks like maybe we're okay. So let's go into our database here and we can see now that we have a record with 'my first article' and 'this is a test article'. So I'm gonna add a couple more articles here. And then if we check our database and we give it a refresh, we'll see that we have all three of these articles here inside of our database now.
Read: Fetching and Displaying Articles
So right now on our application, we're just rendering out an example article, but we actually want to get the articles from the database. So inside of the same page.server.ts file, I'm going to set up a SvelteKit load. It's going to be a PageServerLoad, and I'm just going to return articles: await prisma.article.findMany(). prisma.article.findMany() is going to give us a list of articles.
So we can come back into our page and we can set up a script tag and accept the page data. And then I'll destructure articles from data. And then I'll come down here and I'll say {#each articles as article}. And then we'll replace these values with what they should be. So we have article.title and then we have article.content.
Delete: Removing Articles
Now when we come into our application, we can see that we have these three articles here. Now let's have the ability to delete articles. And to do so, what we can do is we can actually wrap this button here in a form tag and give it a type of submit. And then what we'll do is we'll make the action ?/deleteArticle&id={article.id}. So what we're going to do is we're going to actually submit this request here. We're not submitting any data, any form data, we're just hitting this specific endpoint or this specific action, and then we'll be able to get the article ID from the URL object.
So we can come into our actions here, you can say deleteArticle. It's going to be asynchronous. We're going to get the url out of the request. And then we'll say const id = url.searchParams.get('id'). And we'll say if !id, we just want to return fail(). And then otherwise, we're going to set up a try-catch block. await prisma.article.delete({ where: { id: Number(id) } }), because it's going to be a string or null, right? Because it comes from our URL. We're going to first rule out that it is not null, so now it can only be a string, so we have to convert it into a number so that Prisma can actually accept it, because our IDs are numbers. And then we'll catch any errors that occur, and then we'll just return a status of 200.
And then we have to add method="post" to our form action here. Can't forget that. And then we can go back into our application and try to delete one. So I'm just going to delete article two. So if I click on delete article, we're going to see that the article has just disappeared. And if we look at our database, we refresh, we no longer have the second article.
Setting Up the Update Route
So now the last thing we're going to implement is the ability to update articles. And I already have a button here, but we're going to first create a new route. We're going to call it article/[id] and then add a +page.svelte to that, and then also a +page.server.ts file. And this +page.svelte is just going to have the form that we have here. So if we look at this form here, we'll just copy this down and paste it here.
And so in order to update this article, we're going to have information from that article. We need to have access to that article so that we actually know what we're updating, what content currently exists on that article. So we can get that information if we go into our load function here and set one up. And we're just going to take in the params. And then we'll set up a function called getArticle. It's going to be asynchronous. And what we'll do is we will say const article = await prisma.article.findUnique({ where: { id: Number(params.articleId) } }). Then we need to import Prisma from lib/server/prisma. And then we need to run a check here. So if there's not an article, right, because this could possibly return null here, right? So findUnique can either be an article or null. We're going to throw an error saying the article was not found. Otherwise, we'll return the article. And then from our load function, we'll return { article: getArticle() }.
So then our page here for the article ID, we can set up script tags and receive that data here. And then we'll change this to say... well, actually destructure article from this, and we'll say Editing {article.title}. And then we'll set the value of these two inputs to article.title and then article.content. We'll change the button to say 'Update article' and then we'll change the action to say updateArticle as well. So now inside of our root page, we actually need to make this button here link to that article. So we can do that by just simply saying /article/{article.id} as the href.
Update: Saving Article Changes
And now when I click on an article for my first article, you'll see that this article is now pre-populated with the information of that article. And the last thing we need to do is set up the form action for updating this article. So we can say kit actions, and then I'm going to import this type. updateArticle is going to be very similar to the other one we did, right? It's going to take a request, but it's also going to take in params. And we'll say the title and content are equal to Object.fromEntries(await request.formData()) as { title: string, content: string }. And then we can run another try-catch block. So we can say await prisma.article.update({ where: { id: Number(params.articleId) }, data: { title, content } }). And then we'll catch any errors, we'll log them out, and we'll return a fail() if there is an error, and this will be a 500 status code: 'Could not update article'. And then we'll just return a status of 200 here if everything goes well. And this return here, this return statement here is pretty much arbitrary. You can return whatever would indicate to you that this request was successful, right?
So let's go back into our article ID. We have the proper action set up here, everything looks to be good to go. So let's test this out. So if I update this, 'My first article has been updated', and I say 'Update article', we can see that the title changed, of course, because the title changed here. And then we go back into our home screen, we can see that this was changed as well.
Conclusion
So this has been a brief introduction to SvelteKit and Prisma. I hope it has been informative to you. If you got value out of this video, don't forget to like and subscribe, it greatly helps the channel out a lot. If you'd like to see more content with Prisma and SvelteKit, let me know in the comments below and I will consider for future videos. And I will see you all in the next one.