Tinyslash is an industry-grade, SaaS-ready URL shortening and link management platform. Built for scale with a microservices-ready architecture, it features advanced analytics, custom domains, QR code generation, and team collaboration tools.
Live Production: https://tinyslash.com
Live Development: https://dev.tinyslash.com
- System Architecture & Design
- Detailed Tech Stack
- Project Structure
- Authentication Flow
- Core Features Implementation
- Admin System
- Local Development Setup
- Contribution Guide for Interns
- Troubleshooting
Tinyslash is built on a Decoupled Client-Server Architecture designed for high availability and horizontal scalability.
- Frontend (SPA): A React 18 application handling the presentation layer. It communicates with the backend via RESTful APIs and utilizes JWT for stateless authentication.
- Backend (API): A Spring Boot 3.2 application acting as the core logic engine. It handles business rules, request validation, and database orchestration.
- Data Layer (MongoDB): We use MongoDB (NoSQL) for its flexible schema, allowing us to store complex analytics data in a single document (e.g., nesting click data by country/device within the URL document).
- Edge Layer (Cloudflare): Handles DNS, SSL termination for custom domains (SaaS), and serves as a global CDN to reduce latency.
- User Request: User clicks a short link (e.g.,
tinyslash.com/xyz123). - DNS Resolution: Cloudflare routes traffic to our Render backend.
- Backend Processing:
UrlShorteningServiceintercepts the request.- Looks up
shortCode: xyz123in Redis Cache (Hit) or MongoDB (Miss). - Extracts metadata (Browser, OS, IP -> Country).
- Async Analytics: Fires an event to increment click counters without blocking the redirect.
- Response: Returns
302 Foundwith theLocationheader set to the original URL.
- Core: React 18, TypeScript 5 (Strict Mode)
- Build Tool: Vite (for fast HMR and optimized builds)
- Styling:
- TailwindCSS 3: Utility-first styling for rapid UI development.
- Framer Motion: Complex layout animations and transitions.
- Lucide React: Consistent icon set.
- State Management:
- Context API: For global app state (Auth, Theme).
- React Query (TanStack): For server state caching and synchronization.
- Routing: React Router DOM v6
- Analytics Visualization: Recharts (D3-based composable charts).
- HTTP Client: Axios with interceptors for token refreshment.
- Core: Java 17 LTS, Spring Boot 3.2.0
- Database: MongoDB 6.0 (via Spring Data MongoDB).
- Security:
- Spring Security 6 (FilterChain based).
- JJWT: For extensive JWT creation and validation.
- Google OAuth2 Client: For social login flows.
- Utilities:
- Lombok: Reduces boilerplate (Getters/Setters/Builders).
- Jackson: JSON serialization/deserialization.
- Build Tool: Maven 3.8+
- Testing: JUnit 5, Mockito.
Follows a Layered Architecture ensuring separation of concerns:
src/main/java/com/urlshortener/
├── controller/ # API Layer: Handles HTTP requests, validation, and JSON responses.
│ ├── AuthController.java # /api/v1/auth/** (Login, Register, Google)
│ ├── UrlController.java # /api/v1/url/** (Create, Update, Delete)
│ └── RedirectController.java # /{shortCode} (The public redirect endpoint)
├── service/ # Business Logic Layer: The complexity lives here.
│ ├── UrlShorteningService.java # Core logic for shortening and analytics.
│ └── UserService.java # User management logic.
├── repository/ # Data Access Layer: Interfaces extending MongoRepository.
├── model/ # Domain Entities: POJOs mapped to MongoDB documents.
│ ├── ShortenedUrl.java # Stores URL data + Embedded Analytics Maps.
│ └── User.java # User profile + Role data.
├── config/ # Configuration Classes.
│ ├── SecurityConfig.java # CORS, CSRF, and FilterChain rules.
│ └── MongoConfig.java # Database connection settings.
├── security/ # Security Components.
│ └── JwtAuthenticationFilter.java # Intercepts requests to validate "Bearer" tokens.
└── admin/ # Admin Module.
Organized by Feature-First structure:
src/
├── components/ # Atomic & Molecular Components.
│ ├── ui/ # Generic UI (Button, Input, Card).
│ └── features/ # Feature-specific (UrlList, AnalyticsChart).
├── pages/ # Page-level Components (Views).
│ ├── Dashboard.tsx
│ ├── LandingPage.tsx
│ └── Auth/ # Login/Register pages.
├── context/ # Global Contexts.
│ └── AuthContext.tsx # Handles User session state.
├── services/ # Service Layer (API calls).
│ ├── api.ts # Axios instance configuration.
│ └── authService.ts # Auth-related API calls.
├── hooks/ # Custom React Hooks.
│ └── useAnalytics.ts # Hook for fetching analytics data.
└── App.tsx # Main Router configuration.
We use a Hybrid Auth System supporting both Email/Password and Google OAuth.
- Client POSTs to
/api/v1/auth/login. - Backend authenticates credentials.
- On success, Backend returns a JWT (Access Token) validity: 24h.
- Frontend stores this token (Secure Storage/Memory).
- Frontend attaches
Authorization: Bearer <token>to all subsequent requests.
- Frontend uses
react-oauth/googleto get anauthorization_code. - Frontend sends this code to Backend:
/api/v1/auth/google/callback. - Backend exchanges code for a Google Access Token via
googleapis.com. - Backend fetches user profile from Google.
- Backend creates/updates user in local DB.
- Backend issues a valid App JWT to the Frontend.
- Strategy: Random String Gen with Collision Check.
- Logic:
- Generate a 6-character random alphanumeric string (Base62).
- Check MongoDB:
existsByShortCode(code). - If exists -> User recursion/loop to generate again.
- If unique -> Save to DB.
- Why: Simple, stateless, and efficient for medium-scale.
- Data Model: We utilize MongoDB's document model to store analytics inside the URL document.
- Fields:
clicksByCountry:Map<String, Integer>(e.g.,{"US": 50, "IN": 120})clicksByDevice:Map<String, Integer>clicksByReferrer:Map<String, Integer>
- Benefit: Reading a single URL document gives all its analytics instantly. No expensive JOINs or aggregations needed for basic views.
The Admin Dashboard provides superuser control over the platform.
- User Oversight: View all registered users, their plan status, and total URLs.
- Content Moderation: ability to delete or ban URLs that violate policies (Phishing/Spam).
- Global Analytics: View platform-wide growth metrics.
Currently done via Database Access:
db.users.updateOne(
{ email: "[email protected]" },
{ $addToSet: { roles: "ROLE_ADMIN" } }
)- Install MongoDB Community Server.
- Start the service.
- Open MongoDB Compass (GUI) and create a connection:
mongodb://localhost:27017. - Create a database named:
tinyslash_dev.
- Navigate to:
cd tinyslash-backend/url-service - Create configuration:
src/main/resources/application-dev.ymlspring: data: mongodb: uri: mongodb://localhost:27017/tinyslash_dev google: client: id: <YOUR_GOOGLE_CLIENT_ID> secret: <YOUR_GOOGLE_CLIENT_SECRET> app: jwtSecret: <GENERATE_A_LONG_RANDOM_STRING> jwtExpirationMs: 86400000
- Run the server:
Server will start on
mvn spring-boot:run
http://localhost:8080
- Navigate to:
cd tinyslash-frontend - Create
.envfile:REACT_APP_API_URL=http://localhost:8080/api/v1 REACT_APP_GOOGLE_CLIENT_ID=<YOUR_GOOGLE_CLIENT_ID>
- Install & Start:
App will open on
npm install npm start
http://localhost:3000
We follow a strict Git Flow process. Please adhere to these rules to maintain code quality.
main: 🛑 PROTECTED. Production code. Do not touch.develop: 🟡 STAGING. This is your base branch.feat/feature-name: 🟢 YOUR WORKSPACE. Create this fromdevelop.fix/bug-name: 🔴 BUG FIX. Create this fromdevelop.
- Sync Upgrade: Always pull the latest
developbefore starting.git checkout develop git pull origin develop
- Branch Out:
git checkout -b feat/add-dark-mode
- Commit Often: Use Conventional Commits.
feat: add dark mode togglefix: resolve login spinner issuestyle: update header paddingdocs: update readme
- Push & PR:
git push origin feat/add-dark-mode
- Go to GitHub, open a Pull Request (PR) against
develop. - Add a description and screenshots of your changes.
- Assign a senior developer for review.
- Go to GitHub, open a Pull Request (PR) against
-
"Connection Refused" (Backend)
- Cause: MongoDB is not running.
- Fix: Check services (
brew services liston Mac or Task Manager on Windows). Ensure connection URI is correct.
-
"CORS Error" (Frontend)
- Cause: Frontend (
localhost:3000) is trying to talk to Backend (localhost:8080) but Backend isn't allowing it. - Fix: specific allowed origins are defined in
AuthController.javaorSecurityConfig.java. Ensure@CrossOrigin(origins = "*")is present for dev orhttp://localhost:3000is accepted.
- Cause: Frontend (
-
"401 Unauthorized"
- Cause: JWT Token expired or invalid.
- Fix: Logout and Login again. Check if
jwtSecretinapplication-dev.ymlmatches what the server is using.
© 2025 Tinyslash Tech. Proprietary Software.