Free preview
Add FeedTag and BundleTag relations
Part of End to End React with Prisma 2
Objective: Create tags for our feeds and bundles.
Next, let’s add tags to Bundles and Feeds. Recall, that in the schema.prisma file we have FeedTags and BundleTags and they are actually separate tables in our database so we will need relations to hook them up to our feeds and bundles.
We will need to update the typeDefs file in order to let graphQL know that both feeds and bundles can have tags and also when we create either a feed or a bundle that we can attach an existing tag and/or create a new tag along with it.
This ability to either attach or create new tags all in one go is part of what makes Prisma so amazing. The syntax for creating a new tag is create while attaching an existing one is attach. Below is what the updated typeDefs file looks like- warning it is pretty verbose but the only reason why is that we need to create new types for all of the nested create/attach types for both the feed and bundle items.
Next, we should add the feeds object on the bundle. We are limiting the input parameter so that we can only add existing feeds to a new bundle but that once created, you can always look up how many feeds are in a bundle and how many bundles have a particular feed in it.
utils/api/typeDefs.ts
import { gql } from 'apollo-server-micro'
export const typeDefs = gql`
type Feed {
id: String
name: String
url: String
author: User
tags: [FeedTag]
bundles: [Bundle]
}
type Bundle {
id: String
name: String
description: String
author: User
tags: [BundleTag]
feeds: [Feed]
}
type User {
id: String
auth0: String
nickname: String
picture: String
bundles: [Bundle]
feeds: [Feed]
}
type BundleTag {
id: String
name: String
bundles: [Bundle]
}
type FeedTag {
id: String
name: String
feeds: [Feed]
}
input FeedInput {
id: String
}
input BundleInput {
id: String
}
input FeedCreateInput {
id: String
url: String
name: String
tags: NestedFeedTagCreateInput
}
input NestedFeedTagCreateInput {
create: [FeedTagCreateInput]
connect: [FeedTagWhereUniqueInput]
}
input FeedTagCreateInput {
id: String
name: String
}
input FeedTagWhereUniqueInput {
id: String
name: String
}
input BundleCreateInput {
id: String
name: String
description: String
tags: NestedBundleTagCreateInput
feeds: NestedBundleFeedCreateInput
}
input NestedBundleTagCreateInput {
create: [BundleTagCreateInput]
connect: [BundleTagWhereUniqueInput]
}
input BundleTagCreateInput {
id: String
name: String
}
input BundleTagWhereUniqueInput {
id: String
name: String
}
input NestedBundleFeedCreateInput {
create: [FeedCreateInput]
connect: [FeedWhereUniqueInput]
}
input FeedWhereUniqueInput {
id: String
url: String
}
type Query {
hello: String
feed(data: FeedInput): Feed
bundle(data: BundleInput): Bundle
feeds: [Feed]
bundles: [Bundle]
}
type Mutation {
clearAll: String
createFeed(data: FeedCreateInput): Feed
createBundle(data: BundleCreateInput): Bundle
}
`
utils/api/resolvers.ts
export const resolvers = {
Feed: {
...createFieldResolver('feed', 'author'),
...createFieldResolver('feed', 'tags'),
...createFieldResolver('feed', 'bundles'),
},
Bundle: {
...createFieldResolver('bundle', 'author'),
...createFieldResolver('bundle', 'tags'),
...createFieldResolver('bundle', 'feeds'),
},
BundleTag: {
...createFieldResolver('bundleTag', 'bundles'),
},
FeedTag: {
...createFieldResolver('feedTag', 'feeds'),
},
Query: {
hello: (parent, args, context) => `hi!`,
feed: (parent, { data: { id } }, { prisma }) =>
prisma.feed.findOne({ where: { id } }),
bundle: (parent, { data: { id } }, { prisma }) =>
prisma.bundle.findOne({ where: { id } }),
feeds: (parent, args, { prisma }) => prisma.feed.findMany(),
bundles: (parent, args, { prisma }) => prisma.bundle.findMany(),
},
Mutation: {
clearAll: async (parent, args, { prisma }) => {
await prisma.savedArticle.deleteMany()
await prisma.bundle.deleteMany()
await prisma.feed.deleteMany()
await prisma.bundleTag.deleteMany()
await prisma.feedTag.deleteMany()
await prisma.user.deleteMany()
return 'done!'
},
createFeed: async (parent, { data }, { prisma, user }) => {
const author = { author: { connect: { id: user.id } } }
const result = await prisma.feed.create({ data: { ...data, ...author } })
return result
},
createBundle: async (parent, { data }, { prisma, user }) => {
const author = { author: { connect: { id: user.id } } }
const result = await prisma.bundle.create({
data: { ...data, ...author },
})
return result
},
},
}
Let’s do a sanity check and make sure all these relations are working the way that we think they are. You’ll notice that I snuck in a new mutation called clearAll. This is an easy way to delete the database so we’ll have a clean slate. We’ll use this here- call the mutation mutation { clearAll } and you should see a response of all done!.
mutation createFeedMutation($data: FeedCreateInput) {
createFeed(data: $data) {
...FeedFragment
}
}
fragment FeedFragment on Feed {
id
name
url
tags {
...FeedTagFragment
}
author {
...AuthorFragment
}
}
fragment AuthorFragment on User {
id
auth0
picture
nickname
}
fragment FeedTagFragment on FeedTag {
id
name
}
{
"data": {
"name": "CNN Feed",
"url": "http://rss.cnn.com/rss/cnn_topstories.rss",
"id": "1",
"tags": {
"create": [
{ "id": "100", "name": "News" },
{ "id": "101", "name": "TV" }
]
}
}
}
We should see that a large block returns with the feed, author, and 2 feedTags in it. For illustrative purposes, let’s also see what this would look like if we were to use a feed that already exists. Run the following mutation next:
{
"data": {
"name": "NY Times",
"url": "https://rss.nytimes.com/services/xml/rss/nyt/World.xml",
"id": "2",
"tags": {
"connect": [{ "id": "100" }, { "id": "101" }],
"create": [{ "id": "102", "name": "Worldwide" }]
}
}
}
In the response we should see that there are 3 tags- the two existing News and TV along with the new Worldwide one.
Finally, let’s try creating a bundle with these 2 feeds attached to it.
mutation createBundleMutation($data: BundleCreateInput) {
createBundle(data: $data) {
...BundleFragment
}
}
fragment BundleFragment on Bundle {
id
name
description
feeds {
id
name
}
}
In the variables we are connecting to the 2 feeds that we’ve already created and then creating a new bundleTag with the name “World News”. Since bundleTags and feedTags are separate items, any tags you create in one are not available in the other and you’ll need to recreate them.
{
"data": {
"name": "hi",
"description": "This bundle is about fun stuff",
"id": "10",
"tags": {
"create": [{ "id": "201", "name": "World News" }]
},
"feeds": {
"connect": [{ "id": "1" }, { "id": "2" }]
}
}
}
This mutation should return successfully and you should see a new bundle connected to a new bundle tag and 2 existing feeds.