Build a Team of Hundreds with Appwrite & Railway ๐

Table of contents
- ๐ก Why Teams Are Essential in Todayโs Apps
- ๐ Real-World Use Cases for Teams
- โ๏ธ How It Works in Your App
- ๐ Making It Happen with Appwrite
- ๐ Deploying Like a Pro with Railway
- ๐ Meet TeamX โ Your Ready-Made Appwrite Teams Template
- ๐ Database & ๐ Storage - Where Data Lives
- ๐ก Realtime - Keeping Everyone in Sync
- ๐จ The TeamX UI - Your Collaboration Command Center
- ๐งญ Top-Level Tabs
- ๐ My Teams - Sub-Tabs in the Right Column
- โจ The Add Activity Popup - The Creative Hub
- ๐ฌ Chat Space - Your Teamโs Real-Time Conversation Hub
- ๐ค The Outbox Tab โ Your Teamโs Broadcast Center
- ๐ Functions in Railway โ Powering Your Appโs Magic Behind the Scenes
- ๐ฏ Why Use Railway Functions Instead of Appwrite Functions? ๐ค
- ๐ Deploying Your App with Railway: Focus on Reusability & Step-by-Step Guide
- 1๏ธโฃ Prepare Your Railway Project
- 2๏ธโฃ Define Environment Variables (Secure & Centralized)
- 3๏ธโฃ Build and Deploy Serverless Functions on Railway
- 4๏ธโฃ Deploy the Next.js Frontend on Railway
- 5๏ธโฃ How Reusability Powers Your App

If youโve built or used any software in the past few years, youโve probably noticed something - apps arenโt just about individual users anymore.
From collaboration tools to e-commerce platforms, people expect to work together, share resources, and manage things as a group. Whether itโs coworkers planning a project, friends sharing a playlist, or community members moderating content, teamwork is baked into the experience.
And thatโs where Teams come in.
๐ก Why Teams Are Essential in Todayโs Apps
Hereโs the deal:
Your users want to:
๐ฅ Share access to resources with others
๐ก Assign roles so not everyone is a full admin
โก Add or remove people instantly without breaking the flow
๐ฏ Control permissions for different levels of responsibility
Without a team feature, youโre left with messy permission workarounds, duplicate resources, and frustrated users.
๐ Real-World Use Cases for Teams
Teams arenโt just for corporate tools โ they fit into all kinds of apps:
๐ Project Management โ A creative studio forms a โBrand Refreshโ team where designers, writers, and managers work together.
๐ E-commerce โ A store owner sets up a โStaffโ team so employees can fulfill orders and manage products.
๐ Community Platforms โ A forum creates a โModeratorsโ team to manage different discussion areas.
โฝ Sports & Events โ A club app makes โSoccer Team 2025โ so coaches, players, and parents can share schedules and updates.
โ๏ธ How It Works in Your App
With our platform, adding team features is smooth and flexible.
1๏ธโฃ Create a Team
A user sets up a team โ for example, "Winter Campaign" - and adds roles like Manager, Designer, Copywriter.
The creator automatically becomes the Owner ๐, and only Owners can invite or remove members.
2๏ธโฃ Invite Members
Owners send invites ๐ฉ by creating a team membership. When Alex joins as a Designer, they instantly get access. Remove them later, and itโs gone just as fast.
3๏ธโฃ Manage Permissions
You can grant permissions to:
โ Everyone in the team โ
Role.team
(<TEAM_ID>)
๐ฏ Only certain roles โ
Role.team
(<TEAM_ID>, [<ROLE_1>, <ROLE_2>])
โ Bottom line: Teams help you organize users, control access, and keep collaboration smooth - giving your app not just a feature, but a competitive edge.
๐ Making It Happen with Appwrite
Soโฆ how do you actually bring this Team magic into your app?
Thatโs where Appwrite comes in. ๐
Appwrite is an open-source backend platform that gives you all the building blocks to power your app โ without reinventing the wheel. With Appwriteโs Teams API, you can:
๐ฆ Create and manage teams for any type of app
๐ Assign roles to control exactly who can do what
๐ Secure permissions with built-in authentication
โก Add or remove members instantly
๐ Integrate with any frontend - web, mobile, or desktop
The best part? Appwrite handles the heavy lifting - database, authentication, security rules - so you can focus on building features your users actually love.
While using Appwrite features make sure you enable Permissions to users on each feature settings.
๐ Deploying Like a Pro with Railway
Building an app is excitingโฆ but letโs be honest - deploying it can sometimes feel like trying to launch a rocket ๐ with duct tape and hope.
Thatโs why weโre using Railway - the platform that makes deployment so smooth, youโll think itโs magic. โจ
With Railway, you can:
๐ Launch your app to the world in minutes - no server headaches, no โworks on my machineโ excuses
โก Scale automatically when your app blows up on social media (because it will, right? ๐)
๐ Plug in services like databases, storage, or external APIs without touching complex configs
๐ See logs in real time so you can debug before your coffee gets cold
๐ Push updates straight from GitHub with CI/CD - itโs like having a teleport button for new features
So hereโs the game plan:
๐ง Appwrite โ the brain of our app (teams, permissions, authentication)
๐ Railway โ the delivery system that launches our creation to the world
๐ Integrations โ audio, emails, crawlingโฆ whatever makes our app pop โจ
๐ Meet TeamX โ Your Ready-Made Appwrite Teams Template
โAlone we can do so little, together we can do so much.โ โ Helen Keller
In 2025, apps are no longer built for lone wolves ๐บ - theyโre built for packs ๐บ๐บ๐บ.
So far, weโve talked about why Teams matter ๐งโ๐คโ๐ง, how Appwrite ๐ง powers them, and how Railway ๐ takes your app live.
Nowโฆ what if I told you thereโs already a full working example you can grab and run - today?
Say hello to TeamX ๐ โ a ready-to-use, full-stack template you can find right on Railway, built on Next.js + Appwrite, and packed with features to get your app from idea ๐ก to production ๐ in record time.
๐ What is TeamX?
TeamX is a collaborative app designed to showcase how to use Appwriteโs full set of capabilities in a real-world setting.
Think of it as a recipe book ๐ for building team-based apps with authentication, teams, databases, storage, realtime chat, and serverless functions all wired together out-of-the-box.
Architecture at a glance:
Frontend: โ Next.js (React components, state, and effects)
Backend-as-a-Service: Appwrite SDK in the browser for Account, Teams, Databases, Storage, Functions, and Realtime
Serverless Magic: A custom Appwrite function
join_team_with_code
๐ for secure team joining (You can find this function in the TeamX Repo itself)Realtime Feeds: ๐ก Subscriptions for live chat-activity updates
Access Control: Fine-grained permissions using team-based roles
๐ Setting Up Appwrite Client
Before anything else, we configure the Appwrite client so the SDK knows where our backend is:
const APPWRITE_ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://nyc.cloud.appwrite.io/v1';
const APPWRITE_PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || 'team-x';
client
.setEndpoint(APPWRITE_ENDPOINT)
.setProject(APPWRITE_PROJECT_ID);
const account = new Account(client);
const databases = new Databases(client);
const functions = new Functions(client);
const storage = new Storage(client);
const teams = new Teams(client);
In the Template, we guard against missing config with a validateClient()
helper so the app fails gracefully if .env
is incomplete.
๐ Authentication - Easy Sign-In, Custom Preferences
TeamX uses Appwrite Account for sign up and sign in.
๐ง Email + Password authentication out of the box
โ๏ธ Store user preferences (like theme or role ) via
account.updatePrefs
๐ก Built-in error handling for common sign-up issues (duplicate email, weak password, etc.)
Why it matters:
Without auth, anyone could see or change data. Authentication ensures only verified users can join teams or view resources.
๐ Sign Up:
import { Account, ID } from 'appwrite';
const createAccountWithEmail = async (email, password, name) => {
try {
const user = await account.create(
ID.unique(),
email,
password,
name || undefined
);
return { success: true, user };
} catch (error) {
throw error; // handle email already exists, weak password, etc.
}
};
๐ก How it works:
ID.unique()
โ Generates a random, collision-free user IDaccount.create()
โ Registers the new user in Appwriteโs database
๐ Sign In:
const signInWithEmail = async (email, password) => {
try {
const session = await account.createEmailPasswordSession(email, password);
return { success: true, session };
} catch (error) {
throw error; // handle invalid credentials
}
};
๐ก How it works:
createEmailPasswordSession()
โ Starts a session and returns session dataAppwrite automatically sets a secure cookie for future requests
๐ Get Current User:
const getUser = async () => {
try {
if (!validateClient()) return null;
await fetch('https://nyc.cloud.appwrite.io/v1/health', { method: 'HEAD', mode: 'no-cors' });
return await account.get();
} catch {
return null;
}
};
๐ก Pro tip: You can also add OAuth (Google, GitHub) in Appwrite with almost no extra code.
๐ฅ Teams โ Who Owns What?
Roles in TeamX:
Owner: Creates the team, invites/removes members, manages settings
Member: Can collaborate, view, and contribute depending on role permissions
Appwrite Serverless Function: Handles secure joins by code
Flow:
Owner creates a team โ gets join code
Member enters join code โ function validates & adds them to the team
Owner can remove member โ access is instantly revoked
Create a Team with Join Code:
import { Teams, ID } from 'appwrite';
const teams = new Teams(client);
const team = await teams.create(ID.unique(), "My Awesome Team");
const joinCode = generateJoinCode(); // e.g., "X7H8PQ"
await (teams.updatePrefs?.(team.$id, { joinCode })
?? teams.updatePreferences?.(team.$id, { joinCode }));
๐ก Why join codes? They make onboarding smooth - no fiddling with email invites for every member.
๐ Joining a Team Securely - Serverless Magic
Who does what:
User: Enters join code in the UI
Frontend: Sends code to
join_team_with_code
functionFunction: Verifies code โ match the code with the team code โ adds user to team as โmemberโ
Function Behavior:
Reads
joinCode
from request JSONFinds matching team preferences
Calls
teams.createMembership
with the requesting userโs ID
Serverless Function:
const sdk = require("node-appwrite");
module.exports = async function (req, res) {
const { joinCode, userId } = JSON.parse(req.payload);
const client = new sdk.Client()
.setEndpoint(process.env.APPWRITE_ENDPOINT)
.setProject(process.env.APPWRITE_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
const teams = new sdk.Teams(client);
const allTeams = await teams.list();
const matchingTeam = allTeams.teams.find(
t => t.prefs.joinCode === joinCode
);
if (!matchingTeam) return res.json({ error: "Invalid code" }, 400);
await teams.createMembership(matchingTeam.$id, [], userId);
res.json({ success: true });
};
๐ก How it works:
Reads join code from request
Finds a team whose preferences match the code
Calls
createMembership()
to add user with rolemember
Returns success or error
Frontend Call:
The code is sent to execute the Function. The FunctionId
should be defined in the Env File.
const payload = { joinCode: userInputCode };
await functions.createExecution(functionId, JSON.stringify(payload));
๐ Database & ๐ Storage - Where Data Lives
Appwrite lets us store documents in databases with role-based access.
Example: Every document in our โTasksโ collection is readable/writable only by members of its team.
Who can do what:
All Team Members: Can read team data
Owners (or roles): Can update/delete
ACLs: Enforce permissions at the document/file level ( ACL stands for Access Control List - itโs a way to define who can do what in your app or system. ๐)
Create Document with Team ACLs:
const permissions = [
Permission.read(Role.team(selectedTeam.$id)),
Permission.write(Role.team(selectedTeam.$id)),
Permission.update(Role.team(selectedTeam.$id)),
Permission.delete(Role.team(selectedTeam.$id)),
];
await databases.createDocument(dbId, collId, ID.unique(), data, permissions);
๐ก How it works:
Permission.read(Role.team(teamId))
โ Only team members can readPermission.write(Role.team(teamId))
โ Only team members can edit
Currently for our TeamX template it uses the Database named Activity which have 3 Collections - Activities, Messages and Outbox. You will get to know why we created 3 Collections to store data as you read on further.
Upload File to Team Bucket:
Storage bucket is used to store Files such as Audios an Images.
await storage.createFile(bucketId, ID.unique(), file, [
Permission.read(Role.team(selectedTeam.$id))
]);
๐ก Realtime - Keeping Everyone in Sync
Who benefits:
Users: See new messages instantly
App: Always up-to-date without manual refresh
Subscribe to Messages:
const channel = `databases.${databaseId}.collections.${messagesCollId}.documents`;
const unsubscribe = client.subscribe(channel, (event) => {
if (event.payload.teamId === selectedTeam.$id) {
updateMessages(event.payload);
}
});
๐ก How it works:
Whenever a document changes, Appwrite pushes an event to the client. No manual refresh needed, the UI updates instantly. We have implemented the TeamX App chat function with Subscription to get realtime updates.
๐ Environment Variables - Your Appโs Secret Sauce
Before your shiny new team-powered app can run in the browser, it needs to know where to find your Appwrite backend and what resources to talk to.
Thatโs where environment variables come in. Think of them as the ๐ฆ settings file for your app - but instead of hardcoding them (dangerous ๐ฑ), we keep them in .env.local
or on our hosting providerโs settings panel that is Railway.
๐ .env.local
Example
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://nyc.cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT_ID=64ffSAMPLE
NEXT_PUBLIC_APPWRITE_DB_ID=64ff1a0eabc9SAMPLE
NEXT_PUBLIC_APPWRITE_ACTIVITIES_COLL_ID=activitieSAMPLE
NEXT_PUBLIC_APPWRITE_MESSAGES_COLL_ID=messages456
NEXT_PUBLIC_APPWRITE_OUTBOX_COLL_ID=outbox789
NEXT_PUBLIC_APPWRITE_BUCKET_ID=uploads_bucket_001
NEXT_PUBLIC_APPWRITE_JOIN_TEAM_FUNCTION_ID=joinTeamFn_1122
๐ What Each One Does
NEXT_PUBLIC_APPWRITE_ENDPOINT
๐ โ The URL of your Appwrite API endpoint (where your app sends requests). Example:https://nyc.cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT_ID
๐ท โ Your Appwrite projectโs unique identifier. Without this, your app canโt talk to Appwrite.NEXT_PUBLIC_APPWRITE_DB_ID
๐ โ The ID of the database where your appโs data lives.NEXT_PUBLIC_APPWRITE_ACTIVITIES_COLL_ID
๐ โ Collection for storing activities (e.g., task updates, user actions).NEXT_PUBLIC_APPWRITE_MESSAGES_COLL_ID
๐ฌ โ Collection for storing team chat messages.NEXT_PUBLIC_APPWRITE_OUTBOX_COLL_ID
๐ค โ Collection for queued or pending actions, like unsent messages.NEXT_PUBLIC_APPWRITE_BUCKET_ID
๐ฆ โ Your Appwrite storage bucket for file uploads (images, docs, etc.).NEXT_PUBLIC_APPWRITE_JOIN_TEAM_FUNCTION_ID
๐ โ The ID of the serverless function that handles โjoin team by code.โ
๐ก Pro Tips for Environment Variables
Keep secrets out of your repo ๐ โ
.env.local
should be in.gitignore
.Use
NEXT_PUBLIC_
only for values safe for the browser ๐ โ Anything withoutNEXT_PUBLIC_
stays private to the server.Fail fast โณ โ In your app, validate that
NEXT_PUBLIC_APPWRITE_ENDPOINT
andNEXT_PUBLIC_APPWRITE_PROJECT_ID
are set. If theyโre missing, log a clear error so youโre not debugging for hours later.
Example check:
if (!process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || !process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID) {
console.error("โ Missing Appwrite configuration! Please set environment variables.");
}
๐ Why This Matters
These variables are the glue between your frontend and backend. Without them, your app wonโt know:
Where to send API calls
Which database to query
Which function handles invites
Where to store files
In other words - set them right, or nothing works.
Weโve just set these variables in .env.local
for local development ๐ , but hereโs the catch - when you deploy to Railway, your app wonโt magically know them.
Just like you need keys ๐ to unlock your car, your deployed app needs these exact environment values to unlock Appwriteโs features in production.
๐ค Why They Matter for Railway
When you push your code to Railway:
Railway spins up your app in the cloud โ๏ธ
Your code is running on Railwayโs servers, not your laptop
Railway checks for environment variables you set in its dashboard ๐
If theyโre missing, your app will be like โWaitโฆ whereโs my Appwrite?!โ ๐ฑ
๐ฏ Pro Tip for Smooth Deployment
On Railway:
Go to your project โ Variables
Add each variable exactly as in
.env.local
Triple-check spelling (typos here = production meltdown ๐ฅ)
If you rotate an API key in Appwrite, update it in Railway too
MAKE SURE YOUR PORT IS CORRECT WHILE DEPLOYMENT ( I got this Issue, than Figured it Out. )
In Railway setting, Port is also detected by the Railway magic. If you get Deployment error Make sure it is Port is Correct.
Now , Lets move on to the UI part of the Template -
๐จ The TeamX UI - Your Collaboration Command Center
When you open TeamX, youโll notice itโs simple on the surface, powerful under the hood. Everything is organized into two main tabs so you can jump straight into collaboration without digging through menus.
๐งญ Top-Level Tabs
1๏ธโฃ Overview ๐
This is your starting point - the โlobbyโ of TeamX.
Here, you can:
Join a Team with a Code ๐ - Just enter the short join code, and youโre instantly part of the group.
Create a Team ๐ - Name your team, get an auto-generated join code saved to team preferences for easy invites.
See Status Messages ๐ข - Inline success/error alerts keep you updated without reloading.
2๏ธโฃ My Teams ๐ฅ
This is where you spend most of your time - the collaboration workspace.
Itโs split into two columns:
Left Column ๐ - List of all your teams
Right Column ๐ฅ - Displays the selected teamโs workspace with sub-tabs for different features
๐ My Teams - Sub-Tabs in the Right Column
Once you pick a team from the left column, the right column opens up sub-tabs tailored to that team:
All Activities ๐
Reverse-chronological feed of everything happening in the team.
A Button to Create an Activity.
My Activities ๐
- Only the things you personally created.
Chat Space ๐ฌ
Realtime chat with messages streaming in instantly via Appwrite Realtime.
Shows author, timestamp, and message text.
Members ๐งโ๐คโ๐ง
- See all members, with owner/member role badges and search/filter.
Outbox (Owners Only) ๐ค
Create announcements and send them instantly with Resend ๐ง email integration.
Target all members or selected ones.
Review past announcements in the History log.
โจ The Add Activity Popup - The Creative Hub
This is where the magic happens. Click the โAdd Activityโ button in any team workspace, andโฆ
๐ญ What You See
Trigger: Appears right after clicking Add Activity.
Overlay: A centered modal with a dimmed background (click outside or โCloseโ to dismiss).
Picker Grid: Four cards to choose from, each with a title + short description:
Mini Audiobook ๐ง - Create a short audio activity or mini book (powered by ElevenLabs for high-quality, natural-sounding narration).
Summary ๐ - Share a written update or text recap (with Firecrawl Extract helping you auto-generate summaries from external links or pasted content).
Poll ๐ - Collect team votes on any question.
Announcement ๐ข - Post a message, optionally with an image.
Lets Dive in Detail -
๐ Form Types & Flows
Mini Audiobook Form ๐ง (Powered by ElevenLabs)
Inputs: Title, optional description, and audio file upload or link.
Audio Creation: You can upload your own file, or use ElevenLabsโ API to turn text into studio-grade voice narration.
Preview: Playable audio preview once saved.
Save: Uploads file to team storage, adds an activity record with team-only access.
Feedback: Shows save progress, success, or specific errors.
Summary Form ๐ (Enhanced by Firecrawl Extract)
Inputs: Title + body text.
Smart Assist: Paste a link, and Firecrawl Extract can grab the key points automatically, ready for you to edit before saving.
Save: Adds a text-only activity to the team feed.
Feedback: Spinner while saving, clear error messages, and auto-close on success.
Poll Form ๐
Inputs: Title + multiple answer options.
Save: Stores poll for team-only visibility.
Participation: Members vote; results appear in the activity card.
Announcement Form ๐ข
Inputs: Text + optional image.
Media: Uploaded image is linked in the announcement card.
Save: Posts announcement with team-scoped permissions.
๐ฌ Comments & Likes on Every Activity โค๏ธ
No matter which activity type you create - Mini Audiobook, Summary, Poll, or Announcement โ each one automatically gets:
Likes โค๏ธ - Team members can quickly react to show appreciation or agreement.
Comments ๐ฌ - Discussion threads where members can ask questions, share feedback, or collaborate directly under the activity.
This turns every post into a mini community hub inside your workspace, keeping context and conversation together.
๐ค After Saving an Activity
Instant feed update: New activity appears at the top of the current teamโs Activities feed.
Viewer modal: Click to open a detailed view - audio player, images, text, poll interactions, likes, and comments included.
Consistent permissions: Only team members see or interact; owners get special management tools.
๐ก Error Handling
Validation: No saving without required fields (e.g., at least one poll option).
Configuration checks: If IDs or endpoints are missing, UI shows friendly warnings.
Network handling: Offline or API issues keep your input safe for retry.
๐ฌ Chat Space - Your Teamโs Real-Time Conversation Hub
Every great app needs a place where ideas fly back and forth without delay. Thatโs why every team workspace comes with Chat Space - a lightning-fast, private room where your members can talk, plan, and react instantly โก.
Think of it like your teamโs personal WhatsApp group ๐ฑโฆ but directly inside your app, connected to your backend, and totally under your control.
๐ How Itโs Built
Header - Chat name + a quick manual refresh button for when you just want to be sure ๐.
Message Stream - Recent messages for the selected team, showing sender name and timestamp. Newest messages pop in without you lifting a finger.
Composer - A message box + send button. Sending disables it so you donโt double-tap into chaos ๐.
โก Real-Time Powered by Backend Subscriptions
Hereโs the magic - Chat Space is wired to Appwriteโs realtime subscription API on the backend.
The UI subscribes to your teamโs messages channel.
Whenever a message is created, updated, or deleted in the backend database, Appwrite instantly pushes that change to connected clients.
No polling, no delays - just instant updates, even if multiple members are chatting at once.
Switching to a different team?
- The frontend unsubscribes from the old teamโs channel and subscribes to the new one in milliseconds - no risk of message mix-ups.
๐ Team-Only Privacy
Messages are stored with team-scoped permissions in the database.
If youโre not part of a team, you canโt see or send anything - even if you try to peek ๐.
๐ก Pro Tip
If the internet drops (thanks, coffee shop Wi-Fi โ), the subscription will gracefully reconnect. And for peace of mind, you can always tap refresh to resync the feed.
Itโs your always-on virtual meeting room - alive because the backend is constantly whispering updates to the UI in real-time. ๐
๐ค The Outbox Tab โ Your Teamโs Broadcast Center
Every great team needs a way to shout important updates from the rooftops without cluttering the regular chat.
Thatโs where Outbox comes in - a dedicated, owner-only communication hub that makes sending important announcements smooth, fast, and reliable.
๐ What the Outbox Is
Think of the Outbox as your teamโs loudspeaker ๐ข.
If youโre a team owner, this is where you can:
Compose & Send messages to everyone or select members.
Keep a History of every announcement sent (perfect for record-keeping).
And the best part? All of this is powered by Resend โ๏ธ, so your messages actually hit inboxes reliably and look professional.
๐ Where It Appears
Owners see the Outbox as an extra tab inside the team workspace.
Members who arenโt owners will simply see a friendly note: "Outbox is for team owners only" ๐.
๐ก Why Resend? Because when you send something important - a launch update, a meeting schedule, or an urgent notice - you donโt want it lost in chat scrolls. With Resend, your Outbox becomes a guaranteed delivery system ๐๐ฆ.
๐ Functions in Railway โ Powering Your Appโs Magic Behind the Scenes
Your app isnโt just about managing teams - itโs a powerhouse of smart features ๐ฅ, thanks to some cool backend functions running on Railway. These functions are like little superheroes working behind the curtain, reusable across your app to keep things smooth, fast, and smart.
Here are the three star players you get out of the box:
โ๏ธ Resend โ The Outboxโs Email Engine
Remember the Outbox tab where team owners send broadcast messages? Every time you hit Send, itโs Resend that handles the heavy lifting, delivering those emails straight to your team membersโ inboxes ๐ฌ.
No more worrying about updates getting lost inside the app your team stays informed even when offline or away from the app. Resend makes sure your messages travel far and wide, reliably and efficiently.
๐ ElevenLabs TTS โ Mini Audiobookโs Voice Wizard
In the Add Activity popup, if you pick Mini Audiobook, hereโs where the magic happens:
You can upload your own audio, or just type in some text.
Thanks to ElevenLabs Text-to-Speech (TTS) Function, that text transforms into super-realistic, human-like audio ๐ง.
That audio file is then saved securely in your teamโs Appwrite storage bucket and appears as a slick, playable card in your Activities feed.
Itโs perfect for quick voice notes, training clips, or even storytelling โ making your teamโs communication way more engaging.
๐ Firecrawl Extract โ The Summary Superpower
When you want a quick digest of a long article or webpage, just choose Summary in the Add Activity popup and:
Paste a URL or text chunk.
The Firecrawl Extract function scrapes the content and distills it into a clear, concise summary ๐.
That summary is saved right in your Activities feed, helping your team get the gist without slogging through full docs or lengthy reads.
Itโs like having your own super-smart assistant summarizing content for you, speeding up decision-making and knowledge sharing.
๐ฏ Why Use Railway Functions Instead of Appwrite Functions? ๐ค
Appwrite offers Functions to run backend code close to your Appwrite backend. Theyโre great for project-specific automation or server-side logic tightly coupled with your database and storage.
But why Railway Functions might be the smarter choice for your appโs architecture - especially if you want reusability and flexibility:
๐ Cross-Project Reusability
Appwrite Functions are scoped per project - they live only inside your Appwrite environment.
Railway Functions are standalone services with their own public endpoints, so you can call them from multiple projects, apps, or platforms (web, mobile, etc).
Perfect for when you want to reuse the same backend logic (like email sending or AI processing) across different apps without duplicating code!
๐ Independent Deployment & Scaling
Appwrite Functions scale along with your Appwrite backend.
Railway Functions deploy and scale independently, letting you update or optimize AI, email, or extraction services without touching your Appwrite backend or frontend.
This isolation means better reliability and tailored resource use per function.
TL;DR ๐ก
Use Appwrite Functions for quick, project-focused tasks. Use Railway Functions when you want powerful, reusable backend features that work across multiple projects and apps โ making your code and infrastructure cleaner, scalable, and future-proof!
๐ Deploying Your App with Railway: Focus on Reusability & Step-by-Step Guide
Why Railway?
Railway lets you deploy your frontend and serverless functions (like Resend, ElevenLabs TTS, Firecrawl Extract) in one place โ and reuse those functions across your app without spinning up separate servers or environments. This means cleaner architecture, easier updates, and scalable infrastructure.
1๏ธโฃ Prepare Your Railway Project
Create a new Railway project for your app.
Connect your GitHub repo with your frontend and serverless functions, or upload your code or Use a Template or Railway Function.
This project TeamX will host:
Your Next.js frontend
Your serverless functions for Resend, ElevenLabs, and Firecrawl Extract
2๏ธโฃ Define Environment Variables (Secure & Centralized)
In Railway, define these environment variables โ one place for all secrets, easily updateable and reusable:
RESEND_API_KEY
(for Resend emails)ELEVENLABS_API_KEY
(for text-to-speech conversion)FIRECRAWL_API_KEY
(for content summarization)NEXT_PUBLIC_APPWRITE_ENDPOINT
(your Appwrite cloud endpoint)NEXT_PUBLIC_APPWRITE_PROJECT_ID
Other Appwrite collection/bucket/function IDs as I have mentioned before.
Why this matters:
You manage all keys securely in Railwayโs dashboard, so your frontend and functions pick them up automatically - no hardcoding anywhere!
3๏ธโฃ Build and Deploy Serverless Functions on Railway
Railway Functions are independent, reusable units that you can call from your frontend or other backend code. Here are some of the Function we created for this Template.
Function 1: Resend
Handles sending broadcast emails from the Outbox tab.
Reusable: Can be called anywhere you want to send emails (not just Outbox).
Function 2: ElevenLabs TTS
Converts text to audio for Mini Audiobook activities.
Reusable: Can be triggered from multiple UI points or even other workflows.
Function 3: Firecrawl Extract
Scrapes and summarizes text for Summary activities.
Reusable: Can be plugged into other features requiring text summarization.
Deployment:
Push your function code to Railway.
Railway automatically builds and deploys them.
Each function has its own URL endpoint that your frontend calls.
Make sure to Check for Errors.
4๏ธโฃ Deploy the Next.js Frontend on Railway
Your Next.js app is deployed as a standard web service on Railway.
It directly talks to your cloud-hosted Appwrite backend via the environment variables (e.g.,
NEXT_PUBLIC_APPWRITE_ENDPOINT
).When users perform actions, the frontend calls your Railway functions URLs for sending mail, generating audio, or summarizing text.
5๏ธโฃ How Reusability Powers Your App
Single source of truth: Railway functions are centralized and versioned. Change logic in one place โ instantly affects all parts of your app using them.
Scalable & isolated: Each function scales independently, preventing bottlenecks and letting you optimize resource usage per feature.
Easy to extend: Need a new AI-powered feature? Just add a new function in Railway - no need to redeploy the entire backend.
Shared APIs: If you build a mobile app or other clients later, reuse the same Railway functions without changes.
๐ก Pro Tip: To test or extend, just call your Railway function URLs directly via Postman or curl - no need to touch frontend code. This modular approach makes development and maintenance a breeze!
BELOW IS THE TEMPLATE AND DEPLOYMENT LINK - EXPERIMENT AND BUILD YOUR OWN TEAM BASED APP. ๐
Here are some useful links to help you get started and explore the tools used in TeamX - Appwrite:
Appwrite Documentation โ Official docs for Appwrite cloud backend
Railway Documentation โ Guides and references for deploying your app on Railway
Resend API โ Email sending service powering the Outbox broadcasts
ElevenLabs API โ Text-to-speech API for generating audio content
Firecrawl Extract โ Content extraction and summarization API
Thank you. ๐
Subscribe to my newsletter
Read articles from Ak Deepankar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ak Deepankar
Ak Deepankar
I am a First Year Data Science Student. Creating Projects and Improving Myself.