Skip to main content

Command Palette

Search for a command to run...

Front to Back: What Building a Full-Stack Application Really Taught Me

Published
7 min read
Front to Back: What Building a Full-Stack Application Really Taught Me

Most of my work has lived firmly on the frontend, interfaces, interactions, and user experience. So when I had the opportunity to work on a full-stack project, I didn’t think twice before jumping on the backend of things.

If you’ve followed my journey, you’ll know I’ve built plenty of frontend projects and maybe only one true full-stack application before this. That made this experience especially valuable not just technically, but in how it reshaped the way I think about systems as a whole.

Due to the nature of the work, I can’t share everything, but conceptually the application had functionality similar to Reddit:

  • User authentication and authorisation

  • Creating and viewing posts

  • Upvoting and downvoting content

Tech Stack

  • Frontend: Vanilla JavaScript, HTML, CSS

  • Backend: Java Spring Boot

  • Build Tool: Maven

  • Database: H2

  • API Testing: Postman

I chose to work on the backend deliberately. I’d been spending a lot of time on frontend work, and since my team at work was backend-focused, this was the perfect chance to sharpen those skills and understand what really happens behind the scenes.

Planning Under Pressure

We kicked things off with a structured plan to make sure we could deliver quickly and collaboratively.

  • Requirements were analysed to define an MVP

  • Tasks were broken down using a Kanban board

  • Daily stand-ups helped us track progress and unblock issues

The real challenge?
We had just five days to deliver and two of us missed the first two days due to illness. That meant less time, no room for indecision, and a need for very clear ownership.

We split the project into frontend and backend:

  • 3 engineers on the frontend

  • 2 engineers (including me) on the backend

Within the backend, we divided responsibilities into two clear domains:

  • User & Security

  • Messages & Voting

This separation allowed us to move fast without stepping on each other’s toes.

Design: Keeping It Simple

Before diving into code, I created a low-fidelity prototype in Figma. Nothing fancy just enough to communicate layout, flow, and intent. I shared it with the team, gathered quick feedback, and once everyone aligned, we moved straight into development. The goal wasn’t perfection, it was clarity.

Development: Building the Backbone

Since the database and language were already chosen, I started by translating requirements into clear API contracts especially around authentication and authorisation.

That meant defining:

  • Controllers

  • DTOs

  • Entities

  • Enums

  • Repositories

  • Custom exceptions

  • A dedicated security package

This upfront structure paid off quickly as the project scaled.

Architecture: A Layered System That Scales

A Layered Architecture with Clear Responsibilities

The backend follows a layered architecture, keeping concerns cleanly separated:

  1. Controller Layer – Handles HTTP requests and responses

    • AuthController: registration, login

    • UserController: profile, updates, password changes

  2. Service Layer – Business logic lives here

    • UserService: user operations

    • CustomUserDetailsService: Spring Security integration

  3. Repository Layer – Data access via Spring Data JPA

    • UserRepository
  4. Security Layer – JWT-based authentication

    • JwtTokenProvider

    • JwtAuthenticationFilter

    • SecurityConfig (including CORS rules)

  5. Domain Layer – Core entities

    • User

    • Role enum (USER, ADMIN)

  6. DTO Layer – Clean API communication

    • Requests: RegisterRequest, LoginRequest, ChangePasswordRequest

    • Responses: UserResponse, AuthResponse

This structure made the codebase easier to reason about, test, and extend even under tight deadlines.

Security: Stateless and Intentional

Security was treated as a first-class concern.

I implemented JWT-based authentication, keeping the system fully stateless:

  • Users log in

  • The server issues a signed JWT

  • Every protected request includes the token in the Authorization header

  • The backend validates the token and authorises access

No sessions. No server-side state. Clean and scalable.

Validation & Error Handling

Inputs were validated thoroughly:

  • Email format

  • Password strength

  • Password confirmation matching

When something failed, the API returned clear, human-readable error messages. This improved frontend UX and made testing significantly easier.

Testing: Confidence Before Demo Day

Testing wasn’t an afterthought it was part of development.

Unit Tests

Focused on business logic:

  • Duplicate email validation

  • Password checks

  • Safe response mapping

Integration Tests

End-to-end testing of:

  • Controllers

  • Services

  • Repositories

  • Security filters

This ensured the entire request pipeline worked as expected not just individual pieces.

Postman: The Human Test

Postman was invaluable for manual testing and demos:

  • Registration

  • Login (token generation)

  • Protected endpoints

A Real-World Gotcha (403 in the Spotlight)

During the presentation/demo, I registered a new user but forgot to update the token in Postman. I then tried accessing a protected endpoint with the old user’s token.

Result?
403 Forbidden.

Lesson learned:
JWTs are user-specific. Switch users → switch tokens.

Pro tips:

  • Use Postman environments ({{token}}, {{baseUrl}})

  • Automatically update tokens after login

  • Separate environments for different users

Connecting Frontend to Backend

At its core, the connection is simple: HTTP requests from JavaScript.

  • Signup/Login: frontend sends credentials, backend responds with a message or JWT

  • Authenticated requests: token sent via Authorization: Bearer <token>

  • Error handling:

    • 401 → missing or invalid token

    • 403 → valid token, wrong permissions or wrong user

This is a high level explanation of the connection from front to back but understanding what actually happens at each step is what turns “I can build” into “I understand systems.”

Here’s the general flow:

1. The Frontend Initiates a Request

Everything starts in the browser.

When a user:

  • Signs up

  • Logs in

  • Creates a post

  • Upvotes content

…the frontend (JavaScript) sends an HTTP request to the backend API using fetch or similar tools.

Each request includes:

  • A URL (e.g. /api/auth/login)

  • An HTTP method (GET, POST, PUT, DELETE)

  • Optional headers (like Content-Type or Authorization)

  • Optional body data (JSON payload)

2. The Backend Receives and Routes the Request

On the backend side:

  • The request hits a controller endpoint

  • Spring Boot maps the URL and HTTP method to the correct controller method

  • Request data is parsed into DTOs

At this stage, the backend decides:

  • Is this endpoint public or protected?

  • Does the request include a valid token?

  • Is the user allowed to perform this action?

3. Authentication & Authorisation (JWT Flow)

For protected endpoints, this is where JWT authentication comes into play.

Login flow:

  1. Frontend sends email + password

  2. Backend validates credentials

  3. Backend generates a JWT

  4. JWT is returned to the frontend

Subsequent requests:

  • Frontend attaches the token to the request:

      Authorization: Bearer <token>
    
  • Backend validates the token on every request

  • If valid → request proceeds

  • If missing/invalid → 401 Unauthorized

  • If valid but insufficient permissions → 403 Forbidden

This keeps the backend stateless, scalable, and secure.

4. Business Logic & Data Access

Once authorised:

  • Controllers pass control to services

  • Services handle business rules

  • Repositories interact with the database

  • Entities are mapped and persisted

The backend then constructs a response DTO, ensuring:

  • Sensitive data (like passwords) is never exposed

  • Only relevant fields are returned

5. Response Back to the Frontend

The backend sends a structured response:

  • Success → 200 OK / 201 Created

  • Failure → appropriate error code + message

The frontend then:

  • Updates the UI

  • Stores tokens if needed

  • Shows feedback to the user (success or error)

6. CORS: The Silent Middleman

During development, frontends and backends often run on different origins (e.g. different ports on localhost).

Without proper CORS configuration, browsers block requests even if the backend is working perfectly.

By explicitly allowing trusted origins and headers on the backend, the frontend can communicate freely without security issues.

Token Storage Trade-offs

For this project, I kept things simple with bearer tokens. Other approaches like HTTP-only cookies offer stronger XSS protection but add complexity. Each option has trade-offs depending on context.

Final Thoughts

This project reminded me that backend development isn’t just about endpoints, it’s about:

  • Designing systems under pressure

  • Making security intentional

  • Writing code that others can understand

  • Thinking beyond “does it work?” to “will it scale?”

Stepping away from the frontend and into the backend gave me a much deeper appreciation of how full-stack systems truly come together from front to back.

More from this blog

N

Not Just an Engineer

6 posts

“Not Just an Engineer” is my way of giving back, sharing knowledge, experiences, and tools to help people grow in tech, finances, and life.