In nestjs I created the following endpoint and when I hit it form swagger or postman return
invalid input syntax for integer: "NaN"
#Get('creatives-by-ids')
#UseGuards(AuthGuard('jwt'))
getCreativeByIds(#GetCurrentUser() user: CurrentUser, #Query() ids: number[]) {
return [];
}
I met the same issue recently and it turned out that it was due to the sequence of routes, which is not specific to Nest.js but generally for express.js. Your trouble might be the same as mine.
#Get(':id')
#UseGuards(AuthGuard('jwt'))
getCreativeById(#GetCurrentUser() user: CurrentUser, #Param('id') id: number) {
return [];
}
#Get('creatives-by-ids')
#UseGuards(AuthGuard('jwt'))
getCreativeByIds(#GetCurrentUser() user: CurrentUser, #Query() ids: number[]) {
return [];
}
If my assumtion is correct, you probably have GET(':id') just like above or similar.
In this case, latter one(#Get('creatives-by-ids')) is ignored, because the former one takes the priority and 'creatives-by-ids' is regarded as a param.
So the solution is to adjust the sequence of routes properly, moving the latter one up above the GET(':id').
My Solution for this problem:
#Get(':id') -> #Get('/:id')
Related
I am using nestjsx/crud to control my API endpoints, and I would like to call another service from an existing controller as I would like to receive a set of tags back which I will then do more processing on.
The controller that will return me back the tags:
#Crud({
model: {
type: Test,
},
})
#Controller("test")
export class TestController implements CrudController<Test> {
constructor(public service: TestService) {}
get base(): CrudController<Test> {
return this;
}
#Override()
getMany(
#ParsedRequest() req: CrudRequest,
) {
return this.base.getManyBase(req);
}
}
I then have this controller which calls the getMany endpoint and then does some work with the results.
#Get('gettest')
getTest( #ParsedRequest() req: CrudRequest ) {
const results = this.testService.getMany( {
filter: [ { field: 'name', operator: 'eq', value: 'Justin' } ],
});
//
// Do more processing with the results
//
}
The issue I am experiencing is passing the filter parameter into the getMany as it is of type CrudRequest, so I am getting this message:
Object literal may only specify known properties, and 'filter' does not exist in type 'CrudRequest'
I have tried a number of different options and this was a project using nestjsx/crud 2.x so have moved it to latest but cannot find any material on the best way of passing filter information between services.
Does anyone know what the best way of doing this is?
Many thanks
Am playing around with Typescript, Mongoose, NodeJS and Express, using the sample MongoDB data based on restaurants.
Am attempting to create simple CRUD operations, with the intent on returning All restaurants, then a specific restaurant and eventually filter/sort etc.
See the function below:
const fetchRestaurants = async (request: Request, response: Response): Promise<RestaurantInterface[]> => {
try {
const restaurants: RestaurantInterface[] = await RestaurantModel.find();
response.status(200).json({
restaurants
});
} catch (error) {
throw error;
}
}
If I remove this line Promise<RestaurantInteface[] (or switch strict: false) I have no issues otherwise it alternates between.
A function whose declared type is neither 'void' nor 'any' must return a value.ts(2355) and if I attempt to return the response Type 'Response<any, Record<string, any>>' is missing the following properties from type 'RestaurantInterface[]': length, pop, push, concat, and 26 more.ts(2740)
Do I have basically loosen up the ts compiler or is there something I've misunderstood?
// in another file
export interface RestaurantInterface {
name: string,
borough: string,
cuisine: string,
restaurant_id: string
address: Address,
grades: Grades[],
};
Well it is expected, because the function actually returns void. When you write function name(): Promise<...> it means that you should return something. Whereas you are just calling the json method of the Express. So it is expected.
I am trying to use the "or" operator in Joi ver.17.4.0
As you can see, in the code below, I want either or both of the attributes/properties to be allowed, but not neither.
The problem is that Joi does not allow a string to be empty. So, to have it empty, I need to:
Joi.string().allow('')
This makes it not empty according to the "or" operator. So I can not get the 'name' to be empty in the eyes of "or".
It won't validate properly.
It validates even when I do this (but it shouldn't):
validatePerson(createPerson(''));
Keep in mind that I'm actually validating POST input on a node express API, so this is some simplified code to illustrate the issue:
const Joi = require('Joi');
function createPerson(name, age) {
const person = { name: name, age: age };
console.log(person);
return person;
}
function validatePerson(person) {
const schema = Joi.object({
name: Joi.string().allow(''),
age: Joi.number(),
}).or("name", "age");
console.log(schema.validate(person));
return schema.validate(person);
}
validatePerson(createPerson('')); // This should fail validation but doesn't
validatePerson(createPerson()); // Should fail and does
validatePerson(createPerson('Bob')); // Should pass and does
validatePerson(createPerson('', 7)); // Should pass and does
validatePerson(createPerson('Bob', 7)); // Should pass and does
As far as I understand, you want to allow the name to be empty an string, only if the age exists.
To acheive that, you can use .when:
name: Joi.string().when('age', { is: Joi.exist(), then: Joi.allow('') })
This way, your first example will fail as you expected.
I've made simple CRUD app with React and Apollo client on NestJS server with GraphQL API.
I have this simple Mutations:
schema.gql:
type Mutation {
createUser(input: CreateUserInput!): User! // CreateUserInput type you can see in user.input.ts below
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): User!
}
user.input.ts:
import { InputType, Field } from "#nestjs/graphql";
import { EmailScalar } from "../email.scalar-type";
#InputType()
export class CreateUserInput {
// EmailScalar is a custom Scalar GraphQL Type that i took from the internet and it worked well
#Field(() => EmailScalar)
readonly email: string;
#Field()
readonly name: string;
}
"EmailScalar" type checks if "email" input has *#*.* format basically
And when i make createUser Query to GraphQL API like this:
It cannot pass validation
(because Email type works fine)
But when Query sent from client - it passes validation:
NestJS server log (from code below)
users.resolver.ts:
#Mutation(() => User)
async createUser(#Args('input') input: CreateUserInput) { // Type from user.input.ts
Logger.log(input); // log from screenshot, so if it's here it passed validation
return this.usersService.create(input); // usersService makes requests to MongoDB
}
And it gets into MongoDB
Here is client side part:
App.tsx:
...
// CreateUserInput class is not imported to App.tsx (it is at server part) but it seems to be fine with it
const ADD_USER = gql`
mutation AddMutation($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
`
function App(props: any) {
const { loading, error, data } = useQuery(GET_USERS);
const [addUser] = useMutation(
ADD_USER,
{
update: (cache: any, { data: { createUser } }: any) => {
const { users } = cache.readQuery({ query: GET_USERS });
cache.writeQuery({
query: GET_USERS,
data: {
users: [createUser, ...users],
},
})
}
}
);
...
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return <UserTable users={data.users} addUser={addUser} updateUser={updateUser} deleteUser={deleteUser} />;
}
Can someone please explain to me, how does client Query passes validation and what have i done wrong?
Even two empty strings can pass through.
Never worked with NestJS, Apollo, React or GraphQL before, so I'm kinda lost.
For full code:
https://github.com/N238635/nest-react-crud-test
This is how your custom scalar's methods are defined:
parseValue(value: string): string {
return value;
}
serialize(value: string): string {
return value;
}
parseLiteral(ast: ValueNode): string {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError('Query error: Can only parse strings got a: ' + ast.kind, [ast]);
}
// Regex taken from: http://stackoverflow.com/a/46181/761555
var re = /^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
if (!re.test(ast.value)) {
throw new GraphQLError('Query error: Not a valid Email', [ast]);
}
return ast.value;
}
parseLiteral is called when parsing literal values inside the query (i.e. literal strings wrapped in double quotes). parseValue is called when parsing variable values. When your client sends the query, it sends the value as a variable, not as a literal value. So parseValue is used instead of parseLiteral. But your parseValue does not do any kind of validation -- you just return the value as-is. You need to implement the validation logic in both methods.
It would also be a good idea to implement the serialize method so that your scalar can be used for both input and response validation.
Good morning.
I'm quite new to NodeJS / sequelize world and I'm currently facing a problem while trying to display a dashboard on screen.
This dashboard has three filters: two dates (period), client name, and employee name. The user can select none, one, two, or all the filters and my database needs to work accordingly.
That being said, my problem is with Sequelize because I don't know how to treat this problem of parameters not being "always" there.
I've seen this question:
Sequelize optional where clause parameters?
but this answer doesn't work anymore. I also tried another way of building the where clause, but I failed on it as well (mainly due to sequelize operators).
The last thing I tried was to make a single query with all parameters included but try to find some value (or flag) that would make sequelize ignore the parameter, for the case when the parameter was no there*, but it looks like Sequelize doesn't have anything like that.
* I've read a question here that has an answer saying that {} would do the trick but I tried that as well but didn't work.
In summary: I need to make a query that can "change" over time, for example:
Foo.findAll({
where: {
id : 1,
}
});
Foo.findAll({
where: {
id {
[Op.in] : [1,2,3,4,5]
},
name: "palmeiira",
}
});
Do you know a way of doing it without the need of using a lot if / switch statements?
I'm currently using Sequelize v. 5.5.1.
Update
I tried doing as suggested by #Anatoly and created a function to build the parameters. It was something like that. (I tried a "smaller" version just to test)
async function test() {
const where = {};
where[Op.and] = [];
where[Op.eq].push({
id: {
[Op.in]: [1,2,3]
}
});
return where;
}
I setted the return value to a const:
const query = await test()
And tried console.log(query)
The result was: { [Symbol(and)]: [ { id: [Object] } ] }, which made me believe that the problem was parsing the Op part so i tried using 'Op.and' and 'Op.in' to avoid that and it solved this problem, but led to another on sequelize that said Invalid value
Do you have any idea where is my error ?
P.S.: #Anatoly very nice idea you gave me on original answer. Thank you very much.
If these three conditions should work together then you can use Op.and with an array of conditions:
const where = {}
if (datesFilter || clientNameFilter || employeenameFilter) {
where[Op.and] = []
if (datesFilter) {
where[Op.and].push({
dateField: {
[Op.between]: [datesFilter.start, datesFilter.finish]
}
})
}
if (clientNameFilter) {
where[Op.and].push({
name: {
[Op.iLike]: `%${clientNameFilter.value}%`
}
})
}
if (employeenameFilter) {
where[Op.and].push({
employeeName: {
[Op.iLike]: `%${employeenameFilter.value}%`
}
})
}
}
const dashboardItems = await DashboardItem.findAll({ where }, {
// some options here
})
If the conditions should work as alternatives then just replace Op.and with Op.or