Skip to content

Build Your First App

Create a complete delivery tracking application from scratch using De. Platform.

What You'll Build

A real-time delivery tracking app with:

  • Interactive map with pickup and delivery locations
  • Live vehicle tracking
  • Route visualization with traffic
  • Delivery status updates
  • Customer notifications

Time to complete: 30 minutes

Prerequisites

De. SDK Installed

Completed the installation guide

Learn more →

Authentication Setup

Configured user authentication

Learn more →

Workspace Created

Have a De. workspace and access token

Project Setup

1

Create Project Directory

Set up your project structure

bash
mkdir delivery-tracker
cd delivery-tracker
npm init -y
2

Install Dependencies

Add required packages

bash
npm install @de./sdk
npm install --save-dev vite typescript @types/node
3

Project Structure

Create the following file structure

delivery-tracker/
├── src/
│   ├── main.ts
│   ├── auth.ts
│   ├── map.ts
│   └── tracking.ts
├── public/
│   └── index.html
├── .env
├── package.json
├── tsconfig.json
└── vite.config.ts

Configuration Files

4

Configure TypeScript

Create tsconfig.json

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
5

Configure Vite

Create vite.config.ts

typescript
import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    port: 3000
  },
  build: {
    outDir: 'dist',
    sourcemap: true
  }
})
6

Environment Variables

Create .env file

env
VITE_DE_WORKSPACE_ID=your_workspace_id
VITE_DE_ACCESS_TOKEN=your_access_token
VITE_DE_API_URL=https://api.dedot.io
VITE_DE_MSI_URL=https://map.dedot.io

HTML Structure

7

Create HTML Template

Set up public/index.html

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Delivery Tracker - De. Platform</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: #f5f5f5;
    }

    #app {
      display: flex;
      height: 100vh;
    }

    #sidebar {
      width: 350px;
      background: white;
      border-right: 1px solid #e0e0e0;
      overflow-y: auto;
      padding: 20px;
    }

    #map-container {
      flex: 1;
      position: relative;
    }

    .header {
      margin-bottom: 30px;
    }

    .header h1 {
      font-size: 24px;
      color: #0ea5e9;
      margin-bottom: 8px;
    }

    .header p {
      color: #666;
      font-size: 14px;
    }

    .section {
      margin-bottom: 30px;
    }

    .section h2 {
      font-size: 16px;
      font-weight: 600;
      margin-bottom: 15px;
      color: #333;
    }

    .input-group {
      margin-bottom: 15px;
    }

    .input-group label {
      display: block;
      font-size: 13px;
      color: #666;
      margin-bottom: 5px;
    }

    .input-group input {
      width: 100%;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 6px;
      font-size: 14px;
    }

    .btn {
      width: 100%;
      padding: 12px;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s;
    }

    .btn-primary {
      background: #0ea5e9;
      color: white;
    }

    .btn-primary:hover {
      background: #0284c7;
    }

    .status-card {
      background: #f8f9fa;
      padding: 15px;
      border-radius: 8px;
      margin-bottom: 10px;
    }

    .status-card h3 {
      font-size: 14px;
      color: #333;
      margin-bottom: 8px;
    }

    .status-card p {
      font-size: 13px;
      color: #666;
    }

    .status-indicator {
      display: inline-block;
      width: 8px;
      height: 8px;
      border-radius: 50%;
      margin-right: 6px;
    }

    .status-indicator.active {
      background: #10b981;
      animation: pulse 2s ease-in-out infinite;
    }

    @keyframes pulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.5; }
    }

    #loading {
      display: none;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: white;
      padding: 30px;
      border-radius: 12px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      z-index: 1000;
    }

    #loading.show {
      display: block;
    }
  </style>
</head>
<body>
  <div id="app">
    <div id="sidebar">
      <div class="header">
        <h1>Delivery Tracker</h1>
        <p>Powered by De. Platform</p>
      </div>

      <div class="section">
        <h2>Delivery Details</h2>
        <div class="input-group">
          <label>Pickup Address</label>
          <input type="text" id="pickup-address" placeholder="123 Main St, City">
        </div>
        <div class="input-group">
          <label>Delivery Address</label>
          <input type="text" id="delivery-address" placeholder="456 Oak Ave, City">
        </div>
        <button class="btn btn-primary" id="create-delivery">
          Create Delivery
        </button>
      </div>

      <div class="section" id="tracking-section" style="display: none;">
        <h2>Tracking Status</h2>
        <div id="status-container"></div>
      </div>
    </div>

    <div id="map-container"></div>
  </div>

  <div id="loading">
    <p>Loading map...</p>
  </div>

  <script type="module" src="/src/main.ts"></script>
</body>
</html>

Authentication Module

8

Create Authentication Module

Set up src/auth.ts

typescript
import De from '@de./sdk'

export class AuthManager {
  private access: any

  constructor() {
    this.access = new De.Access({
      workspace: import.meta.env.VITE_DE_WORKSPACE_ID,
      accessToken: import.meta.env.VITE_DE_ACCESS_TOKEN,
      env: 'prod'
    })
  }

  getAccess() {
    return this.access
  }

  async getCurrentUser() {
    try {
      const response = await this.access.request({
        url: '/user',
        method: 'GET'
      })
      return response.data
    } catch (error) {
      console.error('Failed to get user:', error)
      return null
    }
  }
}

Map Integration

9

Create Map Module

Set up src/map.ts

typescript
import De from '@de./sdk'

export class MapManager {
  private msi: any
  private controls: any
  private handles: any

  async initialize() {
    try {
      // Show loading
      document.getElementById('loading')?.classList.add('show')

      // Initialize MSI
      this.msi = new De.MSI({
        element: 'map-container',
        accessToken: import.meta.env.VITE_DE_ACCESS_TOKEN
      })

      // Load map
      const result = await this.msi.load()
      this.controls = result.controls
      this.handles = result.handles

      // Hide loading
      document.getElementById('loading')?.classList.remove('show')

      console.log('Map initialized successfully')
      return true
    } catch (error) {
      console.error('Failed to initialize map:', error)
      return false
    }
  }

  async setPickupLocation(coords: { lng: number; lat: number }, label: string) {
    if (!this.handles) {
      throw new Error('Map not initialized')
    }

    await this.handles.pickupPoint(coords, {
      label: label,
      sublabel: 'Pickup Location',
      draggable: false
    })
  }

  async setDeliveryLocation(coords: { lng: number; lat: number }, label: string) {
    if (!this.handles) {
      throw new Error('Map not initialized')
    }

    await this.handles.dropoffPoint(coords, {
      label: label,
      sublabel: 'Delivery Location',
      draggable: false
    })
  }

  async drawRoute(origin: { lng: number; lat: number }, destination: { lng: number; lat: number }) {
    if (!this.controls) {
      throw new Error('Map not initialized')
    }

    const route = await this.controls.setRoute({
      origin: origin,
      destination: destination,
      options: {
        profile: 'driving-traffic',
        alternatives: false
      }
    })

    return route
  }

  async trackVehicle(coords: { lng: number; lat: number }, label: string) {
    if (!this.handles) {
      throw new Error('Map not initialized')
    }

    const stream = await this.handles.peerLocation(coords, {
      label: label,
      sublabel: 'En Route',
      icon: 'delivery-truck'
    })

    return stream
  }

  async simulateMovement(
    start: { lng: number; lat: number },
    end: { lng: number; lat: number },
    durationMs: number = 30000
  ) {
    const steps = 100
    const interval = durationMs / steps

    let currentStep = 0

    const stream = await this.trackVehicle(start, 'Delivery Vehicle')

    const timer = setInterval(() => {
      currentStep++
      const progress = currentStep / steps

      const currentLng = start.lng + (end.lng - start.lng) * progress
      const currentLat = start.lat + (end.lat - start.lat) * progress

      stream.live(async (controls: any) => {
        await controls.move({
          position: { lng: currentLng, lat: currentLat }
        })
      })

      if (currentStep >= steps) {
        clearInterval(timer)
        console.log('Vehicle arrived at destination')
      }
    }, interval)

    return stream
  }
}

Tracking Module

10

Create Tracking Module

Set up src/tracking.ts

typescript
export class TrackingManager {
  private statusContainer: HTMLElement | null

  constructor() {
    this.statusContainer = document.getElementById('status-container')
  }

  updateStatus(status: string, message: string, isActive: boolean = false) {
    if (!this.statusContainer) return

    const statusCard = document.createElement('div')
    statusCard.className = 'status-card'
    statusCard.innerHTML = `
      <h3>
        <span class="status-indicator ${isActive ? 'active' : ''}"></span>
        ${status}
      </h3>
      <p>${message}</p>
    `

    this.statusContainer.appendChild(statusCard)

    // Show tracking section
    const trackingSection = document.getElementById('tracking-section')
    if (trackingSection) {
      trackingSection.style.display = 'block'
    }
  }

  clearStatus() {
    if (this.statusContainer) {
      this.statusContainer.innerHTML = ''
    }
  }

  async simulateDeliveryFlow() {
    this.clearStatus()

    // Order created
    this.updateStatus('Order Created', 'Your delivery order has been created', false)
    await this.delay(2000)

    // Driver assigned
    this.updateStatus('Driver Assigned', 'A driver has been assigned to your delivery', false)
    await this.delay(2000)

    // Pickup
    this.updateStatus('Picked Up', 'Package picked up from warehouse', false)
    await this.delay(2000)

    // In transit
    this.updateStatus('In Transit', 'Package is on the way to you', true)
    await this.delay(25000)

    // Nearby
    this.updateStatus('Nearby', 'Driver is approaching your location', true)
    await this.delay(3000)

    // Delivered
    this.updateStatus('Delivered', 'Package delivered successfully!', false)
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
}

Main Application

11

Create Main Application

Set up src/main.ts

typescript
import { AuthManager } from './auth'
import { MapManager } from './map'
import { TrackingManager } from './tracking'

class DeliveryTrackerApp {
  private auth: AuthManager
  private map: MapManager
  private tracking: TrackingManager

  constructor() {
    this.auth = new AuthManager()
    this.map = new MapManager()
    this.tracking = new TrackingManager()
  }

  async initialize() {
    console.log('Initializing Delivery Tracker...')

    // Check authentication
    const user = await this.auth.getCurrentUser()
    if (user) {
      console.log('Authenticated as:', user.profile.firstName)
    }

    // Initialize map
    const mapInitialized = await this.map.initialize()
    if (!mapInitialized) {
      alert('Failed to initialize map. Please check your credentials.')
      return
    }

    // Setup event listeners
    this.setupEventListeners()

    console.log('App ready!')
  }

  private setupEventListeners() {
    const createDeliveryBtn = document.getElementById('create-delivery')
    
    createDeliveryBtn?.addEventListener('click', async () => {
      await this.createDelivery()
    })
  }

  private async createDelivery() {
    const pickupInput = document.getElementById('pickup-address') as HTMLInputElement
    const deliveryInput = document.getElementById('delivery-address') as HTMLInputElement

    const pickup = pickupInput.value
    const delivery = deliveryInput.value

    if (!pickup || !delivery) {
      alert('Please enter both pickup and delivery addresses')
      return
    }

    try {
      // For demo purposes, use fixed coordinates
      // In production, use geocoding to convert addresses to coordinates
      const pickupCoords = { lng: -74.0060, lat: 40.7128 } // NYC
      const deliveryCoords = { lng: -73.9855, lat: 40.7580 } // Times Square

      // Set locations on map
      await this.map.setPickupLocation(pickupCoords, pickup)
      await this.map.setDeliveryLocation(deliveryCoords, delivery)

      // Draw route
      const route = await this.map.drawRoute(pickupCoords, deliveryCoords)
      console.log('Route created:', route)

      // Start vehicle simulation
      await this.map.simulateMovement(pickupCoords, deliveryCoords, 30000)

      // Update tracking status
      await this.tracking.simulateDeliveryFlow()

    } catch (error) {
      console.error('Error creating delivery:', error)
      alert('Failed to create delivery. Please try again.')
    }
  }
}

// Initialize app when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
  const app = new DeliveryTrackerApp()
  app.initialize()
})

Update Package.json

12

Add Scripts

Update package.json with development scripts

json
{
  "name": "delivery-tracker",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@de./sdk": "^latest"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "typescript": "^5.3.0",
    "@types/node": "^20.0.0"
  }
}

Run Your App

13

Start Development Server

Launch your application

bash
npm run dev

Open your browser to http://localhost:3000

What You Built

Core Features

  • Interactive map with De. MSI
  • Pickup and delivery location markers
  • Traffic-aware route visualization
  • Real-time vehicle tracking simulation
  • Delivery status updates

Technologies Used

  • De. SDK for map and tracking
  • TypeScript for type safety
  • Vite for fast development
  • Modern ES modules
  • Real-time simulations

Enhancements

Add Geocoding

Convert addresses to coordinates automatically

Learn more →

Real-time Updates

Connect to WebSocket for live tracking

Learn more →

Multiple Deliveries

Handle multiple concurrent deliveries

Learn more →

Notifications

Send push notifications for status updates

Learn more →

Driver Assignment

Integrate with LSP API for driver dispatch

Learn more →

Order Management

Full order lifecycle management

Learn more →

Production Checklist

Before Deploying

Security:

  • [ ] Move credentials to environment variables
  • [ ] Enable HTTPS
  • [ ] Implement proper authentication
  • [ ] Add rate limiting
  • [ ] Sanitize user inputs

Performance:

  • [ ] Minify and bundle code
  • [ ] Enable caching
  • [ ] Optimize map loading
  • [ ] Add loading states
  • [ ] Implement error boundaries

Monitoring:

  • [ ] Add error tracking (Sentry)
  • [ ] Implement analytics
  • [ ] Set up logging
  • [ ] Monitor API usage
  • [ ] Track performance metrics

Next Steps

Your Development Journey
First App Built
Add Features
Deploy to Production

Complete Source Code

The complete source code for this tutorial is available on GitHub: