I published Postiz, my open-source social media scheduling tool, on Reddit, and received much attention.
I guess it was super needed in open-source.
I have received multiple questions from developers on how I built it.
So today, I will take you through the infrastructure and things you need to consider.
It all starts with oAuth (oAuth2)
The core of every social media scheduling tool is oAuth. Let me explain.
oAuth is a standard way for different platforms to give you access to their users to perform a limited-scope action.
For example, you can ask Facebook to give you access to post on a user timeline or read their analytics.
oAuth is limited by time but can be refreshed, and the core of every scheduling tool is:
- Collect oAuths of different platforms.
- Refresh them when needed.
- Use them to post on a user timeline.
Since you want to build a generic platform that you can easily add more and more social platforms, you want to build an interface like this (simplified version):
export interface Provider {
generateAuthUrl(): Promise<GenerateAuthUrlResponse>;
authenticate(params: {code: string; codeVerifier: string; refresh?: string;}): Promise<AuthTokenDetails>;
refreshToken(refreshToken: string): Promise<AuthTokenDetails>;
post(token: string, info: MessageInformation): Promise<PostDetails>;
}
generateAuthUrl
— This function generates a URL for the user to authenticate on a platform (like LinkedIn). It gives you a code you can later convert to a token to post on a user's timeline.
authenticate
- Takes the code of the user, converts it to a token, and creates the user in the database (and saves their token along with the token expiry date)
refreshToken
- Takes the user token and expiry date and refreshes it if needed.
post
— This function takes the user token and message details and posts them to the user's timeline (it can be a picture, text, video, etc.).
Once you have that, you must catch a user's POST request in an authentication request, map it to the right provider that implements IAuthenticator, and return the generateAuthUrl.
The generateAuthUrl
gets a "return URL," which the user will return once they have completed authentication. In that URL, you use the authenticate
function.
Scheduling the posting
Platforms mostly will not give you a way to schedule their posts with an API.
Even if they did, it would be hard to manage some who do and some who don't, so it's better to view it as a platform for those who don't.
To do this, you need to implement a queue.
Example: Post to Facebook: "I love Postiz" and process it in 1 week from now.
You have many options for queues.
Some of the most popular are RabbitMQ, SQS, and ActiveMQ
They are perfect when you have tons of jobs, and if you have streaming Petabytes of data, Kafka can probably help.
But in my case, I don't need a "real" queue.
So, I used Redis (Pub-sub). You push some key inside of Redis and add an expiry date. Once the expiry date passes, it triggers Redis and sends a notification (to some service)
There is a great company called BullMQ that simplifies the process with Redis and your app (it's free and fully open-sourced)
Below are "Workers". They are microservices of your application that process and post the "job" on social media.
They are separated because they are smaller than the main application and might need to be scaled depending on your number of jobs.
We call this "Horizontal scaling," as we will need to add more workers as our application grows.
The DB
Usually, you would need to save a post in the DB with multiple statuses—DRAFT,
SCHEDULED,
COMPLETED,
and ERROR
—so you can track what happened to it.
In my case, I used Prisma.
Prisma is an ORM that wraps and queries your database using terms like One-to-one, Many-to-many, and one-to-many.
It's a known approach that exists for you in all the languages.
I like it because it keeps a consistent response structure and allows me to change to a different DB in the future (easy migration). I don't have even one raw query in my project (good or bad)
I am using PostgreSQL because it's trending now (according to a StackOverflow survey)
The main thing to be careful about when using SQL is deadlocks. You don't want to update a row from multiple places simultaneously, as this will throw a deadlock, and the update will fail.
Help me out
Postiz is trending on GitHub. It's 100% free, and I will try to give you value as much as possible.
Already crafted a small roadmap from all the requests.
I was hoping you could help me with a star that will help me (well, I can't even describe how much)
Top comments (81)
Awesome!
just a small ques, why did you use redis for executing the schedules posts? can I use "cron" for the same ?
You can run a cron every second to check if something will be posted.
But it feels a bit overkill for me :)
cron requires extra validation and control of items to be posted. Also, Redis preserves the schedule list and have retry options.
If your service (with cron) loose some event due a shutdown, you'll require an extra effort to detect these loosed events. Also, you'll need an extra effort to retry it.
Not exactly; you can save it in the DB, for example, and post it on Facebook at 5 pm.
Now you can run a cron every second, check what needs to be scheduled after 5 pm, and don't fail. You will need to lock the job everything, so you want to post it twice (that can be a bit dangerous).
Instead of a cron you might want to run a while(true) event.
I came across this package called "node-schedule" which is similar to schedule in python. I think it fits better to the usecase.
ex.
`const schedule = require('node-schedule');
const { TwitterApi } = require('twitter-api-v2');
// Initialize your Twitter client
const client = new TwitterApi({
appKey: 'YOUR_APP_KEY',
appSecret: 'YOUR_APP_SECRET',
accessToken: 'YOUR_ACCESS_TOKEN',
accessSecret: 'YOUR_ACCESS_SECRET',
});
// Function to post to Twitter
async function postToTwitter(content) {
try {
await client.v2.tweet(content);
console.log('Posted to Twitter successfully');
} catch (error) {
console.error('Error posting to Twitter:', error);
}
}
// Schedule a post
function schedulePost(date, content) {
schedule.scheduleJob(date, function() {
postToTwitter(content);
});
}
// Example usage
const futureDate = new Date(2024, 8, 15, 12, 0, 0); // September 15, 2024, at 12:00:00 PM
schedulePost(futureDate, 'This is a scheduled tweet for a specific date and time!');`
It's a fake scheduler that checks every second on javascript intervals.
It can work, but it's not saving and state, it means that if you close the app and open it again, you will lose all the information.
There must exist a good "real" scheduler for node.
someone suggested "agenda" which syncs with MongoDB
BullMQ with Redis :)
Great article, thanks. Some important aspects to make this a real platform include:
Keep up the good work and you can make this really good.
Very good points!
Thank you!!
Awesome, many stuff!
As a single developer, I will need some time.
But I see some really good ones!
Great article and very nice idea 👏
A small scheduler suggestion - for a stateless and scheduled jobs Im using Cloudwatch events that triggers a lambda function. (another option is to use SQS fifo queues between them to achieve atomic operations - not sure if needed in your case at this point but it's an option)
Yeah, those are more complex infrastructures.
You can also use RabbitMQ or Kafka if you have big streams.
I don't think it's necessary for social media scheduling tools :)
I love love the idea <3
Thank you!
Responsiveness at its peak !
Haha!
Wanna help me fix it? :)
Nice. I've been dreaming of this kind of project. Thanks for your effort in sharing this.
Thank you! :)
Happy Hacking
This statement is telling me you don't know much about SQL deadlocks yet.
Try reading more about it and how to avoid them. We're operating a massively huge SQL database and update the same rows in dozens of places simultaneously without seeing a single failed update, so it's at least possible.
"Try reading more about it and how to avoid them" - surely this is exactly what he has been doing looking at the post.
"We're operating a massively huge SQL database and update the same rows in dozens of places simultaneously without seeing a single failed update" - this statement isnt telling me much either
I didn't say it's impossible, but you must be careful with it.
If you don't use workers or your update takes too much time you will get a deadlock.
This is an impressive project! 🎉 It's clear a lot of thought went into building Postiz, and I love how you've shared the technical breakdown so transparently. The approach you took with OAuth2 for handling social media integrations and how you've simplified the provider interface is brilliant! It makes adding new platforms seem much more manageable.
Using Redis for queue management is also a smart move—especially since you don't need a heavyweight solution like RabbitMQ or Kafka. BullMQ is a fantastic choice to complement Redis, and the horizontal scaling with workers makes perfect sense for a tool like this that has the potential to handle many jobs.
I'm also a big fan of Prisma! It really streamlines database interactions, and the flexibility it offers for future DB migrations is a huge plus. Postgres is rock solid, and it's great to hear you're having a smooth experience with it.
Congratulations on all the attention you're getting with Postiz! You've definitely hit a sweet spot in the open-source space. Looking forward to trying it out and contributing a star ⭐ Keep up the great work!
Thank you 🙏🏻
It is wrong this kind of tool has bussiness potential these days. I'm dissappointed. Anyway nicely done project.
What do you mean?
I'm not fan of how social media works. I don't even have meta or X account. A lot of people share a lot of useless stuff. This kind of tool simplify it, which leads to encouraging this behaviour. If you have nothing meaningful to share then don't share.
Cool!
But I think there is still good stuff on social media you just need to follow the right people :)
Really liked how you handled the above comment 👏
👏
Love this project, @nevodavid. 💪
I recently built a similar tool in the CLI for automating Instagram posts. If you'd like to take a look: github.com/shricodev/insta-cron-po...
Nice Project!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.