Show Navigation

Building a REST API with Grails and AngularJS 1.x

Build a backend for an already existing AngularJS application (Angular 1) with Grails rest api profile

Authors: Sergio del Amo

Grails Version: 4

1 Grails Training

Apache Grails Training

Apache Grails is now part of the Apache Software Foundation. The community-maintained training catalog is being migrated; in the meantime see the Learning page for current resources, recorded talks, and links to other community-supplied training material.

2 Getting Started

In this guide you are going to connect an existing AngularJS todo app to a Grails backend.

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 11 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:

or

The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with some additional code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/grails-restapi-angularjs/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/grails-restapi-angularjs/complete

3 Initial Angular Application

treehouse

This section displays the code developed during the Treehouse AngularJS course. During that course an AngularJS application is developed. The application is a todo list manager.

The initial AngularJS app is not connected to any backend. If you refreshed the browser, todo list changes are lost.
initial

index.html is the entry point to our AngularJS app.

/initial-angularjs/index.html
// missing snippet: ../initial-angularjs/index.html

We create the AngularJS application, in app.js

/initial-angularjs/scripts/app.js
// missing snippet: ../initial-angularjs/scripts/app.js

An AngularJS controller manages the application flow:

/initial-angularjs/scripts/controllers/main.js
// missing snippet: ../initial-angularjs/scripts/controllers/main.js

We have a todos directive. This directive uses the previous controller and the next template:

/initial-angularjs/scripts/directives/todos.js
// missing snippet: ../initial-angularjs/scripts/directives/todos.js
/initial-angularjs/templates/todos.html
// missing snippet: ../initial-angularjs/templates/todos.html

Our data service is currently loading todos from a mock json file.

/initial-angularjs/scripts/services/data.js
// missing snippet: ../initial-angularjs/scripts/services/data.js
/initial-angularjs/mock/todos.json
// missing snippet: ../initial-angularjs/mock/todos.json

4 Writing the Grails Application

The initial project, which we are going to work on, was generated with Grails REST profile.

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

4.1 Domain Class

Create a persistent entity to store Todos. Most common way to handle persistence in Grails is the use of Grails Domain Classes:

A domain class fulfills the M in the Model View Controller (MVC) pattern and represents a persistent entity that is mapped onto an underlying database table. In Grails a domain is a class that lives in the grails-app/domain directory.

complete$ ./grailsw create-domain-class Todo

The Todo domain class is our data model. We define different properties to store the Todo characteristics.

/grails-app/domain/demo/Todo.groovy
package demo

import grails.rest.Resource

@Resource(uri='/todos')
class Todo {
    String name
    boolean completed

    static constraints = {
    }
}

With a Unit Test, we test that name is a required property.

/src/test/groovy/demo/TodoSpec.groovy
package demo

import spock.lang.Specification
import grails.testing.gorm.DomainUnitTest

class TodoSpec extends Specification implements DomainUnitTest<Todo> {

    void "test name is required"() {
        when:
        def todo = new Todo(name: name)

        then:
        !todo.validate()
        todo.errors['name'].code == errorCode

        where:
        name | errorCode
        null | 'nullable'
        ''   | 'nullable'
    }
}

We then load some test data in BootStrap.groovy.

/grails-app/init/demo/BootStrap.groovy
package demo

class BootStrap {

    def init = { servletContext ->

        def todos = [
                [name: 'clean the house'],
                [name: 'water the dog'],
                [name: 'feed the lawn'],
                [name: 'pay dem bills'],
                [name: 'run'],
                [name: 'swim']
        ].each { new Todo(name: it.name).save() }
    }
    def destroy = {
    }
}

We annotated the domain class with @Resource

/grails-app/domain/demo/Todo.groovy
import grails.rest.Resource

@Resource(uri='/todos')
class Todo {

This annotation exposes the Todo domain class as a REST resource

The easiest way to create a RESTful API in Grails is to expose a domain class as a REST resource. Simply by adding the Resource transformation and specifying a URI, your domain class will automatically be available as a REST resource in either XML or JSON formats. The transformation will automatically create a controller called TodoController and register the necessary RESTful URL mapping

This is the URL Mapping registered for our domain class.

HTTP Method URI Grails Action

URI

Grails Action

GET

/todos

index

GET

/todos/create

create

POST

/todos

save

GET

/todos/${id}

show

GET

/todos/${id}/edit

edit

PUT

/todos/${id}

update

DELETE

/todos/${id}

delete

4.2 Enable CORS

Because the client side (AngularJS) and server side (Grails) will be running on separate ports, CORS configuration is required.

Modify your application.yml to enable CORS

/complete/grails-app/conf/application.yml
grails:
    cors:
        enabled: true

5 Connect Angular Application to Grails

We add a directive to detect when the users presses ENTER while editing a Todo name

/complete-angularjs/index.html
// missing snippet: ../complete-angularjs/index.html
/complete-angularjs/scripts/directives/ngEnter.js
// missing snippet: ../complete-angularjs/scripts/directives/ngEnter.js

We are going to modify the UI slightly. E.g. We are going to remove the save button, if the user modifies the todo (either changes the name or completes), we save the changes in the server.

/complete-angularjs/templates/todos.html
// missing snippet: ../complete-angularjs/templates/todos.html
/complete-angularjs/scripts/controllers/main.js
// missing snippet: ../complete-angularjs/scripts/controllers/main.js

Data service now connects to a Grails backend instead of loading a mock json file.

/complete-angularjs/scripts/services/data.js
// missing snippet: ../complete-angularjs/scripts/services/data.js

6 Running the Application

To run the application use the ./gradlew bootRun command which will start the application on port 8080.

Open you angular app and you will enjoy a todo manager powered by a Grails backend. You can refresh your browser without losing the changes.

complete

7 Do you need help with Grails?

Help with Apache Grails

Apache Grails is supported by an active community of contributors and the Apache Software Foundation. If you need help working through a guide, want to discuss the framework, or have run into something that looks like a bug, the channels below are the right place to start.

For Grails plugins, see the matching project on the apache org or the plugin’s own GitHub repository.