Generating a fully RESTful Api and UI from a database with LLBLGen and ServiceStack (templates on Github and Demo)

Posted on March 10, 2013 by Matt C. in .NET, ASP.NET MVC, REST
Tweet

In this post, we’re going to explore building a generic RESTful api on top of a database with custom CRUD and query conventions, using the LLBLGen ORM and code generator, a ServiceStack service layer, and ServiceStack Razor html entity browser layer. This API and html entity browser can be generated on top of any database that LLBLGen supports (see here for supported database drivers).

Intro

See the Demo  Get the Source

In this post, we’re going to explore building a generic RESTful api on top of a database with custom CRUD and query conventions, using the LLBLGen ORM and code generator, a ServiceStack service layer, and ServiceStack Razor html entity browser layer. This API and html entity browser can be generated on top of any database that LLBLGen supports (see here for supported database drivers).

Inevitably, someone will ask, why not just use OData? OData is the route Microsoft is pushing for building RESTful data apis. OData is definitely worth learning and taking a look at, you will most likely use it in the future on some project, if you’re not already using it now. You can learn about OData here (www.odata.org/documentation) if it’s new to you, and browse a sample OData feed of Northwind here: services.odata.org/northwind/northwind.svc/. LLBLGen even supports OData. My personal opinion is, if OData meets your needs, use it! If you want complete control over your API however, and want the ability to plug in business rules, validation, authentication, authorization, generate UI components, impose some level of control over the queries against your database, want to easily be able to refactor your API, create accompanying documentation, and easily create similar APIs with similar logic and conventions over time across projects, you may want to consider the approach described in this post.

(Sidebar: If you’re new to REST and/or interested in a slidedeck on REST, here’s a pretty good one).

*New Updates (May 11, 2013)*
Added new templates to support LLBLGen Typed Views and Typed Lists (for both LLBLGen 3.5 and LLBLGen 4.0). See new post here with more on this: RESTful Api and UI for Typed Views and Typed Lists with LLBLGen and ServiceStack.

*New Updates (May 3, 2013)*
Added new preset file compatible with LLBLGen V4. See Github.

*New Updates (April 4, 2013)*
See GitHub for update details.

*New Updates (March 16, 2013)*
Some new updates posted on the GitHub project on March 16th, 2013, see Change Log for details, including:

  • Clean xml output
  • New “limit” parameter to limit the number of results returned (is trumped by paging)
  • Deep fetching of related items (i.e.: products.orderdetails.order), including the ability to impose a limit of records returned on related items
  • Restricting which fields are returned by a query on related items as well (i.e.: products.orderdetails.quantity)
  • Introduced relation queries to filter a query based on related item filters (i.e.: all products supplied in japan sold between two dates)

Keep your eyes on the GitHub repo for updates moving forward.

Demo

JUST SHOW ME! Screenshots, demo link, and source on GitHub

 spacer

spacer

  • Go here for the demo: northwind.mattjcowan.com
  • Go here for the source (hosted on GitHub): https://github.com/mattjcowan/LLBLGenPro_SS_Api_Razor_Templates

RESTful Uri and Protocol Conventions

To discuss and demonstrate the usage of the auto-generated APIs, I’ll use the live demo above as illustration (which is also downloadable from GitHub).

For the most part you’ll have to click on the links to see how the various formatted results look. For XML and JSON, in your AJAX apis, use the “accept” header, but you can force the format using the format parameter.

We’ll use the “Category” and “Employee” entities throughout as samples for our queries.

Parameters are always optional.

Discoverable Entities

The entity API allows a user or application to discover the entities available in the system.

Uri Parameters Protocol Formats
{baseUri}/entities
{baseUri}/entities/meta
{baseUri}/entities?format=xml
{baseUri}/entities?format=json
<none> GET [ html ]
[ meta ]
[ xml ]
[ json ]

The API gives you an “HREF” property back for each entity that you can store and use to navigate to each particular entity

Querying Entities

Once you have obtained your list of entities, you can browse a specific entity type using a “slug” version of the plural form of an entity name, or just follow the “href” property of the discoverable entity API above.

Querying all instances of an entity type:

Uri Parameters Protocol Formats
{baseUri}/{pluralizedEntityName} See ‘advanced queries’ section
{baseUri}/categories
{baseUri}/categories/meta
{baseUri}/categories?format=xml
{baseUri}/categories?format=json
select={select}
sort={sort}
include={include}
filter={filter}
pageSize={pageSize}
pageNumber={pageNumber}
GET [ html ]
[ meta ]
[ xml ]
[ json ]

Querying a specific instance of an entity type using a primary key:

If the entity has multiple primary keys, just add them one after the other according to their index into the Uri.

Uri Parameters Protocol Formats
{baseUri}/{pluralizedEntityName}/{pk1}/{pk2}
{baseUri}/categories/1 select={select}
include={include}
GET [ meta ]
[ xml ]
[ json ]
Using the filter parameter:{baseUri}/categories?filter=categoryid:eq:1 [ html ]
[ xml ]
[ json ]

Querying a specific instance of an entity type using unique constraints:

If the entity has unique constraints, they are discoverable in the field properties using the “categories/meta” convention.

Unique constraints can be composed of multiple fields, just append each value for each field in the constraint to the URL. You can also use the filter API to achieve the same thing.

Uri Parameters Protocol Formats
{baseUri}/{pluralizedEntityName}/uc/{constraintName}/{value1}/{value2}
{baseUri}/categories/uc/categoryname/{catname} select={select}
include={include}
GET [ meta ]
[ xml ]
[ json ]
Using the filter parameter:{baseUri}/categories?filter=categoryname:eq:{catname} [ html ]
[ xml ]
[ json ]

Creating/Updating/Deleting entities

A note about security. These create/update/delete methods are protected in the generated code with an Authenticate attribute requiring at minimum to be an authenticated user… You can add additional layers of security as needed with ServiceStack filter attributes on your data contracts, methods, and/or classes, or by plugging into the authorization and validation features built into LLBLGen Pro.

Creating an entity:

To create an entity, simply POST your entity to the entity Uri.

Uri Parameters Protocol Formats
{baseUri}/{pluralizedEntityName}
{baseUri}/customers XML or JSON serialized version of your new entity POST

A customer object for example would look like this:

{ "address" : "Obere Str. 57",
  "city" : "Berlin",
  "companyName" : "Alfreds Futterkiste",
  "contactName" : "Maria Anders",
  "contactTitle" : "Sales Representative",
  "country" : "Germany",
  "customerCustomerDemos" : [  ],
  "customerId" : "ALFKI",
  "fax" : "030-0076545",
  "orders" : [  ],
  "phone" : "030-0074321",
  "postalCode" : "12209",
  "region" : ""
}

Updating an entity:

To update an entity, simply POST your entity to the entity Uri.

Uri Parameters Protocol Formats
{baseUri}/{pluralizedEntityName}/{pk1}/{pk2}
{baseUri}/categories/1
{baseUri}/categories/1/update
XML or JSON serialized version of your new entity PUT
POST

The format of your entity is the same as in creating an entity. If your server cannot process a PUT request, you can use a POST request with a “/update” at the end of the Uri.

Deleting an entity:

To delete an entity, simply POST your entity to the entity Uri.

Uri Parameters Protocol Formats
{baseUri}/{pluralizedEntityName}/{pk1}/{pk2}
{baseUri}/categories/1
{baseUri}/categories/1/delete
XML or JSON serialized version of your new entity DELETE
POST

Exceptions

Exceptions will typically come back formatted as follows (JSON syntax):

{ "responseStatus" : { "errorCode" : "NullReferenceException",
      "errors" : [  ],
      "message" : "Object reference not set to an instance of an object.",
      "stackTrace" : ""
    } }


Advanced Queries

FILTER parameter: filtering entities

The syntax for filtering looks similar to Active Directory queries, but the AND clause is represented by a “^” character, and the OR clause is represented by a “|” character.

A basic clause consists of three parts: {fieldName}:{filterOperator}:[{filterValue1},{filterValue2,…{filterValue(n)}]

Here’s a sample clauses to filter categories according to their id:
/categories?filter=categoryid:eq:1

Some operators accept arrays. This clause filters for all categories with either id = 1, 2, 3, or 4:
/categories?filter=categoryid:eq:1,2,3,4

To specify multiple clauses in an AND structure, you would do that as follows:
/categories?filter=(^(categoryid:eq:1,2,3,4)(categoryname:eq:condiments))

To specify the same clause in an OR structure, you would do that as follows:
/categories?filter=(|(categoryid:eq:1,2,3,4)(categoryname:eq:condiments))

You can now combine the AND and OR structures to create compound clauses. This example fetches (all categories with id = 1, 2, 3, or 4 AND where the category name is “condiments”) OR (where the description contains the word ‘and’)
/categories?filter=(|(^(categoryid:eq:1,2,3,4)(categoryname:eq:condiments))(description:lk:”*and*”))

The generated API supports the following operators (with examples):

Operator Description Example
bt Between /categories?filter=categoryid:bt:1,3
nbt Not Between /categories?filter=categoryid:nbt:2,7
in In (same as ‘eq’) /categories?filter=categoryid:in:1,2
nin Not In (same as ‘neq’) /categories?filter=categoryid:nin:1,2,3,4,5,6
eq Equals (accepts arrays) /categories?filter=categoryid:eq:1,2
eqc Equals (case insensitive)
Use with case sensitive databases if needed
/categories?filter=categoryname:eqc:condiments
neq Not Equals (accepts arrays) /categories?filter=categoryid:neq:1,2
neqc Not Equals (case insensitive)
Use with case sensitive databases if needed
/categories?filter=categoryname:neqc:condiments
lk Like /categories?filter=description:lk:”*,%20and*”
nlk Not Like /categories?filter=description:nlk:”*, and *”
nl Is Null /customers?filter=fax:nl
nll Is Not Null /customers?filter=fax:nnl
gt Greater Than /employees?filter=hireDate:gt:”5/1/1992%2012:00:00%20AM”
gte Greater Than Or Equals /employees?filter=hireDate:gte:”5/1/1992%2012:00:00%20AM”
lt Less Than /employees?filter=hireDate:lt:”5/1/1992%2012:00:00%20AM”
lte Less Than Or Equals /employees?filter=hireDate:lte:”5/1/1992%2012:00:00%20AM”
ct Full Text Contains /employees?filter=notes:ct:”fluent%20in%20french”
ft Full Text Free Text /employees?filter=notes:ft:”fluent%20in%20french”

SELECT parameter: selecting specific fields

You can ask the api for specific fields instead of the entire object graph, which can considerably improve performance.

Categories have embedded images, so we wouldn’t want to constantly be returning all that data. So, in this example, we will fetch all categories restricting the query to only the category name and the description (the PK of the entity is always returned!).

Sample select query: /categories?select=categoryname,description  [ HTML version | JSON version | XML version ]

INCLUDE parameter: including related items

You can ask the api to return related objects upfront, which improves performance by preventing you from having to come back and get the objects iteratively.

In this example, we’ll return the “Reports To” and “Employee Territory” fields as a single GET for the employee with ID = 1. As you can see also in the two results below, querying using a filter parameter gives you a response as part of an array, whereas, querying the specific entity gives you the entity directly (not within an array).

/employees?filter=employeeid:eq:1&include=reportsto,employeeterritories [ HTML version | JSON version | XML version ]
OR
/employees/1?include=reportsto,employeeterritories [ META version | JSON version | XML version ]

RELATION parameter: querying on related items

Support for querying on related items is still in the works. LLBLGen provides not only a deep-fetch api, but also a very flexible API to construct almost any SQL you want, including creating filters on items N deep by traversing relationships in the database. The creators of LLBLGen have spent considerable time figuring out how to optimize the SQL for performance, it’s trustworthy. This feature is one of the big reasons I prefer LLBLGen over the Entity Framework. I’ll try to add this as a feature to this generated API in the near future.

PAGING parameters: paging data

Unless otherwise specified, by default, paging is set to 10 items at a time. In this example we’ll query for the 2nd page of customer data using a page size of 5. The HTML interface uses it’s own paging mechanism, so this example showcases paging using the JSON and XML formats.

/customers?pageSize=5&pageNumber=2 [ JSON version | XML version ]

One thing you’ll notice is the “Paging” object that is returned as part of the response. The paging object has everything that’s needed to construct good paging capabilities on the client-side.

{ ...,
  "paging" : { "firstItemOnPage" : 6,
    "hasNextPage" : true,
    "hasPreviousPage" : true,
    "isFirstPage" : false,
    "isLastPage" : false,
    "lastItemOnPage" : 10,
    "pageCount" : 19,
    "pageNumber" : 2,
    "pageSize" : 5,
    "totalCount" : 91
  }
}

SELECT parameter: selecting specific fields

You can easily sort your data, and sort on multiple fields as well.

In this example, we will sort customer data by country in ascending order, city in ascending order, then company name in descending order. If you don’t specify a sort operator for a field, it’s assumed to be ‘ascending’.

/customers?sort=country,city:asc,companyname:desc [ JSON version | XML version ]

Using C# and the ServiceStack Client to call the Query service(s)

Check some of the basic unit tests for samples, it’s ServiceStack out-of-the-box: ConnectivityTests.cs, BasicQueryTests.cs

Using javascript / jQuery to call the Query service(s)

There is nothing fancy going on here. Simply use the standard $.getJSON() or *.ajax() jQuery apis, they work out of the box.

Generating the Api

You can follow along with this tutorial simply by downloading the LLBLGen templates from GitHub, and reproducing the steps I’m going over in this section.

  • Step 1
  • Step 2
  • Step 3
  • Step 4
  • Step 5

Step 1: create your model in LLBLGen
See here for documentation on how to use the LLBLGen designer: www.llblgen.com/documentation/3.5/Designer/hh_start.htm.
When creating your model, make sure to use the “LLBLGen Pro Runtime Framework”.
The templates that were put together to create this API utilize the LLBLGen adapter strategy for talking back and forth to the database. The LLBLGen adapter strategy works for almost every relational database out there, and performs great.This is what my model looks like over the Northwind database, and as used in the examples in this blog post.

spacer

Step 2: add the ServiceStack template files
Unzip the template files into the same directory as your llblgen project file… Something like this:

spacer

With the templates now downloaded, edit your project settings to recognize the templates, by going to “Project >> Settings”

spacer

Press OK. Then press the “Refresh code generation metadata button” in the toolbar. Now when you go to the menus “Window » Show Template Bindings Viewer”, you should see the “MJC.TemplateBindings.ServiceStack” option in the template bindings drop down. This is where you can further extend, copy and/or modify the templates to enhance and refine your code-generated api according to your needs if you want.

Step 3: generate the code
To generate the code, go to Project » Generate source code. In the dialog window that pops up, under “Selected preset”, make sure you pick the “MJC.Presets.Adapter.ServiceStack” option. If you don’t see this option, repeat step 2 (remember to save your project file, you may even have to close and re-open the project file).

spacer

Then, press “Start generator (Normal)”

Step 4: open the solution in visual studio
In visual studio, create an empty solution and add the projects to it.
For the solution to compile, you need LLBLGen obviously in your GAC, and you also need the ServiceStack assemblies. The recommended way to add ServiceStack is to use Nuget (see here for information on Nuget if it’s new to you). Save your solution, and open your command prompt (or install the assemblies with the Nuget dialog, whatever your preference is) and install ServiceStack into the following projects (as follows):

nuget update -self
mkdir packages
nuget install .\ServiceGeneric\packages.config -OutputDirectory .\packages
nuget install .\ServiceSpecific\packages.config -OutputDirectory .\packages
nuget install .\Hosts\ConsoleHost\packages.config -OutputDirectory .\packages

Make sure the solution compiles, and now you’re ready to fire it up. The ConsoleHost is a sample self-hosted application which hosts the ServiceStack services on top of LLBLGen. You can host the services in a variety of ways (Windows Service, Console, IIS, and more…), refer to the extensive ServiceStack documentation to learn how to host ServiceStack according to your needs (feel free to comment and/or contact me if you are having issues with this).

Step 5: run it
Before you run the application, go to the app.config file in your ConsoleHost project and make sure the “ApiDbConnectionString” in the connection string section has the right value.Open a command prompt, CD to the bin\debug directory of the ConsoleHost project and start the server.

spacer

The console will tell you what port to go to in your browser to access the api. Now open your browser to that URL, in my case above it’s localhost:1337.

Your API and website is LIVE!

code generation, llblgen, odata, orm, rest, servicestack

41 comments on “Generating a fully RESTful Api and UI from a database with LLBLGen and ServiceStack (templates on Github and Demo)

  1. spacer Thomas Wagner says:
    April 7, 2014 at 3:57 pm

    I’ve been running Matt’s templates with a team of 6 developers for almost a year