Semantic Types and Drizzle ORM Architecture
Learn how to create semantic types in TypeScript and integrate them with Drizzle ORM for better type safety and developer experience in Next.js applications.
At Brainstud, where I worked on a Next.js + Laravel e-learning platform for Dutch students, I learned a typing strategy that's simple yet incredibly effective. This approach has become my go-to for improving code clarity and speeding up developer onboarding.
The problem with basic TypeScript types like string
, boolean
, and number
is that they don't tell the full story. When you see a string
parameter, you don't know if it's an email, a UUID, a timestamp, or just any old text. This becomes especially problematic as teams grow and codebases scale.
Instead of using generic types like:
We can create semantic types that clearly express intent:
This simple change makes code self-documenting and prevents common mistakes.
Semantic Types
The beauty of this approach is its simplicity. We're not creating complex type hierarchies or fancy abstractions—just clear, semantic names for common patterns.
Every database record typically needs an ID and timestamps. Instead of repeating this pattern everywhere, we can create reusable base types:
Use them in your entities:
This approach provides better type safety and consistency across your application.
Drizzle ORM Schema Design
This semantic typing approach isn't tied to any specific framework—I've used it with Hono.js, Next.js server functions, and Drizzle ORM. The real magic happens when you apply this pattern to your database schemas.
Instead of manually defining the same timestamp and ID fields in every table, we can extract them into reusable helpers:
Use them in your table definitions:
Server Functions and Actions
With our semantic types and schema helpers in place, we can now use them throughout our application. The type safety flows from the database schema all the way to your API endpoints:
For form handling with validation:
Conclusion
After using this approach across multiple projects, I've seen firsthand how semantic types transform the developer experience. New team members can understand the codebase faster, bugs related to type mismatches become rare, and the code becomes self-documenting.
The beauty is in the simplicity—we're not over-engineering anything, just giving our types meaningful names and creating reusable patterns. This approach scales beautifully from small projects to large enterprise applications.
For more advanced patterns, check out the Query Builder I've built to help generate these patterns automatically, or explore modular schema design for larger applications.