|  | 
|  | 1 | +//// | 
|  | 2 | +This guide is maintained in the main Quarkus repository | 
|  | 3 | +and pull requests should be submitted there: | 
|  | 4 | +https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc | 
|  | 5 | +//// | 
|  | 6 | += Quarkus - Generating JAX-RS resources with Panache | 
|  | 7 | + | 
|  | 8 | +include::./attributes.adoc[] | 
|  | 9 | +:extension-status: experimental | 
|  | 10 | + | 
|  | 11 | +A lot of web applications are monotonous CRUD applications with REST APIs that are tedious to write. | 
|  | 12 | +To streamline this task, REST Data with Panache extension can generate the basic CRUD endpoints for your entities and repositories. | 
|  | 13 | + | 
|  | 14 | +While this extension is still experimental and provides a limited feature set, we hope to get an early feedback for it. | 
|  | 15 | +Currently this extension supports Hibernate ORM with Panache and can generate CRUD resources that work with `application/json` and `application/hal+json` content. | 
|  | 16 | + | 
|  | 17 | +include::./status-include.adoc[] | 
|  | 18 | + | 
|  | 19 | +== Setting up REST Data with Panache | 
|  | 20 | + | 
|  | 21 | +To begin with: | 
|  | 22 | + | 
|  | 23 | +* Add the required dependencies to your `pom.xml` | 
|  | 24 | +** Hibernate ORM REST Data with Panache extension (`quarkus-hibernate-orm-rest-data-panache`) | 
|  | 25 | +** A JDBC driver extension (`quarkus-jdbc-postgresql`, `quarkus-jdbc-h2`, `quarkus-jdbc-mariadb`, ...) | 
|  | 26 | +** One of the RESTEasy JSON serialization extensions (`quarkus-resteasy-jackson` or `quarkus-resteasy-jsonb`) | 
|  | 27 | + | 
|  | 28 | +[source,xml] | 
|  | 29 | +---- | 
|  | 30 | +<dependencies> | 
|  | 31 | +    <dependency> | 
|  | 32 | +        <groupId>io.quarkus</groupId> | 
|  | 33 | +        <artifactId>quarkus-hibernate-orm-rest-data-panache</artifactId> | 
|  | 34 | +    </dependency> | 
|  | 35 | +    <dependency> | 
|  | 36 | +        <groupId>io.quarkus</groupId> | 
|  | 37 | +        <artifactId>quarkus-jdbc-postgresql</artifactId> | 
|  | 38 | +    </dependency> | 
|  | 39 | +    <dependency> | 
|  | 40 | +        <groupId>io.quarkus</groupId> | 
|  | 41 | +        <artifactId>quarkus-resteasy-jackson</artifactId> | 
|  | 42 | +    </dependency> | 
|  | 43 | +</dependencies> | 
|  | 44 | +---- | 
|  | 45 | + | 
|  | 46 | +* Implement the Panache entities and/or repositories as explained in the link:hibernate-orm-panache[Hibernate ORM with Panache guide]. | 
|  | 47 | +* Define the interfaces for generation as explained in the sections below. | 
|  | 48 | + | 
|  | 49 | +== Generating resources | 
|  | 50 | + | 
|  | 51 | +REST Data with Panache generates JAX-RS resources based on the interfaces available in your application. | 
|  | 52 | +For each entity and repository that you want to generate, provide an interface that extends either `PanacheEntityResource` or `PanacheRepositoryResource` interface. | 
|  | 53 | +_Do not implement these interfaces and don't provide custom methods because they will be ignored._ You can, however, override the methods from the extended interface in order to customize them (see the section at the end). | 
|  | 54 | + | 
|  | 55 | +=== PanacheEntityResource | 
|  | 56 | + | 
|  | 57 | +If your application has an entity (e.g. `Person`) that extends either `PanacheEntity` or `PanacheEntityBase` class, you could instruct REST Data with Panache to generate its JAX-RS resource with the following interface: | 
|  | 58 | + | 
|  | 59 | +[source,java] | 
|  | 60 | +---- | 
|  | 61 | +public interface PeopleResource extends PanacheEntityResource<Person, Long> { | 
|  | 62 | +} | 
|  | 63 | +---- | 
|  | 64 | + | 
|  | 65 | +=== PanacheRepositoryResource | 
|  | 66 | + | 
|  | 67 | +If your application has a simple entity (e.g. `Person`) and a repository (e.g. `PersonRepository`) that implements either `PanacheRepository` or `PanacheRepositoryBase` interface, you could instruct REST Data with Panache to generate its JAX-RS resource with the following interface: | 
|  | 68 | + | 
|  | 69 | +[source,java] | 
|  | 70 | +---- | 
|  | 71 | +public interface PeopleResource extends PanacheRepositoryResource<PersonRepository, Person, Long> { | 
|  | 72 | +} | 
|  | 73 | +---- | 
|  | 74 | + | 
|  | 75 | +=== The generated resource | 
|  | 76 | + | 
|  | 77 | +The generated resources will be functionally equivalent for both entities and repositories. | 
|  | 78 | +The only difference being the particular data access pattern in use. | 
|  | 79 | + | 
|  | 80 | +If you have defined one of the `PeopleResource` interfaces mentioned above, REST Data with Panache will generate a JAX-RS resource similar to this: | 
|  | 81 | + | 
|  | 82 | +[source,java] | 
|  | 83 | +---- | 
|  | 84 | +@Path("/people") // Default path is a hyphenated lowercase resource name without a suffix of `resource` or `controller`. | 
|  | 85 | +public class PeopleResourceImpl implements PeopleResource { // The actual class name is going to be unique | 
|  | 86 | +    @GET | 
|  | 87 | +    @Produces("application/json") | 
|  | 88 | +    @Path("{id}") | 
|  | 89 | +    public Person get(@PathParam("id") Long id){ | 
|  | 90 | +        // ... | 
|  | 91 | +    } | 
|  | 92 | +
 | 
|  | 93 | +    @GET | 
|  | 94 | +    @Produces("application/json") | 
|  | 95 | +    public List<Person> list(){ | 
|  | 96 | +        // ... | 
|  | 97 | +    } | 
|  | 98 | +
 | 
|  | 99 | +    @Transactional | 
|  | 100 | +    @POST | 
|  | 101 | +    @Consumes("application/json") | 
|  | 102 | +    @Produces("application/json") | 
|  | 103 | +    public Response add(Person entity) { | 
|  | 104 | +        // .. | 
|  | 105 | +    } | 
|  | 106 | +
 | 
|  | 107 | +    @Transactional | 
|  | 108 | +    @PUT | 
|  | 109 | +    @Consumes("application/json") | 
|  | 110 | +    @Produces("application/json") | 
|  | 111 | +    @Path("{id}") | 
|  | 112 | +    public Response update(@PathParam("id") Long id, Person person) { | 
|  | 113 | +        // .. | 
|  | 114 | +    } | 
|  | 115 | +
 | 
|  | 116 | +    @Transactional | 
|  | 117 | +    @DELETE | 
|  | 118 | +    @Path("{id}") | 
|  | 119 | +    public void delete(@PathParam("id") Long id) { | 
|  | 120 | +        // .. | 
|  | 121 | +    } | 
|  | 122 | +} | 
|  | 123 | +---- | 
|  | 124 | + | 
|  | 125 | +== Resource customisation | 
|  | 126 | + | 
|  | 127 | +REST Data with Panache provides a `@ResourceProperties` and `@MethodProperties` annotations that can be used to customize certain features of the resource. | 
|  | 128 | +It can be used in your resource interface: | 
|  | 129 | + | 
|  | 130 | +[source,java] | 
|  | 131 | +---- | 
|  | 132 | +@ResourceProperties(hal = true, path = "my-people") | 
|  | 133 | +public interface PeopleResource extends PanacheEntityResource<Person, Long> { | 
|  | 134 | +    @MethodProperties(path = "all") | 
|  | 135 | +    List<Person> list(); | 
|  | 136 | +
 | 
|  | 137 | +    @MethodProperties(exposed = false) | 
|  | 138 | +    void delete(Long id); | 
|  | 139 | +} | 
|  | 140 | +---- | 
|  | 141 | + | 
|  | 142 | +=== Available options | 
|  | 143 | + | 
|  | 144 | +`@ResourceProperties` | 
|  | 145 | + | 
|  | 146 | +* `hal` - in addition to the standard `application/json` responses, generates additional methods that can return `application/hal+json` responses if requested via an `Accept` header. | 
|  | 147 | +Default is `false`. | 
|  | 148 | +* `path` - resource base path. Default path is a hyphenated lowercase resource name without a suffix of `resource` or `controller`. | 
|  | 149 | + | 
|  | 150 | +`@MethodProperties` | 
|  | 151 | + | 
|  | 152 | +* `exposed` - does not expose a particular HTTP verb when set to `false`. Default is `true`. | 
|  | 153 | +* `path` - operation path (this is appended to the resource base path). Default is an empty string. | 
|  | 154 | + | 
|  | 155 | +== Response body examples | 
|  | 156 | + | 
|  | 157 | +As mentioned above REST Data with Panache supports the `application/json` and `application/hal+json` response content types. | 
|  | 158 | +Here are a couple of examples of how a response body would look like for the `get` and `list` operations assuming there are two `Person` records in a database. | 
|  | 159 | + | 
|  | 160 | +=== GET /people/1 | 
|  | 161 | + | 
|  | 162 | +`Accept: application/json` | 
|  | 163 | + | 
|  | 164 | +[source,json] | 
|  | 165 | +---- | 
|  | 166 | +{ | 
|  | 167 | +  "id": 1, | 
|  | 168 | +  "name": "John Johnson", | 
|  | 169 | +  "birth": "1988-01-10" | 
|  | 170 | +} | 
|  | 171 | +---- | 
|  | 172 | + | 
|  | 173 | +`Accept: application/hal+json` | 
|  | 174 | + | 
|  | 175 | +[source,json] | 
|  | 176 | +---- | 
|  | 177 | +{ | 
|  | 178 | +  "id": 1, | 
|  | 179 | +  "name": "John Johnson", | 
|  | 180 | +  "birth": "1988-01-10", | 
|  | 181 | +  "_links": { | 
|  | 182 | +    "self": "http://example.com/people/1", | 
|  | 183 | +    "remove": "http://example.com/people/1", | 
|  | 184 | +    "update": "http://example.com/people/1", | 
|  | 185 | +    "add": "http://example.com/people", | 
|  | 186 | +    "list": "http://example.com/people" | 
|  | 187 | +  } | 
|  | 188 | +} | 
|  | 189 | +---- | 
|  | 190 | + | 
|  | 191 | +=== GET /people | 
|  | 192 | + | 
|  | 193 | +`Accept: application/json` | 
|  | 194 | + | 
|  | 195 | +[source,json] | 
|  | 196 | +---- | 
|  | 197 | +[ | 
|  | 198 | +  { | 
|  | 199 | +    "id": 1, | 
|  | 200 | +    "name": "John Johnson", | 
|  | 201 | +    "birth": "1988-01-10" | 
|  | 202 | +  }, | 
|  | 203 | +  { | 
|  | 204 | +    "id": 2, | 
|  | 205 | +    "name": "Peter Peterson", | 
|  | 206 | +    "birth": "1986-11-20" | 
|  | 207 | +  } | 
|  | 208 | +] | 
|  | 209 | +---- | 
|  | 210 | + | 
|  | 211 | +`Accept: application/hal+json` | 
|  | 212 | + | 
|  | 213 | +[source,json] | 
|  | 214 | +---- | 
|  | 215 | +{ | 
|  | 216 | +  "_embedded": [ | 
|  | 217 | +    { | 
|  | 218 | +      "id": 1, | 
|  | 219 | +      "name": "John Johnson", | 
|  | 220 | +      "birth": "1988-01-10", | 
|  | 221 | +      "_links": { | 
|  | 222 | +        "self": "http://example.com/people/1", | 
|  | 223 | +        "remove": "http://example.com/people/1", | 
|  | 224 | +        "update": "http://example.com/people/1", | 
|  | 225 | +        "add": "http://example.com/people", | 
|  | 226 | +        "list": "http://example.com/people" | 
|  | 227 | +      } | 
|  | 228 | +    }, | 
|  | 229 | +    { | 
|  | 230 | +      "id": 2, | 
|  | 231 | +      "name": "Peter Peterson", | 
|  | 232 | +      "birth": "1986-11-20", | 
|  | 233 | +      "_links": { | 
|  | 234 | +        "self": "http://example.com/people/2", | 
|  | 235 | +        "remove": "http://example.com/people/2", | 
|  | 236 | +        "update": "http://example.com/people/2", | 
|  | 237 | +        "add": "http://example.com/people", | 
|  | 238 | +        "list": "http://example.com/people" | 
|  | 239 | +      } | 
|  | 240 | +    } | 
|  | 241 | +  ], | 
|  | 242 | +  "_links": { | 
|  | 243 | +    "add": "http://example.com/people", | 
|  | 244 | +    "list": "http://example.com/people" | 
|  | 245 | +  } | 
|  | 246 | +} | 
|  | 247 | +---- | 
0 commit comments