{"id":15138531,"url":"https://github.com/aesteve/nubes","last_synced_at":"2025-10-23T15:30:24.429Z","repository":{"id":87789683,"uuid":"32686538","full_name":"aesteve/nubes","owner":"aesteve","description":"Annotation layer on top of Vert.x 3","archived":false,"fork":false,"pushed_at":"2020-04-01T08:49:37.000Z","size":1218,"stargazers_count":121,"open_issues_count":16,"forks_count":35,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-01-30T19:07:58.807Z","etag":null,"topics":["annotations","java","resteasy","spring-mvc","vertx","vertx-web"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aesteve.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-03-22T17:29:07.000Z","updated_at":"2024-10-21T02:25:06.000Z","dependencies_parsed_at":"2023-10-20T16:23:02.165Z","dependency_job_id":null,"html_url":"https://github.com/aesteve/nubes","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fnubes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fnubes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fnubes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fnubes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aesteve","download_url":"https://codeload.github.com/aesteve/nubes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237843791,"owners_count":19375204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotations","java","resteasy","spring-mvc","vertx","vertx-web"],"created_at":"2024-09-26T07:40:44.586Z","updated_at":"2025-10-23T15:30:23.650Z","avatar_url":"https://github.com/aesteve.png","language":"Java","readme":"----------------\n⚠️ tl;dr: don't use this repository, use something like [Quarkus](https://quarkus.io) instead. ⚠️\n\nUnfortunately, this project relies on an old version of Vert.x. Upgrading it to Vert.x 3.5 requires a lot of work, I am looking at it, but since it's a \"weekend\" project, it might take months. Sorry for the inconvenience. Obviously any kind of help would be greatly appreciated.\nThank you for your comprehension.\n\nIf you feel like you want to help, any pull request would be highly welcomed. You can also fork the project, and start working on your own stuff, with your own vision of what such an annotation framework would be to suit your needs. The license allows you to do so.\n\nYou can start by forking the project, changing Vert.x dependency to 3.5 get it to compile (which requires some work) then run the tests (there's a pretty good code coverage) and start fixing stuff from there. That's probably the best way to help.\n\nAs an alternative, you can have a look at [Quarkus](https://quarkus.io/), which adds JAX-RS annotation capabilities on top of Vert.x (with a lot of other interesting modules).\n\nSpecial thanks to [johnfg10](https://github.com/johnfg10) for upgrading to 3.5.2. \n\n----------------\n\n[![Build Status](https://travis-ci.com/aesteve/nubes.svg?branch=master)](https://travis-ci.com/aesteve/nubes)\n[![Codecov](https://img.shields.io/codecov/c/github/aesteve/nubes.svg)](https://codecov.io/gh/aesteve/nubes)\n\n\n\n# Vert.x Nubes\n\nProvides an annotation layer on top of vertx-web. \n\nDeclare your Vert.x routes with annotated methods and controllers, in a Spring MVC-ish way.\n\n```groovy\nrepositories {\n  jcenter()\n}\n\ndependencies {\n  compile 'com.github.aesteve:nubes:2.0'\n}\n```\n\n\n## Declarative\n\n\nNubes automatically injects method parameters at runtime so that you can express your routes in a declarative manner. By reading the signature of the method, you should be able to have a quick glimpse at what your route uses (query parameters, request body, ...) and produces (void =\u003e status 204, object =\u003e marshalled).\n\n\n`public PeanutsCharacter get(@Param CharacterType type)` lets us know that the method uses a request parameter named `type` and that the response will contain a marshalled `PeanutsCharacter` POJO.\n\n\nNubes comes with a controller layer, but also a service layer. You can declare services as simple POJOS and they'll be injected within your controllers, you can also declare your services as async, they'll be started when your application starts, stopped when the application stops.\n\n\n## Extensible\n\nThe framework is designed to be fully extensible so that you can register and use your own annotations, whether it's an interceptor, a type-adapter, ... \n\nA good example on how to extend the framework is [Nubes Mongo](http://github.com/aesteve/nubes-mongo), a set of additionnal utilities (annotations, interceptors, ...) designed to help you deal with Mongo on top of Nubes. \nFor example, nubes-mongo registers a `@Create` annotation against Nubes framework, meaning that the result of the method should be saved against the Mongo database.\n\nBasically, an annotation will be tied to a set of Vert.x's Handlers, executed before and/or after the 'route' method is executed. \n\n## Non-blocking (obviously) but also non-stumbling\n\nEven though Nubes looks opinionated (declaring your routes in a single way : the controller/method way), keep in mind that at the end of the day, vertx-web's router is still there and fully accessible if you find yourself stuck in an edge case Nubes isn't designed to handle. This way, you should never, ever be stucked. \n\nYou just have a set of additional utilities at your disposal to declare your web routes in another way (a SpringMVC-ish way), to declare and inject services if you don't have a DI framework at your disposal, or to write Verticles differently. \n\nThe `Router` can also be injected as a field within your controllers as an easy way to deal with it, and maybe register routes at runtime if you need to. \n\n## Examples\n\n(Work in Progress)\n\nIf you're interested in how a real-life project built with Nubes would look like, you can have a look at [Nubes Chat](http://github.com/aesteve/nubes-chat) a very simple chat relying on a REST API and Vert.x's event-bus bridge which shows both REST and Socket controllers in action. On top of that, it uses a MongoDB as a persistent store so you can get a nice view of the service layer.\n\n## Basic example :\n\n### A controller : \n\n```java\npackage com.peanuts.controllers;\n\n@Controller(\"/peanuts\")\npublic class PeanutsPages {\n  \n  public ENUM CharacterType {\n    DOG, GIRL, BOY, BIRD;\n  }\n  \n  @GET(\"/character\")\n  @View\n  public String getCharacter(@ContextData Map\u003cString, Object\u003e data, @Param CharacterType type) {\n    switch(type) {\n      case DOG: \n        data.put(\"name\", \"Snoopy\");\n        break;\n      case BOY:\n        data.put(\"name\", \"Charlie Brown\");\n        break;\n      // ...\n    }\n    return \"character.hbs\";\n  }\n}\n```\n\n### The view : \n\n`web/views/character.hbs`\n\n```html\n\u003chtml\u003e\n  \u003cbody\u003e\n    Hello, I'm a Peanuts character, and my name is {{name}}\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n`GET \"/peanuts/character?type=DOG\"` will return the following html view:\n\n`Hello, I'm a Peanuts character, and my name is Snoopy`\n\n\n\n### A JSON api example\n\n```java\npackage com.peanuts.controllers;\n\n@Controller(\"/api/1/peanuts\")\n@ContentType(\"application/json\")\npublic class CharactersController {\n  \n  @Service(\"mongo\")\n  private MongoService mongo;\n  \n  @GET(\"/character\")\n  public PeanutsCharacter getCharacter(@Param CharacterType type) {\n    switch(type) {\n      case DOG: \n        return new PeanutsCharacter(CharacterType.DOG, \"Snoopy\", snoopysBirthDate);\n      // etc. \n    }\n  }\n  \n  @POST(\"/character\") // declaring RoutingContext as parameter means your method is async\n  public void createCharacter(RoutingContext context, @RequestBody PeanutsCharacter character) {\n    mongo.save(character, handler -\u003e {\n      context.next();  \n    }); // save it using JDBC service, mongo service, hibernate service, etc.\n  }\n}\n```\n\nWith this an example of domain object :\n\n```java\npackage com.peanuts.model;\n\npublic class PeanutsCharacter {\n\n  public ENUM CharacterType {\n    DOG, GIRL, BOY, BIRD;\n  }\n\n  private CharacterType type;\n  private String name;\n  private Date birthDate;\n  \n  // getters, setters, constructors and stuff\n}\n```\n\n\n`GET \"/api/1/peanuts/characters?type=DOG\"` with an `Accept` header containing `application/json` will return : \n\n```json\n{\n  \"name\":\"Snoopy\",\n  \"type\":\"DOG\",\n  \"birthDate\":\"1950-11-04T08:00:00.000Z\"\n}\n```\n\n\n`POST \"/api/1/peanuts/characters\"` with the following request body : \n\n```json\n{\n  \"name\":\"Snoopy\",\n  \"type\":\"DOG\",\n  \"birthDate\":\"1950-11-04T08:00:00.000Z\"\n}\n```\n\nWill save our favorite cartoon dog into the database, then return an HTTP 204.\n\n\n\n## How it works\n\n### VertxNubes as the entry point\n\nThe entry point of every work with the framwork is creatning a `VertxNubes` instance.\n\nYou'll notice the constructor takes two arguments : \n\n* a Vertx instance\n* a JsonObject containing the configuration\n\nPlease take a look at [the configuration documentation](/docs/CONFIG.md) for the available, mandatory or not, options.\n\nOnce you've created the VertxNubes instance, you need to `bootstrap` it. What it's gonna do is scanning your application classes (annotated with `@Controller`) in order to create the approriate Web routes/handlers and attach it to a vertx-web `Router`.\n\nYou can provide your own `Router`, if you want to to add custom routes and stuff in the standard vertx way. \n\nYou can also let `VertxNubes` instanciate a `Router`. It's gonna return it to you once it's done bootstrapping. And you'll be able to do pretty much whatever you need with it.\n\n```java\nVertxNubes nubes = new VertxNubes(vertx, config);\nnubes.bootstrap(res -\u003e {\n  if (res.succeeded()) {\n    Router yourRouter = res.result();\n    System.out.println(\"Everything's ready\");\n  } else {\n    System.err.println(\"Something went wrong\");\n    res.cause().printStackTrace();\n  }\n});\n\n```\n\nYou'll find a ton of examples in the tests of the project.\n\nIf you take a look at [the mock controllers](/src/test/java/mock/controllers), you'll pretty much find everything that's possible to do with Nubes out of the box.\n\n## The Controller layer\n\n### What is a `@Controller` ?\n\nA controller is a Java singleton (per Nubes instance) defining a set of methods which will be translated into vertx-web handlers. (~= express middlewares).\n\nIt's important that your controller defines a no-argument constructor, VertxNubes expect that.\n\nIn a controller you'll find routes, annotated with `@GET`, `@POST`, `@PUT`, ... but also filters of two differents types : `@BeforeFilter` and `@AfterFilter`.\n\nFor each route in your controller, before filters will be executed before your actual route method, and after filters, well... after.\n\n\n### Annotations\n\nNubes provides some default annotations. [Here's the list](/docs/ANNOTATIONS.md#framework-default-annotations).\n\nBut you can also define your own annotations, and attach vertx-web handlers to it.\n\nIn this case, you can register what Nubes calls \"Annotation processors\" which will be called before, after (or both) your method is invoked.\n\nNubes itself registers its own annotations using this API. For example, the `@ContentType({\"application/json\", \"application/xml\"})` annotation is bound to a `ContentTypeProcessor` which will : \n\n- check the `Accept` header of the request, and if it doesn't matches the MIME type you're handling, return a 406 status to the client\n- find the most suitable MIME among the ones the client specified in its `Accept` header and the ones you specified in the body of the `ContentType` annotation\n- inject this ContentType as a variable in the RoutingContext so that you can also benefit from it\n- position the `Content-Type` response header so that you don't have to care about it\n\n\n[Read the annotations document](/docs/ANNOTATIONS.md)\n\n### Parameters\n\nParameters are automatically injected into every method at runtime, depending on the context of the request (parameters, body, ...).\n\nFor a complete list of available parameters (by default), see [the parameters documentation](/docs/PARAMETERS.md).\n\nBut you can also register your own parameter resolvers by telling nubes : \"When you find this type of parameter, resolve it like this\".\n\nParameters can be resolved simply by their types (that's how Nubes injects the `RoutingContext` or the `EventBus` parameters if your method asks for it) or by a custom annotation you define.\n\n[Read the parameters injection documentation](/docs/PARAMETERS.MD)\n\n\n## The View Layer\n\nTODO : explain that template engines are created by the user, and bound to a file extension. Then how views are resolved, either by `@View(\"viewName.extension\")` or through the `ViewResolver` parameter.\n\n## The Service Layer\n\nServices in Nubes are simple POJOs you register on the Nubes instance using `nubes.registerService(String name, Object serviceInstance)`. This way, you'll be able to access them from your controllers using the `@Service(\"someName\")` annotation. \n\n### Standard services\n\nAny POJO can be a service. Simply register it against Nubes.\n\n### Async services\n\nIn some case, services take time to startup or to be closed. If you want your service to be started when Nubes bootstraps, just implements Nubes `Service` interface and register it, the same way you would register a POJO.\n\n### RPC and service proxies\n\nTODO : Explain how to use vertx's service proxy.\n\n## SockJS\n\nNubes provides a simple way to declare SockJS handlers with annotations instead of handlers defined in vertx-web.\n\n\nFor example, to handle incoming sockets you would do the following.\n\n\n```java\n@SockJS(\"/sockjs/*\")\npublic class SockJSController {\n  @OnMessage\n  public void onMessage(SockJSSocket emitter, Buffer message) {\n    emitter.write(Buffer.buffer(message)); // simply echo the message back\n  }\n}\n```\n\nYou can also use vertx-web's event-bus bridge if you want your users to access some addresses over the event-bus from client-side using SockJS.\n\n\n```java\n@EventBusBridge(\"/sockjs/*\")\n@InboundPermitted(\"some-allowed-address\")\npublic class BridgeController {\n\n  @SOCKET_CREATED\n  public void whenSocketIsCreated(BridgeEvent event) {\n    // do what you want...\n  }\n  \n  @REGISTER\n  public void whenSomeUserRegisters(BridgeEvent event) {\n    // do what you want...\n  }\n  \n}\n```\n\n","funding_links":[],"categories":["开发框架"],"sub_categories":["Web框架"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faesteve%2Fnubes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faesteve%2Fnubes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faesteve%2Fnubes/lists"}