angular-spring-contract
Skill1 starsChanged 3 months agoMITView on GitHub
Owns the contract between an Angular frontend and a Spring Boot backend, and nothing inside either. Trigger when an identifier or a money value loses precision in the browser, when a date shifts by a day between server and client, when an OpenAPI document or a generated client is involved, when RFC 9457 ProblemDetail must be consumed by Angular, when JWT storage, HTTP interceptors, silent refresh, route guards or a 401 mid-request are in play, when pagination, optimistic concurrency or file upload crosses the wire, when a trace id must survive from click to SQL, or when a Playwright storageState token expires mid-suite. Install angular-developer, spring-boot-developer, java-developer, typescript-developer, postgresql-developer and ngrx-signal-store-developer alongside it; this skill defers everything inside a single layer to them.
In this repository11 other files it gives its agents.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 58
Every reference carries a ## Version notes section stating what differs across Angular 19 to 21 and Spring Boot 3.5 to 4.x, and a ## …
- Distinctive: line 119
Checklist: all 40 seam rules in one scannable list, with the five that cause the most damage first.
- Distinctive: line 154
Seam checklist: all 40 rules in one list, the five most damaging first, each linked to its reference.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Angular and Spring Boot: the Contract Between Them
This skill owns the seam between an Angular client and a Spring Boot server, and only the seam. Full-stack defects concentrate there because no single-technology skill can see both sides of a wire format at once.
What this skill does not own
| Ask about | Use instead |
| Components, signals, forms, routing, DI, SSR, ARIA, Tailwind, Angular testing | the official angular-developer skill |
Controllers, ProblemDetail on the server, Spring Security, JPA, Actuator | spring-boot-developer |
Records, sealed types, Optional, BigDecimal semantics | java-developer |
| tsconfig, strictness, branded types, discriminated unions | typescript-developer |
| Column types, keys, constraints, indexes | postgresql-developer |
SignalStore shape, rxMethod, entity collections | ngrx-signal-store-developer |
| Writing or running e2e tests, browser driving | Microsoft's playwright-cli skill |
- Establish both halves before giving guidance. The Angular major, the Spring Boot major, the OpenAPI generator and its configuration, and the database types in play. Advice correct for Angular 21 and Boot 4 is wrong on Angular 17 and Boot 3.5.
- This skill owns only what neither side owns alone. The test: a claim belongs here only
if it is false, absent, or misleading when either side is read on its own. Never restate a
companion; name it and its file, as in "spring-boot-developer's
error-handling.md". If a companion is not installed, say which one is missing and what is therefore unverified, then answer the seam question anyway under a stated assumption. Do not fill the gap by reproducing its content. - The OpenAPI document is the source of truth and generated code is never hand-edited. A hand-edit survives until the next generation and then vanishes, usually in someone else's branch. Change the server, regenerate, commit the generated output or generate in CI, but never patch it.
- Every identifier and every money value crosses the wire as a string. A JSON number is
an IEEE 754 double. It silently corrupts any integer above 9 007 199 254 740 991 and cannot
represent
0.1. This is not a style preference; the corrupted values are in type-pipeline.md with the exact outputs. - A date-only value is not an instant, and an instant is not a local time.
LocalDatemust not become a JavaScriptDate. See dates-and-times.md. - The server is the sole authority on authorisation. A route guard is a UX affordance that stops a user seeing a broken page. It protects nothing, and anyone can skip it.
- After changing anything on the seam, regenerate the client and typecheck both halves. The contract is only proven by a clean compile on both sides, not by either alone.
Every reference carries a ## Version notes section stating what differs across Angular
19 to 21 and Spring Boot 3.5 to 4.x, and a ## Gotchas list of the specific mistakes
agents make in that area. Read the gotchas even when skimming. Where a claim is executable it
has been executed: the numbers and timezone results in the references are program output, not
recollection.
Determining the versions and configuration
Step 1. Server: pom.xml or build.gradle.kts for the spring-boot-starter-parent or
plugin version, and the Java release. Client: package.json for @angular/core.
Step 2. Find the contract. Look for a committed openapi.json or openapi.yaml, a
springdoc dependency, or an openapi-generator invocation in package.json scripts or the
build. If there is no document, say so plainly: without one, the contract is whatever the two
sides currently happen to agree on, and every claim below is an assumption.
Step 3. Find the generator and its config. The generated client's shape, its date handling and its enum handling all follow generator options, and those options are where most seam defects are configured in rather than coded in.
Step 4. Identify the database types behind the endpoint. bigint, numeric, timestamptz
and uuid each have a wire decision attached, and the column type is where the chain starts.
Adding an Endpoint, End to End, In Order
| # | Step | Watch for |
| 1 | Decide the wire types before writing the DTO | bigint and numeric become strings. type-pipeline.md |
| 2 | Write the server DTO and endpoint | spring-boot-developer owns this |
| 3 | Decide the failure shape | RFC 9457, one type URI per failure mode |
| 4 | Regenerate the OpenAPI document, then the client | Never hand-edit the output |
| 5 | Typecheck the client against the regenerated types | A red compile here is the contract working |
| 6 | Map the response at the boundary, once | Parse strings into domain types at the edge, not in templates |
| 7 | Handle the failure shape, not just the happy path | An unhandled ProblemDetail reaches the user as "undefined" |
Topics
- The OpenAPI document and the generated client: what springdoc actually emits, what the generator makes of it, and the three places the generated client contradicts the server that produced it. Read openapi-contract.md
- Type pipeline: Postgres to Java to the wire to TypeScript, for every type where the naive
mapping silently loses data:
bigint,numeric,uuid,bytea, enums. Read type-pipeline.md - Dates and times:
timestamptzversusdate,InstantversusLocalDate, ISO 8601 on the wire, and why aDatein the browser is the wrong type for a date. Read dates-and-times.md - ProblemDetail: the two error shapes a Spring application actually emits, why the RFC 9457 opt-in does not cover unhandled 500s, and what the Angular interceptor must branch on. Read problem-detail.md
- Pagination: what a
Page<T>puts on the wire, why the default envelope publishes Spring Data's internals, and the one property that works on Boot 3.5 and silently does nothing on Boot 4. Read pagination.md - Auth end to end: where the token lives and what that costs under SSR, the measured refresh
stampede and the trap in its usual fix, 401 mid-request, why a guard protects nothing, and what
a
storageStatetoken does to an e2e suite. Read auth-flow.md - Optimistic concurrency:
ETagandIf-Match, why the quotes matter, and what a 412 does and does not tell the client. Read concurrency.md - File upload: what a rejected upload actually returns, why the limit has to be published, and where the proxy's limit beats yours. Read uploads.md
- Tracing: the
traceparentheader, why the trace has to start in the browser, and why the id is invisible to the client unless you return it. Read tracing.md - Checklist: all 40 seam rules in one scannable list, with the five that cause the most damage first. Use this for a review pass. Read checklist.md
Symptom index
| Symptom | Read |
An id ends in 00 that should not, or two records collide | type-pipeline.md |
A total is out by 0.01, or by 0.000000000000004 | type-pipeline.md |
10.50 displays as 10.5 | type-pipeline.md |
| A date is one day earlier for some users and not others | dates-and-times.md |
| A date is correct in the UK and Nepal but wrong in the US | dates-and-times.md |
toFixed(2) rounds 1.005 down to 1.00 | type-pipeline.md |
| An error renders as "undefined" in the UI | problem-detail.md |
problem.type is undefined after a Boot upgrade | problem-detail.md |
| A 500 is not shaped like the other errors | problem-detail.md |
pageable, paged or unpaged appear in the generated client | pagination.md |
| A snapshot test of a response body fails after a Boot upgrade | pagination.md · problem-detail.md |
A spring.* property stopped working after upgrading to Boot 4 | pagination.md |
problem.properties.x is undefined but problem.x will not compile | openapi-contract.md |
| Every field in the generated client is optional | openapi-contract.md |
--type-mappings appears to do nothing | openapi-contract.md |
| Users are logged out at random, or one refresh succeeds and the rest fail | auth-flow.md |
| Every request 401s and nothing tries to refresh | auth-flow.md |
| The first render is logged out and flips after hydration | auth-flow.md |
| An e2e test passes alone and fails eighth in the suite | auth-flow.md |
| A page hangs on a 403 | auth-flow.md |
| A write silently overwrote someone else's change | concurrency.md |
If-Match always returns 412 | concurrency.md |
| A 412 or 413 arrives with nothing to display | concurrency.md · uploads.md |
| An upload fails and the interceptor cannot parse the response | uploads.md |
| A user reports a failure and nothing links it to a log line | tracing.md |
Checklist
- Seam checklist: all 40 rules in one list, the five most damaging first, each linked to its reference. Use it for a review pass over an existing pair. Read checklist.md
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

