onestopjs

by Martin Koparanov

0%

← Selected work

Frontend and GraphQL Foundations for a Multi-Tenant Cybersecurity Platform

I designed and built the frontend application and GraphQL gateway from scratch, introducing strongly typed resource workflows, authentication, multi-tenancy, real-time updates, and reusable foundations later adopted as the starting point for other projects.

ReactTypeScriptGraphQLOIDCMulti-TenancyRabbitMQRedis

Context

The project was a public-facing cybersecurity platform that brought together resources and workflows from multiple backend services. The product needed to support authentication, multiple tenants, complex resource-management screens, long-running tasks, notifications, and a growing number of platform features.

The architecture was not predefined. As the product evolved, I identified and introduced many of the frontend and gateway foundations needed to keep development consistent and manageable.

Problem

Many areas of the application required the same underlying behavior: forms, modals, searchable and filterable data tables, resource detail pages, navigation, authentication, and real-time updates.

Implementing each feature independently would have created duplicated logic and inconsistent behavior. At the same time, the abstractions had to remain strongly typed and flexible enough to support resource-specific requirements rather than becoming a restrictive generic framework.

The GraphQL gateway also needed to present resources from different backend services through a consistent API while handling multi-tenancy, batching, subscriptions, and differences between the underlying services.

What I owned

I designed and built the frontend application and GraphQL gateway from scratch.

On the frontend, I established the full application foundation: OIDC authentication, multi-tenant handling, Apollo Client, generated GraphQL types, internationalization, theming, and the reusable UI architecture used across the product.

I designed strongly typed systems, including modals, confirmation and deletion flows, forms, wizards, resource tables, and resource detail pages. The resource table abstraction allowed a developer to provide a resource type and the columns to display, while the shared implementation handled fetching, loading states, searching, nested filtering, sorting, pagination, and errors. Generated TypeScript types prevented invalid or unsupported fields from being selected.

I also built a VS Code-style command palette for platform actions and resource lookup, with fuzzy search, keyboard navigation, previous-search history, and usage-weighted suggestions.

On the backend, I designed and built the GraphQL gateway that unified resources from multiple services behind a consistent API. It handled filtering, nested filtering, sorting, searching, pagination, schema generation, query batching, and N+1 mitigation.

I also implemented authentication and multi-tenant isolation in the gateway, along with notifications and task-progress updates through GraphQL subscriptions backed by RabbitMQ and Redis.

Interesting engineering parts

The main challenge was finding the right abstraction boundary. The shared systems needed to remove repetitive infrastructure without hiding important product-specific behavior.

For resource tables and detail pages, I provided sensible defaults while preserving override points for custom rendering and workflows. The same approach was used for forms and modals: common behavior was standardized, but individual features could still define their own inputs, validation, and presentation.

End-to-end type generation connected the gateway schema directly to the frontend. This allowed many API and UI integration mistakes to be caught during development rather than appearing at runtime.

Outcome

The platform gained a reusable application foundation spanning the React frontend, GraphQL gateway, authentication, multi-tenancy, resource workflows, and real-time functionality.

New resources and screens could be added with substantially less boilerplate, while shared behavior remained consistent across the product. The typed abstractions reduced integration mistakes and gave other developers clear patterns for extending the platform without rebuilding the same infrastructure for every feature.

The foundation proved reusable beyond the original product. The frontend and gateway architecture were later copied and used as the starting point for several other projects, allowing different teams to begin with established patterns for application structure, authentication, data access, typing, and common UI workflows rather than building those foundations again from scratch.