In applications, inconsistencies and unexpected errors can arise from type mismatches, especially in languages like JavaScript that lack static type checking. TypeScript mitigates this issue by identifying type errors during compilation rather than runtime.
However, TypeScript does not inherently produce type definitions from databases. This is where tools like Kanel become invaluable. Kanel empowers developers to generate TypeScript types directly from PostgreSQL databases.
This article explores using Kanel to generate reusable type definitions for both front-end and back-end applications. It starts with an explanation of Kanel's functionality and how it distinguishes itself from similar tools. Finally, it demonstrates creating a fundamental to-do list application utilising Vue.js, NestJS, Kanel, and Kysely.
Kanel Database-Driven Development
Kanel simplifies the process of generating TypeScript types by analysing the schema of your PostgreSQL database. It connects to your database, examines its structure, and creates TypeScript types that precisely reflect the tables, columns, and relationships within it. With Kanel, there's no need for a manual definition of TypeScript types for your database entities. This not only saves time but also reduces the likelihood of human error. Moreover, as your database schema evolves, you can rerun Kanel to automatically update the generated types, ensuring your TypeScript code stays aligned with the database structure.
Contemporary ORMs typically adopt a code-driven approach, wherein the ORM constructs a database schema based on the structure of the code. Examples include Prisma, a well-known ORM that exemplifies this methodology. However, database-driven development gains relevance in scenarios where multiple applications rely on a pre-existing database schema. In database-driven development, the database schema holds precedence as the main point of reference for the application. Consequently, there's no need to define anything beyond the types generated by Kanel for interacting with the database.
Prerequisites
Before diving into the tutorial, ensure you have the following prerequisites ready:
- Node.js: Verify that Node.js is installed on your system.
- pnpm: We'll utilise pnpm as this tutorial's package manager.
- Live PostgreSQL database: Ensure you have a PostgreSQL database set up and running with the required tables and columns already defined.
- Docker and Docker Compose (optional): If you prefer, Docker can run the database, providing a consistent environment for development and testing.
Once you've confirmed these prerequisites, you can install Kanel and start generating TypeScript types from your PostgreSQL database.
Kanel Configuration
- To set up Kanel and generate types, initiate a new Node.js project through the terminal using the command pnpm init. Follow the prompts to configure your project's
package.json
file. Subsequently, install Kanel by executingpnpm install kanel
- Once Kanel is installed, create a
.kanelrc.js
file in the root directory of your project. This file will contain the configuration settings for connecting to your PostgreSQL database. Here's an example configuration:
- Now, you can generate TypeScript types from your PostgreSQL database using Kanel. Navigate to the project's root directory and run the following command:
npx kanel
- Assuming your PostgreSQL database is active, this command will prompt Kanel to generate the types in the specified output directory as configured in your
.kanelrc.js
file.
Creating a Monorepo With A Basic Structure
Let's create a new monorepo with a basic structure for our to-do list application. We'll have separate directories for the back-end (NestJS) and front-end (Vue.js), and we'll integrate Kanel and Kysely into the back-end later. Here's a simplified version of how we can set up the structure:
In this structure:
- The
backend/
directory contains all the backend-related code for the NestJS application. - The
frontend/
the directory contains the Vue.js front-end code. - We have the necessary subdirectories and files to organise our code inside each directory.
- We'll later integrate Kanel and Kysely into the back-end by configuring
.kanelrc.js
and updating the code.
Building The Structure
- Begin by creating a fresh directory for your monorepo: use the command
mkdir todo-list-mono repo
. - Move into the newly created directory:
utilise cd todo-list-monorepo
. - Set up the required directories and files for the back-end code structure, including
src/
,.kanelrc.js
, etc. - Return to the root directory: move back with
cd
. - Proceed with setting up your
Vue.js
project by following the provided prompts. - Once the
Vue.js
project setup is complete, create the necessary directories and files for the front-end code structure, such assrc/
,public/
, and others. - Create the
front-end directory
and initialise aVue.js
project inside it:
mkdir frontend
cd frontend
vue create
- Create the
back-end directory
and initialise aNode.js
project inside it:
mkdir backend
cd backend
npm init -y
Once you've completed these steps, you'll have a basic monorepo structure for your to-do list application. You can continue integrating Kanel and Kysely into the back-end and developing the front-end components.
Creating A Shared Package for Kanel And Kysely
The next step is to create a shared package for Kanel and Kysely. This package will ensure compatibility between the front-end and back-end applications and contain the TypeScript types generated from your PostgreSQL database.
- To get started, create a new directory named
todo-shared
:
mkdir packages/todo-shared
- Navigate into the
todo-shared
directory and initialise a new Node.js project using.pnpm
:
cd packages/todo-shared
pnpm init
- Once the project is initialised, install the necessary dependencies:
pnpm i kysely && pnpm i -D kanel kanel-kysely typescript
- If you have Docker installed, proceed to create a
docker-compose.yml
File to set up a PostgreSQL database instance for the application:
- Finally, configure Kanel by creating a
.kanelrc.js
file in thetodo-shared
directory and specify the connection settings for your PostgreSQL database:
Don't forget to include kysely
as a pre-render hook since it will be used as the SQL query builder to access the database.
Once these configurations are in place, your shared package for Kanel and Kysely will be ready to use in both the back-end and front-end applications.
Table Config
- After configuring Kanel, create the necessary tables and schema in your PostgreSQL database using SQL queries. Below is the SQL code to create the tables:
Executing this command will produce a TypeScript schema by analysing the structure of your PostgreSQL database. The resulting schema will be stored in the designated directory, ./src/schemas
, as outlined in your .kanelrc.js
setup.
npx kanel
This command will generate the TypeScript schema based on the database structure defined in your PostgreSQL instance and save it in the ./src/schemas
folder, as specified in your .kanelrc.js
configuration file.
- Once the TypeScript schema is generated, compile your application by running
tsc
to enable other packages within your workspace to import and use the generated TypeScript types from your projects. - To integrate the generated TypeScript types into your back-end API, import them from the
todo-shared
project. Run the following command in the root of thetodo-backend
directory to import the package:
pnpm add todo-shared@workspace:*
- After importing the package, create a new file named
database.ts
inside thesrc
folder of your back-end project. This file will establish the connection to the PostgreSQL database using Kysely:
This database.ts
file initialises the Kysely connection to the PostgreSQL database using the specified credentials.
- Next, you'll create service and controller files for handling user, project, and to-do endpoints. Below is an example of the
UserService
andUserController
:
These files handle the logic related to user operations and expose corresponding API endpoints.
- Similarly, you'll create service and controller files for projects and to-dos and then add them to the
AppModule
. Additionally, enable CORS inside themain.ts
file. - To test your app, open two terminals from the root of the monorepo and run the following commands separately:
pnpm run backend start:dev
pnpm run frontend serve
- This setup allows you to access the front-end app on your local machine by navigating to
http://localhost:8080
in a web browser.
Thank you for reading this article. Please consider subscribing if you liked it.