Integrating Notion into my Site
Over the course of the last few weeks I’ve been revising how several of my pages get their data. Instead of having to manually edit a JSON file that’s in my code base, I’ve been pulling from Notion’s API. The benefit of this is that it’ll be easier to update the pages. I figure I’ll show how I did it in hopes of helping others.
Since it's a relatively simple page, let's work with my movies page. To sum it up, it's a page that list the movies I've watched sorted by date watched descending, grouped by year.
Each movie has seven properties that I care about for the page.
- Status (To Watch, Awaiting Review, & Watched)
- Date Watched
- Cover URL
- IMDb Link
- Rating (Numeric 1-5)
Fortunately for me, Notion has an npm package to make it easy.
npm i @notionhq/clientoryarn add @notionhq/client
This provides a nice library to make the calls easy. The issue I did find though was that it doesn't give me types for the various properties I can work through.
Building the Types
To make my job a little easier, I got some calls working and looked at the JSON returned from Notion's API and built some types. Now these types are not complete. They do have more properties attached to them. I just defined the minimum I needed for my purpose. If and when you make your calls, it would be useful to add the additional properties as you see fit.
These types are used for me defining the response that I expect back from Notion.
As you can see, I define a movie being returned by Notion to have an ID, and then a set of properties each having one of the shared types I created in the
I also created the type
NotionMovieApiResponse which defines the actual response I get back from the Notion API call. It contains two properties. The cursor (
nextCursor) that I would need to pass in if there are more records so Notion knows where to start the return for pagination. The other property is an array of the actual movie object.
Querying the Notion API
Now for the fun part, making the call to Notion and returning the data I need for the page.
I have a file called
notion.ts that contains all my calls to Notion. In there I have an import to bring in the
Client object from the
I created a function called
fetchMoviesFromNotion that takes the cursor as an optional parameter. Remember above when I talked about the
nextCursor property that I need to pass in to handle pagination or multiple calls? That's what this is.
The return for this function is a
Taking a look at this, you can see that the first thing I do is create the Notion client by passing in my Notion API key.
The next step is for me to create the query.
Now just to provide some clarity, I have a property on each movie called "Status" that I don't bother with on my site. I have 4 statuses for movies.
- To Watch
- Awaiting Review
- Couldn't Finish
Whenever I come upon a movie I want to watch, I add it to my Notion database with the status "To Watch". If I start the movie and really just don't like it, to the point of being unable to finish it, I throw it into the "Couldn't Finish" status to let me know that I didn't like it. "Awaiting Review" is where the movie sits after I watch it and stew a bit as to what my thoughts were. "Watched" is when I've put in my rating and thoughts. These are the movies that I want returned to the site.
To query a Notion database, I call the
notion.databases.query() function. This takes at minimum a database ID.
In my use case, I also pass in the cursor as the
start_cursor. If it's not provided, Notion will start from the beginning.
The next argument is
filter. This can either be an array of filters, or a single filter. In my use case, it's a single filter. I tell it the property I want to filter on, in this case, the "Status" property. The next argument within the filter,
select, I tell it I want all database rows where the status equals "Watched".
The final argument is
sorts. Rather than sorting manually after bringing everything back, I let Notion do that for me. This is an array so you can sort by multiple properties if you choose. For me, I just want to sort it by the
DateWatched property in a descending order so the most recent are at the start of the results.
The results from Notion's call are then assigned to the
response variable. For me to handle the function's return.
The first thing I do is default the
null. I then check to see if Notion is telling me there are more rows left in my query. If there are, I assign the cursor from the response; if not, I leave it as
The next line I'm not super proud of. Basically to make TypeScript happy, I assign the results from Notion as type
unknown and then immediately assign them the type
NotionMovie. There might be better ways, but this works well enough.
I then send the data back for processing.
Putting it All Together
The main function that handles bringing back the movies is called
getMovies(). It returns a list of movies defined as:
The first thing I do in the function is declare the movie array that I will be returning to the page. Throughout the function I'll be adding records to it. I also declare the next cursor, but leave it undefined.
I use a
do-while loop to retrieve all the records from Notion. I always will be making at least one call to Notion so I want to make sure it always makes that call. Subsequent calls will be determined by whether the cursor returned is either null or if it contains a value.
I have every call to Notion's API re-assign the cursor returned from Notion assigned to the
nextCursor variable and then go through the results, parsing the results to a movie object, and then added to the array.
This is the
mapResultToMovie() function to help keep the
getMovies() code a little cleaner:
This function maps the values returned from Notion to the object I defined to hold the movies. If you look up at the Notion types I defined above, you can see how the various different properties' values are found. For the
yearWatched properties, I use the handy dayjs library to format the date and get the year.
From there, I return the movies array to the page and display the movies grouped by year.
You can view the source to my site on it's GitHub Page