Next.js + Postgres Setup Walkthrough: Dev/Production

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all the latest next JS and versl updates have me excited to try them out we now have a stable app router in xjs react server components we've got server actions we've got versal postgres where we can actually have postgres in a for sale environment lots of really cool stuff so in this video I wanted to walk through how you might bootstrap a next app get a database set up locally for your development and also deploy that to versel for production let's get going by bootstrapping our next project so we'll just give a call to npx create next app at latest and we do want to install create next app what is your project name I'm going to call this next starter kit we will use typescript eslint sounds good L end of course uh Source directory yes app router absolutely we won't do any further customization to the Imports and or off okay let's open that up in vs code first things first I want to actually use pnpm instead of just plain old npm for this so I'm going to do a pnpm import we're going to import our package lock file from npm and that will create a pnpm lock file generated off of our existing log file so now we can just remove our package lot.json and suddenly we're converted to pnpm now let's just see if we can run our project at all so I'm going to do a pnpm Dev make sure we can call up our project and open this in a browser and it looks like we're running let's get a postgres database running locally and for that we're going to use Docker and Docker compose so we don't have to manage a separate postgres instance and change versions between our different projects Docker Docker and Docker compose will make this really easy for us so we'll go back into our code and create a file called docker hyphencompose.yaml and I already have Docker running on my machine here so if you don't have Docker running head on over to the docker website I I really like the docker desktop app for Mac uh makes it really nice if you've watched my ultimate node stack videos before you know that I'm a big fan of Docker and so I've pulled up my blog post on the ultimate node stack that has my Docker compose config for this so I'm going to copy this over into our Docker compose file and I'll link to this in the description let's go through this so we have a services collection here for us we're going to call our postgres service postgres we're going to run postgres version 15 and that is currently as of the recording of this video the version running inverse cell and we'll run it in Alpine so it's a super thin container we're going to expose the port 5432 which is the standard postgres port to our local machine here because we'll be running next directly from our local machine we're not going to run next in Docker so this will help us connect from next running locally into postgres in Docker then we've got our postgres database name we'll just call this web app right now and then this funny little environment variable we have to include to help with authentication locally so this looks good let me open up a new terminal window here I'm going to do Docker compose up and that will download install and start our postgres container and it does say that database system is ready to accept connections we need two things we need something to connect to our database from our code and we also need something to do database migrations to manage the state and structure of our database over time so for the connection side we're going to use Prisma Prisma is my favorite orm right now they also have a migrations product as well you can totally use that you can use Prisma for both I'm going to use connects for migrations again if you've watched my videos before you know that I'm a connect span for migration's sake in Prisma you write your migrations um or it bases your migrations from your Prisma schema generates SQL files for you and connects you script your migrations in JavaScript so you can choose whatever works for you let's head on over to the Prisma site I'm going to go to the docs page getting started Prisma client quite a few clicks here quick start not quick to get to the page uh it has a few steps here to initialize typescript project which we already have with our next JS create next app thingy so let's go down a bit below and here's really what we need is installing Prisma save Dev so let's uh translate this into what we need what we really need is pnpm ad Prisma hyphen capital d to save this to Dev dependencies okay so we have that uh now we need to run a Prisma init and this has database Source provider uh data source provider sqlite we're going to use postgres so we're just going to do Prisma init so PNP X Prisma init and there we go now we've got a Prisma slash schema.prisma file where we can start to outline our database structure so let's start with a model called post we're not going to go really deep into an app here so I'm just going to create one model called posts that post posts that has uh some records that we can create and just see if it's loading on the page so we'll do an ID sure that sounds good I'm going to make this a string and make it a CU ID which is something new I've just learned recently it's kind of like a mix between uuid and Nano ID if you are familiar with that uh we'll do content which is a required string and create it create it at and updated app and that's really all we're going to do I am going to map this though to a table called posts plural by default it goes into a singular format so I like I like plural and we're going to order these on our page by created at so let's go ahead and do an index on created app so that sounds good for our model so this is really what we need for our prisman model now what we need to do is generate our Prisma client so we can do a pnpm Prisma generate and there we go now it's generated our client for us Prisma has created this file for US called dot EnV as a part of Prisma init and this is where we can store environment variables for use throughout our project now it added the database URL connection string here for us but this is going to vary between development and production so I'm actually going to rename this EnV file to.env.development because this will be used for development purposes only now let's match this string to what we'll need for our local development so postgres is the protocol is great we don't need a username or password in our actually we do need a postgres username uh localhost is good 5432 is good and our database name as you might recall from Docker compose is web app underscore Dev so there we go so we've updated this connection string and renamed this file we do want to commit the dot emv.development file to your GitHub repository there are no secrets in here as of yet if you do need to put secrets in here then you might want to consider a DOT envy.localfile or something like that but be sure you make sure whenever you're messing with DOT EnV files make sure you're not committing secrets to your GitHub repository git repository okay now that we have that set up that really is it for Prisma at the moment so if you are going to do Prisma migrate or any other forms of migrations and this is where you pause and go do that otherwise if you want to follow along with me and do our connects migrations this is where we get going let's go to the connects website at connex.js.org and we can see we've got npm install connects and then you have to install your database driver so I'm going to do connects npg so pnpm and connects and PG there's also a handy utility for initializing connects we can run a pnpx NEX init and pass uh X for extension TS to use typescript so pnpx in it pnpx next init X group what this has done is created a next file where we can come in here and configure our connection information thank you have provided kind of a complex file the one that we need is much simpler so I'm going to copy this again in from my ultimate node stack this looks good now there is one issue between next JS and connects our database in this case migrations library in that the way typescript is configured so there is one special trick that we need to do to make our connects uh really work for us I'm going to add three handy scripts here into our package.json file and you can see that we've added scripts for dbmigrate dbmigrate undo and DB migrate make these are just three handy scripts to run some connects commands but pre-fixing all of those is the setting of an environment variable called tsno compiler options this is where we can set on the Fly some adjustments to our compiler options whenever we are running this in typescript which has made me realize we do need TS node so let's add that to pnpm add TS node there we go the way modules work in next and connects are different so this fixes that problem for us let's run pnpm DB migrate make the script right here and I want to call this init DB there we go this is created a migration file for us now under the migrations directory it is typescript and we can begin our migration so what we're going to do is really just going to mirror the Prisma schema here so I'm going to do an await create table we're going to create a table called posts um let's do a string ID column with the length of 25 to match the cuid spec it will be primary I'm going to do content uh we'll do a tweet length what is a tweet length 280 to 280 that is not nullable we will add in our time stamps ourselves here so time stamp created at and updated at we'll also do an index on created app now let's do our down migration which GitHub co-pilot so generously creates for us uh there is one thing I want to tweak which is personally I love single quotes so again opinions here I'm going to add in a DOT prettier RC file here and configure my nice happy single quotes I'm missing a parent a parenthesis there we go now let's see if we can run pnpm DB migrate and it failed what happened unable to acquire a connection aha we need to make sure and bring in our database URL for connects so let's go to our next file and we are trying to pull in our database URL here but what is happening is this dot m dot development file is not loading so let's do a pnpm ad and we're going to add a package called dot EnV and this is a nice utility package for us to load dot files as they are called dot EnV files so we'll do require.env dot config but we do need to pass this some config options to specify which file we want to load I think I can remove that so let's save and try this again there we go so batch one run one migrations thanks plural that means it successfully connected to the database uh and successfully ran these migrations which means Docker is successfully running and postgres is successfully running Insider Docker exposing that port to us and everything's connected and everyone's happy so I think we're good on migrations now let's get to using the database in our next JS code building up just a very quick prototype of seeing some posts on a page and go from there real fast let's make a commit Dot N dot development which is fine pretty RC dark compose next file we switched away from npm package Lock Road immigration and prism okay add pnpm Prisma Prisma connects and postgres now let's head back over to our next JS code and we will get some functionality going so I'm going to go to Source app page.tsx we've got a lot of stuff in here to load kind of just the boilerplate next.js page as seen here by the way I did have to restart my Dev server for next after our recent changes so if you get a error here on this page just restart your server I think you'll be fine what we want to do is connect to postgres from Prisma load up any posts from the table that are currently in there and maybe provide a button just to generate some mock posts into our database so I'm going to delete most of what's in here let's fetch from the database our posts so because we are in next.js and we have access to react server components we can run this database fetch right inside of our react component in order to do that I need to establish a Prisma client so in our source directory I'm going to create a new file I'm going to create a modules directory slash db.ts so let me do a const DB equals new Prisma client importing that from Prisma client and that's good and I'm just going to export this as our default so here in our home page I'm going to do fonts posts equals DB pulling from that new module we just created and post dot find mini and we'll order that by created at descending now this is an async function so I'm going to do an await here which means we also need to make this home component in async function which may not be something we use two or three at component but we can do in server components land now that we have a post array I can use this to map through our posts and display those on screen so we'll just create divs and just do a post dot content and display that on screen now anytime We're looping of course we do need to provide a key so we'll do key which is our post ID all right let's clean up Imports it's angry at something ah missing a parenthesis again now if I go over to our page it's going to be blank of course why because we don't have any posts so let's also create a button here that will generate some posts for us so generate posts now we have a nice little button so how would we generate hosts let's use a server action for this now this is still in beta as of this recording so we need to enable this in next do next server actions if we go to the docs here for Server actions we can see that a server action is something we specify as use server either within the function itself or at the top of the file of a function now in order to do this like I said it is uh not just beta they call experimental so we do need to add this to our next config let's open up our next config go to experimental do server actions true and there we go let's create a function called generate posts this will be an async function we're going to put at the top of this use server to designate that this is a server action it will be triggered by the client so a user is going to hit this generate post button but then magically it's going to go back up to the server so we'll do a DB post dot create many that sounds good let's say we create three posts out of this so we're going to do data and we're going to create a few posts now we could do some hello worlds here but we could also use some fake data so let's uh pull in the faker package and actually fake some data out here we can head on over to bakerjs.dev here which is a nice uh fake data utility and let's go to let's go to API so we've got API I'm sure there's a sentence yes lorm we've got a lorem sentence here we go so we can do lorem sentence and create some fake data to install Faker we'll do a pnpm ad Faker JS slash Faker we're not going to do save Dev because we're actually going to use this in production right now so we'll do PNP Meen and all of the stuff okay that package is installed and ready to use I'm going to do an array here and we'll do content but instead of just hard coding some content we'll call faker do lorem and oh they even have a paragraph option here but we'll stick with a sentence and let's do this a few different times to generate several posts so this is our server action here it will create these posts now we also need to like somehow refetch the data for the client after this action in order to show that on the screen for that we can use a handy function that next provides called revalidate and I'm just going to say revalidate the path of the home page because this is our home page here I don't need to store posts in a variable even so I'm going to delete that now I need to actually call this function from the button that we have on the page so let me get started on this but we're going to run into a roadblock here normally I would just say on click and pass in our function here to generate posts let's save and see what happens we do in fact have our button but when we click on it we get an error and the error is maximum call stack exceeded this is something that you might see right now as react server components and server actions really are new and part of the trick is is that we're really blurring the lines here between what is a server thing and what is a client thing and in this case we're really mixing a client action which is the click of this button uh with our overall component which is a server component so we can fix this issue by extracting what should be on our client and our client only I'm going to go into source and go to components and make a button component so let me pull that to the side and really I want to move this button to the client so I'm just going to stub out a component here we're going to call this button we want it to accept uh the props that a button would normally take so there's a handy little thing called component uh props without ref and we will take button so that will just give us the props we need for a button I'm going to take those in pass those into a button component and there we go we have a button that just passes things to a button now most importantly this is a client component so I'm going to designate that with use client above over in our server component here on the left instead of using a regular button HTML button I'm going to use a mandated client component so I'm going to use our new button component that we've created here and also in well let's save that and see how far that gets us so I'm going to save refresh click this and again we still have maximum call stack size exceeded uh we have a couple of other layers we need to put in here and that is if I just called this then we have things we're passing back and forth anytime you do event triggers like an on click you're passing all sorts of event information and the thing between server actions and client components is it needs to be serializable but serializable between the client and the server so I want to really remove any sort of arguments or even return values in this case that we're using so I'm going to go ahead and just wrap this in a generic function call we're not returning anything we're not passing anything we're not returning anything here so again let's save this and let's see how far we get here we go okay now we're getting an error message that halfway makes sense event handlers cannot be passed to client component props if you need interactivity consider converting part of this to a client component well we're already trying to do that and again I am by no means an an expert at the client server boundary and division yet still working on that myself but one thing I know we can do is uh adjust how we are handling the on click actions so I'm going to adjust our props here and I'm going to say that the props hour button is going to accept we're going to override what would normally be an on click and this is going to say that we'll accept a function that with no arguments that returns a promise a void or just void itself so this way we're not accepting anything we're not returning anything so I'm going to say prompts and then we will override what is passed into on click we will pull out our on click from our prompts and we want to call this on click passing nothing and returning nothing so we're going to simplify this over here and call our on click ah if it exists now back over in our server component we can simply just pass generate posts and cross your fingers and I think we've done the proper divisions here let me save refresh click and there we go our generate post button is sending that click back to the server running our server action populating a few fake posts revalidating that the path which is then causing this component to refetch its data and send it back into the client all very quickly so there's a little bit of setup here and again I'm really still also trying to establish the Baseline principles for myself here of of how really the server actions and client components communicate and when do you have to do this split but this setup here is working for us now and working for our use case which is back to the database Okay so we've got our Prisma client setup we can fetch data from the database even create data we've got our connects migrations setup we've got a simple page here set up with a button and post generation so really what we have here is an application running that proves we can connect to a database write and fetch some data which is what we need here in this starter kit now that this is running locally it's a good time to make a commit so I'm going to go in here and say we did enable server actions we added Faker we updated our page we've got a client button and we've got a Prisma client so okay so add page to read and generate posts okay so we're running in development now we want to push this to production and specifically want to push it to versel and use Purcell postgres over in the versel docs they have docs for their storage for sale postgres and using an orm in here they're providing us a recommended setup for Prisma in versa postgres so I'm going to copy this in to our code so we'll go to schema.prisma and replace what we have here now one thing this did do is it did replace our environment variable name for just the standard URL which was used to be called database URL so I'm going to go match this in our DOT emv.development file so that we can have the same database variable name in development as production let's restart our Dev server and make sure that we can still connect locally ah we did get an error and that's because after changing the setting we need to regenerate our Prisma client so we'll run a pnpm Prisma generate with that generated another refresh and perhaps another server restart we're good to go again our data's loading and we can click the button to generate more posts now normally when we deploy to production uh what I would do is anytime we need to run migrations SSH into an instance and run migrations with for cell there really isn't an ssh in into an instance so we want to run our migrations as a part of the build step of deploying to versel and really we need to generate our Prisma client as well so there is actually a guide on the Prisma in the Prisma docs called deploying to versl and it walks through some of the things that you would need to know for deploying to versl specifically they have an example repo which I've gone to here and gone to the package.json and I can see that in order to accomplish this they're adding a script called versel build and inside of that instead of just building next they're doing imprisma generate and a Prisma migrate deploy because they're using Prisma migrations and a next build so we're going to utilize this or a flavor of this for our use case for deploying to Purcell so let's go into our own package.json and I'm going to add this script as well but we're in our case we're not using prism migrate so instead here let me do a pnpm DB migrate and that will run our migration script above so we've got Prisma generate pnpmdb migrate and next build now that I think about that too we did change our environment variable name over to this postgres Prisma URL I need to do that for the case of next so connects so let me go into our connects file and change our connection string to reference this postgres Prisma URL now we're ready to push this up to versel and see if it works you can head on over to your versel dashboard click on add new and project for this it really wants a GitHub repository so I'm going to go ahead and do that and come back okay I have my GitHub repository called it next starter kit I'll hit import and We're Off to the Races we did get an error and that's because we haven't set up the database yet so let's go to the project page from here we can go to storage and I do want to create a postgres database so we'll create new postgres and continue we'll leave the name mostly at is but I am going to put a prod in it for production and create and continue and connect now we have a database let's go back over to deployments we have an error here let's try redeploying during the build it tried to run the migrations and we got an error saying connection is insecure try using SSL mode require if you go back over to the storage tab for this project you'll see that under the PG tab here which is the package that connects is using to connect postgres they have an example of appending the SSL mode equals require to the end of the database connection string so let's do that in our code now this is a little tricky because if we if we do this and then run our migrations locally we'll get a failure because we're not using SSL in development so we just need to use this in production or perhaps a better way to do it is if we're in development we don't want to use SSL let's do that so we'll say that process.m dot node in equals development then we'll append nothing otherwise for all other cases including production we'll append the SSL mode equals require now we just need to make sure that in development mode we can say our node m equals development we can try running our migrations and it succeeds let's Commit This up and try our versel deploy again ah something I didn't notice the first time is that this is postgres URL not postgres Prisma URL so over in our code we need to make sure that we're only referencing postgres URL which also means in our in in dot development file we need to also have a postgres URL okay save all this up and commit and we'll try it again there we go this time our migrations did run successfully our build has finished and we can now visit the page it's loading cross your fingers and clicking the generate post button does in fact generate host so there you have it that's what you need to scaffold out in xjs project using Prisma for your orm connects for migrations Docker and postgres locally and versel and postgres in production I hope this video was helpful for you I am super interested in the power of nexjs with app router and react server components so I think I'm going to keep creating videos on these topics if you want to follow those videos subscribe to my channel if you've been a viewer all along thanks for watching and I'll see you next time
Info
Channel: Kevin Wade
Views: 10,708
Rating: undefined out of 5
Keywords: next.js, javascript, typescript, postgres, prisma, vercel, next, docker, docker compose, react, react.js, rsc
Id: 6UYkcOQUcZc
Channel Id: undefined
Length: 36min 24sec (2184 seconds)
Published: Sat Jun 10 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.