-
-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typegoose & Mongoose on client side #33
Comments
typegoose required mongoose to work, and mongoose is a peer-dependencie, so not automaticly installed, only automaticly installed are and probably, no, mongoose & typegoose are not designed to work on client side / in a browser |
Yes but I try to share "entity" classes between front-end and back-end. To keep thing simple, I want a single class per entity, no distinct interface.
Yes, the idea is to get rid of Typegoose or at least of Mongoose on the client side. To be more specific, I have a Angular project with:
and a unique package.json in the project root folder. The Angular builder will then catch the Typegoose dependencies as there are available for the data api server. I tried to exclude Typegoose dependencies, or Typegoose itself, from the Angular build using @angular-builders/custom-webpack with typegoose and / or mongoose as "externals". It failed. That why some work around like in HarelAshwal/typegoose-frontend will be great. |
i would highly recommend checking out graphql, and the typescript implementation:
because it is "by design" not to be in browser, at least i will not support it, at least in the near future |
@hasezoey if you've already finalized your decision on this, please forgive me for reviving the conversation. But I just wanted to mention a few things:
I just browsed through the code today (kudos for it being so well organized and easy to follow btw!), so I hope I'm not oversimplifying. But as you mentioned, you've got mongoose as a peer dependency, so supporting this might be pretty easy - it's just a matter of bypassing mongoose.Model, mongoose.Index, and maybe a couple other little things in case it's a browser build. Would you welcome a PR if it's not too complex? |
@captaincaius i will not support it, but prs are always welcome |
@captaincaius I'm interested in this too and am currently using this on the client-side. It's a complete life-saver especially when you've got 4-5 clients modifying/working with the same data. Keeping the interfaces synchronised with the mongoose models would be a nightmare. |
This is what I do. For any Client side related things I use the ICart interface (unless i make a new class on the client side, then I have that class implement ICart). The cart model implements the interface. Then on the server side (kind of hacky) you can do Server let cart = await CartModel.findById(dspofid90sf0);
let user = Cart.user as DocumentType<User>
user.username = "Updated and no TS errors" Client let cart: Array<ICart> = await http.get("/carts").toPromise();
let user = Cart[0].user as IUser
user.username = "Updated and no TS errors" export interface ICart extends IModel {
name: string;
description: string;
items: BehaviorSubject<IProduct[]> | IProduct[] | string;
cartType: string;
user: IUser | any // this is a ref<User> but breaks on client side, so on server side you just do Cart.User as DocumentType<User>
}
@modelOptions(testSiteMongoDb.options)
export class Cart implements ICart {
@prop() public name: string;
@prop() public description: string;
@prop() public items: string;
@prop({ enum: CartType }) public cartType: string;
@prop({ ref: User, required: true }) public user: Ref<User>;
public setItems(items: any): void {
this.items = JSON.stringify(items);
}
}
export let CartModel = getModelForClass(Cart);
|
I'm running into a similar situation, where I want to have the Typescript types that come with the classes available on the client side, but the model creation is restricted to the server side. As per the Mongoose documentation, the client-side library only supports creating schemas and validating documents, which seems like a reasonable feature to support for Typegoose. I would suggest having something similar to Mongoose, where the package is split into what can in fact be included on the client side, i.e. classes and schemas (which prepares the decorator keys, etc.) and what cannot, i.e. building the model and other functionality. On the browser, we can directly include this as follows, similar to the Mongoose pattern.
I don't know if I'll have the mileage to actually make a PR for this approach, but it would likely be a significant refactor to modularize the code as I described above. |
strictly speaking, typegoose dosnt do anything that couldnt be done in the browser, the problem is just the interop with the other mongoose package [types, etc] (this means the package wouldnt need to be split) |
I just realized I didn't come back here to post my solution. Using typegoose classes in the browser will cause no issues as long as you use them as a dev dependency. From your separate module, you must export type such as:
And then always use the types, and never the classes. The moment you import 1 single class, doesn't matter if it's even decorated or not, you'll be hit with the buffer issues and so on, msot of them caused by trying to include 'mongodb' (the npm module) code in the borwser. However, types don't exist at runtime so you'll have no issues as long as you declare the types in your separate module. |
im interested in doing this, but because i dont use it i cant test for it, could someone make a pr with an extra test directory with web(/client) tests? then i could work on "fixing" them |
@michaelbats That solution worked perfectly, thank you! |
I made a workaround for my project: 'use strict'
Object.defineProperty(exports, '__esModule', {value: true})
exports.dummyValue = ''
function dummyFn() { }
exports.dummyFn = dummyFn
function dummyDecorator() {
return dummyFn
}
exports.dummyDecorator = dummyDecorator
exports.prop = dummyDecorator
exports.arrayProp = dummyDecorator
exports.getModelForClass = dummyFn Then my webpack configuration file contains this: webpack: (config) => {
config.plugins.push(new NormalModuleReplacementPlugin(/type-graphql$|@typegoose\/typegoose$/, resource => {
resource.request = resource.request.replace(/type-graphql/, 'type-graphql/dist/browser-shim.js')
resource.request = resource.request.replace(/@typegoose\/typegoose/, '@typegoose/../../config/typegoose-browser-shim.js')
}))
return config
} I basically combined type-graphql documentation for browser usage with what I need to achieve the same result for Typegoose. Of course, typegoose-browser-shim.js must export dummy decorator and dummy function for everything originally imported from Typegoose. My example does not contains much for now (replaces Even though I do not have the time to work on a complete solution right now, type-graphql browser-shim file can probably be a good start to implement this feature. |
@fromi, thanks for pointing this out, i might include something like this in a future version, i will need to read up on this |
We are using typegoose in a common library which is used in the frontend and backend at the same time. Our goal is to define model-classes at a single point and use them without any fancy validation hooks etc. in the frontend. I have currently added a wrapper module to support this: type decoratorFn = (body?: unknown) => (target: unknown, key: string, descriptor?: PropertyDescriptor) => void;
type tg = {
prop: decoratorFn;
mapProp: decoratorFn;
arrayProp: decoratorFn;
modelOptions: decoratorFn;
pre: decoratorFn;
post: decoratorFn;
index: decoratorFn;
plugin: decoratorFn;
[key: string]: unknown;
};
let typegoose: tg;
// if executed in browser => use dummy decorators
if (typeof process === 'undefined' || typeof window === 'object') {
// dummy decorator
// eslint-disable-next-line @typescript-eslint/no-empty-function
const dummyDecorator = () => (): void => {};
typegoose = {
prop: dummyDecorator,
mapProp: dummyDecorator,
arrayProp: dummyDecorator,
modelOptions: dummyDecorator,
pre: dummyDecorator,
post: dummyDecorator,
index: dummyDecorator,
plugin: dummyDecorator,
};
} else {
// eslint-disable-next-line @typescript-eslint/no-var-requires
typegoose = require('@typegoose/typegoose');
}
export const prop = typegoose.prop;
export const mapProp = typegoose.mapProp;
export const arrayProp = typegoose.arrayProp;
export const modelOptions = typegoose.modelOptions;
export const pre = typegoose.pre;
export const post = typegoose.post;
export const index = typegoose.index;
export const plugin = typegoose.plugin; A sample class inside the common lib looks like this: import { prop } from './typegooseDecorators';
export class User {
@prop({ required: true })
id!: string | undefined;
@prop({ required: true })
firstName!: string;
@prop({ required: true })
lastName!: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
} Maybe adding some similar code to this project checking for browser usage with simple dummy decorators would be enough for now. |
@anbraten thanks for trying to help, but the real problem is to "let the imports stay how they are at the moment" and to still satisfy the types (import structure), while also "removing" usage of node-only functions/imports and to write some tests for this PS: could you give me the name of the problem that uses typegoose for frontend and backend to know how it is used? |
after thinking more in-depth about this, i couldnt find an solution for importing mongoose while also not importing it when in an browser without using an function call to get an mongoose reference PS: a solution like @anbraten used is a no-go for this project, because this would mean breaking types / duplicating much code |
well yeah thats what I have come to realize POJOs are not supported though the documentation is claiming that and im not trying to start anything here about that. They should discriminate between entity vs pojo which IS the line in the sand So I went ahead and started my own POJO hierarchy likely an abstract one to inherit off of with as much as I can model atop and two derivatives entity and pojo the former for backend and latter for front end and the cutoff will happen at build/run time I have yet to complete my trek here with my codebase would something like this play a part in the fold? |
This comment has been minimized.
This comment has been minimized.
still not out of the woods yet I wont have feedback soon but will see this thru when my models are cast and booting for either a typegoose workaround or what I might be seeing as something else preventing me from building nest project thanks for your patience |
feedback, still trying to spot the lapse here some things make sense and other things do not something changed from typegoose 5 to typegoose 6+ in typegoose 5 I extended from typegoose and I flew with typegoose 5 and released a public koa2 node.js server running today still so now Im being blocked with typegoose 6+ and the mandate is the models have to extend from mongoose.Document why has this changed if I was able to load models anywhere on anything Wouldn't this limit the agility? Did someone impose this just for good housekeeping? Its not normal I am impeded but this one is taking me for a ride and that alone cites something went arye somewhere that is serious I shouldnt be impeded like this I dont know the internals well enough of the change from typegoose 5 to typegoose 6+ to say does anyone know the semantics behind this? should it be rolled back? certainly the 2019 web chatter on this caused a flurry on same thing if typegoose models can load independent of mongoose that would be ideal no? can we make it like this? Im not going to get clarity on my space for a bit still and Im running into road blocks modeling a dual hierarchy that NEEDS to at one point extend from mongoose.Document I can tag it at the end of the extends or all classes will be mongoose.Document and Im not seeing a front end delegate yet besides a property named DATA that gets self populated on the fly (gamma pattern delegate) which I find atrocious at a high level hierarchy Im trying to run with. Though Im still going to keep trying it would help to know what the change occurred from typegoose 5 to 6+ and if it was a legitimate and valid change or if it was a mistake any light is appreciated on the 5 to 6+ change for mongoose.Document and advocating the community operate what they deem a POJO (which it isnt if it cant run isomorphically on front end and backend source code) by changing their models to what they call a POJO by NOT extending from typegoose for models like we did in version 5 thanks |
https://typegoose.github.io/typegoose/docs/guides/migration/migrate-7/
|
classes were never needed to extend
typegoose is intended to be used with full typescript, and only with typescript that is why there a stricter types than working just in js or having normal js interop
no, typegoose just translates classes into schemas (and most commonly also directly into models)
there is a migration guide and also a Changelog please keep this discussion in its own thread, because this does not seem to be "just" about "on client side" |
this does not feel right 5.0 flew fine and it seems for the sake of typings this was imposed Im not getting the rationale if we cannot load models anymore this appears to me to be more like a regression shoot me some flames I dont mind here is my 5.0 model
I had implemented this class below
and this is all I needed on the client side to peel models off the http bridge with my service layer
thats it |
so its the migration from 6 to 7 did anyone see this impact during the switch? |
SO How can anyone agree with the regression or justify it |
if its really just types, then maybe consider using
because comparatively, that version was a mess
like i said earlier, it is recommended to not extend
from what i can tell not really (see initial version of this issue's author), also there are were and still are no tests for client end (read my earlier comments about it)
from what i can tell, typegoose 5 to 6 did not take out any functionality, only defined it more clearly also, like i said earlier, it is not recommended (or supported) to use typegoose (or even just mongoose) on the front-end / client side / in the browser |
Im not using typegoose on the front end Im justy trying to adhere to an interface that a typegoose model implements my model is displayed above but now I cant use the interface anymore it has to extend from mongoose.Document this is what was imposed taking out my codebase |
thank you for feedback I will keep working this and hopefully something will break if Im overseeing something but normally I am not impeded like this and I have the public server and code to prove my assertions |
i still dont quite understand what you want to say here. also, even if you would extend from |
at least I offered you my position coming back to typegoose since 2017 and now that I have conveyed it seems extending from typegoose class was sufficient to build and run in 5.0 can the typegoose team at least take this into account and ponder about this and maybe a small refactoring can be done to undo this regression? I hope that time will allow that and something can break from this so typegoose 5.0 models can be used still |
STK913 posted a workaround this is real and happened I hope some time is taken to ponder in this and maybe a refactor will come about thanks |
wow I think I may have just solved it by commenting in my original 5.0 code I use one gentleman on this thread stated mongoose has typdefs now @types/mongoose I look at my current codebase and its all mongoose.types.ObjectId so I just imported (like my 5.0 code) like this and my red compilation errors go away after I change so I will see this thru my current codebase now and get my build out of the way results pending on runtime though im not sure of this exception will continue but I feel confident results pending |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@nhhockeyplayer please keep it on-topic (which in this issue is "Typegoose & Mongoose on client side") |
Just want to point out I was suprised that the remix compiler is smart enough to strip out these decorators for client side code, allowing us to share our typegoose classes with the browser. May be worth looking into what they do with esbuild |
I still don't see the value. 🤷 As I see it, the client should be ingesting DTOs, which should be different than the DAOs (Typegoose classes). The DTOs might share similar names and shapes, but overall, they should be 100% separate so that when data access APIs change, your client APIs won't have to, i.e. causing painful breaking changes. My 2 cents. Scott |
@smolinari the value in my experience is for an app where the API only has JS /TS consumers. Especially if working in a monorepo This way. When you carry out a breaking change to your API you can simply update all of client applications with a global rename. That is why this is very powerful in my opinion. Could you give me an example of some "differences" between the DTO and DAO. I really am struggling to think of any. |
@dan-cooke - Examples of differences. Not sending out passwords or requests are specialized to not entail personal data. Off the top of my head. Scott |
@smolinari okay yeah fair enough. And in that case sharing a data model between the frontend and backend is probably not wise (although could still be achieved by making the personal fields nullable) But for many applications there is a consistent contract between the API and Frontend. I have used several tools that generate the frontend types from the backend schema. Sharing those types directly is just cutting out the middleman. I think the biggest problem we are trying to solve here is one of maintainability. Maintaining 2 type defs for every model (1 client and 1 API) takes twice as long as if we could just share the 1 type between both. I get that not every application can safely do this, but many can. And I think its worth pursuing the changes required. |
@dan-cooke - I've gone a different route with GraphQL and graphql-code-generator. I have a CLI tool (still in POC) that uses gcg to read the introspection API of my GraphQL API and builds out both the "useX" classes for querying and mutating and also the TypeScript types. It's pretty awesome. https://www.graphql-code-generator.com/ Scott |
Haha yeah that is one of the tools I was referring to.
`graphql-let` to be precise.
Very nice DX
…On Sat, 5 Mar 2022, 12:23 Scott, ***@***.***> wrote:
@dan-cooke <https://github.com/dan-cooke> - I've gone a different route
with GraphQL and graphql-code-generator. I have a CLI tool that uses gcg to
read the introspection API of my API and builds out both the "useX" classes
for querying and mutating and also the TypeScript types. It's pretty
awesome.
https://www.graphql-code-generator.com/
Scott
—
Reply to this email directly, view it on GitHub
<#33 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFOCQ532SVCHMAEH577IRZ3U6NG4BANCNFSM4IWBS3DQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Do you mean typegoose isn't? I'm using mongoose to create a single source config for forms, i.e. I fetch |
If you're still looking for a workaround to make your model work both on the client and the server, here's a trick: Create a Prop Wrapper ClassCreate a file called let prop: any;
function isNode(): boolean {
return typeof global !== 'undefined' && typeof process !== 'undefined' && typeof require !== 'undefined';
}
if (isNode()) {
const _require = eval('require');
const { prop: typegooseProp } = _require('@typegoose/typegoose');
prop = typegooseProp;
} else {
// Dummy implementation of prop
prop = () => (target: any, key: string) => {};
}
export { prop }; Import the Prop WrapperInstead of importing import { prop } from './propWrapper'; With this approach, you can use the same model code in both the client and the server! |
Typegoose allows sharing of types between back-end and front-end. But it requires Mongose on client side for no reason.
So Mongoose is embedded inside the client bundle. Hence packages as Buffer. Furthermore with eg Angular 8 the need of some polyfills as:
Is there any plan that previous works/discussion threads on the subject would be taken in account soon?
See:
existingMongoose
inbuildSchema
#96Versions
Code Example
The text was updated successfully, but these errors were encountered: