Welcome back, and here we go and setup some tools we would need to build this application.
Lets start with Prisma
npm install prisma --save-dev
If you are wondering what is prisma and why prisma ?
Prisma
is an ORM, well its more than an ORM, its a query builder, in simple terms this will help us manage our database queries, we can visualise our complete database in 1 file called prisma.schema
and can use Prisma
apis to interact with our database very easily.
Its my goto ORM and unless you have no other strong reason you cannot go wrong with it.
I am choosing Postgresql
as my database, its open source, its best in the industry to work with a relational database, I was always scared with migrations and relationships with a relational database but using something like Prisma
makes it super simple to use.
Its always safe in terms of development to work with SQL, IMO it makes a strict table and schema earlier, so thought is given early in development how the data would look like, but if you dont know how the data would grow and be handled as the app grows NO-SQL wins as no such major pre planning required.
ā Prisma Installed
lets setup prisma
npx prisma init
ā Prisma Schema created
once you init the prisma
a prisma.schema
file is created, this is how my updated prisma.schema
file looks like.
I have 2 models, 1 is to manage users and another to manage the tokens created by the user to access the API, the idea here is anytime a user has to access the API, it will make sure the token is valid.
we can revoke the token and also regenerate a token for the user as well.
model LcUser {
uid String @id
name String?
email String?
phone String?
state VerificationState
createdAt DateTime @default(now())
updatedAt DateTime @default(now())
APIToken APIToken[]
}
model APIToken {
id String @id
token String @unique
user LcUser @relation(fields: [userId], references: [uid])
userId String
tokenState TokenState
valdity Int @default(1)
reason String
createdAt DateTime @default(now())
updatedAt DateTime @default(now())
}
enum VerificationState {
PENDING
VERIFIED
DELETED
}
enum TokenState {
ACTIVE
INACTIVE
}
ā Running Postgres locally
I will be setting up postgres
locally on a local docker container, this makes it super easy and also makes deployment easier, to do this, I will only have to write a docker-compose file, here is how my file looks like :
version: "3.9"
services:
db:
image: "postgres:14.1-alpine"
ports:
- "5432:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=litecode
restart: always
volumes:
- db:/var/lib/postgresql/data
volumes:
db:
After setting this Docker compose file our postgres DB would be running locally with this command
docker compose up -d
-d
is optional when we want our docker to run in background.
This would spin up a docker container, install a postgres 14.1-alpine
version and host it at 5432
port and map it to 5432 port of our local machine.
also the username
password
and database
name is provided as part of the environment variables.
Now update your .env
file as well.
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/litecode?schema=public"
now our DB is running and we can start the prisma magic.
npx prisma db push
This would start creating the Prisma client and also populate our table schema in the database running.
All good so far.
npx prisma studio
This would start the Prisma studio at port 5555 where we can check the tables created in our postgres database.
Now some refactoring and updating all the steps into 1 command.
What I want is, if I share this repo with any other developer he should do the following steps
yarn setup
and thats it. everything should start working for him automatically.
So let's fix that.
setup
I have written this shell script which does the following steps
#!/bin/bash
cecho(){
RED="\033[0;31m"
GREEN="\033[0;32m" # <-- [0 means not bold
YELLOW="\033[1;33m" # <-- [1 means bold
CYAN="\033[1;36m"
NC="\033[0m" # No Color
printf "${!1}${2} ${NC}\n" # <-- bash
}
if [ -x "$(command -v docker)" ]; then
cecho "GREEN" "ā
Docker is present, continuing..."
if (! docker stats --no-stream 2>/dev/null); then
# On Mac OS this would be the terminal command to launch Docker
open /Applications/Docker.app
echo -n "Waiting for Docker to launch"
sleep 1
# Wait until Docker daemon is running and has completed initialisation
while (! docker stats --no-stream >/dev/null 2>&1); do
# Docker takes a few seconds to initialize
echo -n "."
sleep 1
done
fi
echo
cecho "YELLOW" "Docker started"
cecho "GREEN" "Docker compose up db -d"
docker compose up db -d
cecho "GREEN" "Docker Image running"
else
cecho "RED" "ā ļø Docker is missing, Please Install Docker and make sure Docker daemon is running"
fi
Now the next step is to add a script in our package.json
"setup": "./setup.sh"
You might get a permission issue when running the shell script, in that case locally just set the script file permission using this command
sudo chmod +x setup.sh
ok, we are good, now any contributor can clone the repo, run setup
and if docker installed, it will run the postgresql db.
lets add the prisma
setup part of the same command as well, so updating the script to
"setup": "./setup.sh && yarn && yarn prisma:push && yarn prisma:studio",
"migrate:dev": "prisma migrate dev",
"migrate:deploy": "prisma migrate deploy",
"migrate:reset": "prisma migrate reset",
"migrate:resolve": "prisma migrate resolve",
"prisma:generate": "prisma generate",
"prisma:studio": "prisma studio",
"prisma:push": "prisma db push",
all the common prisma
commands made smaller and added in the package.json
file.
Now the setup
command is looking good, it will setup the docker, postgresql
, install node modules, setup tables and run prisma
studio to check for our db in local browser.
Lets seed the data. šŖ“
Now the next step is, we need some data to be in the tables created locally, so that when we setup the db its not empty, this will help us run some local e2e tests, can also help in development with some default data already in the tables.
lets create a file called seed.ts
in the prisma
folder.
also install ts-node, this would be required to run the seed file independently
yarn add ts-node
To use
npm
andyarn
orpnpm
is an individual decision, make sure you understand the pros and cons of either of the approach, I would prefer yarn as its been pretty handy for me since few years, never faced any major issues also compared to npm is quite fast due to parallel dependency resolutions
here is how the seed.ts
looks like for me
import { APIToken, LcUser, PrismaClient, TokenState, VerificationState } from "@prisma/client";
import { v4 } from "uuid";
const prisma = new PrismaClient();
async function main() {
console.log("Seeding...");
await prisma.$connect();
const userDummy: LcUser = {
uid: "id_" + v4(),
name: "Divyanshu Negi",
email: "div@litecode.dev",
phone: "+91999888989",
state: VerificationState.VERIFIED,
createdAt: new Date(),
updatedAt: new Date()
};
const tokenDummyEntry: APIToken = {
token: "api." + v4(),
createdAt: new Date(),
updatedAt: new Date(),
id: v4(),
userId: userDummy.uid,
tokenState: TokenState.INACTIVE,
reason: "expired",
valdity: 1
}
await addDummyUser(userDummy);
await addDummyTokens(userDummy.uid, tokenDummyEntry);
}
const addDummyUser = async (user: LcUser) => {
console.log("Adding dummy user...");
await prisma.lcUser.create({
data: { ...user },
});
};
const addDummyTokens = async (userId: string, tokenDummyEntry: APIToken) => {
console.log("Adding dummy tokens...");
for (let i = 0; i < 5; i++) {
tokenDummyEntry.id = v4();
tokenDummyEntry.userId = userId;
tokenDummyEntry.token = "api." + v4();
tokenDummyEntry.tokenState = i == 4 ? TokenState.ACTIVE : TokenState.INACTIVE;
await prisma.aPIToken.create({
data: { ...tokenDummyEntry },
});
}
}
main()
.catch((e) => console.error(e))
.finally(async () => {
await prisma.$disconnect();
});
now to seed the database run this command
npx ts-node prisma/seed.ts
This will run and all the data in the postgres tables will be populated with dummy entries, which you can see using prisma studio.
now lets add the seed command as part of our setup step, so once we setup some data is also present in our locally running database
updated this script in package.json
"setup": "./setup.sh && yarn && yarn prisma:push && yarn seed && yarn prisma:studio",
"seed":"ts-node prisma/seed.ts",
cool, now with 1 setup
command we are good to go and start the development without worrying about database, docker or data in tables.
One setting which I always want when working on a JS project is, to set some default rules which everyone should use.
and vscode
provides this handy way to manage that.
lets create a .vscode
folder in the root directory of our project
add a setting.json
file in this folder and add this data
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"eslint.validate": ["typescript"]
}
here we are setting some default setting when working on a vscode editor, make sure these changes are also pushed to VCS (github).
Now when we save a file automatically the formatting works and updates the file.
This is it for the second part, we are very close to make a production ready nestJS project.
In the next part we will be taking a look at
API endpoint
and deploy it with 1 command when we pushThanks, will see you soon.
X
I'd appreciate your feedback so I can make my blog posts more helpful. Did this post help you learn something or fix an issue you were having?
Yes
No
X
If you'd like to support this blog by buying me a coffee I'd really appreciate it!
X
Subscribe to my newsletter
Join 107+ other developers and get free, weekly updates and code insights directly to your inbox.
Email Address
Powered by Buttondown
Divyanshu Negi is a VP of Engineering at Zaapi Pte.
X