Building a post scheduler with AdonisJS

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.

Migrations, migrations, migrations…

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;
...

The Scheduling Function

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();
  });
});

Publish Event

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.

The Admin panel changes

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.

Description

Conclusion

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! 😎

Table of Contents