- Published on
Rebuilding Bakia - Write consistent TypeScript with Prisma
- Authors
- Name
- Thang Huu Vu
- @thanghvu
Photo by Linda Christiansen on Unsplash
About the product
Bakia is short for "Bất kì ai". In Vietnamese, it means "Anyone". The product is a E-commerce website that sells custom toys. Features include:
- Customize Lab: Customize your own toy
- Shopping cart
- Payment form
You can check it out at www.bakia.vn
Why I am rebuilding
When I first built Bakia, I was a beginner in web development. I constantly found myself wrestling with new concepts, technologies, and frameworks. I ended up with the following stack:
- Front-end: React.js, styled-components
- Back-end: PostgreSQL, Java Spring Boot
- Deployment: AWS
The reasons behind this stack are pretty straight-forward:
- I already knew React.js, and was interested in css-in-js approach.
- I was teaching a group of interns in my company about Java Spring Boot and PostgreSQL.
- I have some experience with AWS.
This stack is a classic approach to web development with separate front-end and back-end. It works fine after the first release. However, after a while, I realize as a solo dev, it is hard to manage this stack:
- I found myself having to jump between codebases all the time
- Performance & SEO issue with SPA approach
- To deploy a single ecommerce application on AWS is overkill IMO: I have to manage EC2, S3 buckets, CloudFront, etc.
- All components are built manually and it's hard to maintain them all at once.
After much considerations, I decided to rebuild Bakia with a new stack, this time delegating as much as possible:
- Next.js as framework
- Prisma for ORM. Rewrite with TypeScript
- PlanetScale and MySQL for serverless database
- Deploy to Vercel
- TailwindCSS and Radix UI for styling
- react-hook-form for form implementation
In this post, I will focus on sharing the experience using Prisma and a useful technique for defining types/interfaces in my project.
Building with Prisma
Benefits
By delegating to Prisma I can focus on other feature development. Prisma helps me move so much faster:
- Auto-generated client.
- Easy migration with PlanetScale. I covered the migration workflow in my previous post.
- Prisma Studio means free Admin console
- Void SQL queries. (I don't miss them much 🙃)
- TypeScript safety. I can ship with confidence and the code is much easier for new maintainers who is familiar with TypeScript.
TypeScript + Prisma = 💪
To create consistent Type
, I first create some utility type:
// For return type of Await methods
export type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
// For Array elements
export type ArrayElement<
ArrayType extends readonly unknown[]
> = ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
Then, I create a db.ts
file to write Prisma code:
export const getCategories = async () => {
try {
const categories = await prisma.category.findMany();
// some magic applied to categories
return categories;
} catch (error) {
throw new Error(error);
}
};
export type CategoryType = ArrayElement<Awaited<ReturnType<typeof getCategories>>>;
And comsume CategoryType
in components. I found this approach is much easier to read and maintain.
- I can reuse the code in different pages / API routes.
- All the changes in Prisma generated client are immediately reflected and recognized.
Conclusion
I hope you find this post useful. Stay tuned for more Prisma-related posts.