It looks really cool but I feel it's too much to take in a one bite. Making the framework do (almost) everything makes me think it won't be able to cover all the nuances of single-purpose libraries. I'd be much more open to trying it if it was just a general wiring around Fastify & Prisma for a better Node.js backend rather than a one tool to do everything.
That is a non-goal. One of the biggest advantages of Deepkit and its runtime type feature is that you can reuse TypeScript types throughout your whole application stack. This does not work with Fastify and Prisma. Let me give you an example.
Let's say you build a little user management app and need a `User` type. So you define one:
And with that the framework automatically validates all incoming requests against the User object and its containing validation constraints. It also deserialises it automatically from JSON for example. If the `User` is a class, it instantiates a class instance.
But you don't want to require all fields, like id and created should be set by the server:
router.post('/user', (user: Omit<User, 'id' |'created'>): User => {
//...
});
This works equally well. And the best part: you get API documentation for free with the Deepkit API Console, just like you know from Swagger UI.
Ok, but we need to save the User in our database, right? We do not need to duplicate the User in yet another language like with Prisma. Just re-use the User interface and annotate the fields.
Next, let's go to the frontend. We obviously have to request the data from our rest API. We can just do that and reuse the `User` interface once again.
const userJson = await fetch('/user/2');
const user = cast<User>(userJson);
`cast` automatically validates and deserialises the received JSON. We import `User` wherever needed and this works because the meta-data is not tightly coupled to any library, so you won't pull in for example ORM code just because you use database meta-data. You can literally use TypeScript's powerful type-system to your full advantage in many small to super complex use-cases and let the framework handle all the validation, serialisation, and database stuff.
And like that you are able to reuse types everywhere: From database to http router, configuration, RPC, API documentation, dependency injection, message broker, frontend, and more. That's not possible in this form in any other framework or when you combine lots of third-party libraries and glue them manually together.
Deepkit separated the functionality in libraries which would allow you to use their features in Fastify and Prisma, too, but that would mean you lose one of the biggest advantage of all: Reusing types.
IMO reusing types is an anti-pattern. It leads to knots of functions which are all tightly coupled since they “use the same types” even though they actually only need small subsets of those types. Changing one of these functions will then often break several of the conjoined functions.
When you start to hear people say of their PRs “it works but I just have to fix the types”, that usually means the codebase is trying to re-use too many types. In my experience.
The beauty of TypeScript is that you can have two different very narrowly specified types and the compiler will tell you if they’re compatible or not.
Reusing types is throwing away the most valuable feature of TypeScript.
For me, the type signature is part of the function signature and therefore should not be re-used.
Imagine what a disaster it would be if function signatures were reusable:
It's not an anti-pattern to reuse types, it's an anti-pattern to apply something you don't need or apply too much of it, which is exactly what you are talking about. There is of everything a "too much". It's up to you how and in which part of your app you will use that. The difference is: you have the opinion to do so now where it makes sense. Nobody says you have to reuse everything up to a point where it becomes counterproductive. Having the option vs not having the option. It's definitely better in my opinion than being forces to even learn new inflexible languages/APIs to describe your model like JSON-schema, decorators, openAPI json, GraphQL, Prisma, ... - whether you reuse is up to you.
Ok you got me sold on the idea - being able to configure just one schema for everything with validation and deserialization does sound amazing. And really appreciate the detailed reply!
While it does sound quite great I have to try it in practise to understand would it work for me. To prevent framework fatigue I really try not to switch frameworks too often which is why I'd like to be able to reuse what I know already. And your scope sounds really big. Yet I always have liked how TS/JS community keep pushing the envelope constantly!
It makes the types available in runtime. You can take a look at the current WIP version of the new upcoming Deepkit book which explains it in great detail: https://deepkit-book.herokuapp.com
See section "2. Runtime Types"
My summary is that the DeepKit compiler is a JS transpiler (used as a ts-node or webpack plugin, or conveniently/invasively as a replacement of node_modules/typescript) that produces code like this:
//TypeScript
export type TypeA = string[];
//generated JavaScript
export const __ΩtypeA = ['&F'];
//TypeScript
function log(message: string): void {}
//generated JavaScript
function log(message) {}
log.__type = ['message', 'log', 'P&2!$/"'];
Where '&F' and 'P&2!$/"' are the compact bytecode translations of the types ("&" means string, "F" means array) and '__Ω' is a prefix used to avoid naming conflicts.
Personally, I'd love to see a live example of code in & compiled code out on the homepage. I'm sure it's on your roadmap :)
This looks to me a lot like the FastAPI framework in Python. Thanks for the demo.
> This does not work with Fastify and Prisma
The point of Prisma is that it generates types for you from the Prisma schema, right? Are the generated types not interchangeable throughout the application?
They surely can be used in the app, but a) requires that code generation and b) you are limited by the expressiveness of Prisma's DSL. The DSL supports much less than TypeScript. The last time I looked: No support for generics, conditional types, mapped types, (const) enums, index signatures, class instances (+methods), and more. Basically you lose the full power of TypeScript that way. Also you would have no way of using that interface in validation and serialisation, which is more important than just using it in type checking - you would need to rewrite the entity in another format like json-schema or zod-like libraries, which means at the end you still duplicate it.
I was pretty sure that Prisma also supported generating OpenAPI schemas and GraphQL, but maybe I'm wrong.
Fair point about expressiveness, but if your data must cross application boundaries then you are somewhat limited in expressiveness no matter what, because your types need to be understood by arbitrary clients.
Lack of generics is pretty annoying though. It's probably my biggest annoyance with GraphQL.
Good point. That's why I'm working on a C++ version of TypeScript's type checking./computation so it can be used in all languages. That would allow us to use TypeScript types literally everywhere and remove that barrier.
Prisma's DSL has no syntax for validation constraints, so even with openAPI generators it's incomplete.
I'm really bad at marketing, but I regularly tweet [0] and chat with the discord [1] community. I'm trying keep the followers up to date with the C++ typescript stuff. Feel free to join