NodeJS, Mongoose - How to define a recursive model in Mongoose - javascript

I'm implementing GraphQL with Mongoose.
In the typedef.js, I specified the Channel structure like this:
import { gql } from 'apollo-server-express';
export const Channel = gql`
type Channel {
"Name of the channel, this is the full path."
name: String!
parent: Channel
children: [Channel]!
}
`
It's clear that when I defining the model Channel, I also used it as the data structure of its elements.
And I would like to know how to write the model of it in mongoose.
What I'm writing now is like this:
//models/Channel.js
import mongoose from 'mongoose';
export const Channel = mongoose.model('Channel',
{
name: String,
parent: String,
children: [String]
});
Which I don't think is correct.
Note: I'm writing in ES6 of Javascript.

Related

Property 'save' does not exist on type 'CardType'

In a function I have, I'm doing some stuff with the properties of an object of type CardType. Then, I want to be able to save those changes into my MongoDB database using the document.save() function. However, I have my code like this:
import { Types } from 'mongoose';
import Card from '../models/Card';
static async updatePriceForCardByID(cardID: Types.ObjectId) {
const card = await Card.findById(cardID).exec();
return updatePriceForCard(card!);
}
static async updatePriceForCard(card: CardType) {
// This function needs some properties of 'card', it only updates the price of the card
const newPrice = await StarCityController.getUpdatedPriceForCard(card.name, card.number);
card.prices.push(newPrice);
card.save(); // <-- Error appears here
return Card.findById(card._id).populate({ path: 'prices' }).exec();
}
and my CardType type like this:
import { Types } from 'mongoose';
export type CardType = {
_id: Types.ObjectId,
name: string,
prices: [Types.ObjectId],
number: string,
sku: string
}
the card.save() function is not working anymore like it was when my code was in JS. Now, I know that I'm passing a CardType as my parameter on my updatePriceForCard function, and as such, instead of a mongoose Document, I'm passing a variable of a type that doesn't have a save property in it.
My question is, what would be the best approach to tackle something like this?
You will have to add the mongoose Document type to your CardType.
import { Types, Document } from 'mongoose';
export type CardType = {
_id: Types.ObjectId,
name: string,
prices: [Types.ObjectId],
number: string,
sku: string
} & Document
This will add all the necessary document methods to the type. Make sure to import the Document type from mongoose, otherwise it may use the DOM-Document type which will not work.
Playground

Creating Schema in Node/MongoDb

So, I am just starting with Node/Mongo db and typescript, and I am kind of stuck in creating a Schema for this nested JSON Database. I have started something and, I would like to know if I am on the right track. So please if anyone would help me it would be great. Thanks in Advance
Data
[{
"a_class":[
{
"brand":"A-class",
"id":"1",
"year":"2015"
"price":"12665"
...
"engine_spec":{
...
}
...
}]
}
]
Interfaces
import { Document } from 'mongoose';
export default interface ICars extends Document {
a_class: BrandList[],
}
interface BrandList {
brand: string;
id:number;
year:number;
main_image: string;
price:number;
transmission:string;
fuel_type:string;
seating_capacity:number;
engine:number;
engine_specs:Specs;
volume_weights:Volume;
performance: Performance
images_interior: Interior
images_exterior: Exterior
}
interface Specs {
power:number;
power_per_litre:number;
torque:number;
fuel_system:number
}
interface Volume {
max_weights:number;
fuel_tank:number
}
interface Performance {
acceleration:number;
maximum_speed:number;
fuel_urban:number;
fuel_extra_urban:number
fuel_combined:number
}
interface Interior {
image_one:string;
image_two:string;
image_three:string;
image_four:string;
}
interface Exterior {
image_one:string;
image_two:string;
image_three:string;
image_four:string;
}
Schema
import mongoose, { Schema } from 'mongoose';
import ICars from '../interfaces/cars'
const CarsSchema: Schema = new Schema({
a_class:Array,
brand: String,
year:Number,
})
export default mongoose.model<ICars>('Cars', CarsSchema);
Get Route, So when I try to get all the data through postman, it is not allowing me, it is saying that the route is not being found 404. I have imported it to the server.js import carsRouter from './routes/cars'; router.use('./cars',carsRouter ), I don't know where the error could be
import express from 'express'
import Cars from '../models/cars'
const router = express();
router.get('/cars', (req:any, res:any )=>{
Cars.find()
.then(car=> res.json(car))
.catch(err => res.status(400).json(`Error: ${err}`))
})
export=router
According to mongoose document, we should avoid extending mongoose Document.
This approach works, but we recommend your document interface not
extend Document. Using extends Document makes it difficult for
Mongoose to infer which properties are present on query filters, lean
documents, and other cases.
We recommend your document interface contain the properties defined in
your schema and line up with what your documents look like in MongoDB.
Although you can add instance methods to your document interface, we
do not recommend doing so.
For more information, please reference: https://mongoosejs.com/docs/typescript.html#using-extends-document
And It seems that we can use mongoose sub documents for this requirement. For more information regarding the sub documents, please reference: https://mongoosejs.com/docs/subdocs.html#subdocuments
So in this case, we can rephrase your code a little bit:
Interfaces:
export default interface ICars {
a_class: BrandList[],
}
// The other interfaces can be kept
Schema:
const Exterior = new Schema({
// own fields
});
const Interior = new Schema({
// own fields
});
const Performance = new Schema({
// own fields
});
const Volume = new Schema({
// own fields
});
const SpecsSchema = new Schema({
// own fields
});
const BrandListSchema = new Schema({
// other fields
engine_specs: [SpecsSchema],
volume_weights: [VolumeSchema],
performance: [PerformanceSchema],
images_interior: [InteriorSchema],
images_exterior: [ExteriorSchema],
});
const CarsSchema: Schema = new Schema({
a_class: [BrandListSchema],
});

How to use another model in model definition in Mongoose

I'm writing mongoose in Node.js, ES6.
I first specified a model called Address, and would like to use the Address model in the definition of another model, Channel.
The codes are like the following:
// Definition of Address
import mongoose from 'mongoose';
export const Address = mongoose.model('Address',
{
id: mongoose.SchemaTypes.ObjectId,
customer_id: String,
addresses: [{
address_type: String,
address_info: String,
}]
});
For another model Channel, I would like to have a subscriber field, which is a list of Address.
My tentative code is like the following:
import mongoose from 'mongoose';
import {Address} from './Address.js';
export const Channel = mongoose.model('Channel',
{
id: mongoose.SchemaTypes.ObjectId,
name: String,
path: String,
subscribers: [Address],
});
However, I got error like this:
TypeError: Invalid schema configuration: `model` is not a valid type within the array `subscribers`
I wonder how should I implement the idea in NodeJS?
If I got it right, you want each channel have an array of addresses specified to it. so you have to specify address field in your channel this way:
import mongoose from 'mongoose';
//import {Address} from './Address.js';
export const Channel = mongoose.model('Channel',
{
id: mongoose.Schema.Types.ObjectId,
name: String,
path: String,
subscribers: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Address'
}],
});
you do not need Address model imported into your Channel model, MongoDB will recognize it automatically. then when you want to create a channel document create it like this:
import {Address} from './Address';
import {Channel} from './Channel';
async function createChannel(){
Channel.create({
name: 'theName',
path: 'thePath',
subscribers: [await Address.find()] //you can add all addresses by just use find or use your specific query to find your favored addresses.
})
}

Add custom GraphQL resolvers and types into Prisma/Nexus schema

Using: TypeScript, Prisma, MySQL, GraphQLServer, ApolloClient, building schema this way:
const schema = makePrismaSchema({
// Provide all the GraphQL types we've implemented
types: [Query, Mutation, User, Post],...
And then:
const server = new GraphQLServer({
schema,
context: { prisma }
});
How to combine that with custom resolvers and types unrelated to the SQL?
(I would like to call some REST endpoint by the GQL as well)
While nexus was created to be used alongside prisma, it's really just a schema builder. You could easily use it to create a schema without even utilizing Prisma. For example:
export const User = prismaObjectType({
name: 'User',
definition(t) {
t.list.field('comments', {
type: 'Comment',
resolve(root, args, ctx) {
return getComments();
},
});
},
})
export const Comment = prismaObjectType({
name: 'Comment',
definition(t) {
t.string('body');
},
})
Here getComments can return an array of comment objects, or a Promise that resolves to one. For example, if you're calling some other API, you'll normally return a Promise with the results of the call. As shown above, the resolver exposes the parent value, the field's arguments and a context object -- you can use any of this information in determining how to resolve a particular field.

graphqljs - Query root type must be provided

How do I get around this error? For this particular schema, I do not need any queries (they're all mutations). I cannot pass null and if I pass an empty GraphQLObjectType it gives me the error:
Type Query must define one or more fields.
If you're using graphql-tools (maybe other SDL tools too), you can specify an empty type (such as Query) using:
type Query
Instead of
type Query {}
If you're building the schema programatically you'll have to add dummy query, much like:
new GraphQLObjectType({
name: 'Query',
fields: {
_dummy: { type: graphql.GraphQLString }
}
})
Which is the programmatic equivalent of the SDL:
type Query {
_dummy: String
}
If you are using Node.js express -
Your schema should have both Root query and root Mutation, although you don't have any query resolvers but still you need to define the root query.
E.g. -
type RootQuery {
hello: String!
}
schema {
query: RootQuery
mutation: RootMutation
}
If you're using express you can do this:
const {GraphQLObjectType, GraphQLSchema, GraphQLString} = require("graphql")
const Schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "Query",
fields: () => ({
message: {
type: GraphQLString,
resolve: () => "Hello World"
}
})
})
})
Use GraphQLObjectType. And the property Query should be in lowercase as query.

Categories

Resources