Generate TypeScript Definitions from PostgreSQL Schema with Supabase CLI

Jon Meyers
InstructorJon Meyers
Share this video with your friends

Social Share Links

Send Tweet

Typescript reduces runtime errors and makes our code more maintainable. Supabase allows us to introspect our PostgreSQL schema and generate TypeScript definitions.

In this lesson we generate an access token to use the Supabase CLI and generate TypeScript definitions. Additionally, we write our types to a file, which can be passed to our Supabase client, giving our application full type-safety across client, server and database.

Lastly, we make our Database types globally available so we don't need to import them every time we create a Supabase client.

Code Snippets

Login to Supabase CLI

npx supabase login

Generate TS definitions file

npx supabase gen types typescript --project-id your-project-id > lib/database.types.ts

Resources

Instructor: Our Next.js application is a TypeScript project. When we're doing generic things with supabase, we have some type safety. We know this getSession function is going to give us back either the user's session or null.

But when we're doing things that are unique to the structure of our supabase project, like selecting these tweets from the tweets table, you'll see supabase is doing its best, but it doesn't know the shape of that tweet that's going to come back.

Let's generate some TypeScript definitions based on our database's schema. We can do that by using npx to run the supabase CLI. The first thing we want to do is log in. This is asking us for an access token. We can go to this URL to generate one. I'm going to generate a new token.

The name for this one is going to be blue-bird. Again, this can be anything you'd like. Let's generate that token. Here's our new access token. Let's go back over to the terminal and paste that one in. You should see, Finished supabase login. Now, the supabase CLI has the same permissions that we do when we're signed into the Supabase dashboard.

If we want to generate TypeScript types for one of our projects, we can run npx supabase gen, or generate, types typescript, and then give it a project-id. We can get that from the Supabase dashboard for our project by going to Settings, and then under General settings, we have Reference ID. Let's copy this and paste it into our command.

Then if we run this, we're going to see all of the types for our project dumped out into the console. You can see we have Tables, tweets, and then each of these rows represent one of our tweets from the database. This doesn't help us, dumped out in the console, we probably want to save this to a file.

If we run that same command, we can then use > to write the output to a file. The file we want to write this to is lib/database.types.ts. If we run this, we're going to see, no such file or directory exists. We need to create this lib folder. We can run mkdir, or make directory, and then lib.

Now, when we run our command to generate our types and go back over to our project, we'll see we have this new lib folder with our database.types.ts file, which has all of the information about our database schema. Now we need to tell our SupabaseClient about our Database types.

If we scroll up to where we're creating our server component client, we can then pass this, our Database type, which we can import from @/lib/database.types. Now our SupabaseClient is correctly typed. If we scroll down to our tweets variable, we can see it's going to be an array of this specific type or null.

We have several instances throughout our application where we're creating a SupabaseClient. We don't want to have to import this file every single time. Let's declare this Database type as a global type so it's available throughout our application.

Let's start by creating a new file in the app directory called global.d.ts. Then we can move that import statement from page.tsx into our global types file. We can then declare our global types, which is an object, where we can say we want the type of Database to be equal to our Database type that we imported above.

Now, annoyingly, this doesn't work if the name of the type is the same on either side of the equal sign. We can use an alias to import Database as DB, and then we can declare a global type for Database = DB. Now, if we go back to page.tsx, we can see that even though we've removed that import statement, our SupabaseClient is still correctly typed.

If we scroll down to tweets, our Database types are correctly flowing through. Now, as mentioned before, we do have several places where we're creating SupabaseClients, but we can do a find and replace for anywhere. We're saying Client(, with a capital C, and then an open parenthesis.

You'll see each of these are us creating a SupabaseClient except for this first one, because we called this component AuthButtonClient(. We can right-click this one, and dismiss, and then confirm that each of these are us creating a SupabaseClient. We can then click this arrow to add a replace.

That's going to be Client, with a capital C, and then our <Database>( types. Make sure we have that open parenthesis, and then we can replace all occurrences. Yes, we want to do that. Now we have a properly typed SupabaseClient throughout our entire application.