paint-brush
Como introducir unha nova API rapidamente usando Spring Boot e Gradlepor@johnjvester
355 lecturas
355 lecturas

Como introducir unha nova API rapidamente usando Spring Boot e Gradle

por John Vester14m2025/03/24
Read on Terminal Reader

Demasiado longo; Ler

O tempo de comercialización pode facer ou romper calquera idea ou solución. Comproba a rapidez con que se pode crear unha API RESTful aproveitando ChatGPT, Spring Boot, Gradle e Heroku.
featured image - Como introducir unha nova API rapidamente usando Spring Boot e Gradle
John Vester HackerNoon profile picture
0-item
1-item
2-item


Durante os últimos cinco anos, teño a cita "todo comeza cunha idea" na parede da miña oficina.


A miña muller atopou este produto en Etsy pouco despois de comezar a desenvolver unha colección de API para unha aplicación de fitness. Encántame esta afirmación porque recolle a paixón que me consume durante as etapas de creación dun novo proxecto. Este aínda é o meu aspecto favorito de ser enxeñeiro, aínda que teña tres décadas de carreira.


O que aprendín durante este tempo é que unha idea só importa se alguén ten a oportunidade de experimentala. Se unha idea tarda demasiado en facerse realidade, acabas cunha oportunidade perdida xa que outra persoa te vence ao golpe. É por iso que as startups sempre están a correr para sacar as súas ideas ao mercado o máis rápido posible.


Vexamos como podemos facer realidade unha idea... rapidamente.

Suposicións

Para este artigo, imos manter as cousas sinxelas. Usaremos Java 17 e Spring Boot 3 para crear unha API RESTful. Neste exemplo, usaremos Gradle para a nosa automatización de compilación.


Aínda que a idea de servizo que pensamos levar ao mercado normalmente usaría unha capa de persistencia, deixarémola de lado para este exemplo e definiremos de forma estática os nosos datos dentro dunha clase de repositorio.


Non nos preocuparemos por engadir ningunha seguridade para este exemplo, simplemente permitir o acceso anónimo para esta proba de concepto.

A API de citas motivadoras

Supoñamos que a nosa idea é unha API de citas motivacionais. Para asegurarme de que corremos o máis rápido posible, pedinlle a ChatGPT que me crease unha especificación de OpenAPI.


En segundos, ChatGPT deu a resposta:


Aquí está a especificación OpenAPI, en YAML, que xerou ChatGPT:


 openapi: 3.0.0 info: title: Motivational Quotes API description: An API that provides motivational quotes. version: 1.0.0 servers: - url: https://api.example.com description: Production server paths: /quotes: get: summary: Get all motivational quotes operationId: getAllQuotes responses: '200': description: A list of motivational quotes content: application/json: schema: type: array items: $ref: '#/components/schemas/Quote' /quotes/random: get: summary: Get a random motivational quote operationId: getRandomQuote responses: '200': description: A random motivational quote content: application/json: schema: $ref: '#/components/schemas/Quote' /quotes/{id}: get: summary: Get a motivational quote by ID operationId: getQuoteById parameters: - name: id in: path required: true schema: type: integer responses: '200': description: A motivational quote content: application/json: schema: $ref: '#/components/schemas/Quote' '404': description: Quote not found components: schemas: Quote: type: object required: - id - quote properties: id: type: integer quote: type: string


Só necesitaba facer unha actualización manual, asegurándome de que as propiedades de id e quote fosen necesarias para o esquema Quote . E iso só porque esquecín mencionar esta restrición a ChatGPT no meu aviso orixinal.

Con iso, estamos preparados para desenvolver o novo servizo utilizando un enfoque API-First .

Creando o servizo Spring Boot usando API-First

Para este exemplo, usarei a CLI de Spring Boot para crear un novo proxecto. Aquí tes como podes instalar a CLI usando Homebrew:


 $ brew tap spring-io/tap $ brew install spring-boot

Crea un novo servizo de arranque de primavera

Chamaremos o proxecto quotes , creándoo co seguinte comando:


 $ spring init --dependencies=web quotes


Examinemos o contido do cartafol quotes :


 $ cd quotes && ls -la total 72 drwxr-xr-x@ 11 jvester 352 Mar 1 10:57 . drwxrwxrwx@ 90 jvester 2880 Mar 1 10:57 .. -rw-r--r--@ 1 jvester 54 Mar 1 10:57 .gitattributes -rw-r--r--@ 1 jvester 444 Mar 1 10:57 .gitignore -rw-r--r--@ 1 jvester 960 Mar 1 10:57 HELP.md -rw-r--r--@ 1 jvester 545 Mar 1 10:57 build.gradle drwxr-xr-x@ 3 jvester 96 Mar 1 10:57 gradle -rwxr-xr-x@ 1 jvester 8762 Mar 1 10:57 gradlew -rw-r--r--@ 1 jvester 2966 Mar 1 10:57 gradlew.bat -rw-r--r--@ 1 jvester 28 Mar 1 10:57 settings.gradle drwxr-xr-x@ 4 jvester 128 Mar 1 10:57 src


A continuación, editamos o ficheiro build.gradle como se mostra a continuación para adoptar o enfoque API-First.


 plugins { id 'java' id 'org.springframework.boot' version '3.4.3' id 'io.spring.dependency-management' version '1.1.7' id 'org.openapi.generator' version '7.12.0' } openApiGenerate { generatorName = "spring" inputSpec = "$rootDir/src/main/resources/static/openapi.yaml" outputDir = "$buildDir/generated" apiPackage = "com.example.api" modelPackage = "com.example.model" configOptions = [ dateLibrary: "java8", interfaceOnly: "true", useSpringBoot3: "true", useBeanValidation: "true", skipDefaultInterface: "true" ] } group = 'com.example' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.openapitools:jackson-databind-nullable:0.2.6' implementation 'io.swagger.core.v3:swagger-annotations:2.2.20' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } sourceSets { main { java { srcDirs += "$buildDir/generated/src/main/java" } } } compileJava.dependsOn tasks.openApiGenerate tasks.named('test') { useJUnitPlatform() }


Finalmente, colocamos a especificación OpenAPI xerada no cartafol resources/static como openapi.yaml .

Xera os obxectos API e Model

Despois de abrir o proxecto en IntelliJ, executei o seguinte comando para construír os stubs da API e os obxectos do modelo.


 ./gradlew clean build


Agora, podemos ver a api e os obxectos model creados a partir da nosa especificación OpenAPI. Aquí está o ficheiro QuotesAPI.java :


Engade a lóxica empresarial

Co servizo base listo e xa adherido ao noso contrato OpenAPI, comezamos a engadir algunha lóxica empresarial ao servizo.


En primeiro lugar, creamos unha clase QuotesRepository que devolve os datos do noso servizo. Como se indicou anteriormente, isto normalmente almacenaríase nalgunha capa de persistencia dedicada. Para este exemplo, a codificación de datos de cinco comiñas funciona ben e manténnos concentrados.


 @Repository public class QuotesRepository { public static final List<Quote> QUOTES = List.of( new Quote() .id(1) .quote("The greatest glory in living lies not in never falling, but in rising every time we fall."), new Quote() .id(2) .quote("The way to get started is to quit talking and begin doing."), new Quote() .id(3) .quote("Your time is limited, so don't waste it living someone else's life."), new Quote() .id(4) .quote("If life were predictable it would cease to be life, and be without flavor."), new Quote() .id(5) .quote("If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success.") ); public List<Quote> getAllQuotes() { return QUOTES; } public Optional<Quote> getQuoteById(Integer id) { return Optional.ofNullable(QUOTES.stream().filter(quote -> quote.getId().equals(id)).findFirst().orElse(null)); } }


A continuación, creamos un QuotesService que interactuará co QuotesRepository . Tomar este enfoque manterá os datos separados da lóxica empresarial.


 @RequiredArgsConstructor @Service public class QuotesService { private final QuotesRepository quotesRepository; public List<Quote> getAllQuotes() { return quotesRepository.getAllQuotes(); } public Optional<Quote> getQuoteById(Integer id) { return quotesRepository.getQuoteById(id); } public Quote getRandomQuote() { List<Quote> quotes = quotesRepository.getAllQuotes(); return quotes.get(ThreadLocalRandom.current().nextInt(quotes.size())); } }


Finalmente, só necesitamos implementar o QuotesApi xerado a partir do noso enfoque API-First:


 @Controller @RequiredArgsConstructor public class QuotesController implements QuotesApi { private final QuotesService quotesService; @Override public ResponseEntity<List<Quote>> getAllQuotes() { return new ResponseEntity<>(quotesService.getAllQuotes(), HttpStatus.OK); } @Override public ResponseEntity<Quote> getQuoteById(Integer id) { return quotesService.getQuoteById(id) .map(quote -> new ResponseEntity<>(quote, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @Override public ResponseEntity<Quote> getRandomQuote() { return new ResponseEntity<>(quotesService.getRandomQuote(), HttpStatus.OK); } }


Neste punto, temos unha API de Motivational Quotes totalmente funcional, completa cunha pequena colección de respostas.

Algúns elementos finais

Spring Boot ofrécenos a opción dunha interface de usuario de Swagger Docs baseada na web mediante a dependencia springdoc-openapi-starter-webmvc-ui .


 dependencies { ... implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5' ... }


Aínda que o marco permite aos enxeñeiros usar anotacións sinxelas para describir a súa API, podemos usar o noso ficheiro openapi.yaml existente no cartafol resources/static .


Podemos implementar este enfoque no ficheiro application-properties.yaml , xunto con algunhas outras actualizacións de configuración menores:


 server: port: ${PORT:8080} spring: application: name: quotes springdoc: swagger-ui: path: /swagger-docs url: openapi.yaml


Só por diversión, imos engadir un ficheiro banner.txt para usar cando se inicie o servizo. Colocamos este ficheiro no cartafol resources .


 ${AnsiColor.BLUE} _ __ _ _ _ ___ | |_ ___ ___ / _` | | | |/ _ \| __/ _ \/ __| | (_| | |_| | (_) | || __/\__ \ \__, |\__,_|\___/ \__\___||___/ |_| ${AnsiColor.DEFAULT} :: Running Spring Boot ${AnsiColor.BLUE}${spring-boot.version}${AnsiColor.DEFAULT} :: Port #${AnsiColor.BLUE}${server.port}${AnsiColor.DEFAULT} ::


Agora, cando iniciamos o servizo localmente, podemos ver o banner:


Unha vez iniciado, podemos validar que Swagger Docs está a funcionar visitando o punto final /swagger-docs .



Finalmente, crearemos un novo repositorio baseado en Git para que poidamos seguir calquera cambio futuro:


 $ git init $ git add . $ git commit -m "Initial commit for the Motivational Quotes API"


Agora, vexamos o rápido que podemos implementar o noso servizo .

Usando Heroku para rematar a viaxe

Ata agora, o foco principal para presentar a miña nova idea foi crear unha especificación OpenAPI e escribir algunha lóxica empresarial para o meu servizo. Spring Boot encargouse de todo o demais por min.


Cando se trata de executar o meu servizo, prefiro usar Heroku porque é ideal para os servizos de Spring Boot. Podo implementar os meus servizos rapidamente sen estar atascado en problemas de infraestrutura na nube. Heroku tamén facilita o paso dos valores de configuración para as miñas aplicacións baseadas en Java .


Para que coincida coa versión de Java que estamos a usar, creamos un ficheiro system.properties no cartafol raíz do proxecto. O ficheiro ten unha liña:


 java.runtime.version = 17


A continuación, creo un Procfile na mesma localización para personalizar o comportamento de implantación. Este ficheiro tamén ten unha liña:


 web: java -jar build/libs/quotes-0.0.1-SNAPSHOT.jar


É hora de despregar. Co Heroku CLI , podo implementar o servizo mediante algúns comandos sinxelos. Primeiro, autentico a CLI e despois creo unha nova aplicación Heroku.


 $ heroku login $ heroku create Creating app... done, vast-crag-43256 https://vast-crag-43256-bb5e35ea87de.herokuapp.com/ | https://git.heroku.com/vast-crag-43256.git


A miña instancia da aplicación Heroku chámase vast-crag-43256 (podería ter un nome especificado) e o servizo executarase en https://vast-crag-43256-bb5e35ea87de.herokuapp.com/.


O último que hai que facer é implementar o servizo usando un comando Git para enviar o código a Heroku:


 $ git push heroku master


Unha vez completado este comando, podemos validar unha implantación exitosa a través do panel de control de Heroku:


Agora estamos preparados para probar o noso novo servizo.

Citas de motivación en acción

Co servizo Motivational Quotes funcionando en Heroku, podemos validar que todo funciona como se espera usando unha serie de comandos curl .


Primeiro, imos obter unha lista completa das cinco citas motivadoras:


 $ curl \ --location 'https://vast-crag-43256-bb5e35ea87de.herokuapp.com/quotes'


 [ { "id":1, "quote":"The greatest glory in living lies not in never falling, but in rising every time we fall." }, { "id":2, "quote":"The way to get started is to quit talking and begin doing." }, { "id":3, "quote":"Your time is limited, so don't waste it living someone else's life." }, { "id":4, "quote":"If life were predictable it would cease to be life, and be without flavor." }, { "id":5, "quote":"If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success." } ]


Imos recuperar unha única cita motivacional por ID:


 $ curl \ --location 'https://vast-crag-43256-bb5e35ea87de.herokuapp.com/quotes/3'


 { "id":3, "quote":"Your time is limited, so don't waste it living someone else's life." }


Imos obter unha cita motivacional aleatoria:


 $ curl --location \ 'https://vast-crag-43256-bb5e35ea87de.herokuapp.com/quotes/random'


 { "id":5, "quote":"If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success." }


Tamén podemos navegar por Swagger Docs.


Conclusión

O tempo do mercado pode facer ou romper calquera idea. É por iso que as startups están centradas no láser en ofrecer as súas innovacións o máis rápido posible. Canto máis tempo tarde en chegar á meta, maior será o risco de que un competidor chegue antes que ti.


Os meus lectores poden lembrar a miña declaración de misión persoal, que creo que se pode aplicar a calquera profesional de TI:


"Concentra o teu tempo en ofrecer funcións/funcionalidades que amplían o valor da túa propiedade intelectual. Aproveita marcos, produtos e servizos para todo o demais".

- J. Vester


Neste artigo, vimos como Spring Boot xestionaba todo o necesario para implementar unha API RESTful. Aproveitando ChatGPT, incluso puidemos expresar en palabras humanas o que queriamos que fose o noso servizo e creou unha especificación OpenAPI para nós en cuestión de segundos. Isto permitiunos aproveitar un enfoque API-First. Unha vez listos, puidemos entregar a nosa idea usando Heroku emitindo algúns comandos CLI.

Spring Boot, ChatGPT e Heroku proporcionaron os marcos e os servizos para que puidese seguir centrado no láser en realizar a miña idea. Como resultado, puiden cumprir a miña declaración de misión persoal e, o que é máis importante, entregar a miña idea rapidamente. Todo o que tiña que facer era centrarme na lóxica empresarial detrás da miña idea, ¡e así debería ser!


Se estás interesado, o código fonte deste artigo pódese atopar en GitLab .


Que teñades un gran día!