edit

Quick Start Guide

A Grails Rest API in 60 seconds

Creat a new grails app ( this was done with 3.2.11 )

grails create-app restify --profile=rest-api

To use gorm-rest-api in Grails 3 you can specify the following configuration in build.gradle:

    dependencies {
        compile "org.grails.plugins:gorm-rest-api"
    }

create a domain

grails create-rest-domain restify.AppUser

This will create a domain in grails-app/domain/restify/ as well as an integration test.
Edit AppUser and we'll add a few fields with descriptions and examples so our Swagger/OpenApi docs look good. This will also feed our [functional] tests

package restify
import gorm.restapi.RestApi

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

    Date dateCreated
    Date lastUpdated

    static constraints = {
        userName  description: 'The login name', 
                  example:"billy1",
                  nullable: false, maxSize:50
        magicCode description: 'The keymaster code. Some call this a password',
                  example:"b4d_p455w0rd", 
                  nullable: false
        email     description: "Email will be used for evil.",
                  example:"billy@gmail.com",
                  email:true, maxSize:50, nullable: true
    }

}

now run grails test-app to see the api exercised.

Lets try run-app with curl

insert a user

curl -i -X POST -H "Content-Type: application/json" -d '{"userName":"Joe", "magicCode": "Cool"}' localhost:8080/api/appUser
HTTP/1.1 201
X-Application-Context: application:development
Location: http://localhost:8080/api/appUser/1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 11 Aug 2017 07:34:05 GMT

{"id":1,"dateCreated":"2017-08-11T07:34:05Z","lastUpdated":"2017-08-11T07:34:05Z","magicCode":"Cool","userName":"Joe"}

list users

curl -i -X GET -H "Content-Type: application/json" localhost:8080/api/appUser
HTTP/1.1 200
--8<-- snipped ...

//FIME this should be showing pagination
[{"id":1,"dateCreated":"2017-08-11T07:34:05Z","lastUpdated":"2017-08-11T07:34:05Z","magicCode":"Cool","userName":"Joe"}]

JSON Schema

curl -i localhost:8080/api/appUser/schema

{
    "$schema": "http://json-schema.org/schema#",
    "$id": "http://localhost:8080/api/schema/appUser#",
    "title": "AppUser",
    "Description":"The user for the restify application",
    "type": "Object",
    "required": [
        "userName",
        "magicCode"
    ],
    "properties": {
        "id": {
            "type": "integer",
            "readOnly": true
        },
        "version": {
            "type": "integer",
            "readOnly": true
        },
        "userName": {
            "title": "User Name",
            "description": "The login name",
            "example": "billy1",
            "type": "string",
            "required": true,
            "maxLength": 50
        },
        "magicCode": {
            "title": "Magic Code",
            "description": "The keymaster code. Some call this a password",
            "example": "b4d_p455w0rd",
            "type": "string",
            "required": true
        },
        "email": {
            "title": "Email",
            "description": "Email will be used for evil.",
            "example": "billy@gmail.com",
            "type": "string",
            "format": "email",
            "maxLength": 50
        },
        "lastUpdated": {
            "title": "Last Updated",
            "type": "string",
            "format": "date-time",
            "readOnly": true
        },
        "dateCreated": {
            "title": "Date Created",
            "type": "string",
            "format": "date-time",
            "readOnly": true
        }
    }
}

OpenApi Swagger

Open a browser and go to http://localhost:8080/swagger.html

to see yml that feeds this check out localhost:8080/api/open-api.yml

swagger: "2.0"
info:
  description: "TODO"
  version: "0.1"
  title: "Restify"
host: "localhost:8080"
basePath: "/api"
tags:
- name: "appUser"
  description: "The user for the restify application"
paths:
  /appUser:
    post:
      tags:
        - "appUser"
      summary: "Add a new App User"
      description: ""
      operationId: "create_appUser"
      parameters:
      - in: "body"
        name: "Object"
        description: "App User object to be created"
        required: true
        schema:
          $ref: "#/definitions/AppUser"
      responses:
        200:
          description: "success"
          schema:
            $ref: "#/definitions/AppUser"
        422:
          description: "Validation exception"
    get:
      tags:
      - "appUser"
      summary: "List App Users"
      operationId: "findAppUsers"
      responses:
        200:
          description: "successful operation"
          schema:
            type: "array"
            items:
              $ref: "#/definitions/AppUser"
        400:
          description: "Invalid status value"
  /appUser/{id}:
    get:
      tags:
      - "appUser"
      summary: "Get App User by ID"
      operationId: "get_appUserById"
      produces:
      - "application/json"
      parameters:
      - name: "id"
        in: "path"
        description: "ID of [AppUser] to return"
        required: true
        type: "integer"
        format: "int64"
      responses:
        200:
          description: "successful operation"
          schema:
            $ref: "#/definitions/AppUser"
        400:
          description: "Invalid ID supplied"
        404:
          description: "not found"
    patch:
      tags:
      - "appUser"
      summary: "Update an existing App User"
      description: ""
      operationId: "update_appUser"
      consumes:
      - "application/json"
      produces:
      - "application/json"
      parameters:
      - in: "body"
        name: "body"
        description: "AppUser object that needs to be updated"
        required: true
        schema:
          $ref: "#/definitions/AppUser"
      responses:
        400:
          description: "Invalid ID supplied"
        404:
          description: "not found"
        422:
          description: "Validation exception"
    delete:
      tags:
      - "appUser"
      summary: "Deletes a [App User]"
      description: ""
      operationId: "deleteAppUser"
      produces:
      - "application/json"
      parameters:
      - name: "id"
        in: "path"
        description: "id to delete"
        required: true
        type: "integer"
        format: "int64"
      responses:
        400:
          description: "Invalid ID supplied"
        404:
          description: "not found"
definitions:
  AppUser:
    type: "object"
    required:
    - "userName"
    - "magicCode"
    properties:
      id:
        type: "integer"
        format: "int64"
        readOnly: true
      userName:
        title: User Name
        description: The login name
        example: billy1
        type: string
        maxLength: 50
      magicCode:
        title: Magic Code
        description: The keymaster code. Some call this a password
        example: "b4d_p455w0rd"
        type: string
      email:
        title: "Email"
        description: Email will be used for evil.
        example: "billy@gmail.com"
        type: string
        format: email
        maxLength: 50
      lastUpdated:
        title: Last Updated
        type: string
        format: date-time
        readOnly: true
      dateCreated:
        title: Date Created
        type: string
        format: date-time
        readOnly: true

Associations

lets modify the App User to add an Org domain to it. We can also add a default static for the includes or excludes to pass to the renderer. includes accepts either a map or csv string in a SQL statement select like format.

    ...
    Org org

    static constraints = {
        ...
        org       description: "The organization this user belongs to",
                  title: "Organization",
                  example:'{"id":1}',
                  nullable: false
    }

    static includes = "*, org.num, org.name"

}

create the Org and then add some fields to it

package restify
import gorm.restapi.RestApi

@RestApi(description = "The _organizations_ and _tribes_ we all belong to")
class Org {
    String num
    String name
    String address

    static constraints = {
        num   description: 'identifier or nickname', 
              example:"vg1",
              maxSize:10, nullable: true
        name  description: 'Name of this organizatoin', 
              example:"Virgin Galactic",
              maxSize:50, nullable: false
        address description: 'Name of this organizatoin', 
                example:"123 Main St.\nAnyTown, WI 60456",
                maxSize:255, nullable: true
    }
}

//TODO show curl show and list on AppUser shows org.num and org.name

//TODO show curl -i localhost:8080/api/appUser/schema has Org object reference