
Research
/Security News
Critical Vulnerability in NestJS Devtools: Localhost RCE via Sandbox Escape
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
⚠️ Native linking support is currently limited to Expo. Native iOS/Android support is in progress.
The Rowt SDK provides deep linking capabilities for React Native applications, supporting both Expo and pure React Native implementations. This guide will walk you through integrating the SDK into your project.
npm install rowt-sdk
Initialize the SDK at the root of your application:
import { Rowt } from 'rowt-sdk';
// Initialize with your configuration
Rowt.initialize({
debug: true // Enable debug logging
});
const initialUrl = await Rowt.getInitialDeepLink();
if (initialUrl) {
console.log('App opened with deep link:', initialUrl);
}
const subscription = Rowt.addDeepLinkListener((url) => {
console.log('Received deep link:', url);
// Handle the deep link
});
// Don't forget to remove the listener when done
subscription.remove();
const parsed = Rowt.parseDeepLink('myapp://products/123?promo=summer');
console.log(parsed);
// {
// scheme: 'myapp',
// host: 'products',
// path: '/123',
// segments: ['123'],
// params: { promo: 'summer' },
// originalUrl: 'myapp://products/123?promo=summer'
// }
import { useDeepLink } from 'rowt-sdk';
function App() {
useDeepLink((url) => {
console.log('Deep link received:', url);
// Navigate based on the URL
});
return <YourApp />;
}
For Expo projects, the SDK automatically detects and uses Expo's Linking module. No additional native configuration is required.
Update your app.json
or app.config.js
:
{
"expo": {
"scheme": "myapp",
// ... other config
}
}
// This method is only available in Expo
const deepLink = new RowtLink(
{
serverUrl: 'https://rowt.app', // or your custom rowt instance url
apiKey: 'your-secret-api-key',
projectId: 'project-uuid',
},
{
url: `/products/${item.id}`,
title: product.name,
description: product.description,
imageUrl: product.imageUrl,
})
console.log(deepLink);
// generates shortlink
// ex: https://rowt.app/d8kj8eo8s03
// opens myapp://products/123
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { Rowt, useDeepLink } from 'rowt-sdk';
export default function App() {
useEffect(() => {
// Initialize the SDK
Rowt.initialize({
apiKey: 'your-api-key',
debug: true
});
// Check if app was opened with a deep link
Rowt.getInitialDeepLink().then(url => {
if (url) {
console.log('Initial deep link:', url);
}
});
}, []);
// Use the hook to listen for deep links
useDeepLink((url) => {
const parsed = Rowt.parseDeepLink(url);
console.log('Deep link received:', parsed);
// Handle navigation based on parsed data
});
return (
<View>
<Text>My App</Text>
</View>
);
}
After installing the npm package, run:
cd ios && pod install
Add URL schemes to your ios/[YourApp]/Info.plist
:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
In your AppDelegate.m
or AppDelegate.mm
, add the following imports:
#import <React/RCTLinkingManager.h>
#import <RowtDeepLink/RowtDeepLinkModule.h>
Add these methods to handle deep links:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
// Handle deep link
[RowtDeepLinkModule setDeepLink:url.absoluteString];
// Also handle with React Native's linking
return [RCTLinkingManager application:application openURL:url options:options];
}
// For iOS < 9.0
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
[RowtDeepLinkModule setDeepLink:url.absoluteString];
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
}
// Handle universal links
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
[RowtDeepLinkModule setDeepLink:userActivity.webpageURL.absoluteString];
}
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
// Handle initial deep link when app launches
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ... existing code ...
// Check for initial deep link
NSURL *initialUrl = launchOptions[UIApplicationLaunchOptionsURLKey];
if (initialUrl) {
[RowtDeepLinkModule setInitialDeepLink:initialUrl.absoluteString];
}
return YES;
}
In your android/app/src/main/AndroidManifest.xml
, add intent filters to your main activity:
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<!-- Deep link intent filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
<!-- App links (for HTTPS URLs) -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="yourapp.com" />
</intent-filter>
</activity>
In your MainActivity.java
(or .kt
for Kotlin), add:
import android.content.Intent;
import com.rowt.deeplink.DeepLinkModule;
public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Handle initial intent
Intent intent = getIntent();
DeepLinkModule.handleInitialIntent(intent);
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
DeepLinkModule.handleNewIntent(intent);
}
}
The module should auto-link, but if not, add to MainApplication.java
:
import com.rowt.deeplink.DeepLinkPackage;
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new DeepLinkPackage() // Add this line
);
}
import React, { useEffect } from 'react';
import { View, Text, Platform } from 'react-native';
import { Rowt, useDeepLink } from 'rowt-sdk';
export default function App() {
useEffect(() => {
// Initialize the SDK
Rowt.initialize({
apiKey: 'your-api-key',
debug: true
});
// Get initial deep link
Rowt.getInitialDeepLink().then(url => {
if (url) {
console.log('App opened with:', url);
handleDeepLink(url);
}
});
}, []);
// Listen for deep links while app is running
useDeepLink((url) => {
handleDeepLink(url);
});
const handleDeepLink = (url: string) => {
const parsed = Rowt.parseDeepLink(url);
console.log('Parsed deep link:', parsed);
// Navigate based on the parsed data
if (parsed.host === 'products' && parsed.segments[0]) {
// Navigate to product with ID: parsed.segments[0]
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>My React Native App</Text>
</View>
);
}
The SDK also provides functionality to create shortened links:
import { RowtLink } from 'rowt-sdk';
const config = {
serverUrl: 'https://your-server.com',
apiKey: 'your-api-key',
projectId: 'your-project-id'
};
const linkOptions = {
url: 'myapp://products/123',
title: 'Check out this product!',
description: 'Amazing product on sale',
imageUrl: 'https://example.com/product.jpg',
expiration: new Date('2025-12-31'),
metadata: {
campaign: 'summer-sale'
}
};
const rowtLink = new RowtLink(config, linkOptions);
// Create the link
const shortLink = await rowtLink.createLink();
console.log('Short link:', shortLink);
console.log('Shortcode:', rowtLink.getShortcode());
[IMPORTANT] This is especially important for generating deep links.
If you want the preview to show the content from your app, you must overwrite the data. Otherwise, it will attempt to load metadata from your fallback URL.
In the case of web links, the original link's preview metadata will be used by default unless overwritten.
You can check which platform you're on:
import { isExpoEnvironment } from 'rowt-sdk';
if (isExpoEnvironment()) {
console.log('Running in Expo');
} else {
console.log('Running in React Native CLI');
}
xcrun simctl openurl booted "myapp://products/123"
adb shell am start -W -a android.intent.action.VIEW -d "myapp://products/123" com.yourapp
When creating dynamic links with the Rowt SDK, you can include custom metadata using the additionalMetadata
field. This field accepts any JSON object and is stored as a PostgreSQL JSONB column on the backend.
The additionalMetadata
field is a flexible key-value store that can contain any JSON-serializable data you need to associate with your link:
import { RowtLink } from 'rowt-sdk';
const config = {
serverUrl: 'https://your-server.com',
apiKey: 'your-api-key',
projectId: 'your-project-id'
};
const linkOptions = {
url: 'myapp://products/123',
title: 'Summer Sale Product',
// Include any custom JSON data in additionalMetadata
additionalMetadata: {
campaign: {
name: 'summer-2024',
channel: 'email',
variant: 'A'
},
analytics: {
source: 'newsletter',
medium: 'email',
content: 'hero-banner'
},
product: {
sku: 'PROD-123',
category: 'electronics',
price: 299.99,
currency: 'USD'
},
user: {
segmentId: 'high-value',
cohort: '2024-Q2'
},
// Arrays are supported
tags: ['featured', 'sale', 'limited-time'],
// Nested objects are supported
customData: {
experimentId: 'exp-456',
metadata: {
version: 'v2',
timestamp: new Date().toISOString()
}
}
}
};
const rowtLink = new RowtLink(config, linkOptions);
const shortLink = await rowtLink.createLink();
additionalMetadata: {
utm_source: 'facebook',
utm_medium: 'social',
utm_campaign: 'summer-sale-2024',
utm_content: 'carousel-ad-1',
utm_term: 'discount-electronics'
}
additionalMetadata: {
experiment: {
id: 'pricing-test-001',
variant: 'B',
group: 'treatment'
}
}
additionalMetadata: {
referrer: {
userId: 'user-789',
referralCode: 'FRIEND2024',
tier: 'gold'
},
attribution: {
firstTouch: 'google-ads',
lastTouch: 'email'
}
}
additionalMetadata: {
content: {
type: 'video',
duration: 180,
format: 'mp4',
thumbnail: 'https://example.com/thumb.jpg'
},
localization: {
language: 'en-US',
region: 'north-america'
}
}
additionalMetadata: {
promotion: {
code: 'SAVE20',
validUntil: '2024-12-31',
minimumPurchase: 50
},
inventory: {
stock: 145,
warehouse: 'east-coast',
reserved: 12
}
}
additionalMetadata
field is stored as PostgreSQL JSONB, which provides efficient querying and indexing capabilities{}
if no metadata is providedThe backend enforces this structure using TypeORM:
@Column({ type: 'jsonb', default: {} }) // JSONB with default value
additionalMetadata?: Record<string, any>;
import { RowtLink } from 'rowt-sdk';
async function createRichLink() {
const config = {
serverUrl: 'https://api.rowt.io',
apiKey: 'your-api-key',
projectId: 'your-project-id'
};
const linkOptions = {
url: 'myapp://special-offer',
title: 'Exclusive Black Friday Deal',
description: 'Save 50% on all electronics',
imageUrl: 'https://cdn.example.com/black-friday.jpg',
expiration: new Date('2024-11-30'),
additionalMetadata: {
// Campaign tracking
campaign: {
id: 'bf-2024',
type: 'seasonal',
budget: 50000,
department: 'marketing'
},
// Target audience
targeting: {
segments: ['high-value', 'electronics-interested'],
geoTargets: ['US', 'CA'],
ageRange: { min: 25, max: 54 }
},
// Performance tracking
performance: {
expectedCTR: 0.05,
conversionGoal: 1000,
revenueTarget: 150000
},
// Feature flags
features: {
showCountdown: true,
enableChat: false,
personalizedOffers: true
},
// Timestamps and versioning
metadata: {
createdBy: 'marketing-team',
createdAt: new Date().toISOString(),
version: '2.1',
environment: 'production'
}
},
// Regular properties (non-metadata)
properties: {
source: 'email-campaign'
}
};
const rowtLink = new RowtLink(config, linkOptions);
const shortLink = await rowtLink.createLink();
console.log('Created link:', shortLink);
console.log('Link shortcode:', rowtLink.getShortcode());
return rowtLink;
}
This flexible metadata system allows you to attach any contextual information to your links, making them powerful tools for tracking, analytics, personalization, and business logic.
Native module not found
pod install
for iOScd android && ./gradlew clean
npx react-native start --reset-cache
Deep links not working on iOS
Deep links not working on Android
android:launchMode="singleTask"
is setExpo-specific issues
FAQs
SDK for using Rowt Deeplinking with Expo
The npm package rowt-sdk receives a total of 2 weekly downloads. As such, rowt-sdk popularity was classified as not popular.
We found that rowt-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.