Introduction

In today’s connected world, users expect applications to work seamlessly even without the internet. Whether it's a field service app, warehouse system, or inspection tool — offline-first experiences are a must.

Most developers use cloud APIs that fail when there’s no internet. But what if you could still let users:

That’s exactly what we’ll build using:
✅ IndexedDB on the Angular side for offline storage
✅ ASP.NET Core Sync API for synchronization when the network is available

This architecture combines the power of local browser storage with intelligent server sync, giving a smooth, reliable user experience.

angular

1. The Problem

In traditional web apps:

This can be a huge problem for mobile and remote teams.

Goal:
Make the Angular app work offline and automatically sync when the user reconnects.

2. The Solution Architecture

We’ll use the following technologies:

Architecture Flow

+---------------------------+                +-----------------------------+
| Angular Frontend (Browser)|                | ASP.NET Core Web API Server |
+---------------------------+                +-----------------------------+
| IndexedDB (Offline Store) | <--- Sync ---> | SQL Server (Central Store)  |
| - Orders                  |                | - Orders Table              |
| - Customers               |                | - Sync Logs Table           |
| - Changes Queue           |                | - Conflict Resolver         |
+---------------------------+                +-----------------------------+
        ^        |
        |        v
 Offline CRUD   Sync Trigger
 (user actions) (on reconnect)

3. How It Works (Step-by-Step Flow)

Step 1: User Works Offline

Step 2: Connectivity Restored

Step 3: Sync Response

4. Implementing IndexedDB in Angular

We can use ngx-indexed-db or the native IndexedDB API.

Installation

npm install ngx-indexed-db

Set up in app.module.ts

import { NgxIndexedDBModule, DBConfig } from 'ngx-indexed-db';

const dbConfig: DBConfig = {
  name: 'OfflineSyncDB',
  version: 1,
  objectStoresMeta: [
    {
      store: 'orders',
      storeConfig: { keyPath: 'id', autoIncrement: true },
      storeSchema: [
        { name: 'customerName', keypath: 'customerName', options: { unique: false } },
        { name: 'status', keypath: 'status', options: { unique: false } }
      ]
    },
    {
      store: 'syncQueue',
      storeConfig: { keyPath: 'id', autoIncrement: true },
      storeSchema: [
        { name: 'action', keypath: 'action', options: { unique: false } },
        { name: 'data', keypath: 'data', options: { unique: false } }
      ]
    }
  ]
};

@NgModule({
  imports: [
    NgxIndexedDBModule.forRoot(dbConfig)
  ]
})
export class AppModule { }

Adding Data Offline

this.dbService.add('orders', {
  customerName: 'John Doe',
  status: 'Pending'
}).subscribe(() => {
  console.log('Order saved offline');
});

Queue Changes When Offline

When the network is unavailable:

this.dbService.add('syncQueue', {
  action: 'ADD_ORDER',
  data: orderObject
});

When the app comes back online:

5. Sync API in ASP.NET Core

Step 1: Create Controller

[ApiController]
[Route("api/[controller]")]
public class SyncController : ControllerBase
{
    private readonly AppDbContext _context;

    public SyncController(AppDbContext context)
    {
        _context = context;
    }

    [HttpPost("sync-orders")]
    public async Task<IActionResult> SyncOrders([FromBody] List<OrderDto> orders)
    {
        foreach (var order in orders)
        {
            var existing = await _context.Orders
                .FirstOrDefaultAsync(o => o.LocalId == order.LocalId);

            if (existing == null)
            {
                _context.Orders.Add(new Order
                {
                    LocalId = order.LocalId,
                    CustomerName = order.CustomerName,
                    Status = order.Status,
                    LastModified = DateTime.UtcNow
                });
            }
            else
            {
                existing.Status = order.Status;
                existing.LastModified = DateTime.UtcNow;
            }
        }

        await _context.SaveChangesAsync();
        return Ok(new { message = "Sync complete" });
    }
}

Step 2: Sync Log Table (Optional)

CREATE TABLE SyncLogs (
    SyncId INT IDENTITY PRIMARY KEY,
    TableName NVARCHAR(100),
    SyncedAt DATETIME,
    RecordsSynced INT
)

This helps track how many records were synced and when.

6. Network Detection in Angular

Use window.navigator.onLine or RxJS fromEvent to detect network status.

fromEvent(window, 'online').subscribe(() => {
  this.syncPendingData();
});
fromEvent(window, 'offline').subscribe(() => {
  console.log('App is offline, storing data locally...');
});

7. Conflict Handling

Conflicts occur when:

You can handle this by comparing:

8. Real-World Use Cases

IndustryExample Use Case
Field OperationsWorkers collect data in remote areas
Warehouse AppsBarcode scanning when Wi-Fi drops
MedicalRecording patient data offline
RetailMobile billing in store basements
InspectionOffline checklist with later sync

9. Advantages

FeatureBenefit
Works OfflineFull functionality without internet
Reliable SyncAuto-updates when connected
PerformanceFaster local operations
User ExperienceNo data loss or downtime
ScalableWorks for large data sets

10. Security Considerations

Conclusion

Offline-first architecture is no longer optional — it’s a necessity for modern web apps.

By using Angular’s IndexedDB and a smart ASP.NET Core Sync API, you can:

It’s lightweight, scalable, and production-ready — all with native web technologies.

Other useful article: Dynamic Stored Procedure Mapping Using Reflection in Repository Pattern