Angular 20

Angular 20 CRUD Application Using Fake JSON Server

Angular 20 CRUD Application Using Fake JSON Server

In this blog, we will build a complete CRUD (Create, Read, Update, Delete) application using Angular 20 and a Fake JSON Server. This is perfect for learning API integration without creating a real backend.

We will cover Angular project setup, fake backend creation, model design, service creation, standalone components, routing, CRUD UI, and common issues faced during development.

 


⭐ What is JSON Server?

JSON Server is a lightweight tool that allows you to create a fake REST API using a simple JSON file. It supports REST operations like GET, POST, PUT, PATCH, and DELETE, which makes it useful for frontend practice and API testing.

Angular 20 supports modern standalone application structure, and HTTP requests can be handled using Angular HttpClient with provideHttpClient(). :contentReference[oaicite:0]{index=0}


angular-json-crud/
│
├── db.json
├── package.json
└── src/
    └── app/
        │
        ├── app.config.ts
        ├── app.routes.ts
        │
        ├── models/
        │   └── user.model.ts
        │
        ├── services/
        │   └── user.service.ts
        │
        └── pages/
            ├── user-list/
            │   ├── user-list.component.ts
            │   ├── user-list.component.html
            │   └── user-list.component.css
            │
            └── user-form/
                ├── user-form.component.ts
                ├── user-form.component.html
                └── user-form.component.css

📌 Step 1: Create Fake Backend (db.json)

Create a file named db.json in your project root directory and add the following content:

{
  "users": [
    {
      "id": "1",
      "name": "Umar Rahman",
      "email": "umar@gmail.com",
      "phone": "9999999999"
    }
  ]
}

Now start the JSON server using the command below:

npx json-server --watch db.json --port 3000

Test the API in your browser:

http://localhost:3000/users

📌 Step 2: Create Angular 20 Project

Create a new Angular project using Angular CLI:

ng new angular-json-crud

Move inside the project folder:

cd angular-json-crud

Run the Angular project:

ng serve

Open the project in browser:

http://localhost:4200

📌 Step 3: Create User Model

Create a file:

src/app/models/user.model.ts
export interface User {
  id?: string;
  name: string;
  email: string;
  phone: string;
}

Here, ID is optional because JSON Server automatically creates an ID when we add a new user.


📌 Step 4: Configure HttpClient

Open app.config.ts and add provideHttpClient(). In modern Angular standalone apps, this is the recommended way to make HttpClient available for dependency injection. :contentReference[oaicite:1]{index=1}

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient()
  ]
};

📌 Step 5: Create User Service

Create a service file:

src/app/services/user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user.model';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private apiUrl = 'http://localhost:3000/users';

  constructor(private http: HttpClient) {}

  // READ
  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  // CREATE
  addUser(user: User): Observable<User> {
    return this.http.post<User>(this.apiUrl, user);
  }

  // UPDATE
  updateUser(id: string, user: User): Observable<User> {
    return this.http.put<User>(`${this.apiUrl}/${id}`, user);
  }

  // DELETE
  deleteUser(id: string): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`);
  }

  // SINGLE USER
  getUserById(id: string): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }
}

📌 Step 6: Create Routes

Open app.routes.ts and add routes for list, add, and edit pages. Angular Router is the official Angular library for navigation in single-page applications. :contentReference[oaicite:2]{index=2}

import { Routes } from '@angular/router';
import { UserListComponent } from './pages/user-list/user-list.component';
import { UserFormComponent } from './pages/user-form/user-form.component';

export const routes: Routes = [
  {
    path: '',
    component: UserListComponent
  },
  {
    path: 'add-user',
    component: UserFormComponent
  },
  {
    path: 'edit-user/:id',
    component: UserFormComponent
  }
];

📌 Step 7: User List Component

Create component:

ng generate component pages/user-list

user-list.component.ts

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';
import { User } from '../../models/user.model';

@Component({
  selector: 'app-user-list',
  standalone: true,
  imports: [CommonModule, RouterLink],
  templateUrl: './user-list.component.html',
  styleUrl: './user-list.component.css'
})
export class UserListComponent implements OnInit {

  users: User[] = [];
  isLoading = false;
  errorMessage = '';

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    this.getUsers();
  }

  getUsers(): void {
    this.isLoading = true;

    this.userService.getUsers().subscribe({
      next: (data) => {
        this.users = data;
        this.isLoading = false;
      },
      error: (error) => {
        this.errorMessage = 'Something went wrong while fetching users';
        this.isLoading = false;
        console.error(error);
      }
    });
  }

  deleteUser(id: string | undefined): void {
    if (!id) return;

    if (confirm('Are you sure you want to delete this user?')) {
      this.userService.deleteUser(id).subscribe({
        next: () => {
          this.getUsers();
        },
        error: (error) => {
          console.error(error);
          alert('Failed to delete user');
        }
      });
    }
  }
}

user-list.component.html

<div class="container">

  <div class="header">
    <h2>Angular 20 CRUD Users</h2>
    <a routerLink="/add-user" class="add-btn">Add User</a>
  </div>

  <div *ngIf="isLoading" class="loading">
    Loading users...
  </div>

  <div *ngIf="errorMessage" class="error">
    {{ errorMessage }}
  </div>

  <table *ngIf="!isLoading && users.length > 0">
    <thead>
      <tr>
        <th>Name</th>
        <th>Email</th>
        <th>Phone</th>
        <th>Action</th>
      </tr>
    </thead>

    <tbody>
      <tr *ngFor="let user of users">
        <td>{{ user.name }}</td>
        <td>{{ user.email }}</td>
        <td>{{ user.phone }}</td>
        <td>
          <a [routerLink]="['/edit-user', user.id]" class="edit-btn">Edit</a>
          <button (click)="deleteUser(user.id)" class="delete-btn">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>

  <div *ngIf="!isLoading && users.length === 0" class="no-data">
    No users found
  </div>

</div>

user-list.component.css

.container {
  width: 90%;
  max-width: 900px;
  margin: 40px auto;
  font-family: Arial, sans-serif;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.add-btn,
.edit-btn,
.delete-btn {
  padding: 8px 14px;
  border: none;
  text-decoration: none;
  border-radius: 5px;
  cursor: pointer;
  color: white;
  font-size: 14px;
}

.add-btn {
  background: #2563eb;
}

.edit-btn {
  background: #16a34a;
  margin-right: 8px;
}

.delete-btn {
  background: #dc2626;
}

table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 25px;
}

th,
td {
  padding: 12px;
  border: 1px solid #ddd;
  text-align: left;
}

th {
  background: #f3f4f6;
}

.loading,
.error,
.no-data {
  margin-top: 20px;
  font-size: 18px;
}

.error {
  color: red;
}

📌 Step 8: Add / Edit User Component

Create component:

ng generate component pages/user-form

user-form.component.ts

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';
import { User } from '../../models/user.model';

@Component({
  selector: 'app-user-form',
  standalone: true,
  imports: [CommonModule, FormsModule, RouterLink],
  templateUrl: './user-form.component.html',
  styleUrl: './user-form.component.css'
})
export class UserFormComponent implements OnInit {

  user: User = {
    name: '',
    email: '',
    phone: ''
  };

  userId: string | null = null;
  isEditMode = false;
  isLoading = false;

  constructor(
    private userService: UserService,
    private router: Router,
    private route: ActivatedRoute
  ) {}

  ngOnInit(): void {
    this.userId = this.route.snapshot.paramMap.get('id');

    if (this.userId) {
      this.isEditMode = true;
      this.getUser(this.userId);
    }
  }

  getUser(id: string): void {
    this.isLoading = true;

    this.userService.getUserById(id).subscribe({
      next: (data) => {
        this.user = data;
        this.isLoading = false;
      },
      error: (error) => {
        console.error(error);
        alert('User not found');
        this.isLoading = false;
      }
    });
  }

  saveUser(): void {
    if (!this.user.name || !this.user.email || !this.user.phone) {
      alert('All fields are required');
      return;
    }

    this.isLoading = true;

    if (this.isEditMode && this.userId) {
      this.userService.updateUser(this.userId, this.user).subscribe({
        next: () => {
          this.router.navigate(['/']);
        },
        error: (error) => {
          console.error(error);
          alert('Failed to update user');
          this.isLoading = false;
        }
      });
    } else {
      this.userService.addUser(this.user).subscribe({
        next: () => {
          this.router.navigate(['/']);
        },
        error: (error) => {
          console.error(error);
          alert('Failed to add user');
          this.isLoading = false;
        }
      });
    }
  }
}

user-form.component.html

<div class="form-container">

  <h2>{{ isEditMode ? 'Update User' : 'Add User' }}</h2>

  <form (ngSubmit)="saveUser()">

    <div class="form-group">
      <label>Name</label>
      <input
        type="text"
        name="name"
        [(ngModel)]="user.name"
        placeholder="Enter name"
      />
    </div>

    <div class="form-group">
      <label>Email</label>
      <input
        type="email"
        name="email"
        [(ngModel)]="user.email"
        placeholder="Enter email"
      />
    </div>

    <div class="form-group">
      <label>Phone</label>
      <input
        type="text"
        name="phone"
        [(ngModel)]="user.phone"
        placeholder="Enter phone"
      />
    </div>

    <button type="submit" [disabled]="isLoading">
      {{ isLoading ? 'Saving...' : 'Save' }}
    </button>

    <a routerLink="/" class="back-btn">Back</a>

  </form>

</div>

user-form.component.css

.form-container {
  width: 90%;
  max-width: 500px;
  margin: 40px auto;
  font-family: Arial, sans-serif;
  padding: 25px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

h2 {
  margin-bottom: 20px;
}

.form-group {
  margin-bottom: 16px;
}

label {
  display: block;
  margin-bottom: 6px;
  font-weight: bold;
}

input {
  width: 100%;
  padding: 10px;
  border: 1px solid #bbb;
  border-radius: 5px;
}

button {
  padding: 10px 18px;
  background: #2563eb;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

button:disabled {
  background: #94a3b8;
}

.back-btn {
  margin-left: 10px;
  text-decoration: none;
  color: #2563eb;
}

📌 Step 9: Add Router Outlet

Open app.component.html and replace everything with:

<router-outlet></router-outlet>

Make sure RouterOutlet is imported in app.component.ts:

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  title = 'angular-json-crud';
}

 


📌 Step 10: Output

After running both Angular and JSON Server, you will be able to:

  • Display users from fake API
  • Add new user
  • Edit existing user
  • Delete user
  • Refresh data automatically after CRUD operations

⭐ Common Issues and Fixes

  • HttpClient not working: Add provideHttpClient() in app.config.ts.
  • ngModel not working: Import FormsModule in the standalone component.
  • *ngFor or *ngIf not working: Import CommonModule in the standalone component.
  • API not loading: Make sure JSON Server is running on http://localhost:3000.
  • CORS issue: Use JSON Server locally or enable proper CORS support.
  • ID error: Always treat ID as string because JSON Server may use string or generated IDs.

📌 Final Output

  • Fake REST API using JSON Server
  • Angular 20 CRUD operations
  • Standalone component-based structure
  • Service-based API integration
  • Clean routing for add and edit pages
  • Beginner-friendly real-world API practice

📌 Summary

In this blog, we learned how to build a complete Angular 20 CRUD application using Fake JSON Server. This project is very useful for beginners who want to understand real-world API integration, service creation, routing, and standalone component-based Angular development.

This setup is also helpful before moving to a real backend like Node.js, Laravel, Django, or Spring Boot.

Code at Github

Leave a Reply

Your email address will not be published. Required fields are marked *