Tue Dec 19 2023
In my last post, I explained a bit about how I was using Adonis as a headless CMS to power this blog and that going forward I’d be adding features to it.
This weeks edition of What did I add to my blog this week is a Post Scheduling feature. In short, I want to be able to stockpile articles ready to publish so that I’m not rushing on a Monday evening to publish an article (All posts come out on a Monday).
So I set about building a post scheduling feature in Adonis, the following will detail how I did it.
The first step was to add a “PostAt” field to the Post table, this is a Datetime field and would store the date & time I wanted to publish the post.
To do this, I ran Adonis’ CLI “ace”; node ace make:migration Post
. This creates a new file in database/migrations along the lines of [TIMESTAMP]_posts.ts.
In this file, I changed the scheme function from createTable to alterTable, and underneath added a line of code to add the post_at
field, like so;
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
export default class extends BaseSchema {
protected tableName = 'posts'
public async up () {
// Alter table, not create
this.schema.alterTable(this.tableName, (table) => {
// Add timestamp field 'post_at' to existing Post table
table.timestamp('post_at', { useTz: true })
})
}
public async down () {
this.schema.dropTable(this.tableName)
}
}
With this saved, I ran node ace migration:run
to run the latest migration files on the database.
Once that was done, I went to the models/Post file and added a column for the new PostAt field.
...
@column.dateTime()
public postAt: DateTime;
...
For the scheduling part of the feature, I went with node-cron and the following rule, 1 9 * * *
, which will run the function at 9:01 every morning. Within this function, I check the database for posts that haven’t been published yet and have a postAt datetime that falls within the past 10 minutes.
Adonis doesn’t come with cron jobs already built-in, so I installed node-cron and created a new Cron folder in /App
/App/Cron/index.ts
scheduler.scheduleJob("1 9 * * *", async function () {
console.log("Running Post job");
const now = new Date();
const tenMinutesAgo = new Date(now.getTime() - 10 * 60 * 1000);
const posts = await Post.query()
.where("published", false)
.where("postAt", ">=", tenMinutesAgo);
if (!posts.length) {
return console.log("No posts to publish");
}
posts.forEach(async (post) => {
post.published = true;
Event.emit("published:post", post);
return await post.save();
});
});
You may have noticed in the code above that once a post is published a published:post
event is emitted. This uses Adonis’ event listeners and the listener for this event triggers a Netlify build of my website so show the new post.
/App/Listeners/Event.ts
public async onPublishedPost(post: EventsList["published:post"]) {
Logger.info("Events: Published:Post");
axios.post(
`https://api.netlify.com/build_hooks/somerandomid?trigger_title=new+post+published+${post.id}`
);
}
I send the Post object to the listener function so that in Netlify I can see which post triggered a build using the trigger_title
query params.
On the create post page on the admin panel, I installed v-calendar
which lets me select a date and time to schedule the post for when writing. Since the last update, I made the UI of the Admin panel look a bit cleaner. This is how it looks with a cleaner UI and the calendar addition.
This feature took about 2 hours to add and has been working great since I deployed the changes. In a following post, I’ll dive deeper into the Adonis Events and Listeners by building a webhook manager feature.
Thanks for reading & happy coding! 😎