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