Simple CRUD Application using Ktor and Angular


To implement a simple CRUD (Create, Read, Update, Delete) application using Ktor 3 (a Kotlin web framework) for the backend and Angular for the frontend, follow these general steps:

1. Backend Setup (Ktor)

1.1. Create a Ktor 3 Project

First, create a Ktor project. You can use the Ktor project generator (https://start.ktor.io/) or manually set up the project with Gradle or Maven. Ensure you have the required dependencies for Ktor 3, such as:

  • ktor-server-core
  • ktor-server-netty (for the server)
  • ktor-server-content-negotiation (for JSON serialization)
  • ktor-server-cors (for cross-origin requests)

1.2. Install Dependencies

Add the necessary dependencies in the build.gradle.kts file:

plugins {
    kotlin("jvm") version "1.9.0"
    id("io.ktor.plugin") version "3.0.0"
}

dependencies {
    implementation("io.ktor:ktor-server-core:3.0.0")
    implementation("io.ktor:ktor-server-netty:3.0.0")
    implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.0")
    implementation("io.ktor:ktor-server-content-negotiation:3.0.0")
    implementation("io.ktor:ktor-server-cors:3.0.0")
}

1.3. Create Ktor Routes

Next, define the CRUD routes for the backend:

import io.ktor.application.*
import io.ktor.features.ContentNegotiation
import io.ktor.http.HttpStatusCode
import io.ktor.response.respond
import io.ktor.routing.Routing
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.delete
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.features.ContentNegotiation
import io.ktor.serialization.kotlinx.json
import kotlinx.serialization.Serializable
import io.ktor.application.install

@Serializable
data class Item(val id: Int, val name: String)

val items = mutableListOf<Item>()

fun Application.module() {
    install(ContentNegotiation) {
        json()
    }

    routing {
        route("/items") {
            // Create item
            post {
                val item = call.receive<Item>()
                items.add(item)
                call.respond(HttpStatusCode.Created, item)
            }

            // Get all items
            get {
                call.respond(items)
            }

            // Update item
            put("{id}") {
                val id = call.parameters["id"]?.toInt() ?: return@put call.respond(HttpStatusCode.BadRequest)
                val updatedItem = call.receive<Item>()
                val index = items.indexOfFirst { it.id == id }
                if (index != -1) {
                    items[index] = updatedItem
                    call.respond(HttpStatusCode.OK, updatedItem)
                } else {
                    call.respond(HttpStatusCode.NotFound)
                }
            }

            // Delete item
            delete("{id}") {
                val id = call.parameters["id"]?.toInt() ?: return@delete call.respond(HttpStatusCode.BadRequest)
                val index = items.indexOfFirst { it.id == id }
                if (index != -1) {
                    items.removeAt(index)
                    call.respond(HttpStatusCode.NoContent)
                } else {
                    call.respond(HttpStatusCode.NotFound)
                }
            }
        }
    }
}

fun main() {
    embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)
}

1.4. Run the Ktor Application

Run your Ktor backend using:

./gradlew run

Your Ktor server will start on port 8080, with CRUD routes for managing items.


2. Frontend Setup (Angular)

2.1. Create an Angular Project

Generate a new Angular project:

ng new angular-crud-app
cd angular-crud-app

2.2. Install Angular HTTP Client

Ensure the HttpClientModule is imported in your app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';  // Import HttpClientModule

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

2.3. Create a Service to Handle CRUD Operations

Create a service to interact with the backend API:

ng generate service item

In item.service.ts, define the CRUD methods:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface Item {
  id: number;
  name: string;
}

@Injectable({
  providedIn: 'root',
})
export class ItemService {
  private apiUrl = 'http://localhost:8080/items';

  constructor(private http: HttpClient) {}

  getItems(): Observable<Item[]> {
    return this.http.get<Item[]>(this.apiUrl);
  }

  createItem(item: Item): Observable<Item> {
    return this.http.post<Item>(this.apiUrl, item);
  }

  updateItem(id: number, item: Item): Observable<Item> {
    return this.http.put<Item>(`${this.apiUrl}/${id}`, item);
  }

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

2.4. Create a Component to Display and Manage Items

Create a component to display the list of items and interact with the backend:

ng generate component item-list

In item-list.component.ts, add the logic for CRUD operations:

import { Component, OnInit } from '@angular/core';
import { ItemService, Item } from '../item.service';

@Component({
  selector: 'app-item-list',
  templateUrl: './item-list.component.html',
  styleUrls: ['./item-list.component.css'],
})
export class ItemListComponent implements OnInit {
  items: Item[] = [];

  constructor(private itemService: ItemService) {}

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

  loadItems(): void {
    this.itemService.getItems().subscribe((data) => {
      this.items = data;
    });
  }

  createItem(name: string): void {
    const newItem: Item = { id: Date.now(), name };
    this.itemService.createItem(newItem).subscribe(() => this.loadItems());
  }

  updateItem(id: number, name: string): void {
    const updatedItem: Item = { id, name };
    this.itemService.updateItem(id, updatedItem).subscribe(() => this.loadItems());
  }

  deleteItem(id: number): void {
    this.itemService.deleteItem(id).subscribe(() => this.loadItems());
  }
}
In i

In item-list.component.html, create a simple interface for CRUD operations:

<div>
  <h1>Item List</h1>
  <ul>
    <li *ngFor="let item of items">
      {{ item.name }} <button (click)="deleteItem(item.id)">Delete</button>
      <button (click)="updateItem(item.id, 'Updated Name')">Update</button>
    </li>
  </ul>

  <input #newItemName type="text" placeholder="New Item Name" />
  <button (click)="createItem(newItemName.value)">Create Item</button>
</div>

2.5. Run the Angular Application

Run the Angular application with:

ng serve

Your Angular frontend will be running on http://localhost:4200, and it will interact with the Ktor backend for CRUD operations.


3. Test the Application

  • Start the Ktor backend by running the Gradle run task.
  • Start the Angular frontend with ng serve.
  • Access the Angular app at http://localhost:4200 to manage items through the Ktor API.

This setup provides a basic CRUD implementation using Ktor and Angular. You can extend it with features like form validation, error handling, and more complex business logic.

Comments

Popular posts from this blog

Spring Boot OpenAI Integration: Step-by-Step Guide

Orchestration-Based Saga Architecture and Spring Boot Microservices Implementation Guide

Spring Boot 3 + Angular 15 + Material - Full Stack CRUD Application Example