Out of the box Restful API for grails.

This is a library and [Grails] plugin to enhance [Gorm] to provide and expose a somewhat opinionated, well documented restful API with intelligent out of the box defaults and minimal tweaking. It also keeps it easy to configure and customize on the fly at a deployed client site without the need to recompile.

While its somewhat opinionated it frames out Rest best practices outlined in the [Rest Design Principles] section. It uses the OpenAPI spec (formerly Swagger) as well as how it maps the Gorm domains into that spec. Its accepts and returns only JSON and eschews the complexity of XML

While REST is not CRUD a lot of it really is. This project aims to makes it easy and quick to setup the common use cases that are really just CRUD Controller over the [GORM] domains so you can focus on the unique business logic use cases that are hard.

Key features and goals of this plugin include:

  • The simple stuff should be simple and automatic
  • DRY : Don't repeat yourself.
  • Autogenerated code is evil. This includes tests
  • Use AST, traits and tanspiling where possible

  • Near zero configuration to expose Swagger documented resources (the Gorm domains)
  • Rely on the domain's contraints as much as possible for configurations and docs.
  • Out of the box CRUD Controller modified from Grails @Resource annotation and RestfulController that can delegate to transactional Services (Repositories, Data Services) flavored services based on naming convention or configuration. Uses the [gorm-tools] plugin by deafault for this.
  • Custom schema selects for retruned JSON fields that can be confiugured at runtime by a customer (does not require recompiling views)
  • Consistent documented use of HTTP status codes and headers
  • Uses cache-headers for faster loading
  • Use [Repository] pattern by defualt to keep the CRUD code in its own transactional service
  • Ability to 'query by POST' (to allow query criteria to be provided within the request body)
  • Intelligent defaults for security plugin with OATH and JWT.
  • Tools to ease GEB functional tests with Spock
  • Extensive regression tests

Install


JSON Generation

There are 3 ways to generate json.

  1. using renderers, Customizing Response Rendering
  2. using the grails-views plugin.
  3. using JSON.ObjectMarshaller which seems to be out of favor in the docs and is not longer mentioned but remains in the grails-plugin-converter's main JSON. this is a good overview and this for using name config with unit testing example and mrhaki's write up on name configurtions

https://jlstrater.github.io/restful-grails3/

This slide show is a must preview does a good job of explaining the different options towards the end.

The goal is to provide the fastest way to render and object or collection that allows custom includes or excludes.

A static includes = ... can be set on the domain, the repository or the controller. An gets overriden in that order. For example: if I set excludes on the domain if I set in on a Repository CRUD controller for that domain the it uses that. A setting in the AppSetupConfig overides them all and a includes param sent into the endpoint will be used above all others. I would be nice to put the grails-views into this picture as well so that if there is a *.gson file it gets used. Will need to figure out the resolution order.

There is a way to register a name wigh JSON.createNamedConfig and then they can be used with JSON.use(name){...} This might be something we want to look into as an option as it may be really easy to incorporate our AppSetupConfig.

https://stackoverflow.com/questions/23940641/render-metadata-for-pagination-from-grails-restfulcontroller-index-search-action

Explains that is views/object/_object.gson is present then its not picking up ObjectMarshallers https://stackoverflow.com/questions/43357469/grails-3-respond-method-doesnt-use-defined-json-marshaller-format https://kylewbanks.com/blog/Customizing-JSON-Grails-Object-Marshalling

older 2.x plugin but has some good ideas.
https://github.com/danveloper/grails-rest-renderers

include class and include version config options in the marshallers are exaplained a bit here https://github.com/grails-plugins/grails-plugin-converters/commit/d7ef874a59e19abc03821181ff33e11f612c63a6

shows an example of pumping a pdf through the response.outputStream http://grails.1312388.n4.nabble.com/Grails-REST-Web-Services-Using-custom-Renderer-to-render-PDF-td4654403.html

DefaultRendererRegistry in org.grails.plugins.web.rest.render is where the defaults aer registered.

Setting up the domains

most of a rest api can be setup and documented with nothing more than just the domains

@RestApi annotation

inspired by the Grails @Resource annotation Adding this to your domains with create a controller using AST under the api namespace with common actions and UrlMappings for a rest api. It uses a transactional Repository data service by default.

attribute desc default
description information to appear to a dev and in the Open api docs ""
endpoint if this annotation only for api docs and you don't want a controller generated then set this to false. true
readOnly Whether this is a read-only endpoint (one that doesn't allow DELETE, POST, PUT or PATCH requests) false
controllerClass The Controller class to generate. Can be set to null to skip the generation RestRepoApiController

Example

import gorm.restapi.RestApi

@RestApi(description = "The user for the restify application")
class AppUser {

see the section on the RestRepoApiController for more details about the controller setup

OpenAPI and json-schema

We use additional constraints on the domain to help with the json-schema and Open API docs as well as for DRY testing.

added meta-contraints | contraints | desc | | ----------- | ----------------------------------------------------------------------- | | description | information to appear to a dev and in the Open api docs | | title | override the title. converts camel case to word with spaces by defaults | | example | used in docs as well as auto mocking out test data |

Example

static constraints = {
    userName  description: 'The login name',
              title: 'Login Name',
              example:"billy1"

contraint json-schema notes
min minimum
max maximum
maxSize maxLength
minSize minLength
scale multipleOf = 1/Math.pow(10, constraints.scale))
email format='email'