UPDATES

Node.js and TypeScript Tutorial: Construct a relaxation API with Typescript, NodeJS, and a file-based storage system.


Introduction

Welcome to my weblog! On this tutorial, I’ll information you thru the method of constructing a sturdy micro e-commerce API utilizing Node.js, Categorical, and TypeScript. Collectively, we’ll discover numerous options and strategies that may empower you to create a strong API on your e-commerce functions.

One in every of our key selections on this challenge was to implement a file-based storage system as an alternative of counting on conventional databases like MongoDB. This strategy provides simplicity and ease of implementation, making it perfect for smaller-scale functions or situations the place a full-fledged database administration system could also be pointless.

The tutorial will cowl important matters reminiscent of person administration, product dealing with, and authentication.

You may acquire hands-on expertise working with options that span each person and product information, demonstrating how these entities work together inside an e-commerce API. By the tip of this tutorial, you will have a complete understanding of constructing a strong API that permits seamless interactions with person and product assets.

So, be part of me on this thrilling journey as we dive into making a micro e-commerce API utilizing Node.js, Categorical, and TypeScript.

Get Began with TypeScript in Node.js
Begin by making a challenge listing that appears like this.

Image description

Subsequent, initialize a Node.js challenge inside the challenge listing by making a package deal.json file with default settings, utilizing this command :

npm init -y

Set up Mission Dependencies

Your Node.js challenge requires a few dependencies to create a safe Categorical server with TypeScript. Set up them like so:

npm i categorical dotenv helmet cors http-status-codes uuid bcryptjs
To make use of TypeScript, you additionally want to put in a secure model of typescript as a developer dependency:

npm i -D typescript

To make use of TypeScript successfully, you could set up kind definitions for the packages you put in beforehand:

npm i -D @varieties/categorical @varieties/dotenv @varieties/helmet @varieties/cors @varieties/http-status-codes @varieties/uuid @varieties/bcryptjs
Enter fullscreen mode

Exit fullscreen mode

Populate the .env hidden file with the next variable that defines the port your server can use to pay attention for requests:

PORT=7000
Enter fullscreen mode

Exit fullscreen mode

Subsequent, find the app.js file within the root of the src folder and import the challenge dependencies you put in earlier and cargo any environmental variables from the native .env file utilizing the dotenv.config() methodology:

import categorical from "categorical"
import * as dotevnv from "dotenv"
import cors from "cors"
import helmet from "helmet"

dotevnv.config()

if (!course of.env.PORT) {
    console.log(`No port worth specified...`)
}

const PORT = parseInt(course of.env.PORT as string, 10)

const app = categorical()

app.use(categorical.json())
app.use(categorical.urlencoded({prolonged : true}))
app.use(cors())
app.use(helmet())

app.pay attention(PORT, () => {
    console.log(`Server is listening on port ${PORT}`)
})
Enter fullscreen mode

Exit fullscreen mode

On this code snippet, a Node.js utility is being arrange utilizing the Categorical framework. This is a breakdown of what is occurring:

The required modules are imported:

categorical is imported as the primary framework for constructing the net utility.

dotenv is imported to deal with surroundings variables.

cors is imported to allow Cross-Origin Useful resource Sharing.

helmet is imported so as to add safety headers to HTTP responses.

The code checks if the PORT surroundings variable is outlined. If not, a message is logged to the console.

The PORT variable is parsed from a string to an integer utilizing parseInt().

An occasion of the Categorical utility is created utilizing categorical() and assigned to the app variable.

Middleware capabilities are added to the Categorical utility:

categorical.json() is used to parse JSON our bodies of incoming requests.

categorical.urlencoded({prolonged : true}) is used to parse URL-encoded our bodies of incoming requests.

cors() is used to allow Cross-Origin Useful resource Sharing.

helmet() is used to reinforce the safety of the appliance by setting numerous HTTP headers.

The Categorical utility begins listening on the desired PORT by calling app.pay attention(). As soon as the server is working, a message indicating the port quantity is logged to the console.

Enhance TypeScript Improvement Workflow

The TypeScript compilation course of can enhance the bootstrapping time of an utility. Nonetheless, you need not recompile the whole challenge at any time when there is a change in its supply code. You may arrange ts-node-dev to considerably lower the time it takes to restart your utility while you make a change.

Begin by putting in this package deal to energy up your improvement workflow:

npm i -D ts-node-dev

ts-node-dev restarts a goal Node.js course of when any of the required information change. Nonetheless, it shares the Typescript compilation course of between restarts, which may considerably enhance the restart pace.

You may create a dev npm script in package deal.json to run your server. Replace your package deal.json file like this.

{
"identify": "typescript-nodejs",
"model": "1.0.0",
"description": "",
"predominant": "index.js",
"scripts": {
"take a look at": "echo "Error: no take a look at specified" && exit 1",
"dev": "ts-node-dev --pretty --respawn ./src/app.ts"
},
"key phrases": [],
"writer": "",
"license": "ISC",
"dependencies": {
"@varieties/nanoid": "^3.0.0",
"@varieties/uuid": "^9.0.2",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.3.0",
"categorical": "^4.18.2",
"helmet": "^7.0.0",
"http-status-codes": "^2.2.0",
"nanoid": "^4.0.2",
"uuid": "^9.0.0"
},
"devDependencies": {
"@varieties/bcryptjs": "^2.4.2",
"@varieties/cors": "^2.8.13",
"@varieties/dotenv": "^8.2.0",
"@varieties/categorical": "^4.17.17",
"@varieties/helmet": "^4.0.0",
"@varieties/http-status-codes": "^1.2.0",
"ts-node-dev": "^2.0.0"
}
}

Let’s briefly break down the choices that ts-node-dev takes:

–respawn: Hold waiting for adjustments after the script has exited.

–pretty: Use fairly diagnostic formatter (TS_NODE_PRETTY).

./src/app.ts: That is the appliance’s entry file.

Now, merely run the dev script to launch your challenge:

npm run dev

If every little thing is working accurately, you will see a message indicating that the server is listening for requests on port 7000.

Mannequin Information with TypeScript Interfaces

Earlier than creating any routes, outline the construction of the information you wish to handle. Our person database could have the next properties:

id : (string) Distinctive identifier for the merchandise file.
username : (string) Identify of the merchandise.
e mail : (quantity) Worth of the merchandise in cents.
password : (string) Description of the merchandise.

Populate src/customers/person.interface.ts with the next definition:

export interface Consumer {
    username : string,
    e mail : string,
    password : string
}

export interface UnitUser extends Consumer {
    id : string
}

export interface Customers {
    [key : string] : UnitUser
}
Enter fullscreen mode

Exit fullscreen mode

This code defines three TypeScript interfaces:

  • The Consumer interface represents a fundamental person object with three properties:

username, which is a string representing the username of the person.
e mail, which is a string representing the e-mail tackle of the person.
password, which is a string representing the password of the person.

  • The UnitUser interface extends the Consumer interface and provides an id property:

id, which is a string representing the distinctive identifier of the person.

  • The Customers interface represents a set of person objects with dynamic keys:

[key: string] signifies that the keys of the Customers object may be any string.
The values of the Customers object are of kind UnitUser, which implies every person object within the assortment ought to conform to the UnitUser interface.
In less complicated phrases, these interfaces outline the construction and forms of person objects. The Consumer interface defines the essential properties of a person, whereas the UnitUser interface provides an id property to characterize a person with a novel identifier. The Customers interface represents a set of person objects, the place the keys are strings and the values are UnitUser objects.

Subsequent, we’ll create the logic for our information storage. you may name it a database for those who like.
Populate src/customers/person.database.ts with the next code:

import { Consumer, UnitUser, Customers } from "./person.interface";
import bcrypt from "bcryptjs"
import {v4 as random} from "uuid"
import fs from "fs"

let customers: Customers = loadUsers() 

operate loadUsers () : Customers {
  strive {
    const information = fs.readFileSync("./customers.json", "utf-8")
    return JSON.parse(information)
  } catch (error) {
    console.log(`Error ${error}`)
    return {}
  }
}

operate saveUsers () {
  strive {
    fs.writeFileSync("./customers.json", JSON.stringify(customers), "utf-8")
    console.log(`Consumer saved efficiently!`)
  } catch (error) {
    console.log(`Error : ${error}`)
  }
}

export const findAll = async (): Promise<UnitUser[]> => Object.values(customers);

export const findOne = async (id: string): Promise<UnitUser> => customers[id];

export const create = async (userData: UnitUser): Promise<UnitUser | null> => {

  let id = random()

  let check_user = await findOne(id);

  whereas (check_user) {
    id = random()
    check_user = await findOne(id)
  }

  const salt = await bcrypt.genSalt(10);

  const hashedPassword = await bcrypt.hash(userData.password, salt);

  const person : UnitUser = {
    id : id,
    username : userData.username,
    e mail : userData.e mail,
    password: hashedPassword
  };

  customers[id] = person;

  saveUsers()

  return person;
};

export const findByEmail = async (user_email: string): Promise<null | UnitUser> => {

  const allUsers = await findAll();

  const getUser = allUsers.discover(end result => user_email === end result.e mail);

  if (!getUser) {
    return null;
  }

  return getUser;
};

export const comparePassword  = async (e mail : string, supplied_password : string) : Promise<null | UnitUser> => {

    const person = await findByEmail(e mail)

    const decryptPassword = await bcrypt.evaluate(supplied_password, person!.password)

    if (!decryptPassword) {
        return null
    }

    return person
}

export const replace = async (id : string, updateValues : Consumer) : Promise<UnitUser | null> => {

    const userExists = await findOne(id)

    if (!userExists) {
        return null
    }

    if(updateValues.password) {
        const salt = await bcrypt.genSalt(10)
        const newPass = await bcrypt.hash(updateValues.password, salt)

        updateValues.password = newPass
    }

    customers[id] = {
        ...userExists,
        ...updateValues
    }

    saveUsers()

    return customers[id]
}

export const take away = async (id : string) : Promise<null | void> => {

    const person = await findOne(id)

    if (!person) {
        return null
    }

    delete customers[id]

    saveUsers()
}


Enter fullscreen mode

Exit fullscreen mode

Let me clarify each operate within the code above :

loadUsers: This operate reads the information from a file known as “customers.json” utilizing the fs module. It makes an attempt to parse the information as JSON and returns it because the customers object. If an error happens throughout the course of, it logs the error and returns an empty object.

saveUsers: This operate saves the customers object to the “customers.json” file by writing the JSON string illustration of the customers object utilizing the fs module’s writeFileSync methodology. If an error happens throughout the course of, it logs the error.

findAll: This operate returns a promise that resolves to an array of UnitUser objects. It makes use of Object.values(customers) to extract the values (customers) from the customers object.

findOne: This operate takes an id parameter and returns a promise that resolves to the UnitUser object equivalent to that id within the customers object.

create: This operate takes a userData object as enter and returns a promise that resolves to the newly created UnitUser object. It generates a random id utilizing the uuid package deal and checks if a person with that id already exists. If a person with that id exists, it generates a brand new id till a novel one is discovered. It then hashes the userData object’s password utilizing bcrypt and saves the hashed password within the UnitUser object. The UnitUser object is added to the customers object, saved utilizing saveUsers, and returned.

findByEmail: This operate takes a user_email parameter and returns a promise that resolves to a UnitUser object if a person with the desired e mail exists, or null in any other case. It retrieves all customers utilizing findAll and finds the person with the matching e mail utilizing the discover methodology.

comparePassword: This operate takes an e mail and supplied_password as parameters and returns a promise that resolves to a UnitUser object if the provided password matches the person’s saved password, or null in any other case. It calls findByEmail to retrieve the person by e mail after which makes use of bcrypt.evaluate to match the hashed saved password with the provided password.

replace: This operate takes an id and updateValues as parameters and returns a promise that resolves to the up to date UnitUser object if the person with the desired id exists. It checks if the person exists utilizing findOne and updates the person’s password if updateValues comprises a brand new password. The person’s properties are up to date with the values from updateValues, and the customers object is saved utilizing saveUsers.

take away: This operate takes an id parameter and returns a promise that resolves to null if the person with the desired id would not exist, or void in any other case. It makes use of findOne to verify if the person exists and deletes the person from the customers object utilizing the delete key phrase. The up to date customers object is then saved utilizing saveUsers.

These capabilities function the strategies our API can use to course of and retrieve data from the database.

Subsequent, let all import all of the required capabilities and modules into the routes file ./src/customers.routes.ts and populate as follows :

import categorical, {Request, Response} from "categorical"
import { UnitUser, Consumer } from "./person.interface"
import {StatusCodes} from "http-status-codes"
import * as database from "./person.database"

export const userRouter = categorical.Router()

userRouter.get("/customers", async (req : Request, res : Response) => {
    strive {
        const allUsers : UnitUser[] = await database.findAll()

        if (!allUsers) {
            return res.standing(StatusCodes.NOT_FOUND).json({msg : `No customers at the moment..`})
        }

        return res.standing(StatusCodes.OK).json({total_user : allUsers.size, allUsers})
    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})

userRouter.get("/person/:id", async (req : Request, res : Response) => {
    strive {
        const person : UnitUser = await database.findOne(req.params.id)

        if (!person) {
            return res.standing(StatusCodes.NOT_FOUND).json({error : `Consumer not discovered!`})
        }

        return res.standing(StatusCodes.OK).json({person})
    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})

userRouter.put up("/register", async (req : Request, res : Response) => {
    strive {
        const { username, e mail, password } = req.physique

        if (!username || !e mail || !password) {
            return res.standing(StatusCodes.BAD_REQUEST).json({error : `Please present all of the required parameters..`})
        }

        const person = await database.findByEmail(e mail) 

        if (person) {
            return res.standing(StatusCodes.BAD_REQUEST).json({error : `This e mail has already been registered..`})
        }

        const newUser = await database.create(req.physique)

        return res.standing(StatusCodes.CREATED).json({newUser})

    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})

userRouter.put up("/login", async (req : Request, res : Response) => {
    strive {
        const {e mail, password} = req.physique

        if (!e mail || !password) {
            return res.standing(StatusCodes.BAD_REQUEST).json({error : "Please present all of the required parameters.."})
        }

        const person = await database.findByEmail(e mail)

        if (!person) {
            return res.standing(StatusCodes.NOT_FOUND).json({error : "No person exists with the e-mail offered.."})
        }

        const comparePassword = await database.comparePassword(e mail, password)

        if (!comparePassword) {
            return res.standing(StatusCodes.BAD_REQUEST).json({error : `Incorrect Password!`})
        }

        return res.standing(StatusCodes.OK).json({person})

    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})


userRouter.put('/person/:id', async (req : Request, res : Response) => {

    strive {

        const {username, e mail, password} = req.physique

        const getUser = await database.findOne(req.params.id)

        if (!username || !e mail || !password) {
            return res.standing(401).json({error : `Please present all of the required parameters..`})
        }

        if (!getUser) {
            return res.standing(404).json({error : `No person with id ${req.params.id}`})
        }

        const updateUser = await database.replace((req.params.id), req.physique)

        return res.standing(201).json({updateUser})
    } catch (error) {
        console.log(error) 
        return res.standing(500).json({error})
    }
})

userRouter.delete("/person/:id", async (req : Request, res : Response) => {
    strive {
        const id = (req.params.id)

        const person = await database.findOne(id)

        if (!person) {
            return res.standing(StatusCodes.NOT_FOUND).json({error : `Consumer doesn't exist`})
        }

        await database.take away(id)

        return res.standing(StatusCodes.OK).json({msg : "Consumer deleted"})
    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})
Enter fullscreen mode

Exit fullscreen mode

That is what every operate does :

userRouter.get(“/customers”): This operate handles a GET request to “/customers”. It calls the findAll operate from the database module to retrieve all customers. If no customers are discovered, it returns a 404 standing code with a message. If customers are discovered, it returns a 200 standing code with the full variety of customers and the array of all customers.

userRouter.get(“/person/:id”): This operate handles a GET request to “/person/:id” the place :id represents a particular person’s ID. It calls the findOne operate from the database module to retrieve the person with the desired ID. If the person is just not discovered, it returns a 404 standing code with an error message. If the person is discovered, it returns a 200 standing code with the person object.

userRouter.put up(“/register”): This operate handles a POST request to “/register” for person registration. It extracts the username, e mail, and password from the request physique. If any of those fields are lacking, it returns a 400 standing code with an error message. It calls the findByEmail operate from the database module to verify if the e-mail is already registered. If the e-mail is discovered, it returns a 400 standing code with an error message. If the e-mail is just not discovered, it calls the create operate from the database module to create a brand new person and returns a 201 standing code with the newly created person object.

userRouter.put up(“/login”): This operate handles a POST request to “/login” for person login. It extracts the e-mail and password from the request physique. If any of those fields are lacking, it returns a 400 standing code with an error message. It calls the findByEmail operate from the database module to verify if the e-mail exists. If the e-mail is just not discovered, it returns a 404 standing code with an error message. If the e-mail is discovered, it calls the comparePassword operate from the database module to verify if the provided password matches the saved password. If the passwords do not match, it returns a 400 standing code with an error message. If the passwords match, it returns a 200 standing code with the person object.

userRouter.put(‘/person/:id’): This operate handles a PUT request to “/person/:id” the place :id represents a particular person’s ID. It extracts the username, e mail, and password from the request physique. If any of those fields are lacking, it returns a 401 standing code with an error message. It calls the findOne operate from the database module to verify if the person with the desired ID exists. If the person is just not discovered, it returns a 404 standing code with an error message. If the person is discovered, it calls the replace operate from the database module to replace the person’s particulars and returns a 201 standing code with the up to date person object.

userRouter.delete(“/person/:id”): This operate handles a DELETE request to “/person/:id” the place :id represents a particular person’s ID. It extracts the id from the request parameters. It calls the findOne operate from the database module to verify if the person with the desired ID exists. If the person is just not discovered, it returns a 404 standing code with an error message. If the person is discovered, it calls the take away operate from the database module to delete the person and returns a 200 standing code with a hit message.

All These capabilities outline the routes and corresponding logic for user-related operations reminiscent of retrieving all customers, retrieving a particular person, registering a brand new person, logging in a person, updating a person’s particulars, and deleting a person.

lastly, to make API calls to those routes we have to import them into our app.ts file and replace our code like this :

import categorical from "categorical"
import * as dotevnv from "dotenv"
import cors from "cors"
import helmet from "helmet"
import { userRouter } from "./customers/customers.routes"

dotevnv.config()

if (!course of.env.PORT) {
    console.log(`No port worth specified...`)
}

const PORT = parseInt(course of.env.PORT as string, 10)

const app = categorical()

app.use(categorical.json())
app.use(categorical.urlencoded({prolonged : true}))
app.use(cors())
app.use(helmet())

app.use("https://dev.to/", userRouter)

app.pay attention(PORT, () => {
    console.log(`Server is listening on port ${PORT}`)
})
Enter fullscreen mode

Exit fullscreen mode

Nice! now let’s begin our server and take a look at our API utilizing Postman.

run npm run dev in your terminal

your terminal must be just like this

[INFO] 20:55:40 ts-node-dev ver. 2.0.0 (utilizing ts-node ver. 10.9.1, typescript ver. 5.1.3)
Server is listening on port 7000

Nice! Let’s make calls to our endpoints.

Register customers

register user

Login customers

login user

Get all customers

All users

Get a single person

single user

Replace person

update user

Delete person :

delete user

NOTE : When you’ve got added customers, your customers.json file ought to repeatedly append new customers and will appear like this.

Customers-data-storage-file :

database

Lastly, allow us to create the login and routes for our merchandise.
So let’s duplicate the contents of our customers interface with minor adjustments into the file ./src/product.interface.ts

export interface Product {
    identify : string,
    value : quantity;
    amount : quantity;
    picture : string;
}

export interface UnitProduct extends Product {
    id : string
}

export interface Merchandise {
    [key : string] : UnitProduct
}
Enter fullscreen mode

Exit fullscreen mode

You may reference the part on the Customers interface for particulars about what these interfaces do.

Subsequent, identical to within the ./src/customers.database.ts file, allow us to populate the ./src/merchandise.database.ts with an identical logic.

import { Product, Merchandise, UnitProduct } from "./product.interface";
import { v4 as random } from "uuid";
import fs from "fs";

let merchandise: Merchandise = loadProducts();

operate loadProducts(): Merchandise {
  strive {
    const information = fs.readFileSync("./merchandise.json", "utf-8");
    return JSON.parse(information);
  } catch (error) {
    console.log(`Error ${error}`);
    return {};
  }
}

operate saveProducts() {
    strive {
        fs.writeFileSync("./merchandise.json", JSON.stringify(merchandise), "utf-8");
        console.log("Merchandise saved efficiently!")
    } catch (error) {
        console.log("Error", error)
    }
}


export const findAll = async () : Promise<UnitProduct[]> => Object.values(merchandise)

export const findOne = async (id : string) : Promise<UnitProduct> => merchandise[id]

export const create = async (productInfo : Product) : Promise<null | UnitProduct> => {

    let id = random()

    let product = await findOne(id)

    whereas (product) {
        id = random ()
        await findOne(id)
    }

    merchandise[id] = {
        id : id,
        ...productInfo
    }

    saveProducts()

    return merchandise[id]
}

export const replace = async (id : string, updateValues : Product) : Promise<UnitProduct | null> => {

    const product = await findOne(id) 

    if (!product) {
        return null
    }

    merchandise[id] = {
        id,
        ...updateValues
    }

    saveProducts()

    return merchandise[id]
}

export const take away = async (id : string) : Promise<null | void> => {

    const product = await findOne(id)

    if (!product) {
        return null
    }

    delete merchandise[id]

    saveProducts()

}

Enter fullscreen mode

Exit fullscreen mode

Once more, you may reference the person’s part for extra particulars on what these capabilities present to our API.

As soon as our logic checks out, it is time to implement the routes for our merchandise.

Populate the ./src/merchandise.routes.ts file with the next code :

import categorical, {Request, Response} from "categorical"
import { Product, UnitProduct } from "./product.interface"
import * as database from "./product.database"
import {StatusCodes} from "http-status-codes"

export const productRouter = categorical.Router()

productRouter.get('/merchandise', async (req : Request, res : Response) => {
    strive {
       const allProducts = await database.findAll()

       if (!allProducts) {
        return res.standing(StatusCodes.NOT_FOUND).json({error : `No merchandise discovered!`})
       }

       return res.standing(StatusCodes.OK).json({whole : allProducts.size, allProducts})
    } catch (error) {
       return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error}) 
    }
})

productRouter.get("/product/:id", async (req : Request, res : Response) => {
    strive {
        const product = await database.findOne(req.params.id)

        if (!product) {
            return res.standing(StatusCodes.NOT_FOUND).json({error : "Product doesn't exist"})
        }

        return res.standing(StatusCodes.OK).json({product})
    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})


productRouter.put up("/product", async (req : Request, res : Response) => {
    strive {
        const {identify, value, amount, picture} = req.physique

        if (!identify || !value || !amount || !picture) {
            return res.standing(StatusCodes.BAD_REQUEST).json({error : `Please present all of the required parameters..`})
        }
        const newProduct = await database.create({...req.physique})
        return res.standing(StatusCodes.CREATED).json({newProduct})
    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})

productRouter.put("/product/:id", async (req : Request, res : Response) => {
    strive {
        const id = req.params.id

        const newProduct = req.physique

        const findProduct = await database.findOne(id)

        if (!findProduct) {
            return res.standing(StatusCodes.NOT_FOUND).json({error : `Product doesn't exist..`})
        }

        const updateProduct = await database.replace(id, newProduct)

        return res.standing(StatusCodes.OK).json({updateProduct})
    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})


productRouter.delete("/product/:id", async (req : Request, res : Response) => {
    strive {
        const getProduct = await database.findOne(req.params.id)

        if (!getProduct) {
            return res.standing(StatusCodes.NOT_FOUND).json({error : `No product with ID ${req.params.id}`})
        }

        await database.take away(req.params.id)

        return res.standing(StatusCodes.OK).json({msg : `Product deleted..`})

    } catch (error) {
        return res.standing(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
    }
})
Enter fullscreen mode

Exit fullscreen mode

Do not forget to import and name the product’s route in our app.ts file, which ought to now appear like this :

import categorical from "categorical"
import * as dotevnv from "dotenv"
import cors from "cors"
import helmet from "helmet"
import { userRouter } from "./customers/customers.routes"
import { productRouter } from "./merchandise/product.routes"

dotevnv.config()

if (!course of.env.PORT) {
    console.log(`No port worth specified...`)
}

const PORT = parseInt(course of.env.PORT as string, 10)

const app = categorical()

app.use(categorical.json())
app.use(categorical.urlencoded({prolonged : true}))
app.use(cors())
app.use(helmet())

app.use("https://dev.to/", userRouter)
app.use("https://dev.to/", productRouter)

app.pay attention(PORT, () => {
    console.log(`Server is listening on port ${PORT}`)
})

Enter fullscreen mode

Exit fullscreen mode

Excellent. We now have a full-fledged API constructed with Typescript and Nodejs. Hurray!!

Let’s take a look at our endpoints.

Create product

Create product

All merchandise

All products

Single product

Single product

Replace product

Update product

Delete product

Delete product

When you add new merchandise they are going to be appended to the merchandise.json file and it’ll appear like this :

Products.json file

And that is we’re executed. When you got here this far, Congratulations and Thanks!

Feedback and suggestions are welcome.

You will discover the whole code on github right here -> GITHUB

Leave a Reply

Your email address will not be published. Required fields are marked *