How a Student Built an Award-Winning App with Stream

New
4 min read

Discover how a university student used Stream’s free chat APIs to build a real-time service exchange app—in just four months.

Sarah L
Fathul F
Sarah L & Fathul F
Published June 9, 2025
The Barter App cover image

When Fathul Fahmy began work on his final-year university project, he set out to build a fully functional application with real-world utility.

The result was The Barter App, a mobile-first platform where users exchange services, either directly or with optional monetary compensation. Think: "I'll design your logo if you help me fix my website."

Over the course of four months, Fathul brought this idea to life by building:

  • A cross-platform mobile app
  • A custom administrative web panel
  • A Laravel-based backend API
  • And a fully integrated real-time chat experience using Stream

That last piece, chat, played a surprisingly important role in helping The Barter App stand out. By using Stream, Fathul was able to integrate real-time messaging quickly and spend more time refining the app's core features.

The result: a polished, feature-rich product that earned him his university's Outstanding Final Year Project Award.

Scoping the Build

With just four months to deliver 18 distinct modules, Fathul had to make careful tradeoffs.

He wanted the app to include: 

  • Service discovery and listing
  • Request management and negotiation
  • Real-time messaging to chat and finalize terms
  • Transaction handling
  • User profiles and ratings

Building a real-time messaging system from scratch wasn't feasible, especially when features like service discovery, request workflows, and transaction management were the core of the app.

"Initially, I planned to implement WebSocket communication manually using Laravel on the backend and React Native on the frontend," Fathul shared. "However, after assessing the time investment required to learn, integrate, and scale such a solution, it became clear that a more efficient approach was necessary." 

Choosing Stream for Real-Time Messaging

Fathul evaluated a few options for adding chat to his app. But Stream stood out for several key reasons:

  • SDKs for both React Native and PHP
  • Developer-centric documentation and tooling
  • High-speed integration cycle
  • Scalability out of the box

By signing up for a free Stream account, Fathul was able to access all the core functionality needed to ship a polished, scalable messaging experience in less than one week, without writing his own real-time infrastructure.

Here's how Fathul built The Barter App.

Developer Setup

Architecture Overview

  • Frontend: React Native (Expo), using stream-chat-react-native
  • Backend: Laravel, using stream-chat-php
  • Stack Highlights: Expo Router, RESTful APIs, Stream's hosted messaging layer

Free Stream Account

Before proceeding with the integration, Fathul:

  1. Signed up for a free Stream account
  2. Created a project/app on his Dashboard
  3. Noted the API public and secret key, as these would be used in frontend and backend integrations.

Frontend Development

1. Install Stream Chat SDK

sh
1
npx expo install stream-chat-expo

2. Create a Chat Client Singleton

typescript
1
2
3
import { StreamChat } from "stream-chat"; export const client = StreamChat.getInstance(process.env.EXPO_PUBLIC_STREAM_CHAT_KEY!);

3. Connect User on Login

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const useLogin = (options?: Omit<UseMutationOptions<AuthResponse, Error, LoginInput>, "mutationFn">) => { // auth logic const setChatUser = async (data: AuthResponse) => { await client.connectUser( { id: String(data.user.id), }, data.chat_token, ); }; return useMutation({ mutationFn: loginFn, ...options, onSuccess: (response, ...rest) => { // auth logic setChatUser(response); }, }); };

4. List Conversations with All Users

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React, { useMemo } from "react"; import { router } from "expo-router"; import { ChannelSort } from "stream-chat"; import { ChannelList } from "stream-chat-expo"; import { useUser } from "@/lib/auth/auth"; import { useChat } from "@/lib/stream-chat/chat-store"; const sort: ChannelSort = { last_message_at: -1 }; export const Conversations = () => { const { setChannel } = useChat(); const userQuery = useUser(); const user = userQuery.data; const memoizedFilters = useMemo( () => ({ members: { $in: [user?.id || ""] }, type: "messaging", last_message_at: { $gte: "2020-01-01T00:00:00.00Z" }, }), [user?.id], ); return ( <ChannelList filters={memoizedFilters} sort={sort} onSelect={(channel) => { setChannel(channel); router.push(`/chat/${channel.cid}`); }} /> ); };

This screen displays the list of active chat conversations, sorted by most recent activity.

List conversations with all users in The Barter App

5. Show a Conversation with a User

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from "react"; import { router } from "expo-router"; import { Channel, MessageInput, MessageList } from "stream-chat-expo"; import { useChat } from "@/lib/stream-chat/chat-store"; import { CustomAttachButton } from "./custom-attach-button"; export const Conversation = () => { const { channel, setThread } = useChat(); if (!channel) return null; return ( <Channel channel={channel} keyboardVerticalOffset={90} AttachButton={CustomAttachButton}> <MessageList onThreadSelect={(thread) => { setThread(thread); router.push(`/chat/${channel.cid}/thread/${thread?.cid}`); }} /> <MessageInput /> </Channel> ); };

Once a user selects a conversation, the Channel component renders a real-time chat view. Here, users can read and send messages, view threads, and engage in rich conversations.

Show a conversation with a user in The Barter App

Backend: Laravel + Stream PHP SDK

1. Install the SDK

sh
1
composer require get-stream/stream-chat

2. Create Base Controller or Util Functions

php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function upsertChatUser(mixed $user): void { $chat_client = new Client(config('app.stream_chat.key'), config('app.stream_chat.secret')); $chat_client->upsertUsers([ [ 'id' => (string) $user->id, 'name' => $user->name, 'role' => 'user', 'image' => $user->avatar['uri'], ], ]); } public function createChatToken($user_id): string { $chat_client = new Client(config('app.stream_chat.key'), config('app.stream_chat.secret')); $chat_token = $chat_client->createToken((string) $user_id); return $chat_token; }

3. Connect User on Login

php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public function login(AuthLoginRequest $request): JsonResponse { try { DB::beginTransaction(); // auth logic $user = auth()->user(); $this->upsertChatUser($user); $chat_token = $this->createChatToken($user->id); DB::commit(); return response()->apiSuccess('Logged in successfully', // other responses 'chat_token' => $chat_token, 'user' => auth()->user(), ]); } catch (\Exception $e) { DB::rollBack(); return response()->apiError('Failed to login', $e->getMessage()); } }

After logging in, users can navigate to the "Ongoing" tab, where they can view and manage their ongoing service exchanges, check deliverables, and jump straight into chat.

Connect user on login in The Barter App

Overcoming Challenges

One technical challenge occurred when the chat attachment button didn't behave as expected in the React Native environment. 

However, after raising a GitHub issue, Fathul received a quick and helpful response from a Stream contributor, which helped him resolve the issue without blocking development.

Fathul said, "This level of developer support solidified my confidence in Stream, not only as a product but also as a platform that understands and actively supports the development lifecycle."

Final Results

With Stream, Fathul shipped a polished messaging experience in just one week—leaving him free to focus on the rest of The Barter App.

"Stream enabled me to deliver a fully functional, real-time chat feature with minimal configuration and an exceptional developer experience. Given the time constraints of an academic project, this would have been unattainable through traditional WebSocket development," Fathul shared.

For students, startups, or solo developers aiming to implement chat with limited time and resources, Stream offers a scalable, robust, and developer-friendly solution.

Create your free account today.

Ready to Increase App Engagement?
Integrate Stream’s real-time communication components today and watch your engagement rate grow overnight!
Contact Us Today!