Skip to content

SimoneGuidi/devvault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DevVault — Code Snippet Manager

A full-stack code snippet manager built with Spring Boot 3 and Vanilla JS. Store, search, and organize reusable code snippets across multiple programming languages — accessible instantly from any browser, with zero external infrastructure.


What it does

DevVault is a personal snippet library. You can:

  • Save code snippets with a title, description, language, and tags
  • Search across title, tags, and language in real time (debounced input)
  • Filter by programming language from a dynamically populated dropdown
  • View snippets with syntax highlighting and copy them to the clipboard in one click
  • Create, edit, and delete snippets through the web interface or directly via the REST API

Eight sample snippets (Scala/Spark, Java/Spring, Python, SQL, Bash) are pre-loaded on every startup.


Why these technology choices

Choice Alternative Reason
Spring Boot 3 Quarkus, Micronaut De-facto enterprise standard, extensive ecosystem, immediately recognizable by any Java recruiter.
Java 17 Java 21, Java 11 LTS release, stable, widely available in enterprise CI/CD pipelines.
H2 in-memory PostgreSQL, SQLite Zero setup: the database starts with the app and is pre-seeded from data.sql. Anyone can clone and run in seconds without installing a database. Data resets on every restart, keeping demos clean.
Maven Gradle Standard in enterprise Java. pom.xml is explicit and easy for teams to audit. Spring Initializr defaults to Maven.
Vanilla JS + HTML React, Vue, Angular Demonstrates that a clean, functional SPA doesn't require a framework at this complexity level. No build step, no node_modules, no bundler — the entire frontend is one file.
Spring Data JPA JDBC Template, MyBatis Repository abstraction removes boilerplate CRUD; custom JPQL queries are still possible when needed.

Architecture

Browser (index.html)
    │  fetch() calls
    ▼
┌─────────────────────────────────────────────┐
│  Spring Boot 3 / Embedded Tomcat (:8080)    │
│                                             │
│  SnippetController  (@RestController)       │
│         │  delegates to                     │
│  SnippetService     (@Service)              │
│         │  delegates to                     │
│  SnippetRepository  (@Repository)           │
│         │  Hibernate ORM                    │
│  H2 In-Memory DB  (devvaultdb)              │
└─────────────────────────────────────────────┘

Layer responsibilities:

  • Controller — HTTP only: parses path variables, query params, request bodies, returns correct status codes. No business logic.
  • Service — business rules: guards against null ids, prevents client-supplied ids on create, owns transaction boundaries (@Transactional).
  • Repository — data access: inherits CRUD from JpaRepository, adds two custom JPQL queries (full-text search, distinct language list).
  • GlobalExceptionHandler@RestControllerAdvice that intercepts all exceptions and returns a uniform JSON error payload, keeping error handling out of individual controllers.

Data model:

Snippet
├── id          Long          PK, auto-generated
├── titolo      String(100)   required
├── descrizione String(300)   optional
├── codice      TEXT          required
├── linguaggio  String(50)    e.g. "Java", "Python"
├── tag         String(500)   comma-separated, e.g. "spark,join"
├── createdAt   LocalDateTime set on @PrePersist, never updated
└── updatedAt   LocalDateTime refreshed on @PreUpdate

How the code works

Snippet.java uses @PrePersist / @PreUpdate lifecycle callbacks to manage timestamps automatically — no caller ever sets them. Bean Validation annotations (@NotBlank, @Size) live alongside the field declarations so constraints are enforced both at the API layer (via @Valid) and at the persistence layer.

SnippetRepository.java adds two hand-written JPQL queries on top of the Spring Data auto-generated CRUD. The search query uses LOWER(CONCAT('%', :q, '%')) for case-insensitive partial matching across title, tags, and language. Empty strings short-circuit to return all rows, so the same query handles both "search" and "list all" scenarios.

SnippetService.java owns the @Transactional boundaries (read-only by default, explicit for mutations). The create method forces snippet.setId(null) so a client can never inject a custom id. The update method loads the existing entity first — throwing ResourceNotFoundException if absent — and only transfers the mutable fields, protecting id and createdAt.

SnippetController.java is intentionally thin: it maps HTTP verbs and delegates everything to the service. The /languages mapping is declared before /{id} because Spring MVC resolves literal path segments before path variables — without this order, GET /api/snippets/languages would be misinterpreted as a request for a snippet with id "languages".

Frontend (index.html) is a single-file SPA. State is a plain JS object (state.view, state.selectedId). All seven REST calls are wrapped in an api object. Search triggers loadSnippets() after 300 ms of inactivity to avoid hammering the API on every keystroke. Syntax highlighting is done with a regex-based highlightCode() function — no external library needed.


API Reference

Method Endpoint Params Description
GET /api/snippets All snippets, newest first
GET /api/snippets/{id} id One snippet or 404
GET /api/snippets/search q, language (optional) Full-text + language filter
GET /api/snippets/languages Distinct language list
POST /api/snippets JSON body Create a snippet, returns 201
PUT /api/snippets/{id} id + JSON body Update a snippet
DELETE /api/snippets/{id} id Delete a snippet, returns 204

Request body (POST / PUT):

Field Type Required Constraints
titolo string yes max 100 chars
descrizione string no max 300 chars
codice string yes no limit
linguaggio string no max 50 chars
tag string no max 500 chars, comma-separated

Sample create request:

POST /api/snippets
Content-Type: application/json

{
  "titolo": "Hello World in Go",
  "descrizione": "Minimal Go program",
  "codice": "package main\n\nimport \"fmt\"\n\nfunc main() {\n    fmt.Println(\"Hello, World!\")\n}",
  "linguaggio": "Go",
  "tag": "go,hello-world"
}

Error response format (all 4xx / 5xx):

{
  "status": 404,
  "message": "Snippet non trovato con id: 99",
  "errors": null,
  "timestamp": "2024-06-01T10:31:00"
}

How to run

Prerequisites: Java 17+, Maven 3.6+. No database installation needed.

cd devvault
mvn spring-boot:run

The app starts in ~5 seconds. Open http://localhost:8080.

Access points:

URL Description
http://localhost:8080 Web UI
http://localhost:8080/api/snippets REST API
http://localhost:8080/h2-console H2 Database Console (dev only)

H2 Console login:

Field Value
JDBC URL jdbc:h2:mem:devvaultdb
Username sa
Password (leave empty)

Build a self-contained JAR:

mvn package
java -jar target/devvault-1.0.0.jar

Project structure

devvault/
├── pom.xml
└── src/main/
    ├── java/com/devvault/
    │   ├── DevVaultApplication.java          # entry point
    │   ├── controller/
    │   │   ├── SnippetController.java        # REST endpoints
    │   │   └── GlobalExceptionHandler.java   # centralized error handling
    │   ├── dto/
    │   │   └── ErrorResponse.java            # uniform error JSON structure
    │   ├── exception/
    │   │   └── ResourceNotFoundException.java
    │   ├── model/
    │   │   └── Snippet.java                  # JPA entity
    │   ├── repository/
    │   │   └── SnippetRepository.java        # Spring Data + custom JPQL
    │   └── service/
    │       └── SnippetService.java           # business logic + transactions
    └── resources/
        ├── application.properties
        ├── data.sql                          # 8 sample snippets
        └── static/
            └── index.html                   # single-page frontend

About

Gestore snippet di codice con Spring Boot e REST API

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors