{"id":13406816,"url":"https://github.com/stefanoslig/angular-ngrx-nx-realworld-example-app","last_synced_at":"2026-01-02T05:14:12.757Z","repository":{"id":29030360,"uuid":"118358642","full_name":"stefanoslig/angular-ngrx-nx-realworld-example-app","owner":"stefanoslig","description":"Real world application built with Angular 18, NgRx 18, nrwl/nx 18","archived":false,"fork":false,"pushed_at":"2024-10-29T17:28:10.000Z","size":27391,"stargazers_count":902,"open_issues_count":19,"forks_count":264,"subscribers_count":27,"default_branch":"main","last_synced_at":"2024-10-29T18:51:14.763Z","etag":null,"topics":["angular","angular18","component-store","ngrx","ngrx-effects","ngrx-signals-store","ngrx-store","nrwl","nrwl-nx","nx","nx-workspace","realworld-angular","standalone-components"],"latest_commit_sha":null,"homepage":"https://angular-ngrx-nx.netlify.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stefanoslig.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-21T17:48:04.000Z","updated_at":"2024-10-29T11:17:06.000Z","dependencies_parsed_at":"2024-11-06T00:36:04.932Z","dependency_job_id":null,"html_url":"https://github.com/stefanoslig/angular-ngrx-nx-realworld-example-app","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefanoslig%2Fangular-ngrx-nx-realworld-example-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefanoslig%2Fangular-ngrx-nx-realworld-example-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefanoslig%2Fangular-ngrx-nx-realworld-example-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefanoslig%2Fangular-ngrx-nx-realworld-example-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stefanoslig","download_url":"https://codeload.github.com/stefanoslig/angular-ngrx-nx-realworld-example-app/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243569101,"owners_count":20312358,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular18","component-store","ngrx","ngrx-effects","ngrx-signals-store","ngrx-store","nrwl","nrwl-nx","nx","nx-workspace","realworld-angular","standalone-components"],"created_at":"2024-07-30T19:02:40.025Z","updated_at":"2026-01-02T05:14:12.750Z","avatar_url":"https://github.com/stefanoslig.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","State Management","Open Source Apps"],"sub_categories":["NgRx","CI/CD \u0026 Deployment"],"readme":"# Angular NgRx NX realworld example app\n\n\u003cimg src=\"logo.png\" alt=\"RealWorld Example App\" width=\"200\"/\u003e\n\n### Modern Angular features:\n\n- New Control Flow\n- Deferred Loading\n- Zoneless\n- Signal inputs and outputs\n- State management using NgRx Signals Store\n- DI using the inject function\n- Functional resolvers and guards\n\n\u003e ### Angular, ngrx/platform, nrwl/nx codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the [RealWorld](https://github.com/gothinkster/realworld) spec and API.\n\n### [Demo](https://angular-ngrx-nx-realworld-example-app-lyart.vercel.app)\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[RealWorld](https://github.com/gothinkster/realworld)\n\n#### [NgRx Signal Store Article](https://www.stefanos-lignos.dev/posts/ngrx-signals-store)\n\nThis codebase was created to demonstrate a fully fledged fullstack application built with Angular, ngrx/platform, nrwl/nx including CRUD operations, authentication, routing, pagination, and more.\n\nWe've gone to great lengths to adhere to the Angular community styleguides \u0026 best practices.\n\nFor more information on how to this works with other frontends/backends, head over to the [RealWorld](https://github.com/gothinkster/realworld) repo.\n\n## Functionality overview\n\nThe example application is a social blogging site (i.e. a Medium.com clone) called \"Conduit\". It uses a custom API for all requests, including authentication.\n\n**General functionality:**\n\n- Authenticate users via JWT (login/signup pages + logout button on settings page)\n- CRU\\* users (sign up \u0026 settings page - no deleting required)\n- CRUD Articles\n- CR\\*D Comments on articles (no updating required)\n- GET and display paginated lists of articles\n- Favorite articles\n- Follow other users\n\n**The general page breakdown looks like this:**\n\n- Home page (URL: /#/ )\n  - List of tags\n  - List of articles pulled from either Feed, Global, or by Tag\n  - Pagination for list of articles\n- Sign in/Sign up pages (URL: /#/login, /#/register )\n  - Uses JWT\n  - Cookie based authentication\n- Settings page (URL: /#/settings )\n- Editor page to create/edit articles (URL: /#/editor, /#/editor/article-slug-here )\n- Article page (URL: /#/article/article-slug-here )\n  - Delete article button (only shown to article's author)\n  - Render markdown from server client side\n  - Comments section at bottom of page\n  - Delete comment button (only shown to comment's author)\n- Profile page (URL: /#/profile/:username, /#/profile/:username/favorites )\n  - Show basic user info\n  - List of articles populated from author's created articles or author's favorited articles\n\n## Commands\n\n### Run the application\n\n`npm run start`\n\n### Unit tests\n\nRun all the tests: `nx run-many -t test`\n\n### Lint\n\n`nx run-many -t lint`\n\n## Architecture\n\nThe project utilizes a cutting-edge Angular architecture with Nx monorepo workspace and NgRx Signal Store for state management. Here's a comprehensive overview of the key architectural concepts:\n\n### Monorepo Structure with Nx\n\nThis project is organized as a monorepo using Nx, which enables a modular, scalable architecture with clear boundaries between different parts of the application. The main benefits include:\n\n- **Scalability**: The codebase can easily grow while maintaining clear separation of concerns\n- **Dependency Graph Management**: Nx automatically tracks dependencies between libraries\n- **Improved Build Performance**: Nx's powerful caching and affected commands allow for faster builds and tests\n\n### Library Organization\n\nLibraries are classified using two dimensions:\n\n1. **Scope (Domain)**: Defines which section of the app can use the library\n\n   - `auth`: Authentication-related features\n   - `articles`: Article-related features\n   - `profile`: User profile features\n   - `home`: Home page features\n   - `core` (to be renamed to `shared`): Common utilities and components that can be used across the application\n\n2. **Type**: Defines the purpose of the library\n   - `feature-*`: Contains smart components that communicate with data sources\n   - `data-access`: Contains services and state management for interacting with the server\n   - `ui`: Contains presentational (dumb) components that are reusable within their scope\n   - `api-types`: Contains TypeScript interfaces for API models\n   - `forms`: Contains form-related components and utilities\n\nThe folder structure follows this pattern:\n\n```\n├── libs\n│   ├── articles\n│   │   ├── data-access\n│   │   ├── feature-article-edit\n│   │   ├── feature-article\n│   │   ├── feature-articles-list\n│   ├── auth\n│   │   ├── data-access\n│   │   ├── feature-auth\n│   ├── core\n│   │   ├── api-types\n│   │   ├── error-handler\n│   │   ├── http-client\n│   │   ├── forms\n│   ├── profile\n│   │   ├── data-access\n│   │   ├── feature-profile\n│   ├── ui\n│   │   ├── components\n```\n\n### State Management with NgRx Signal Store\n\nThe application uses NgRx Signal Store, a modern state management approach based on Angular's Signals, providing:\n\n- **Reactivity**: Based on Angular's Signal API for efficient change detection\n- **TypeScript Integration**: Strong typing throughout the state management system\n- **Simplified API**: More concise and intuitive compared to traditional NgRx with reducers and effects\n- **Immutability**: Enforces immutable state updates\n\nHere's how the store pattern is implemented:\n\n```typescript\nexport const AuthStore = signalStore(\n  { providedIn: 'root' },\n  withState\u003cAuthState\u003e(authInitialState),\n  withMethods(\n    (store, formErrorsStore = inject(FormErrorsStore), authService = inject(AuthService), router = inject(Router)) =\u003e ({\n      getUser: rxMethod\u003cvoid\u003e(\n        pipe(\n          switchMap(() =\u003e authService.user()),\n          tap(({ user }) =\u003e patchState(store, { user, loggedIn: true, ...setLoaded('getUser') })),\n        ),\n      ),\n      // Additional methods for login, register, updateUser, logout, etc.\n    }),\n  ),\n  withCallState({ collection: 'getUser' }),\n);\n```\n\nThe store uses:\n\n- `withState`: To define the initial state\n- `withMethods`: To define methods that can modify the state\n- `rxMethod`: To handle asynchronous operations using RxJS\n- `patchState`: To update the state immutably\n- `withCallState`: To track loading, error and success states\n\n### Standalone Components\n\nThe application exclusively uses standalone components, eliminating the need for NgModules. This results in:\n\n- **Simplified Architecture**: No need for complex module hierarchy\n- **Improved Tree-Shaking**: Better optimization of the final bundle\n- **Explicit Dependencies**: Each component declares its own dependencies\n\nExample of a standalone component:\n\n```typescript\n@Component({\n  selector: 'cdt-login',\n  templateUrl: './login.component.html',\n  imports: [ListErrorsComponent, RouterLink, ReactiveFormsModule, InputErrorsComponent],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LoginComponent {\n  private readonly authStore = inject(AuthStore);\n  private readonly fb = inject(FormBuilder);\n\n  // Component implementation\n}\n```\n\n### Dependency Injection with inject()\n\nThe application uses the modern `inject()` function instead of constructor-based dependency injection:\n\n- **Cleaner Code**: Reduces boilerplate compared to constructor injection\n- **Better TypeScript Inference**: TypeScript can better infer types with inject\n- **More Flexible**: Can be used within functions, not just classes\n\n### Lazy Loading\n\nThe application implements lazy loading for all major routes to improve initial load time:\n\n```typescript\n{\n  path: 'home',\n  loadChildren: () =\u003e import('@realworld/home/src/lib/home.routes').then((home) =\u003e home.HOME_ROUTES),\n},\n{\n  path: 'login',\n  loadComponent: () =\u003e import('@realworld/auth/feature-auth').then((m) =\u003e m.LoginComponent),\n},\n// Additional routes...\n```\n\nThis implementation uses:\n\n- `loadComponent`: For lazy loading standalone components\n- `loadChildren`: For lazy loading entire route trees\n\n### Smart vs Dumb Components Pattern\n\nThe application follows the smart/dumb component pattern:\n\n- **Smart Components**:\n\n  - Handle data fetching and state management\n  - Located in `feature-*` libraries\n  - Inject services and stores\n  - Pass data to dumb components\n\n- **Dumb Components**:\n  - Are purely presentational\n  - Located in `ui` libraries\n  - Receive data via inputs and emit events via outputs\n  - Have no dependencies on services or stores\n  - Easily testable and reusable\n\n### Minimal External Dependencies\n\nThe project avoids external UI libraries and frameworks to:\n\n- Maintain full control over the codebase\n- Avoid opinionated styles\n- Simplify migration to newer Angular versions\n- Reduce bundle size\n\n### Testing Strategy\n\nThe application uses Jest for unit testing and Playwright for end-to-end testing:\n\n- Unit tests focus on testing individual components, services, and stores in isolation\n- E2E tests validate the full user experience\n\n### Modern Angular Features\n\nThe application leverages the latest Angular features:\n\n- **New Control Flow**: Uses the new `@if`, `@for`, and `@switch` syntax for clearer templates\n- **Deferred Loading**: Implements content deferral for better initial load performance\n- **Zoneless**: Uses zoneless change detection for improved performance\n- **Signal Inputs and Outputs**: Uses the new signals-based inputs/outputs for better reactivity\n- **Functional Resolvers and Guards**: Replaces class-based guards and resolvers with more concise functions\n\n### Build and Deployment\n\nThe application uses Nx's build system for:\n\n- Fast builds with caching\n- Affected-only testing and building\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefanoslig%2Fangular-ngrx-nx-realworld-example-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstefanoslig%2Fangular-ngrx-nx-realworld-example-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefanoslig%2Fangular-ngrx-nx-realworld-example-app/lists"}