⚠️⚠️ Project is no longer maintained or hosted. The code is available on Github
If you are unaware of Bytes then check out this blog that I wrote:
Bytes - A Platform to share bite-sized learnings!
In this blog, I'll talk about the decision to choosing the tech stack, the database modeling of Bytes
Table of Contents
Tech Stack
- NextJS
- HarperDB
- TailwindCSS
- Firebase
NextJS
- I chose to go with NextJS because it provides SSR support and Pages support out of the box.
HarperDB
- Since Bytes is made as a part of the HashNode-HarperDB hackathon, HarperDB acts as the Database for Bytes.
- The ability to queries in SQL way or NoSQL ways make it so easy to work with it.
TailwindCSS
- I always praise Tailwind in my blogs, It's easier to write CSS with all the utilites classes
- It also has Dark/Light theme support.
Firebase ( Storage and Authentication )
- Images for Bytes are handled by Firebase storage.
- All the URLs of the uploaded image are then stored in Harper
- Authentication is implemented using Firebase authentication as it handles also the user sessions gracefully.
Database Modelling
- This is the fun part of working with databases for me. In this section, I'll share the schemas of the tables and their relationships.
- I have tried to keep the models and their relationships normalized.
Schemas
User Schema
export type UserSchema = {
uid: string;
email: string;
name: string;
username: string;
verified: boolean;
__createdtime__?: string;
__updatedtime__?: string;
};
Post Schema
export type PostSchema = {
pid: string;
images: Array<string>;
slug: string;
title: string;
uid: string;
reactions: number;
__createdtime__?: string;
__updatedtime__?: string;
};
Tag Schema
export type TagType = {
tid: string;
name: string;
color: string;
image?: string;
slug?: string;
__createdtime__?: string;
__updatedtime__?: string;
};
Post_Tag Schema
export type Post_Tag_Type = {
pid: string;
ptid: string;
tid: string;
__createdtime__?: string;
__updatedtime__?: string;
};
Relationships
User <-> Post
- This relationship would be one-to-many
- So the Post Schema would have a foreign key i.e User's uuid as
uid
- To get a user posts, I would just inner join User and Post on this
uid
-- Gets all the Posts of a User
SELECT p.*,u.name,u.username FROM bytes.post AS p INNER JOIN bytes.user AS u ON u.uid=p.uid WHERE u.username='${username}'
Post <-> Tags
- The relationships b/w Post and Tags would be many-to-many.
- One post can have many tags and One tag can have posts
- I found this amazing article that shows various ways on implementing N:M relationships
- Based on it, I made separate table called
Post_Tag
that would contain a post id aspid
and tag id astid
- So now to get a post with all it's tags, I would write this SQL query
-- Gets all Tags for a Post
SELECT t.* FROM bytes.post_tag AS pt INNER JOIN bytes.tag AS t ON pt.tid=t.tid WHERE pt.pid='${post.pid}'
-- Get all Posts of a Tag
SELECT p.*,u.name,u.username FROM bytes.post_tag AS pt INNER JOIN bytes.post AS p ON pt.pid=p.pid INNER JOIN bytes.user AS u ON p.uid = u.uid WHERE pt.tid='${tag.tid}'
Protecting Routes
- At the moment, Bytes has the following routes:
- /
- /upload
- /login
- /profile/:id/
- /byte/:id/
- /tag/:id/
Out of this routes, the /upload
route is protected, It can be only accessed if the user is logged in.
- So to do so, I made a custom hook that would check for user.
- If user is logged in, it allows the user else redirect to "/login"
// useRequireLogin.tsx
const router = useRouter();
const { user, setUser } = useContext(UserContext);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
const authUser = await getAuthUserFromHarper(user.uid);
setUser({ ...authUser, isLoggedIn: true });
setLoading(false);
router.push(to);
} else {
setUser({});
setLoading(false);
router.push("/login");
}
});
}, []);
return { user, loading };
// ------------------------
// Using in the Upload Page
// /pages/upload.tsx
// ------------------------
const { user, loading } = useRequireLogin({ to: "/upload" });
if (loading || !user.isLoggedIn) return null;
Hope you liked this blog and learned something from it.
There are still some improvements and features that I am adding to Bytes.
Socials
You can follow me on my Twitter - @Shubham_Verma18
Top comments (0)