Solved in my second post
I still have some questions regarding Redis store though, any help appreciated!
I was using a cell with polling to grab recent notifications that have been assigned to a user. It looked like this:
export const QUERY: TypedDocumentNode<
FindNotificationsByOwnerQuery,
FindNotificationsByOwnerQueryVariables
> = gql`
query FindNotificationsByOwnerQuery($id: Int!) {
notificationsByOwner: notificationsByOwner(id: $id) {
...NotificationInfo
}
}
`
And the resolver:
export const notificationsByOwner: QueryResolvers['notificationsByOwner'] = ({
id,
}) => {
if (context.currentUser?.id !== id || !hasPermission('notifications:read')) {
throw new ForbiddenError('You are not authorized to perform this action')
}
return db.notification.findMany({
where: {
users: { some: { id: id } },
},
orderBy: {
updatedAt: 'asc',
},
})
}
Now I’m trying to switch over to subscriptions and have removed the cell in favor of a NotificationSub component which looks like this:
import { useEffect, useState } from 'react'
import { Notification } from 'types/graphql'
import { useSubscription } from '@redwoodjs/web'
import { useAuth } from 'src/auth'
const NEW_NOTIFICATION_SUBSCRIPTION = gql`
subscription ListenForNewNotifications($userId: Int!) {
newNotification(userId: $userId) {
id
content
title
createdAt
updatedAt
link
type
viewed
users {
id
}
}
}
`
const NotificationSub = () => {
const { currentUser } = useAuth()
const [notificationFeed, setNotificationFeed] = useState([])
const { data, error } = useSubscription(NEW_NOTIFICATION_SUBSCRIPTION, {
variables: { userId: currentUser?.id },
skip: !currentUser,
})
useEffect(() => {
if (error) {
console.error('Subscription error:', error)
}
}, [error])
useEffect(() => {
if (data) {
const notification = data.newNotification
if (notification) {
const newNotification: Notification = {
id: notification.id,
content: notification.content,
title: notification.title,
users: notification.user,
}
setNotificationFeed((prevFeed) => [...prevFeed, newNotification])
}
}
}, [data])
return (
<div>
{notificationFeed.map((notification) => (
<div key={notification.id}>{notification.content}</div>
))}
</div>
)
}
export default NotificationSub
And under api/src/subscriptions I have a newNotification.ts sub file:
import gql from 'graphql-tag'
import { User } from 'types/graphql'
import type { PubSub } from '@redwoodjs/realtime'
export const schema = gql`
type Subscription {
newNotification(userId: Int!): Notification! @requireAuth
}
`
export type NotificationChannel = {
newNotification: [
userId: number,
payload: {
id: number
title: string
content: string
users: User[]
}
]
}
export type NotificationChannelType = PubSub<NotificationChannel>
const newNotification = {
newNotification: {
subscribe: (
_,
{ userId },
{ pubSub }: { pubSub: NotificationChannelType }
) => {
return pubSub.subscribe('newNotification', userId)
},
resolve: (payload) => {
return payload
},
},
}
export default newNotification
I followed the docs and enabled realtime with the server file and haven’t touched those except for adding realtime to the createGraphQLHandler. A bit concerning to me was that when I hovered over the realtime import it said:
Only supported in a server deploy and not allowed with GraphQLHandler config
Issue
So, a notification can be sent to many users, hence the Users relationship on Notification. I make mutations in other parts of my codebase and I tried to follow the subscription demo examples to make sure the subscription resolver and component look correct. However, I am unable to get a payload when:
- I run a mutation elsewhere in the code that creates a new row in the Notification table
- I use prisma studio to insert a new row in the Notification table
Console and network dev tools don’t show anything, no payload or error. My current thinking is that I’m understanding the mutation → subscription incorrectly. A mutation could look like:
export const CREATE_NOTIFICATION = gql`
mutation CreateNotification($userIds: [Int!]!, $input: CreateNotificationInput!) {
createNotification(userIds: $userIds, input: $input) {
id
}
}
`
And then inside the resolver on the API side I connect it to the users by their ids. My question is, how does the subscription resolver pick up if a new row was inserted for specific user? Sure I send the userId with useSubscription as a variable, but on the resolver end I don’t understand how this line of code picks up on any changes in the database when a notification is added for a user that matches the userId?
return pubSub.subscribe('newNotification', userId)