Type safe your entire node project with Typescript, Express, and Prisma ORM
Tags: node, prisma, typescript, tutorial
Published: 2023-03-10 11:03:20

We know that without proper knowledge and use of typescript it can circle back to your old JS code, which can become even more difficult to debug and develop.
So in this tutorial, We will create a simple typescript project with Express and Prisma that will use types from a single source of truth, which would be the database schema. So that we can avoid creating bad TS code.


![Typescript meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7yb0v95vw3o3lyx6ptt.jpg)

We will be making a simple express project with REST endpoints for recording Owner to their dogs, endpoints would look like:

```jsx
Owner Endpoints:
[GET]    /api/owners
[GET]    /api/owners/:id
[GET]    /api/owners/:id/dogs
[POST]   /api/owners
[PUT]    /api/owners/:id
[DELETE] /api/owners/:id

Dog Endpoints:
[GET]    /api/dogs
[GET]    /api/dogs/:id
[POST]   /api/dogs/
[PUT]    /api/dogs/:id
[DELETE] /api/dogs/:id
```
Link to full github repo: [express_ts](https://github.com/Aditya201551/express_ts)

## Project Setup

Lets start with creating a work folder named [**_express_ts_**](https://github.com/Aditya201551/express_ts) and initializing it as a node project

```jsx
> mkdir express_ts
> npm init -y
```

Lets install required dependencies for this project

```jsx
//Dev dependencies 
> npm i -D typescript @types/express @types/node nodemon prisma ts-node uuid

//Dependencies 
> npm i @prisma/client dotenv express yup
```
Now lets configure the project as typescript project by:
```jsx
npx tsc -init
```
This would generate a file named `tsconfig.json` that would look like this:
```ts
//tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist"
  },
  "lib": ["es2015"]
}
```
The above file simply tells the typescript compiler about the metadata of the project and rules that will be used to compile the project. [Know more about tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)

Add the below code in your `"scripts"` property in **package.json** file
```ts
{
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "npm run build && cp .env ./dist/.env && cd dist/ && node index.js",
    "db:pull": "npx prisma db pull",
    "build": "npx prisma generate && npx tsc",
    "dev": "npx prisma generate && npx nodemon server/index.ts"
  }
```
this will help you to run your project without typing extra lines in CLI!

Create `nodemon.json` file in your root folder and add these code into the file:
```ts
{
  "watch": ["server"],
  "ext": "js ts"
}
```
Great! now you dont have to restart your server everytime you make any changes.

You can view [package.json here](https://github.com/Aditya201551/express_ts/blob/main/package.json) to know more about the project itself.

I would assume you have setup mysql or any database you prefer, here is how you can setup mysql and mongodb if you haven't

- [Mysql](https://dev.mysql.com/doc/mysql-getting-started/en/)
- [Mongodb](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-windows/), or you can use mongodb atlas!

Now we need to setup **Prisma** for this project
```jsx
npx prisma init
```
Create a `.env` file in your root folder to store sensetive credentials, like server `PORT`, `database connection URL`, etc.

Refer here on how to connect your database with prisma, to make this tutorial brief:
- [Connect you database with prisma](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/connect-your-database-typescript-mysql)

This would create a folder named **prisma** in your root folder contaning file named **schema.prisma**.

**schema.prisma** would contain database schema in prisma language that would be used for:

- Generating types from database schema
- Allow prisma client to create database query in JS format(without manually writing database query)

Assuming you have already created your database tables, you need to import you schema data, to do that we need to run below command:
```jsx
npx prisma db pull
```
Depending on what you database schema is, your schema.prisma file would change accordingly, here is what my schema.prisma looks like:

**[Schame.prisma](https://github.com/Aditya201551/express_ts/blob/main/prisma/schema.prisma)**

##Setting up **server** folder
create a folder named `server` with these files inside of it:

- [index.ts](https://github.com/Aditya201551/express_ts/blob/main/server/index.ts) where our main app will run
- [prisma-client.ts](https://github.com/Aditya201551/express_ts/blob/main/server/prisma-client.ts) for using prisma client in a singleton pattern
- [server-config.ts](https://github.com/Aditya201551/express_ts/blob/main/server/server-config.ts) for getting data from `.env` to your project

Click on above links to view the content of the files.

This is how the final file structure of the project would look like:

![file structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bnj24jx2hhida5zf3svk.png)

## API development

Awesome! Now we are done setting up our project, now its time to start cooking our api!

![Hehe boiii](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1mflwiu923v8edsufzm.gif)

## Model Function

We will be creating prisma functions for owner table to perform CRUD operation on the table for the application

```ts
//owner.model.ts

import { prisma } from "../prisma-client";
import { owner, Prisma } from "@prisma/client";

const getOwner = async (owner_id: owner["owner_id"]) => {
  const owner = await prisma.owner.findUnique({
    where: {
      owner_id,
    },
  });

  return owner;
};
```
As we can see `owner_id` does not have type written manually, instead it is derived from prisma's database schema of owner table.

Also the return type of the above function is of type `owner` which is created by prisma from the databse schema.
>
Here we go! now we dont have to go through the process of creating types for every entity, we can now just import them from **prisma** itself, which makes the development experiance much easier and faster!

[See the rest of the model function: **owner.model.ts**](https://github.com/Aditya201551/express_ts/blob/main/server/owner/owner.model.ts)

## Typesafe Routes
```ts
app.get<Params,ResBody,ReqBody,ReqQuery,Locals>('/api/v1/dogs',(req,res) => {})
```
In every HTTP method the router provides 5 [generic type](https://www.digitalocean.com/community/tutorials/typescript-generics-in-typescript) which can take custom types as input for type safing routes of the app.

Here is how we can type safe our **owner.routes.ts**:
```ts
//owner.routes.ts

import { Prisma, owner } from "@prisma/client";
ownerRouter.get<never, owner[]>("/", async (req, res, next) => {
//your code implementation
});
```
Lets understand the above router, we can see that in generic params we have written:
- `never`: this specifies that request `params` will be an empty input(specified by never keyword)
- `owner`: this specifies that `response` type of the request will be of type `owner`, which is again derived by prisma type

Now lets understand a bit complex route:
```ts
//This route is used to update owner model(PUT request)

ownerRouter.put<
  { owner_id: owner["owner_id"] },
  owner,
  Prisma.ownerUncheckedUpdateInput
>("/:owner_id", async (req, res, next) => {
//your code implementation
});
```
The above route takes the following generic types:
- `params`: object containing `owner_id` of type `owner["owner_id"]`
- `response`: owner object
- `request body`: It takes a prisma's custom type for update input

Great, now we have learned to typesafe our code with database schema, well obviously to create a production code we need extra types like `IBaseApiResponse`, `IPagination`, request validation  etc, but that's for another article.

View complete project [here](https://github.com/Aditya201551/express_ts).

Hope this tutorial helped, thank you so much!

![Thank You](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/puvi1rbl4hm4lsgya19t.gif)