Code-First REST APIs With XPages Jakarta EE Support

Aug 25, 2022, 11:43 AM

Tags: jakartaee
  1. Code-First REST APIs With XPages Jakarta EE Support
  2. Code-First REST APIs Followup: OpenAPI

Today, I'd like to do a bit of a demonstration post. Specifically, I'd like to demonstrate the basics of making a basic CRUD (Create, Read, Update, Delete) REST API using the XPages Jakarta EE Support project, storing data in the NSF of the app. This will kind of act like a condensed version of the longer series on rewriting the OpenNTF site.

I think it will be a good example of how you can design an API starting from the data level up, with all of the pieces fitting together the whole time in a cohesive whole. There are some bugs for me to address stopping it from also being the source of an OpenAPI spec you could give to front-end developers, but that will come along for the ride once I fix that.

In any event, the core here will be simple: it will be an NSF that will have one document type - "Employee" - and the ability to manipulate those documents in a type-safe way from a REST client. I won't be going over how to actually use this in a browser or remote app, just because that's essentially an infinite rabbit hole. As it is, the code involved is written entirely in an NSF using Designer. This assumes you have a recent build installed and that your NSF has all of the libraries from this checked in Xsp Properties.

The Data Model

We'll be starting with defining the data model. While you could make a Form design element for this too, you don't need to. Our model will be pretty bare-bones: an ID and four scalar properties, without worrying in this exercise about relationships with other model objects. The class (with getters/setters snipped) looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package model;

import java.util.stream.Stream;

import org.openntf.xsp.nosql.mapping.extension.DominoRepository;

import jakarta.nosql.mapping.Column;
import jakarta.nosql.mapping.Entity;
import jakarta.nosql.mapping.Id;
import jakarta.nosql.mapping.Sorts;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;

@Entity
public class Employee {
	public interface Repository extends DominoRepository<Employee, String> {
		Stream<Employee> findAll(Sorts sorts);
	}
	
	private @Id String id;
	private @Column @NotEmpty String name;
	private @Column @NotEmpty String title;
	private @Column @NotEmpty String department;
	private @Column @Min(1) int age;
	
	/* (snip) "Source" -> "Generate Getters and Setters..." */
}

This will cover all of our data-access needs. The Repository interface there is a Jakarta NoSQL repository that has built-in knowledge for CRUD and query operations that we'll need. In a larger app, you might also add some view-backed sources or other complexities, but we don't need it here.

Beyond the NoSQL annotations - @Entity, @Id, and @Column - this model also uses Jakarta Bean Validation annotations to ensure that the data being stored meets our requirements. The string all have to be non-empty and the age has to be an integer greater than zero (labor laws are lax in this imagined country, apparently). Those annotations will be enforced by Jakarta NoSQL, and will also be used when we get to the REST services. Having this sort of thing is a huge relief: since this is the only way our app will deal with data storage, there's inherently no path in the codebase that can store invalid data.

REST Services

Next, we'll start on the REST services. For a basic CRUD app like this, we'll have a few to define: listing all of them, creating a new one, and then reading, updating, and deleting an individual Employee. We'll start with the "list all" operation. This is in a second class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package rest;

import java.util.List;
import java.util.stream.Collectors;

import jakarta.inject.Inject;
import jakarta.nosql.mapping.Sorts;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import model.Employee;

@Path("employees")
public class EmployeeResource {
	
	@Inject
	private Employee.Repository employees;
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<Employee> get() {
		return employees.findAll(Sorts.sorts().asc("name")).collect(Collectors.toList());
	}
}

This class will listen at foo.nsf/xsp/app/employees for a GET and provide back a JSON array of Employee objects. Behold, in all its glory:

Call to the employees list with no entries returned

Okay, well, we haven't created anything, so the fact that the JSON result over on the right is empty is correct. It's returning JSON - that JSON just happens to be [].

Create

So we'd better add a method to actually create a new Employee document. REST-idiom-wise, this should be POST to the same path that gets the list of employees, to create a new entity:

1
2
3
4
5
6
7
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Employee create(@Valid Employee employee) {
	employee.setId(null);
	return employees.save(employee);
}

Now we're getting somewhere. Compared to the previous method, this one sprouts a @Consumes annotation to indicate that it expects a valid JSON form of an Employee, which it then takes as a method parameter. That parameter is annotated with jakarta.validation.Valid, which tells JAX-RS that it should perform bean validation on the incoming object before even calling the method. This isn't strictly necessary, but it's nice to have: without it, the method call would still fail, but the failure would show up as a stack trace from Jakarta NoSQL's innards and would have a 500 status code. We'll see in a bit what it looks like instead with this.

But first, for the normal case:

Call to create a valid employee entity

Shown here, the REST client POSTs valid JSON to this new endpoint (which is the same URL as previously) and receives back the new state of the entity with a 200 OK response. Because we don't have any extra computation going on here, it's just the same value but with the UNID filled in from having saved the document to the NSF.

If I mangle the data - say, by removing a property or, in this case, making an invalid age - I'll instead get back a 400 Bad Request response with some descriptive text:

Trying to create an invalid Employee entity

There are two minor things of note here. The first is that the response isn't in JSON. While this isn't wholly wrong per se, it's not ideal. There's an open issue to improve this. The second is something that can be changed readily within the app: the method parameter name here is arg0 instead of employee. While this again isn't wrong, since the important information is still conveyed, it'd be nice to improve this. Fortunately, we can: in Package Explorer, right-click the NSF project and go to properties. There, you can enable custom compilation settings to store the method parameter names.

Setting custom project compiler settings

I don't know why this is disabled by default.

Once you set that, the message will say employee instead of arg0, which is a bit nicer, and the better name will come along in other content types when that improves in the project too.

Query (redux) and Get

Now that we've created a document, we can re-run the base GET request and see a single-entry array:

Call to the employees list with one entry returned

That's more like it. We'll also want the ability to retrieve an individual entry by UNID, though, so we'll go back and add the method to our EmployeeResource class:

1
2
3
4
5
6
7
@Path("{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Employee getEmployee(@PathParam("id") String id) {
	return employees.findById(id)
		.orElseThrow(() -> new NotFoundException(MessageFormat.format("Could not find employee for ID {0}", id)));
}

Compared to our previous method, this adds a few new tricks:

  • The @Path("{id}") bit specifies a next level of path below employees, and the brackets indicate that it's an arbitrary value that can be picked up as a parameter.
  • The @PathParam("id") annotation indicates that the id method argument will be populated with the variable part of the path.
  • The orElseThrow(() -> new NotFoundException(...) bit uses the orElseThrow method of Optional to handle the case where no document can be found with that UNID, and then throws the JAX-RS-specific NotFoundException to trigger a proper 404 Not Found response to the client.

The results of calling this are what you might expect, returning a single JSON object representing the Employee:

Call to get a single entity

Modification

Next up is the "U" part of CRUD: updating an existing document. This method essentially composes the "create new" and "read single" methods above. In REST verbiage, this should be a PUT to the same URL as the individual GET:

1
2
3
4
5
6
7
8
@Path("{id}")
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Employee update(@PathParam("id") String id, @Valid Employee employee) {
	employee.setId(id);
	return employees.save(employee);
}

The only new concept here is the @PUT annotation - the rest is a re-composition of earlier operations. Defining this allows the caller to send a new version of an Employee entity to replace the existing one:

Call to update an existing entity

With some more work, you could also make an PATCH method that would take an unvalidated Employee and update only changed fields, but that's out of scope for this for now. That'd be a good addition for a fully-fleshed-out REST endpoint, though.

Deletion

Finally, we'll get to the last part of CRUD: deleting documents. This actually ends up being the simplest method of all:

1
2
3
4
5
@Path("{id}")
@DELETE
public void delete(@PathParam("id") String id) {
	employees.deleteById(id);
}

This listens at the same path as the last two, but for DELETE verbs. Then, all it does is delete the entity and return no content. If you chose, you could return JSON like {"success":true} or something - it's always kind of arbitrary what you respond with on DELETE beyond the success status code.

Call to delete an entity

In that screenshot, you can see that it returns 204 No Content, which is the HTTP way to say "yep, that worked, and I don't have anything else to tell you".

Conclusion

This was two classes (and a nested interface) in total, and it allowed us to create a type- and validation-safe REST API for NSF documents. Beyond just the relatively-small amount of code, there are a few things that make this foundation important.

First of all, the code is (as long as you're comfortable with Java and some of the concepts) eminently readable. This is code that you could hand off to another team member or come back to in five years and be able to very-quickly comprehend. This is a critical distinction from less-declarative frameworks like traditional XPages.

Secondly, this is a capable basis for future development. You can come back in and expand this app - more entities, additional methods, etc. - and this original code will still hold strong. You can scale the app up to medium-sized (like the OpenNTF site) or all the way to monstrosity and your framework will be consistent the whole time. This contrasts from frameworks that are either too limited to scale up or (like XPages) turn into an unmaintainable mess above a basic level.

I could go on, but I'll leave it there for now. I continue to find this environment quite pleasant to develop for, and it's always satisfying to see how several of the specs tie together like this.

Commenter Photo

Jonas - Aug 25, 2022, 1:00 PM

Nice post. Really like this way of java dev compared to all code we write today for same functionality. We are looking to move from REST Service in Xpages to using some other framework like this or Spring Boot . Is this production Ready and how will it be maintained or developed for future Domino versions?

Commenter Photo

Heiko Voigt - Aug 26, 2022, 3:11 AM

Awesome stuff. When, oh when will HCL ask you to get that into the product itself? How many countless hours of banging heads against the walls would that save?

Commenter Photo

Uwe Brahm - Aug 26, 2022, 9:03 AM

For all those that start thinking about using this XPages Jakarta EE project, I can confirm that we are using it in production. Jesse Gallagher's XPages Jakarta EE Support implementation blew away the code we have written before as complicated OSGI Java program in Domino: the better is the enemy of the good. Great work and yes it should be included in the product itself like Heiko suggested!

Commenter Photo

Jesse Gallagher - Aug 26, 2022, 1:43 PM

I can say it's production-ready in the sense that I use most components in production, and it sounds like others do too. As for future Domino versions, my intent is to continue updating it and adapting to underlying changes - that's not a contractual guarantee, as it's not an official product, but I don't expect to drop it any time soon. And, I suppose, even if I did, it's open source and could be picked up by others.

Commenter Photo

FrankTew - Sep 28, 2022, 4:23 PM

http://toolbarqueries.google.lu/url?sa=i&url=https://rraorra.com https://sdexpert.ru/bitrix/redirect.php?goto=https://rraorra.com/ https://zhigulevsk.kolesa-darom.ru/bitrix/redirect.php?goto=https://rraorra.com/ http://bifaceb.unblog.fr/?wptouch_switch=desktop&redirect=http://rraorra.com http://images.google.sk/url?q=https://rraorra.com/

Commenter Photo

Franciscleaw - Sep 29, 2022, 7:42 AM

Что можно сказать о нашем онлайн-магазине? https://toyota-prius-club.ru/klubnyi-servis-toyota-prius/216454-kupit-diplom.html#post244416 пожалуй стоит начать материал с ассортимента дипломов, которые можем предложить своим покупателям на текущий день. выбор в общем-то не ограниченный ничем. кроме этого отметим, что раньше создавались дипломы иначе. клиенту требовалось самостоятельно писать ФИО, а кроме того вписать нужные оценки. такое иногда встречается еще сейчас. однако наверное вы знаете, это обычная подделка по сути. для того, чтобы создать высокого качества копию, надо качественно соблюдать дизайн документа. здесь имеется проблема, так как к примеру есть формат, что применяли с 2002 года, а вам потребовался диплом о завершении универа за 1995-ый год. в случае если вы будете применить дизайн другого года, то тут же ясно станет, что решились просто напросто обмануть собственного руководителя. поэтому магазины, реализующие качественные дипломы, осуществляют изготовление по годами в отдельности. в случае если вы решились купить диплом например за пару тысяч, понятно, что магазин не будет заморачиваться сильно и вышлет диплом наугад в принципе. Мы выставили разумеется довольно таки дорогие расценки, однако зато в случае если у вас имеется оригинал, найти отличия от нашего документа не сможете! поясним, что частенько наши покупатели осуществляют повторный заказ и снова платят деньги. причем отлично они осознают, что ошиблись сами, мы же итак назначали стоимость близкую к себестоимости, именно поэтому сделать скидку не сможем. в случае если хотите избежать подобных сложностей - не торопитесь, так как даже небольшая ошибка потребует после изготовление нового документа, а значит и дополнительных трат. отметим, что в случае если вы заказываете бюджетный диплом, тогда в принципе страшного ничего, цена будет минимальной. однако в случае если обратитесь к профессионалам, которые делают печать на ГОЗНАКе , либо типографском бланке, затраты окажутся намного больше. многие заказчики нашего магазина выкладывают благодарные отзывы в интернете, в которых часто отмечают профессионализм тех поддержки. в том случае, если при заполнении личных данных имеются какие-либо вопросы, менеджер проконсультирует вас , а кроме этого объяснит, что конкретно стоит указать. нужна доставка каким-то определенным вариантом? Напишите оператору! кроме всего этого на веб сайте нашего интернет-магазина всегда существует FAQ, там все подробно объясняется. посоветуем прочитать, в случае если хотите купить диплом у нас в фирме. Некоторые клиенты опасаться, что вероятна ответственность в случае если купить диплом в интернете. Не волнуйтесь, в РФ накажут лишь в случае если применять такой документ для мошенничества. ответственность грозит производителю, причем штрафом здесь не обойдетесь. так что анонимность у нас значит немало. на веб-сайте не обнаружите официальный адрес, куда можно будет приехать. однако не переживайте, используем мы различные виды доставки, так что маловероятно, что появятся трудности. заметим, соблюдаем всегда полную анонимность и конечно же защищаем данные покупателей. после полной оплаты, личные данные сразу удаляются с серверов. именно поэтому здесь тоже беспокоиться не нужно, мы сформировали на самом деле профессиональную систему защиты своих клиентов.

Commenter Photo

Richardweemn - Sep 29, 2022, 8:06 PM

Commenter Photo

Stevenfloom - Sep 30, 2022, 6:52 AM

On top of this, there is substantial research to suggest that CBD is an anti-inflammatory agent, reducing pain levels and providing soothing relief for affected individuals. Compost is a great way to recycle organic material in your garden. The most important packaging requirement for CBD gummies is that the packaging protects the inner CBD gummies from all physical factors which include dust, moisture, and harsh temperature. https://2020cannabis.org/nordic-oil-cbd-ol/

Commenter Photo

Jamesjat - Oct 1, 2022, 4:34 AM

Nothing helps you unwind and get ready for bed quite like a hot, soothing drink, which is why we like these Dreamy Mixie Sticks from Zolt. Truly multidisciplinary. Is it Li Jiaqi would follow along every time, and now she remembers it pretty much, and Wuya and Liuya occasionally admit their mistakes. green weed seed

Commenter Photo

Derekapore - Oct 1, 2022, 8:35 PM

Так ну самое https://sites.google.com/view/news-online-guid-for-betting/ смотрится и ко всем другим управляющий спорта, поэтому наш брат знакомим вам ознакомиться с полным вербовкой устранять ошибки, регулирующих эти ставки, чтобы разузнать, эпизодически ваша штаб-квартира может быть уменьшена. such Link here such. like this here she is

Commenter Photo

Jerrywinee - Oct 2, 2022, 11:49 AM

You certainly won t regret it. Therefore, external factors, such as noise and caffeine, need your activities to improve sleep. That s one of the lowest, if not the lowest price-per-milligram we ve ever seen. cbd oil additive

Commenter Photo

Albertintek - Oct 2, 2022, 7:29 PM

The final most common effect in cannabis is cottonmouth. Even if you notice a saboteur in time, its removal can cause certain difficulties, because you ll inevitably have to shake the bush. CBD oil and hemp oil are not the same. https://cannabisheaven.org/marijuana-seeds-dc/

Commenter Photo

LloydTib - Oct 2, 2022, 9:56 PM

We believe in helping people live better lives. However, in the meantime, there is growing evidence to suggest that regular use is safe and that you won t build a tolerance to its effects. She had received a higher education and was quite opposed to her sister s choice, and she never gave Li Jianhui a good face. https://cannabiskingsofficial.com/can-you-eat-grapefruit-and-take-cbd-oil/

Commenter Photo

AltonKen - Oct 3, 2022, 5:55 AM

Loan restructuring - the lender's actions to change the terms of the loan repayment. These actions are primarily aimed at facilitating debt service. The most common type of restructuring is the prolongation of the loan, in some cases, banks go to reduce the interest rate on loans issued. Most often, borrowers apply to the bank for mortgage restructuring. If you roll over your mortgage, your monthly payment will be decreased, but by increasing the length of your loan, your total interest payments will be higher. As a result, the total amount of payments on the restructured loan will be higher than it would have been without the restructuring. If the client wants to restructure his loan, but the bank for some reason does not agree to it, he can try to refinance it at another bank. Some banks refinance loans made by other credit institutions.

Xe cho vay hoặc kế hoạch trả góp mà thích

Commenter Photo

IsaacGok - Oct 3, 2022, 7:45 AM

Our pets are incredibly important parts of our family and at Hemp Evolution, we enjoy nothing more than providing our customers with a way to maximise the health of their beloved cats and dogs. what to do Wei Jue lowered his head and looked at her collar, which was slightly open to the left and right because she was in his arms, and found pack of CBD gummies that she still had a vest skirt inside. You think too much. https://cannabisverifications.com/cheap-marijuana-seeds/

Commenter Photo

Jasonvab - Oct 3, 2022, 12:45 PM

At the federal level doctors are prohibited from prescribing CBD in Texas. , so the Bull Demon King chose Jindi as the venue for the banquet, and we also have the opportunity to be invited by the Bull Demon King. How about you go to eat next door. https://cbdacbd.com/cbd-oil-for-dogs-thunderstorms/

Commenter Photo

DonaldTer - Oct 3, 2022, 5:42 PM

Currently there is not guidance on who can access cannabis use for medicinal purposes. However, she found that doing so was really tiring. Sie helfen sowohl kurzfristig als auch langfristig bei psychischen und physischen Beschwerden jeglicher Art. https://cbdfarmer.org/weed-seed-feed-plexus/

Commenter Photo

JasonPrinD - Oct 3, 2022, 10:36 PM

If we fall too far outside these ranges, we become sick and die. The only way to get ahold of legal CBD in Malta is with a doctor s prescription and approval from the Superintendent of Public Health, which will grant you a control card. Each strain bifurcates the comment section on TrustPilot, so users of that particular strain can learn from other people s experiences. cbd oil for golfers

Commenter Photo

Danielletty - Oct 4, 2022, 3:30 AM

More than a dozen people, who move as do jolly cbd gummies work fast as Lin Lang one by one, seem to be as effortless as cracking egg do cbd gummies interfere with medications shells, but those who move slowly can rescue people with a few repeated knocks. Elderberry, the main ingredient, is not only delicious but also filled with antioxidants and vitamins that may boost your immune system when that dreaded cold and flu season comes around. Diabetes mellitus can present a extensive variety of symptoms, from none at all to nature s boost cbd gummies shark tank profound ketosis and coma Guidelines on the management and prevention of prediabetes 15Chinese Diabetes Society National Offic for Primary Diabetes Care National pointers for the prevention and management of diabetes in major care. https://cbdrxoil.com/types-of-weed-seeds/

Commenter Photo

ChrisJaH - Oct 4, 2022, 6:24 PM

Why Choose Joy Organics for Wholesale CBD Gummies. Umeharazawa did not say that, but he repeatedly emphasized botanical farms cbd gummies on shark tank that the state owned assets must not be lost, so in the end, the Department of Machinery Industry issued an offer to the Fang family. Full Spectrum CBD, Broad Spectrum CBD or Isolate CBD The CBD with the greatest entourage effect comes from Full Spectrum Hemp Plants, this is because full spectrum hemp plants contain the widest variety of cannabinoids and terpenes. https://coloradocannabismagazine.com/cbd-oil-tasteless/

Commenter Photo

Keithner - Oct 4, 2022, 8:49 PM

About the Brand. First, the brand uses sustainable processes, to include drip irrigation, natural pest deterrents instead of pesticides, and an organic ethanol extraction. Tourists or people who like to travel might need to travel with CBD gummies. joy organics cbd oil

Commenter Photo

Stephendoove - Oct 5, 2022, 1:43 AM

So, what is the truth about CBD oil for Sjogren s syndrome. As more brands hit the market with custom terpene and cannabinoid profiles, patients are provided with increased treatment options, and in ways that we have not seen before. This means we do not cut corners. how long does it take to grow marijuana from seed

New Comment