How to query Enum using Prisma? - node.js

The goal is simple, I want to query enum field in the where clause. Example
prisma version is: "prisma": "3.11.0"
schema.prisma
model Todo {
id Int #default(autoincrement()) #id
title String
content String
status TodoStatus #default(IN_PROGRESS)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
enum TodoStatus {
IN_PROGRESS
DONE
}
api call
app.get('/todos', (req, res){
const { status } = req.query
const todos = await prisma.todo.findMany({
where: { status: status },
orderBy: {
id: "asc",
},
});
res.json(todos);
})
Frontend I use Next.js and it is a select option
example
<select
id="todo"
name="todo"
onChange={(e) => setSelectedStatus(e.target.value as Status)}
>
{[Status.IN_PROGRESS, Status.DONE].map((status: Status) => {
return (
<option value={status}>{todoStatusToString[status]}</option>
);
})}
</select>
The enum value from Next.js
export enum Status {
IN_PROGRESS = "IN_PROGRESS",
DONE = "DONE",
ALL = "ALL",
}
export const todoStatusToString = {
[Status.IN_PROGRESS]: "In progress",
[Status.DONE]: "Done",
[Status.ALL]: "All",
};
the req.query will be sent from clientside in this format
localhost:3000/todos?status=DONE
{ status: "DONE" }
or
localhost:3000/todos?status=IN_PROGRESS
{ status: "IN_PROGRESS" }
I know that Prisma is built-in to be typesafe. So my assumption is because the data that we get from the frontend is a string type while the enum on Prisma is looking for either IN_PROGRESS or DONE specifically if we send "DECLINED" to status where clause it will spit the same error.
Any help would be appreciated!

In this case you need to cast the type.
You can do it like this
where: { status: status as unknown as TodoStatus}

Related

Do i need parse int all the time in Prisma?

My prisma model
model Todo {
id Int #default(autoincrement()) #id
title String
content String
status TodoStatus #default(IN_PROGRESS)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
The api call
app.get("/todos/:id", async (req: Request, res: Response) => {
const id = parseInt(req.params.id); // this line
const todo = await prisma.todo.findFirst({
where: { id },
});
res.json(todo);
});
Do I have to always parseInt the id? because by default req.params is a string type, and obviously my Todo primary key is int. I don't see any online resources parsing the int and then only passing to the Prisma query, it feels a bit odd to always parsing the req.params.id to int all the time in every api call.
You can use
prisma.todo.findUnique({
where: { id: +id }
});
Note that you have to specify the field you want to find.
You can use Express Query Parser to automatically parse strings to valid types.
This would automatically convert query params to the valid type.
Example:
// without this parser
req.query = {a: 'null', b: 'true', c: {d: 'false', e: '3.14'}}
// with this parser
req.query = {a: null, b: true, c: {d: false, e: 3.14}}

Prisma query returns null, when data exists

I've built a Nestjs app with Prisma and GQL. I just changed computer (from a Mac to PC) and all of a sudden my login mutation seems to have stopped working, literally the only one... Here is some code:
auth.resolver.ts:
#Mutation((returns) => AuthenticatedUser)
async login(
#Context() { res }: Auth.GqlContext,
#Args('payload', { type: () => UserLoginDto }) payload: UserLoginDto
): Promise<AuthenticatedUser> {
const authenticatedUser = await this.authService.login(payload)
res.cookie('jwt', authenticatedUser.jwt, cookieConfig)
return authenticatedUser
}
auth.service.ts:
public async login(payload: UserLoginDto): Promise<AuthenticatedUser> {
const { password, email } = payload
Logger.log({ password, email })
const user = await this.usersService.getUser(email) // returns null since the change
Logger.log(JSON.stringify(user))
const isValidPassword = await this.verifyPassword(user.password, password) // error is thrown here
if (!isValidPassword) {
throw new AuthenticationError('Password does not match')
}
const { id } = user
const jwt = await this.jwtService.signAsync(
{ id, email },
{
secret: process.env.JWT_SECRET
}
)
return {
jwt,
user
}
}
users.service.ts:
public async getUser(email: string): Promise<User | null> {
const { user } = this.prismaService
Logger.log(email) // returns 'email#gmail.com'
const temp = user.findUnique({ where: { email } }) // returns null here
Logger.log(`PRISMA USER: ${JSON.stringify(temp)}`) // null
return temp
}
What's weird is that I have the same queries set up for posts and categories, but those work fine. Nothing to do with JWT either.
My user is in the DB too:
Full GQL mutation:
mutation {
login(payload: { email: "email#gmail.com", password: "123" }) {
jwt
user {
name
id
email
}
}
}
GQL error:
{
"errors": [
{
"message": "Cannot read property 'password' of null",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"login"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'password' of null",
" at AuthService.login (H:\\work\\blog-api\\src\\auth\\auth.service.ts:25:60)",
" at AuthResolver.login (H:\\work\\blog-api\\src\\auth\\auth.resolver.ts:43:31)",
" at target (H:\\work\\blog-api\\node_modules\\#nestjs\\core\\helpers\\external-context-creator.js:77:28)",
" at H:\\work\\blog-api\\node_modules\\#nestjs\\core\\helpers\\external-proxy.js:9:24"
]
}
}
}
],
"data": null
}
Prisma schema:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Post {
id String #id #default(uuid())
title String
slug String #unique
description String
relativeImage String #map("relative_image")
rawMdx String #map("raw_mdx")
published Boolean #default(true)
views Int #default(0)
likes Int #default(0)
createdAt DateTime #default(now()) #map("created_at")
updatedAt DateTime #updatedAt #map("updated_at")
categories Category[]
}
model Category {
id String #id #default(uuid())
name String #unique
color String
createdAt DateTime #default(now()) #map("created_at")
updatedAt DateTime #updatedAt #map("updated_at")
posts Post[]
}
model User {
id String #id #default(uuid())
name String #unique
email String #unique
password String
createdAt DateTime #default(now()) #map("created_at")
updatedAt DateTime #updatedAt #map("updated_at")
}
I've also run npx prisma db pull && npx prisma generate since switching computers...
Update
After about an hour this issue disappeared. If anyone knows why I had this issue, I'll love to know why.

How to get around the "Missing field" warning when executing a mutation with an optimiticResponse in the apollo-client?

I'm using aws-appsync with the apollo-client and when I try to execute a mutation without providing all fields I get a warning like "Missing field x in {...}". Do I really need to provide all (including optional) fields? How can I handle this gracefully?
I wonder if this is the expected behaviour or wether I'm missing something obvious. I don't want to maintain the added complexity of having to pass all optional fields and having those fields stored in the database as null values.
I figured since they are just warnings I 'll just ignore them but I found that the updates would be executed in the database, but then the inmemorycache cache would not always update. It would sometimes show the update and other times not.
import {compose, graphql} from "react-apollo";
import gql from "graphql-tag";
import React from "react";
export const EditCard = (props) => {
const handleSave = () => {
props.update({
givenName :'someGivenName',
//middleName omitted on purpose
familyName :'someFamilyName',
});
};
return (
<>...more stuff here...</>
);
};
export const card = gql`
fragment card on Identity{
givenName
middleName
familyName
}
`;
export const CardsGraphQL = gql`
query GerCards {
cards: listIdentitys(filter: {type: {eq: "CARD"}}) {
items {
...card
}
}
}
${card}
`;
export const UpdateCardGraphQL = gql`
mutation UpdateCard($input: UpdateIdentityInput!) {
updateObject: updateIdentity(input: $input) {
...card
}
}
${card}
`;
export const selectConfig = () => {
return {
options: {
fetchPolicy: 'cache-and-network',
},
props: (props) => {
return {
cards: props.data.cards ? props.data.cards.items : [],
};
},
};
};
export const updateConfig = (query) => {
return {
options: {
update: (cache, {data: {updateObject}}) => {
// Read query from cache
const data = cache.readQuery({query});
// Add updated object to the cache
data.cards.items = data.cards.items.map(item => item.id === updateObject.id ? updateObject : item);
//Overwrite the cache with the new results
cache.writeQuery({query, data});
},
},
props: (props) => {
return {
update: (input) => {
props.mutate({
variables: {input},
optimisticResponse: () => ({
updateObject: input,
}),
});
},
};
},
};
};
export default compose(
graphql(CardsGraphQL, selectConfig),
graphql(UpdateCardGraphQL, updateConfig(CardsGraphQL)))
(EditCard);
For GraphQL this mutation seems to run without problems and the result in the dynamoDB is what I expect:
{
givenName :'someGivenName',
familyName :'someFamilyName'
}
However the cache is not always updated with the mutation result and the apollo-client shows the warning:
"Missing field middleName in {..."
If I add the middleName field, the warning goes away and the cache updates correctly but the result in the dynamoDB is:
{
givenName :'someGivenName',
middleName : null,
familyName :'someFamilyName'
}
This approach results in additional complexity in my client that I would like to avoid maintaining.
Does anyone else have this problem? How to solve this gracefully?
Any help is appreciated.

Graphiql variables not being passed to server

I'm building an Apollo Server. I have one simple endpoint communicating with Mongo. There's a collection of announcements.
export const typeDefs = gql`
type Query {
announcements: [Announcement]
announcementsByAuthor(author: String!): [Announcement]
}
type Announcement {
_id: ID!
msg: String!
author: String!
title: String
}
`;
export const resolvers = {
Query: {
announcements: () => {
return new AnnouncementController().getAnnouncements();
},
announcementsByAuthor: (author: string) => {
console.log('RESOLVER: ', author);
return new AnnouncementController().getAnnouncementsByAuthor(author);
}
},
}
In my graphiql interface, the announcements query works correctly:
{
announcements {
msg
author
}
}
The announcementsByAuthor query does not seem to be accepting the string argument, either from a variable or when hardcoded into the query.
query($author: String!){
announcementsByAuthor(author: $author) {
msg
author
}
}
Variables:
{
"author":"Nate"
}
I've logged out from the resolver, and an empty string is being passed in, instead of the specified value for the author variable. I'm new to graphql and I'm hoping someone can enlighten me as to what I'm sure is a simple oversight.
Try this instead:
announcementsByAuthor: (doc, {author}) => {

Graphql: How can get field arguments of a type in resolver?

I use graphql-tools library and makeExecutableSchema function to make my schema by passing schema and resolver to it
here is my schema:
type Trip {
code: String!
driver: User!
vehicle: Vehicle!
destination: Location!
passengers(page: Int, count: Int): [User!]!
}
type Query {
trip(id: String!): Trip
}
and here is my resolver:
// some imports...
export default {
Query: {
async trip(_, { id }, ctx, info) {
const trip = await Trip.findById(id);
// const page = ???, count = ???
// work on fetch data...
return result;
},
};
how can I get page and count which are defined as nested argument for passengers?
You should define a resolver for the type Trip, such as:
export default {
Query: {
async trip(_, { id }, ctx, info) {
const trip = await Trip.findById(id);
// const page = ???, count = ???
// work on fetch data...
return result;
},
Trip: {
async passengers(trip, { page, count }, ctx, info) {
...
},
}
};
In GraphQL, it's not the concept of "nested fields of a type", but just combinations of "the type of a field". The trip field of type Query has the Trip type, so when you want to work with the passengers field, it should be considered as a field under the Trip type, not a nested field of the Query type.

Resources