Skip to content

Tutorial#

Build Your First Social Media App with Jaseci#

You'll build LittleX, a Twitter-like application, in just 200 lines of code. This tutorial guides you through each step, from installation to deployment.


What You'll Learn#

By the end of this tutorial, you'll understand how to:

  • Store data in connected graph structures
  • Navigate through relationships between data
  • Add AI features to your application
  • Deploy a working social media platform

What You'll Build: LittleX#

LittleX lets users:

  • Create accounts and profiles
  • Post messages (tweets)
  • Follow other users
  • View a personalized feed

Complete Code Preview#

Here's what you'll build - just 200 lines of code for a full social media platform:

LittleX Frontend

    has username: str = "";

    can update with update_profile entry;

    can get with get_profile entry;

    can follow with follow_request entry;

    can un_follow with un_follow_request entry;
}

obj TweetInfo {
    has username: str;
    has id: str;
    has content: str;
    has embedding: list;
    has likes: list;
    has comments: list;
}

node Tweet {
    has content: str;
    has embedding: list;
    has created_at: str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S");

    can update with update_tweet exit;

    can delete with remove_tweet exit;

    can like_tweet with like_tweet entry;

    can remove_like with remove_like entry;

    can comment with comment_tweet entry;

    def get_info()-> TweetInfo;

    can get with load_feed entry;
}

node Comment {
    has content: str;

    can update with update_comment entry;

    can delete with remove_comment entry;
}

edge Follow {}

edge Like {}

edge Post {}

walker visit_profile {
    can visit_profile with `root entry;
}

walker update_profile(visit_profile) {
    has new_username: str;
}

walker get_profile(visit_profile) {}

walker load_user_profiles {
    obj __specs__ {
        static has auth: bool = False;
    }
    can load_profiles with `root entry;
}

walker follow_request {}

walker un_follow_request {}

walker create_tweet(visit_profile) {
    has content: str;

    can tweet with Profile entry;
}

walker update_tweet {
    has updated_content: str;
}

walker remove_tweet {}

walker like_tweet {}

walker remove_like {}

walker comment_tweet {
    has content: str;
}

walker update_comment {
    has updated_content: str;
}

walker remove_comment {}

walker load_feed(visit_profile) {
    has search_query: str = "";
    has results: list = [];

    can load with Profile entry;

}
impl Profile.update {
    self.username = visitor.new_username;
    report self;
}

impl Profile.get {
        follwers=[{"id": jid(i), "username": i.username} for i in [self-->(`?Profile)]];
        report {"user": self, "followers": follwers};
    }

impl Profile.follow{
        current_profile = [root-->(`?Profile)];
        current_profile[0] +>:Follow():+> self;
        report self;
    }

impl Profile.un_follow {
        current_profile = [root-->(`?Profile)];
        follow_edge = [edge current_profile[0] ->:Follow:-> self];
        del follow_edge[0];
        report self;
    }

impl Tweet.update {
        self.content = visitor.updated_content;
        report self;
    }

impl Tweet.delete {
        del self;
    }

impl Tweet.like_tweet {
        current_profile = [root-->(`?Profile)];
        self +>:Like():+> current_profile[0];
        report self;
    }

impl Tweet.remove_like {
        current_profile = [root-->(`?Profile)];
        like_edge = [edge self ->:Like:-> current_profile[0]];
        del like_edge[0];
        report self;
    }

impl Tweet.comment {
        current_profile = [root-->(`?Profile)];
        comment_node = current_profile[0] ++> Comment(content=visitor.content);
        grant(comment_node[0], level=ConnectPerm);
        self ++> comment_node[0];
        report comment_node[0];
    }

impl Tweet.get_info {
        return TweetInfo(
            username=[self<-:Post:<-][0].username,
            id=jid(self),
            content=self.content,
            embedding=self.embedding,
            likes=[i.username for i in [self->:Like:->]],
            comments=[{"username": [i<--(`?Profile)][0].username, "id": jid(i), "content": i.content} for i in [self-->(`?Comment)]]
        );
    }

impl Tweet.get {
        tweet_info = self.get_info();
        similarity = search_tweets(visitor.search_query, tweet_info.content);
        visitor.results.append({"Tweet_Info": tweet_info, "similarity": similarity});
    }

impl Comment.update {
        self.content = visitor.updated_content;
        report self;
    }

impl Comment.delete {
        del self;
    }

impl visit_profile.visit_profile {
        visit [-->(`?Profile)] else {
            new_profile = here ++> Profile();
            grant(new_profile[0], level=ConnectPerm);
            visit new_profile;
        }
    }

impl load_user_profiles.load_profiles {
        self.profiles: list = [];

        for each_root in allroots() {
            profile = [each_root --> (`?Profile)][0];
            self.profiles.append(
                {"name": profile.username, "id": jid(profile)}
            );
        }
    }

impl create_tweet.tweet {
        embedding = sentence_transformer.encode(self.content).tolist();
        tweet_node = here +>:Post:+> Tweet(content=self.content, embedding=embedding);
        grant(tweet_node[0], level=ConnectPerm);
        report tweet_node;
    }

impl load_feed.load {
        visit [-->(`?Tweet)];
        for user_node in [->:Follow:->(`?Profile)] {
            visit [user_node-->(`?Tweet)];
        }
        report self.results;
    }
test visit_profile {
    root spawn visit_profile();
    profile = [root --> (`?Profile)][0];
    check isinstance(profile,Profile);
}

test update_profile {
    root spawn update_profile(
        new_username = "test_user",
    );
    profile = [root --> (`?Profile)][0];
    check profile.username == "test_user";
}

test follow_request {
    followee = Profile("Sam");
    followee spawn follow_request();
    followee_profile = [root --> (`?Profile)->:Follow:->(`?Profile)][0];
    check followee_profile.username == "Sam";
}

test un_follow_request {
    followee = [root --> (`?Profile)->:Follow:->(`?Profile)][0];
    followee spawn un_follow_request();
    check len([root --> (`?Profile)->:Follow:->(`?Profile)]) == 0;
}

test create_tweet {
    root spawn create_tweet(
        content = "test_tweet",
    );
    test1 = [root --> (`?Profile) --> (`?Tweet)][0];
    check test1.content == "test_tweet";
}

test update_tweet {
    tweet1 = [root --> (`?Profile) --> (`?Tweet)][0];
    tweet1 spawn update_tweet(
        updated_content = "new_tweet",
    );
    check tweet1.content == "new_tweet";
}

test remove_tweet {
    tweet2 =  [root --> (`?Profile)--> (`?Tweet)][0];
    tweet2 spawn remove_tweet();
    check len([root --> (`?Profile) --> (`?Tweet)]) == 0;
}

test like_tweet {
    root spawn create_tweet(
        content = "test_like",
    );
    tweet1 = [root --> (`?Profile) --> (`?Tweet)][0];
    tweet1 spawn like_tweet();
    test1 = [tweet1 ->:Like:-> ][0];
    check test1.username == "test_user";
}

test remove_like {
    tweet1 = [root --> (`?Profile) --> (`?Tweet)][0];
    tweet1 spawn remove_like();
    check len([tweet1 ->:Like:-> ]) == 0;
}

test comment_tweet {
    tweet = [root --> (`?Profile) --> (`?Tweet)](?content == "test_like")[0];
    tweet spawn comment_tweet(
        content = "test_comment",
    );
    comment = [tweet --> (`?Comment)][0];
    check comment.content == "test_comment";
}

test update_comment {
    tweet = [root --> (`?Profile) --> (`?Tweet)](?content == "test_like")[0];
    comment = [tweet --> (`?Comment)][0];
    comment spawn update_comment(
        updated_content = "new_comment",
    );
    check comment.content == "new_comment";
}

test remove_comment {
    comment = [root --> (`?Profile) --> (`?Tweet) --> (`?Comment)][0];
    comment spawn remove_comment();
    check len([root --> (`?Profile) --> (`?Tweet) --> (`?Comment)]) == 0;
}

Before You Start#

You'll need:

  • 15 minutes to complete this tutorial
  • Python 3.12 or later installed
  • A text editor or IDE

Step 1: Install Jaseci#

Install the required libraries:

pip install jac_cloud

If the install is successful, you'll see:

Successfully installed jac_cloud

You're ready to start building!


Step 2: Get the Code#

Clone the repository:

git clone https://github.com/Jaseci-Labs/littleX.git
cd littleX

Install dependencies:

# Backend dependencies
pip install -r littleX_BE/requirements.txt

# Frontend dependencies
cd littleX_FE
npm install
cd ..

Understanding Jaseci's Building Blocks#

Jaseci uses three main components to build applications. Let's see how they work together:

File Structure: Three Files, One Application#

Jaseci organizes code into three files that work together automatically:

littleX.jac - What Your App Has#

# Define what exists
node Profile {
    has username: str;
    can update with update_profile entry;
}

littleX.impl.jac - How Your App Works#

# Define how things work
impl Profile.update {
    self.username = here.new_username;
    report self;
}

littleX.test.jac - Proving It Works#

# Test functionality
test create_tweet {
    root spawn create_tweet(content = "Hello World");
    tweet = [root --> (?Profile) --> (?Tweet)][0];
    check tweet.content == "Hello World";
}

Running Your Code#

Jaseci automatically links these files:

# Run the application
jac run littleX.jac

# Run tests
jac test littleX.jac

# Start API server
jac serve littleX.jac

1. Nodes: Store Your Data#

Nodes hold information. In LittleX:

  • Profile nodes store user information
  • Tweet nodes store message content
  • Comment nodes store replies

Simple Example:

node User {
    has username: str;
}

This creates a user object with a username.

2. Edges: Connect Your Data#

Edges create relationships between nodes. In LittleX:

  • Follow edges connect users who follow each other
  • Post edges connect users to their tweets
  • Like edges connect users to tweets they liked

Simple Example:

edge Follow {}

This creates a "Follow" connection between users.

3. Walkers: Make Things Happen#

Walkers move through your graph and perform actions. They make your app interactive.

Simple Example:

walker create_tweet(visit_profile) {
    has content: str;
    can tweet with Profile entry;
}

This walker creates new tweets when users post messages.


Build LittleX Step by Step#

Now let's build your social media app by combining these pieces:

Step 3: Create User Profiles#

When someone signs up, we create their profile:

walker visit_profile {
    can visit_profile with `root entry;
}

impl visit_profile.visit_profile {
    visit [-->(`?Profile)] else {
        new_profile = here ++> Profile();
        grant(new_profile[0], level=ConnectPerm);
        visit new_profile;
    }
}

What this does: Creates a new profile if one doesn't exist, or visits the existing profile.

Step 4: Post Messages#

Users can create and share posts:

walker create_tweet(visit_profile) {
    has content: str;
    can tweet with Profile entry;
}

impl create_tweet.tweet {
    embedding = sentence_transformer.encode(self.content).tolist();
    tweet_node = here +>:Post:+> Tweet(content=self.content, embedding=embedding);
    grant(tweet_node[0], level=ConnectPerm);
    report tweet_node;
}

What this does: Creates a new tweet with the user's message and connects it to their profile.

Step 5: Follow Other Users#

Build your network by following others:

walker follow_request {}

impl Profile.follow {
    current_profile = [root-->(`?Profile)];
    current_profile[0] +>:Follow():+> self;
    report self;
}

What this does: Creates a follow relationship between the current user and another user.

Step 6: View Your Feed#

See posts from people you follow:

walker load_feed(visit_profile) {
    has search_query: str = "";
    has results: list = [];
    can load with Profile entry;
}

impl load_feed.load {
    visit [-->(`?Tweet)];
    for user_node in [->:Follow:->(`?Profile)] {
        visit [user_node-->(`?Tweet)];
    }
    report self.results;
}

What this does: Collects tweets from the current user and everyone they follow.


Step 7: Run Your App#

Let's see your social media platform in action:

Start the Backend#

jac serve littleX_BE/littleX.jac

You should see:

INFO: Uvicorn running on http://127.0.0.1:8000

Your backend is running!

Start the Frontend#

Open a new terminal:

cd littleX_FE
npm run dev

You should see:

Local: http://localhost:5173/

Your frontend is ready!

Try Your App#

  1. Open your browser to: http://localhost:5173

  2. Test these features

    • Create an account
    • Post a message
    • Follow someone
    • Check your feed

If everything works, you've successfully built a social media platform!


Key Code Components#

Let's examine the main parts of your LittleX app:

Profile Node#

node Profile {
    has username: str = "";

    can update with update_profile entry;
    can get with get_profile entry;
    can follow with follow_request entry;
    can un_follow with un_follow_request entry;
}

This stores user information and defines what users can do.

Tweet Node#

node Tweet {
    has content: str;
    has embedding: list;
    has created_at: str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S");

    can update with update_tweet exit;
    can delete with remove_tweet exit;
    can like_tweet with like_tweet entry;
    can remove_like with remove_like entry;
    can comment with comment_tweet entry;

    def get_info() -> TweetInfo;
    can get with load_feed entry;
}

This stores tweet content and handles all tweet interactions.

Follow Implementation#

impl Profile.follow {
    current_profile = [root-->(`?Profile)];
    current_profile[0] +>:Follow():+> self;
    report self;
}

This creates the follow relationship between users.


Try These Extensions#

Ready to add more features? Try implementing:

  1. Like system for posts
  2. User search by username
  3. Comment replies for deeper conversations
  4. Profile pages showing user-specific content

What You've Accomplished#

You've built a complete social media application. You now understand:

  • Nodes for storing data
  • Edges for connecting information
  • Walkers for creating functionality

Jaseci's graph-based approach works well for social networks where relationships between data are essential.


Next Steps#

Ready to build more? Explore these resources:

You're ready to build your own graph-based applications!

Happy coding with Jaseci! 🚀