
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
👉 Bunny (create your free account and get one extra free month): https://jsm.dev/jsm-bunny
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, the presenter guides viewers through setting up a Postgres database using Zeta, integrating BetterAuth for authentication. This approach deviates from the typical MongoDB setup to accommodate popular requests. Steps include signing up for Zeta, creating a database, managing API keys, and connecting Drizzle ORM with TypeScript to facilitate the setup.
drizzle-ORM
, drizzle-kit
, and pg
is outlined.00:00:02 In this lesson, let's set up our database.
00:00:05 This time, I wanted to do something different and not just do a typical MongoDB setup that we always do.
00:00:12 Not that there's anything wrong with it, it's just that I wanted to do what you guys requested me to do.
00:00:17 For example, BetterAuth has been one of the highly requested authentication solutions.
00:00:23 And so is Postgres.
00:00:25 And in this case, I decided to use a Postgres platform called Zeta, which is completely free to get started with.
00:00:32 So just click the link down in the description and click start free and then sign up either using Google or GitHub.
00:00:39 Then you'll be able to see the onboarding where you can create your database.
00:00:43 Start with JSM underscore and then give it a name like snapcast and choose the location that is closest to you.
00:00:51 Click next and then you'll be redirected to the credentials page.
00:00:55 Here you can click generate API key and then simply download all of these credentials as a text file.
00:01:01 Then you can copy the contents of this credentials file, create a new comment called Zeta and then paste the contents you just copied.
00:01:09 Once you do that, you can continue to the next step It's going to ask you how you want to connect, so choose TypeScript with a Drizzle ORM through SQL.
00:01:19 Not Drizzle ORM HTTP, just choose SQL right here and click Continue.
00:01:24 Now, it'll give you instructions on how you can connect with Drizzle ORM in TypeScript, but this is using the Node Postgres documentation.
00:01:34 See, Node Postgres is not yet supported in the Edge runtime, with a stable release of the current Next.js version.
00:01:41 So if you want to use Node Postgres, you'll have to switch to the next cannery, which supports Next.js middleware.
00:01:48 Or, alternatively, we can use the Zeta adapter instead of Node Postgres, which is exactly what we'll do.
00:01:54 So I'll click Finish, and I'll teach you how we can set it up.
00:01:58 So, back within our code, now that we have the ENVs, open up your terminal, and run npm install –g, this is for global, at zeta.io forward slash cli.
00:02:12 And on macOS and Linux devices, I believe you have to add sudo in front to be able to install a package globally, and then you have to type in your password.
00:02:20 On Windows, I think you can just install it.
00:02:22 They don't really care.
00:02:24 Once the package is installed, you can run zeta auth login.
00:02:28 And it'll ask you whether you want to use an existing API key or create a new API key.
00:02:34 In this case, we can say use an existing API key.
00:02:37 And you can copy the one from our .env.
00:02:41 So this is the Zeta API key.
00:02:43 Just copy it.
00:02:45 and paste it right here in the terminal.
00:02:48 It's going to check it and it looks we're good.
00:02:51 So if that is the case, we can now run zeta init.
00:02:55 It's going to say initializing project.
00:02:57 Let me ask you a couple of questions.
00:02:59 Instead of creating a new database, I'll choose the one that we already created.
00:03:02 We're going to select TypeScript right here.
00:03:05 After that, just hit enter to accept the current output path for the generated code.
00:03:09 And it's going to create two new files.
00:03:12 So it'll say project is done.
00:03:15 And if you check out your file, you'll see that under source, you have zeta.ts as well as zeta.rc.
00:03:23 But we'll have to fix up a couple of things from this zeta.ts file.
00:03:26 Specifically, we need to add the API key and branch to the options.
00:03:31 So let's first look at the imports right here.
00:03:33 We just need to import the base client options type.
00:03:37 We don't need those tables, infer tables, and so on.
00:03:40 We just need to export a type of database schema.
00:03:44 And then right here, as the default options, where we have the database URL, I'll also provide the API key as the second parameter,
00:03:53 set it equal to process.env.zeta API key, as well as add the API key equal to process.env.zeta API key.
00:04:07 And we can also add the branch of main.
00:04:11 This is not referring to our code base branch, but rather to the branch of our database.
00:04:16 And at the end, we can remove the tables from here.
00:04:20 Now you'll notice that this Zeta file is within the source, but we need to have it within the root of our application.
00:04:27 So simply drag and drop it to the root and delete the source folder.
00:04:32 Now we have to hook it up with Drizzle.
00:04:35 Drizzle is an ORM.
00:04:37 object-relational mapper for Postgres databases.
00:04:41 And it works incredibly well with TypeScript and we can hook it up to any kind of a database super quickly.
00:04:48 So if you take a look, Zeta is right here in the list and we can just connect it very easily using their adapter.
00:04:55 So you can take a look at the documentation as to how to install it.
00:04:59 You can choose which Postgres provider you're using.
00:05:02 In this case, it's Zeta, and it's going to give you some tips on how to install it.
00:05:07 But in this case, I'll show you how to do it from scratch.
00:05:10 So let's clear up our terminal and run MPMI drizzle-ORM.
00:05:16 This will happen very quickly.
00:05:19 Next, we also have to install a Drizzle Kit as a dependency.
00:05:24 See, Drizzle Kit is a CLI migrator tool for Drizzle, one of the best tools that lets you completely generate SQL migrations and works super well.
00:05:35 So you can just type MPMI-D for development, and then we can say drizzle-kit.
00:05:45 We'll also need an additional package, which is Postgres, to just hook it up to our Postgres database.
00:05:50 So I'll say MPMI-PG.
00:05:52 Once that is done, we can create a new db.ts file inside of a drizzle folder.
00:06:00 So I'll head over to the root and I'll create a new folder called drizzle.
00:06:07 Within Drizzle, I'll create a new file and I'll call it db.ts.
00:06:12 And here we'll have to connect Drizzle and Zeta.
00:06:15 So I'll say const Zeta is equal to get Zeta client.
00:06:21 And then I'll say export const db is equal to Drizzle.
00:06:27 So you can import it by saying import.
00:06:31 drizzle within curly braces from drizzle-ORM forward slash zeta HTTP.
00:06:40 And then your database is basically just zeta wrapped with drizzle.
00:06:46 There we go.
00:06:47 And it looks like WebStorm thinks that zeta is not a real world, but it is our database.
00:06:52 So in this case, I'll just say, save it to dictionary.
00:06:57 Great.
00:06:58 So now that we have our database file, we also need to create a schema for future models.
00:07:04 So I'll create a new schema file right here within Drizzle called schema.ts, but I will leave it empty for now.
00:07:13 Later on, we'll add the authentication schemas here that'll be auto-generated by better auth.
00:07:19 But for the time being, we need to create the drizzle config file inside of the root of the project.
00:07:26 So I'll create a new file called drizzle.
00:07:32 And within here, we'll have to import our environment variables.
00:07:37 And for that, we can use a package called .env, which loads the environment variables from a .env file into process.env.
00:07:47 So I'll simply say mpmi.env.
00:07:53 And now at the top, we can import the config coming from .env and we can export default define config which we need to import from DrizzleKit.
00:08:07 So I'll say import define config coming from DrizzleKit.
00:08:13 We need to pass over an object for settings, and we can define where the schema is coming from.
00:08:20 It's coming from dot slash drizzle forward slash schema dot ts, the file we just created.
00:08:26 The out, so where do we spit out the migrations?
00:08:30 We can put them out into the dot slash drizzle forward slash migrations.
00:08:35 And finally, a dialect of our database.
00:08:38 In this case, it'll be a Postgres.
00:08:41 And DB credentials, which will be an object of URL process.env.database URL Postgres.
00:08:53 And since we know it's there, we can add an exclamation mark at the end.
00:08:57 But to be able to load this environment variable, we have to call the config from .env.
00:09:05 and pass to it a path to the ENV file.
00:09:08 So that's dot slash dot ENV.
00:09:11 And we have to do this to ensure that it'll be loaded properly.
00:09:15 Perfect.
00:09:16 Make sure that you don't have any typos here because that could mess up with our configuration of Drizzle and our database.
00:09:24 And now for the final steps.
00:09:25 We have to hook it all up together with BetterAuth.
00:09:29 So create a new folder within the root of your application and call it lib, as in library.
00:09:37 Next, within it, create a new file called auth.ts.
00:09:43 Here we can hook up Drizzle and BetterAuth.
00:09:48 as well as the database we just created.
00:09:50 So it all comes together into this single file.
00:09:53 Here, we need to export an instance of our authentication.
00:09:58 So I'll say export const auth is equal to better auth, coming from better auth, to which we provide options of database,
00:10:08 which we wrap with a drizzle adapter to which we pass the database coming from db and finally we provide an additional object of provider equal to pg.
00:10:22 So we let it know all of the options that we have chosen so far.
00:10:26 Now open up the terminal and run the command mpx at better-auth forward slash cli generate.
00:10:37 And this will generate the schema file, which will include the user, session, account, and verification schemas, all needed for authentication.
00:10:50 It will take some time.
00:10:51 Oh, but it looks like it failed right here.
00:10:53 It is possible that it didn't have enough access to write into this file.
00:10:59 So I will rerun this command, but I'll type sudo at the start and provide my password.
00:11:06 So let's see if we get lucky this time.
00:11:08 There we go.
00:11:08 It's asking us whether we want to generate the schema to Auth.ts file.
00:11:14 I'll say yes.
00:11:16 And you'll see that a new schema has been generated within Auth schema.ts.
00:11:22 This contains everything needed for the Auth.
00:11:24 So copy its contents and move it over to schema.ts within the drizzle folder.
00:11:31 This is a better place to keep it.
00:11:33 And once you move it over, you can even delete this current auth schema.
00:11:38 We want to work only within the one that is under drizzle schema.ts.
00:11:45 Once this is done, we want to push the database.
00:11:48 So I'll open up the terminal and say mpx drizzle-kit push.
00:11:54 This will say that either connection URL or host database are required for Postgres database connection.
00:12:01 It says no config path provided using the default drizzle config, and it was trying to access it from loom clone drizzle config.
00:12:10 Let's see if this is the right path.
00:12:13 It is right here under loom clone, drizzle.config.ts.
00:12:19 And yep, this is looking good to me.
00:12:22 So why was it not able to read the database URL?
00:12:26 We're calling it database URL Postgres.
00:12:29 And if you check it out right here under our ENV, you can also see that we have our Postgres URL.
00:12:35 I don't think we'll even need this regular database URL.
00:12:38 We just need to keep the Postgres one because that's the connection to our database.
00:12:43 And I think this looks okay.
00:12:45 Oh, but wait, this should say Postgres.
00:12:48 I have an extra R right here.
00:12:50 So now we're pointing to the right environment variable.
00:12:53 So with that in mind, let's run the mpx DrizzleKit push command, which will first pull the schema from the database.
00:13:00 And once it does that, we can now head over into the lib auth.ts file and we can edit it further by adding that newly added schema to our better auth setup.
00:13:12 So right after the provider, I will add a comma and also define the schema to be equal to schema.
00:13:20 This schema is coming from Drizzle schema so I'll import it at the top by saying import in curly braces schema from at forward slash Drizzle forward slash
00:13:32 schema and then we can just use it right here.
00:13:34 Now it does say here that a schema doesn't exist so if you head over into the schema file You'll notice that we are individually exporting all of these
00:13:44 different schemas.
00:13:45 The schema for the user, the session, the account, and the verification.
00:13:50 But what we can do at the end is just say export const schema, which is equal to an object, where we have all of these different types,
00:14:00 such as the user, account, session, and verification.
00:14:05 And now we can import it here and pass it as a part of Drizzle Adapter options.
00:14:09 Next, we can also add social providers.
00:14:13 In this case, that'll be equal to Google.
00:14:16 And here we need to pass the client ID equal to process.env.googleclientID.
00:14:24 as well as a client secret equal to process.env.google client secret.
00:14:32 Finally, outside of social providers, we can also provide some plugins.
00:14:37 So I'll say plugins and we'll add a plugin for next cookies, which will allow us to save our session into the cookies.
00:14:46 as well as a base URL, which will be equal to process.env.next public base URL.
00:14:55 And now we actually have to add this next public base URL.
00:14:59 So I'll say base URL.
00:15:03 at the top, next public base URL, it'll be equal to localhost 3000. And now that we have this auth setup done, we can also create a new auth client.
00:15:18 So right here within lib, I'll create a new file called auth-client.ts and this file is needed for OAuth logins, such as Google.
00:15:30 So I'll say export const auth client is equal to create auth client coming from better auth react.
00:15:41 And to it, I'll provide options of base URL is equal to process.env.next public base URL.
00:15:53 Finally, and most importantly, we have to create an API route that we're going to use for our authentication.
00:16:00 So, head over into App and create a new folder called API.
00:16:07 Within API, create another folder called Auth.
00:16:12 And within Auth, create another folder that will start and end with a square bracket.
00:16:19 And then within it, you'll say dot dot dot all.
00:16:24 This is the practice that we use with BetterAuth to implement authentication.
00:16:28 Within this folder, you can create a new route.ts.
00:16:33 And here we simply need to expose this route.
00:16:36 So I'll say export const, destructure the get and the post, and make it equal to to next.js handler.
00:16:47 to which you want to pass the auth coming from auth.ts.handler.
00:16:53 And this simply exposes that route, allowing us to authenticate our users.
00:16:57 We are now ready to implement the login functionality on the sign-in page.
00:17:02 So head over to app auth sign-in page and add the login function to the login button.
00:17:09 The login button is right here.
00:17:11 So simply give it an on click.
00:17:15 and on click call handle sign in and this one we can create just above by saying const handleSignIn is equal to an asynchronous function.
00:17:28 And don't forget, since we're using the onClick, we also have to turn this page into a client rendered page.
00:17:34 So I'll add useClient.
00:17:36 And within here, I'll simply say return await authClient coming from libauthClient.signIn.social.
00:17:47 And then we'll provide a provider of a string of Google.
00:17:52 This is it.
00:17:53 After we have implemented all of the logic, the rest is simple.
00:17:56 We just call this AuthClient sign-in social method.
00:17:59 Now, just before we test it out, there's one important thing to keep in mind.
00:18:03 And that is that right now, sure, we have redirected ourselves to the sign-in page, but if we try to go to the home page,
00:18:10 you can see that we can visit it.
00:18:12 And that should not be the case with this type of application.
00:18:16 To be able to visit a dashboard, you need to have an account.
00:18:19 So let's implement a piece of middleware that'll check if a user has been authenticated.
00:18:25 And if they're not, it's going to redirect them from the page they're trying to visit, but they don't have access to, to sign in.
00:18:31 So I'll create a new middleware.ts file within the root of our application.
00:18:39 And within it, I will export a new async function called middleware that'll accept a request of a type next request, as well as a response of a type next response.
00:18:54 And here we'll try to access the session by saying const session is equal to await auth coming from libauth.api dot get session and we'll pass the headers
00:19:08 right here equal to await headers and this is coming from next headers in order to be able to get the session next if there is no session in that case
00:19:20 we will simply return a next response dot redirect to a new url of forward slash sign dash in and we'll pass the request URL to it so that we know where
00:19:33 we're redirecting it from so that once they do sign in we can bring them back to that page but else if they have a session we'll simply allow them to pass
00:19:42 through by saying return next response dot next meaning continue with what you are trying to do and right after that we'll have to export a new config
00:19:53 for the middleware where we need to tell it for which routes do we want to actually turn it on.
00:19:59 We do that using a matcher array to which we can provide a string or a regular expression to match our routes.
00:20:06 And this is actually a perfect job for AI.
00:20:09 Actually, I used AI to generate this string, which matches different routes.
00:20:15 everything that starts with API, static pages, images, favicons, and the sign-in page, as well as the assets.
00:20:22 This ensures that the middleware is only applied to the relevant pages and not the API routes, static assets, or the sign-in page itself.
00:20:33 So now that you've implemented this, and just so you don't have to copy it manually, I will leave it within the video kit down below.
00:20:39 Once that is done, if you try to head over to the homepage, you see that you can no longer do it because you immediately get redirected back to the sign-in.
00:20:48 So what do you say that we finally test out our sign in with Google functionality?
00:20:53 And with that, we have a lot of stuff to test, not just the authentication and saving the user into the session, but actually creating the user within
00:21:01 the database.
00:21:03 So I'll click sign in with Google and I'll click continue.
00:21:06 If you do that, you can notice that we got successfully redirected back to the homepage.
00:21:11 And if our middleware is working and we just tested it and it is, that means that we have been successfully authenticated.
00:21:19 So now if you try to head over to sign in once again, sure you can do it and we can implement a redirect which brings you back to home since you don't
00:21:26 need to see the login page if you're already logged in.
00:21:29 But what matters most is that if you try to go to the home page, now the app knows that you're actually logged in.
00:21:36 This was a long one, I know, but there's a lot of stuff that we added.
00:21:40 From authentication to databases and more, all of it working seamlessly together.
00:21:46 So in the next lesson, it's time to put that database to the test and allow this new user to create and upload some videos within it.
00:21:53 So in the next lesson, we'll focus on upload a video form.