CRUD Operations with Reactjs, TypeScript and Asp.Net Core (5.0)
Hi, We will make crud operations with asp.net core 5, react ve typescript . You can find github url of the project at the bottom of the post.
The post will be in two parts, the first is the backend and the second is the frontend.
Development Environment
- Windows 11
- Visual Studio 2019
- .Net 5.0
- Node.js (Npm paketleri için)
Project Stages
- Project Create (DDD architecture)
- EntityFramework Core implementation
- Adding service that perform crud opertaions and adding DTO
- Api Controller
- Api Test
- Adding React (CRA) and asp.net core middleware settings
- CRUD operation and frontend designing
- Adding exception pages
- Read - Delete
- Create - Update
- Project's Github Url
Project Beginning
The Project will make personnel adding, list operations, It will be DDD (Domain Driven Design) architecture. Let's create project as Blank Solution.




EntityFrameworkCore Code First
Previously We add Domain Layer to Infrastructure, Infrastructure to Web Layer as reference.
We install the following packages for ef core.

Microsoft.EntityFrameworkCore.Design we install it to web layer for migration commands.
Let's create entites folder and it within add entities (Employee and EmployeeType).

Employee.cs :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Employee { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime? BirthDate { get; set; } public int EmployeeTypeId { get; set; } public bool IsDeleted { get; set; } public string Email { get; set; } public virtual EmployeeType EmployeeType { get; set; } [NotMapped] public int? Age => BirthDate.HasValue ? (DateTime.Now - BirthDate.Value).Days / 365 : (int?)null; } |
EmployeeType.cs :
1 2 3 4 5 |
public class EmployeeType { public int Id { get; set; } public string Name { get; set; } } |
Let's create AppDbContext to Infrastructure.
1 2 3 4 5 6 7 8 9 |
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Employee> Employees { get; set; } public DbSet<EmployeeType> EmployeeTypes { get; set; } } |
Let's write connection string to appsettings.json.
1 2 3 |
"ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ReactCore;Trusted_Connection=True;MultipleActiveResultSets=true" } |
Let's Dependcy Injection for AppDbContext in Startup.cs.
1 2 |
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); |
at now let's open console and select project as Infrastructure then web layer set as start up project. Let's run the following commands.
1 2 |
add-migration InitalDb update-database |
Bu işlemleri gerçekleştirdikten sonra Infastructure katmanında aşağıdaki migration klasörü ve sql server local veritabanımızda belirttiğimiz database oluşacaktır.


Servis ve DTO
Veritabanımız hazır şimdi Employee ile ilgili database işlemlerimizi yapacak EmployeeService ve IEmployeeService servislerini oluşturalım.
Aşağıdaki gibi iki katmana da services klasörü oluşturup içerilerine servisleri ekleyelim.

IEmployeeService :
1 2 3 4 5 6 7 8 9 |
public interface IEmployeeService { void Add(Employee employee); void Update(Employee employee); void Delete(Employee employee); Employee Get(int id); IEnumerable<Employee> GetAll(); IEnumerable<EmployeeType> GetEmployeeTypes(); } |
EmployeeService :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public class EmployeeService : IEmployeeService { private readonly AppDbContext db; public EmployeeService(AppDbContext db) { this.db = db; } public void Add(Employee employee) { db.Add(employee); db.SaveChanges(); } public void Delete(Employee employee) { employee.IsDeleted = true; db.Update(employee); db.SaveChanges(); } public Employee Get(int id) { return db.Employees.FirstOrDefault(e => e.Id == id && !e.IsDeleted); } public IEnumerable<Employee> GetAll() { return db.Employees.Where(em => !em.IsDeleted).AsEnumerable(); } public void Update(Employee employee) { db.Employees.Update(employee); db.SaveChanges(); } public IEnumerable<EmployeeType> GetEmployeeTypes() { return db.EmployeeTypes.ToList(); } } |
Startup.cs’de servisimizi tanımlayalım
1 |
services.AddScoped<IEmployeeService, EmployeeService>(); |
Apiyi yazmadan önce veri modelleme işlemlerini yapmalıyız. Öncelikle Domain altında DTOs (Data Transfer Object) adında bir klasör oluşturup içine EmployeeIndexDTO ve EmployeeDTO adında iki tane class ekleyelim.
EmployeeDTO :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class EmployeeDTO { [Required] [StringLength(50, MinimumLength = 2)] public string FirstName { get; set; } [Required] [StringLength(50, MinimumLength = 2)] public string LastName { get; set; } [DisplayFormat(DataFormatString ="{0:yyyy-MM-dd}",ApplyFormatInEditMode =true)] public DateTime? BirthDate { get; set; } [Required] [EmailAddress] public string Email { get; set; } [Required] public int EmployeeTypeId { get; set; } } |
EmployeeIndexDTO :
1 2 3 4 5 6 7 8 9 |
public class EmployeeIndexDTO { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public string EmployeeTypeName { get; set; } public int? Age { get; set; } } |
Mapper işlemleri için web katmanına automapper paketlerini yükleyelim

Web katmanın kök dizinine MapperProfile.cs ekleyelim ve mapleme işlemlerini yapalım
1 2 3 4 5 6 7 8 9 |
public class MapperProfile : Profile { public MapperProfile() { CreateMap<Employee, EmployeeIndexDTO>() .ForMember(src => src.EmployeeTypeName, opts => opts.MapFrom(dest => dest.EmployeeType.Name)); CreateMap<Employee, EmployeeDTO>().ReverseMap(); } } |
startup.cs :
1 2 3 |
var mapperConfig = new MapperConfiguration(cfg => cfg.AddProfile(new MapperProfile())); services.AddAutoMapper(typeof(Startup)); services.AddSingleton(mapperConfig); |
EmployeeController adında bir api controller açalım
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
[Route("api/employee")] [ApiController] public class EmployeeController : ControllerBase { private IEmployeeService EmployeeService; private IMapper Mapper; public EmployeeController(IEmployeeService employeeService, IMapper mapper) { EmployeeService = employeeService; Mapper = mapper; } [HttpGet] public IActionResult Get(int? id) { if (id.HasValue) { var employee = EmployeeService.Get(id.Value); if (employee != null) { var dto = Mapper.Map<EmployeeDTO>(employee); return Ok(dto); } else { return NotFound(); } } var employees = EmployeeService.GetAll(); var model = Mapper.Map<IEnumerable<EmployeeIndexDTO>>(employees); return Ok(model); } [HttpPost] public IActionResult Post(EmployeeDTO dto) { if (!ModelState.IsValid) { return Ok(ModelState); } var employee = Mapper.Map<Employee>(dto); EmployeeService.Add(employee); return Ok(); } [HttpPut] public IActionResult Update(EmployeeDTO dto, int id) { if (!ModelState.IsValid) { return Ok(ModelState); } var employee = EmployeeService.Get(id); employee = Mapper.Map(dto, employee); EmployeeService.Update(employee); return Ok(); } [HttpDelete] public IActionResult Delete(int? id) { if (id.HasValue) { var employee = EmployeeService.Get(id.Value); if (employee != null) { EmployeeService.Delete(employee); return Ok(); } } return NotFound(); } [Route("/api/employeeTypes")] [HttpGet] public IActionResult GetEmployeeTypes() { var employees = EmployeeService.GetEmployeeTypes(); return Ok(employees); } } |
EmployeeType tablosuna sql server üzerinden aşağıdaki gibi eklemeler yapabilirsiniz.

Apiyi Test edelim
Apiyi test etmek için postman yada swagger kullanabilirsiniz ben popüler olan postman üzerinden yapacağım.
Employee Post Api test:


Şimdi bu api projemizin içine bir react projesi ekleyelim ve crud işlemlerini ekran üzerinden yapalım.
React – Frontend
projemize react eklemek için önümüzde iki yol var, UI adında 4. bir katman açmak yada Web katmanın içine eklemek. Biz ikinci yoldan devam edeceğiz
Create Rreact App uygulamasını web katmanında client klasörü altında oluşturacağız, typescript kullanacağız.
Aşağıdaki komutu web katmanı içinde çalıştırınız.
1 |
npx create-react-app client --template typescript |
Create react app başarılı bir şekilde yüklendi şimdi middlewareda bazı ayarları yapmadan önce Nuget üzerinden aşağıdaki paketleri yükleyelim.

startup.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}"); }); app.UseSpa(spa => { spa.Options.SourcePath = Path.Join(env.ContentRootPath, "client"); if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); } }); |
Typescript build edebilmek için Microsoft.TypeScript.MSBuild paketini nuget üzerinden kuralım. reactı başarılı bir şekilde ekledik vs code üzerinden açtığımızda aşağıdaki şekilde görünecektir. (componenets ve pages klasörlerini de ekleyelim)

http bağlantıları için axios, routing işlemleri için de react-router‘ı ve tasarım için de bootstrapı kuralım
1 |
npm i axios react-router-dom react-bootstrap |
1 |
npm i --save @types/react-router-dom |
Bootstap’ın css dosyasını şu linkten inidirip ve dist klasörü altına ekleyelim
EmployeeService.ts:
Bir tablo üzerinde personelleri listeleyeceğiz. Bu tablo üzerinden güncelleme, ekleme ve silme işlemleri yapacağız.
Bu işlemleri yapabilmek için employeeService adında bir servis oluşturacağız. Bu servisi oluşturacağımız Services klasörü altına ekleyeceğiz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import axios from "axios"; import history from "../history"; export interface EmployeeDTO { firstName: string; lastName: string; birthDate?: string; email: string; employeeTypeId: number | undefined; } export interface EmployeeIndexDTO { id: number; firstName: string; lastName: string; age?: number; email: string; employeeTypeName: string; } export interface EmployeeType { id: string; name: string; } const baseUrl = `/api/`; const http = axios.create({ baseURL: baseUrl, headers: { "Content-type": "application/json", }, }); const getAll = () => { return http.get<EmployeeIndexDTO[]>("employee"); }; const getById = (id: number) => { return http.get<EmployeeDTO>(`employee?id=${id}`); }; const create = (model: EmployeeDTO) => { return http.post<EmployeeDTO>("employee", model); }; const update = (model: EmployeeDTO, id: number) => { return http.put<EmployeeDTO>(`employee?id=${id}`, { ...model }); }; const _delete = (id: number) => { return http.delete(`employee?id=${id}`); }; const getEmployeeTypes = () => { return http.get<EmployeeType[]>("employeeTypes"); }; http.interceptors.response.use( (response) => { return response; }, (error) => { if (!error.response || typeof error.response.data !== "object") { setTimeout(() => {}, 1000); return Promise.reject(error); } const { status, config, data } = error.response; console.log(status, config, data); if (config.method === "get") { if (status === 401) { history.push("/exception/type=401"); } else if (status === 403) { history.push("/exception/type=403"); } else if (status === 404) { history.push("/exception/type=404"); } else if (status === 500) { history.push("/exception/type=500"); } } } ); export const employeeService = { getAll, getById, create, update, delete: _delete, getEmployeeTypes, }; |
İki tane EmployeeDTO veri tipimiz var EmployeeIndexDTO listelemede kullanmak için EmployeeDTO ise güncelleme ve ekleme yaparken kullanacağımız tip. axios üzerinden bir http nesnesi oluşturup get, post, put, delete gibi işlemleri bunun üzerinden yaptık.
Servis hazır, şimdi Pages klasörü altında bir Employee klasörü oluşturalım buraya index ve createOrEdit adında tsx tipinde sayfalarımız ekleyelim.

react-router ve bootstrap ayarları
kök klasörümüzdeki index.tsx dosyasına bootstrap css dosyasını import edelim ve App componentini Router tagleri içine alalım
index.tsx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import App from "./App"; import { Router } from "react-router-dom"; import "./dist/bootstrap/bootstrap.min.css"; import history from "./history.js"; ReactDOM.render( <React.StrictMode> <Router history={history}> <App /> </Router> </React.StrictMode>, document.getElementById("root") ); |
history.js
1 2 |
import { createBrowserHistory } from "history"; export default createBrowserHistory(); |
app.tsx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { Switch, Route } from "react-router-dom"; import Edit from "./pages/Employee/edit"; import Create from "./pages/Employee/create"; import Index from "./pages/Employee"; import "./App.css"; function App() { return ( <div className="App"> <Switch> <Route path="/employee/edit/:id" component={Edit} /> <Route path="/employee/create" component={Create} /> <Route path="/employee/index" component={Index} /> <Route path="/exception/type=:type" component={Exception} /> </Switch> </div> ); } export default App; |
Hata sayfalarının ekleme (404, 500 vs.)
Yanlış bir url sonucunda örneğin, Employee Controller’a 5 idli bir get işlemi yaptık ve cevap olarak 404 geldi, employeeService bunu yakalıyor ve ilgili hataya göre sayfayı yönlendiriyor. Bunun için Exception adından bir klasör oluşturup içine Index.tsx dosyası ekleyelim
Exception/Index.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import { useParams, useHistory } from "react-router-dom"; import { Button } from "react-bootstrap"; interface RouteParams { type: string; } const Exception = () => { const params = useParams<RouteParams>(); const history = useHistory(); return ( <> <div> <p>Hata : {params.type}</p> <Button variant="primary" onClick={() => history.push("/employee/index")} > Back to Home </Button> </div> </> ); }; export default Exception; |
useParams: query ile gelen değerleri yönetmemiz sağlayan bir hook.
Listeleme-Silme (Employee/Index.tsx)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
import { useState, useEffect } from "react"; import { employeeService, EmployeeIndexDTO, } from "../../services/EmployeeService"; import { Table, Button } from "react-bootstrap"; import { useHistory } from "react-router-dom"; function Index() { const [employees, setEmployees] = useState<EmployeeIndexDTO[]>([]); const history = useHistory(); useEffect(() => { employeeService .getAll() .then((response) => { setEmployees(response.data); }) .catch((e) => { console.log(e); }); }, []); const deleteRow = (id: number) => { console.log(id); employeeService.delete(id).then((response) => { if (response.status === 200) { setEmployees(employees.filter((e) => e.id !== id)); } }); }; return ( <div className="card col-md-10 text-start"> <div className="card-header"> <h4>Personeller</h4> </div> <div className="card-body"> <div className=""> <div className="d-flex justify-content-end me-3 mt-3"> <Button variant="danger" onClick={() => history.push("/employee/create")} > Personel Ekle </Button> </div> <Table hover responsive> <thead> <tr> <th>Ad Soyad</th> <th className="d-none d-sm-table-cell">Pozisyon</th> <th className="d-none d-sm-table-cell">Yaş</th> <th className="d-none d-md-table-cell">E-posta</th> <th></th> </tr> </thead> <tbody> {employees.map((pt, index) => { return ( <tr key={index}> <td>{pt.firstName + pt.lastName}</td> <td className="d-none d-sm-table-cell"> {pt.employeeTypeName} </td> <td className="d-none d-sm-table-cell">{pt.age}</td> <td className="d-none d-md-table-cell">{pt.email}</td> <td> <Button variant="primary" className="mx-1" onClick={() => history.push("/employee/edit/" + pt.id)} > Güncelle </Button> <Button variant="danger" onClick={() => deleteRow(pt.id)}> Sil </Button> </td> </tr> ); })} </tbody> </Table> </div> </div> </div> ); } export default Index; |
Listeleme sayfamız ilk açılışta useEffect ile state’i dolduruyor, deleteRow fonksiyonu da hem clienttan hem serverdan ilgili veriyi kaldırıyor
Ekleme-Güncelleme (createOrEdit.tsx)
Create ve Edit sayfalarında html elementleri tekrar ettiği için aynı sayfada işlemleri yapacağız. useParam kullanarak id parametresi olup olmadığını kontrol edeceğiz eğer id var ise servise gidip o idli verimizi alıp ilgili state’e atacağız değil ise de initial state’i kullanacağız.
State ve Interface tanımları
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
interface RouteParams { id: string; } interface EmployeeValidationState { firstName: boolean; lastName: boolean; birthDate: boolean; email: boolean; employeeTypeId: boolean; } const params = useParams<RouteParams>(); const history = useHistory(); const [employee, setEmployee] = useState<EmployeeDTO>({ firstName: "", lastName: "", birthDate: new Date().toISOString().substring(0, 10), email: "", employeeTypeId: undefined, }); const [employeeTypes, setEmployeeTypes] = useState<EmployeeType[]>([]); const [employeeValidation, setEmployeeValidation] = useState<EmployeeValidationState>({ firstName: true, lastName: true, birthDate: true, email: true, employeeTypeId: true, }); |
Verilerin yüklenmesi (Employee ve EmployeeTypes)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
useEffect(() => { const idParam = parseInt(params.id); employeeService .getEmployeeTypes() .then((response) => { setEmployeeTypes(response.data); }) .catch((e) => { console.log(e); }); if (idParam) { employeeService .getById(idParam) .then((response) => { setEmployee(response.data); }) .catch((e) => { console.log(e); }); } }, [params.id]); |
Validation fonksiyonları
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
const setValid = () => { if ( employee.firstName === "" || employee.firstName == null || employee.firstName?.length < 2 ) { employeeValidation.firstName = false; } else { employeeValidation.firstName = true; } if ( employee.lastName === "" || employee.lastName == null || employee.lastName?.length < 2 ) { employeeValidation.lastName = false; } else { employeeValidation.lastName = true; } if ( employee.email === "" || employee.email == null || !/\S+@\S+\.\S+/.test(employee.email) ) { employeeValidation.email = false; } else { employeeValidation.email = true; } if ( employee.employeeTypeId === undefined || employee.employeeTypeId == null ) { employeeValidation.employeeTypeId = false; } else { employeeValidation.employeeTypeId = true; } setEmployeeValidation({ ...employeeValidation }); return employeeValidation; }; const isValidForm = (model: EmployeeValidationState) => { return ( model.firstName && model.lastName && model.email && model.employeeTypeId ); }; |
formu submit eden fonskiyon:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
const submitForm = () => { const idParam = parseInt(params.id); var model = setValid(); if (isValidForm(model)) { if (!idParam) { employeeService .create(employee) .then((response) => { history.push("/employee/index"); }) .catch((error) => { console.log(error.response); }); } else { employeeService .update(employee, idParam) .then((response) => { history.push("/employee/index"); }) .catch((error) => { console.log(error); }); } } }; |
html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<div className="card col-md-8"> <div className="card-header text-start">Create/Güncelleme</div> <div className="card-body"> <div className="text-start"> <div className="col-md-6"> <label className="form-label">Ad*</label> <input type="text" className={`form-control ${ employeeValidation.firstName ? "" : "is-invalid" }`} id="name" required value={employee?.firstName} onChange={(e) => setEmployee({ ...employee, firstName: e.target.value }) } /> {employeeValidation.firstName ? ( "" ) : ( <div className="invalid-feedback"> En az iki karakter girmelisiniz </div> )} </div> <div className="col-md-6"> <label className="form-label">Soyad*</label> <input type="text" className={`form-control ${ employeeValidation.lastName ? "" : "is-invalid" }`} id="name" value={employee?.lastName} onChange={(e) => setEmployee({ ...employee, lastName: e.target.value }) } /> {employeeValidation.lastName ? ( "" ) : ( <div className="invalid-feedback"> En az iki karakter girmelisiniz </div> )} </div> <div className="col-md-6"> <label className="form-label">E-posta*</label> <input type="email" className={`form-control ${ employeeValidation.email ? "" : "is-invalid" }`} id="email" required value={employee?.email} onChange={(e) => setEmployee({ ...employee, email: e.target.value }) } /> {employeeValidation.email ? ( "" ) : ( <div className="invalid-feedback"> E-posta alanı boş ya da doğru biçimde değil </div> )} </div> <div className="col-6"> <label className="form-label">Doğum Tarihi</label> <input type="date" className="form-control" id="birthDate" value={employee.birthDate?.substring(0, 10) || ""} onChange={(e) => setEmployee({ ...employee, birthDate: Date.parse(e.currentTarget.value) ? new Date(e.currentTarget.value) .toISOString() .substring(0, 10) : undefined, }) } /> </div> <div className="col-md-6"> <label className="form-label">Pozisyon*</label> <select id="inputState" className={`form-control ${ employeeValidation.employeeTypeId ? "" : "is-invalid" }`} value={employee?.employeeTypeId} onChange={(e) => setEmployee({ ...employee, employeeTypeId: parseInt(e.target.value), }) } > <option value="">Seçiniz...</option> {employeeTypes.map((et) => ( <option key={et.id} value={et.id}> {et.name} </option> ))} </select> {employeeValidation.employeeTypeId ? ( "" ) : ( <div className="invalid-feedback"> Lütfen pozisyon tipi seçiniz </div> )} </div> </div> </div> <div className="card-footer text-start"> <div className="col-12"> <button type="submit" onClick={submitForm} className="btn btn-primary me-1" > Kaydet </button> <a href="/employee/index" className="btn btn-secondary"> Çıkış </a> </div> </div> </div |
Gif:

Github Linki : https://github.com/okankrdg/ReactCoreCRUD
MVC ile react için: Asp.Net Core (5.0) MVC + Webpack + React
Özet:
Özet olarak yaptıklarımızın üstünden geçelim. Domain Driven Design mimarsiyle bir Asp.Net core 5 web api projesi inşa ettik, veritabanımızı bağladık arka kısmı bitirdikten sonra react-typescript uygulamasını ekledik (create-react-app), services, pages, exception, dist şeklinde uygulamamızı parçaladık, router, bootstrap ayarlarını yaptık,service klasöründe EmployeeService adında bir servis ekledik bu servis, axios vasıtasıyla apiyle bağlantı kurmamıza yaradı, son olarak da pages altında Employee klasörü ekleyip altına index ve createOrEdit sayfalarını ekledik.