<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Nevos.io]]></title><description><![CDATA[Nevos.io]]></description><link>https://nevodavid.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 06 Sep 2026 06:57:41 GMT</lastBuildDate><atom:link href="https://nevodavid.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I built my Open-source social media scheduling Tool]]></title><description><![CDATA[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 throug...]]></description><link>https://nevodavid.hashnode.dev/how-i-built-my-open-source-social-media-scheduling-tool</link><guid isPermaLink="true">https://nevodavid.hashnode.dev/how-i-built-my-open-source-social-media-scheduling-tool</guid><dc:creator><![CDATA[Nevo David]]></dc:creator><pubDate>Tue, 03 Sep 2024 07:03:45 GMT</pubDate><content:encoded><![CDATA[<p>I published <a target="_blank" href="https://postiz.com/">Postiz</a>, my open-source <a target="_blank" href="https://github.com/gitroomhq/postiz-app">social media scheduling tool</a>, on <a target="_blank" href="https://www.reddit.com/r/selfhosted/comments/1f4x806/postiz_opensource_social_media_scheduling_tool">Reddit</a>, and received much attention.</p>
<p>I guess it was super needed in open-source.<br />I have received multiple questions from developers on how I built it.</p>
<p>So today, I will take you through the infrastructure and things you need to consider.</p>
<p><a target="_blank" href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwjkbemvdketw9gx0zb8.png"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwjkbemvdketw9gx0zb8.png" alt="Social Media" /></a></p>
<hr />
<h2 id="heading-it-all-starts-with-oauth-oauth2">It all starts with oAuth (oAuth2)</h2>
<p>The core of every social media scheduling tool is oAuth. Let me explain.</p>
<p>oAuth is a standard way for different platforms to give you access to their users to perform a limited-scope action.</p>
<p>For example, you can ask Facebook to give you access to post on a user timeline or read their analytics.</p>
<p>oAuth is limited by time but can be refreshed, and the core of every scheduling tool is:</p>
<ol>
<li><p>Collect oAuths of different platforms.</p>
</li>
<li><p>Refresh them when needed.</p>
</li>
<li><p>Use them to post on a user timeline.</p>
</li>
</ol>
<p>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):</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Provider {
  generateAuthUrl(): <span class="hljs-built_in">Promise</span>&lt;GenerateAuthUrlResponse&gt;;
  authenticate(params: {code: <span class="hljs-built_in">string</span>; codeVerifier: <span class="hljs-built_in">string</span>; refresh?: <span class="hljs-built_in">string</span>;}): <span class="hljs-built_in">Promise</span>&lt;AuthTokenDetails&gt;;
  refreshToken(refreshToken: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">Promise</span>&lt;AuthTokenDetails&gt;;
post(token: <span class="hljs-built_in">string</span>, info: MessageInformation): <span class="hljs-built_in">Promise</span>&lt;PostDetails&gt;;
}
</code></pre>
<p><code>generateAuthUrl</code> — 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.</p>
<p><code>authenticate</code> - 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)</p>
<p><code>refreshToken</code> - Takes the user token and expiry date and refreshes it if needed.</p>
<p><code>post</code>— 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.).</p>
<p>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 <code>generateAuthUrl.</code></p>
<p>The <code>generateAuthUrl</code> gets a "return URL," which the user will return once they have completed authentication. In that URL, you use the <code>authenticate</code> function.</p>
<hr />
<h2 id="heading-scheduling-the-posting">Scheduling the posting</h2>
<p>Platforms mostly will not give you a way to schedule their posts with an API.</p>
<p>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.</p>
<p>To do this, you need to implement a queue.</p>
<p><strong>Example</strong>: Post to Facebook: "I love Postiz" and process it in 1 week from now.</p>
<p>You have many options for queues.<br />Some of the most popular are <a target="_blank" href="https://www.rabbitmq.com/">RabbitMQ</a>, <a target="_blank" href="https://aws.amazon.com/sqs">SQS</a>, and <a target="_blank" href="https://activemq.apache.org/">ActiveMQ</a></p>
<p>They are perfect when you have tons of jobs, and if you have streaming Petabytes of data, <a target="_blank" href="https://kafka.apache.org/">Kafka</a> can probably help.</p>
<p>But in my case, I don't need a "real" queue.</p>
<p>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)</p>
<p>There is a great company called <a target="_blank" href="https://bullmq.io/">BullMQ</a> that simplifies the process with Redis and your app (it's free and fully open-sourced)</p>
<p><a target="_blank" href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34dsfux8q48t4yb7c2a8.png"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34dsfux8q48t4yb7c2a8.png" alt="Archtecture" /></a></p>
<p>Below are "Workers". They are microservices of your application that process and post the "job" on social media.</p>
<p>They are separated because they are smaller than the main application and might need to be scaled depending on your number of jobs.</p>
<p>We call this "Horizontal scaling," as we will need to add more workers as our application grows.</p>
<hr />
<h1 id="heading-the-db">The DB</h1>
<p>Usually, you would need to save a post in the DB with multiple statuses—<code>DRAFT,</code> <code>SCHEDULED,</code> <code>COMPLETED,</code> and <code>ERROR</code>—so you can track what happened to it.</p>
<p>In my case, I used <a target="_blank" href="https://www.prisma.io/">Prisma</a>.<br />Prisma is an ORM that wraps and queries your database using terms like One-to-one, Many-to-many, and one-to-many.</p>
<p>It's a known approach that exists for you in all the languages.<br />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)</p>
<p>I am using PostgreSQL because it's trending now (according to a <a target="_blank" href="https://survey.stackoverflow.co/2024/technology#1-databases">StackOverflow survey</a>)</p>
<p><a target="_blank" href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgccweu9mqautwij4b0co.png"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgccweu9mqautwij4b0co.png" alt="StackOverflow survey" /></a></p>
<p>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.</p>
<hr />
<h2 id="heading-help-me-out">Help me out</h2>
<p><a target="_blank" href="https://github.com/gitroomhq/postiz-app">Postiz</a> is trending on GitHub. It's 100% free, and I will try to give you value as much as possible.</p>
<p>Already crafted a small <a target="_blank" href="https://github.com/gitroomhq/postiz-app/discussions/185">roadmap</a> from all the requests.</p>
<p>I was hoping you could help me with a star that will help me (well, I can't even describe how much)</p>
<p><a target="_blank" href="https://github.com/gitroomhq/postiz-app">Star Postiz</a></p>
]]></content:encoded></item></channel></rss>