Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Engage 2020: Six Polite Ways to Design a RESTful API for Your Application!

sbasegmez
March 04, 2020

Engage 2020: Six Polite Ways to Design a RESTful API for Your Application!

Engage 2020 session

"Six Polite Ways to Design a RESTful API for Your Application!"
"With Domino v10 and v11, HCL is delivering on a vision to provide many new ways to architect our applications. One of the key technologies is creating RESTful APIs for our data and processes. APIs are very powerful in allowing us to build integrations between Domino and all other enterprise applications. Regardless of your experience, come to this session to see what options are available to you, what pitfalls you may experience and how to break down the borders between your applications and others. We will present the basic concepts and best practices, allowing you to walk away with tips and tricks on performance, scalability and security."

Serdar Basegmez
Developi Information Systems, London

sbasegmez

March 04, 2020
Tweet

More Decks by sbasegmez

Other Decks in Programming

Transcript

  1. #engageug Six Polite Ways to Design a RESTful API for

    your Domino Applica<on! Serdar Basegmez Developi Informa/on Systems @serdar_basegmez
  2. #engageug Serdar Basegmez HCL Master (2020) IBM Champion Alumni (2011

    - 2018) Developi InformaGon Systems, London Notes/Domino/XPages/Java Developer, half-blooded admin! Member Director at OpenNTF Board Blog: LotusNotus.com / TwiWer: @serdar_basegmez Also blogging/speaking/podcasGng on scienGfic skepGcism / criGcal thinking
  3. #engageug Agenda • Why should you care? • RESTful APIs

    • PracGcal ImplicaGons of RESTful Architectures • Designing an API • Ways to Provide REST Services for HCL Domino • Tools • Wrap-up
  4. #engageug Why Should You Care? • New Front-ends and Enhanced

    UX • Faceli`ing, but keeping data and business logic in NSF. • JS Frameworks (e.g. Angular, React), Mobile Apps, etc. • Richer experiences with chatbots, AI, etc. • IntegraGon for third party sites/applicaGons • IT becomes a huge jungle, we can’t walk alone! • Financial Systems, AI Systems, CRM, S/M AutomaGon • CollaboraGve Apps, Office 365 • AutomaGng processes using APIs • Domino Apps not independent from Business Processes • AccounGng/Sales/MarkeGng/ERP Processes User Experience Business Processes Integration
  5. #engageug RESTful Web Services Source: https://en.wikipedia.org/wiki/Representational_state_transfer Representa/onal state transfer (REST)

    is an architectural style used for web development. Systems and sites designed using this style aim for fast performance, reliability and the ability to scale (to grow and easily support extra users). To achieve these goals, developers work with reusable components that can be managed and updated without affec/ng the system as a whole while it is running.
  6. #engageug Source: https://speakerdeck.com/jeffschenck/rest-easy-api-security-done-right User Interface Business Logic Datastore Front-end Back-end

    ASP, PHP, CGI, Web Agents, JSP, etc. ← HTML, CSS, JavaScript Forms → Old School Web Applica<ons
  7. #engageug Web Applica<ons Evolving User Interface Business Logic Datastore Front-end

    Back-end Async web apps, Ruby on Rails, Django, JSF, XPages, etc. ← HTML, CSS, JavaScript Forms, AJAX →
  8. #engageug Web Applica<ons Evolving User Interface Business Logic Datastore Front-end

    Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST →
  9. #engageug [Web] Applica<ons Evolving User Interface Business Logic Datastore Mobile

    Applications Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST → Front-end
  10. #engageug [Web] Applica<ons Evolving User Interface Business Logic Datastore Mobile

    Applications Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST → Front-end Microservice Microservice Microservice
  11. #engageug [Web] Applica<ons Evolving Back-end Modern Web frameworks, Angular.js, React.js,

    etc. ← HTML, CSS, JavaScript ← REST → User Interface Business Logic Datastore Mobile Applications Front-end External Apps Microservice Microservice Microservice Integration
  12. #engageug The Conversa<on Makes Sense! GET /twink/contacts/DLEY-ACLH6Y HTTP/1.1 Host: homer.developi.info

    Cache-Control: no-cache { "zip": "13202", "state": "NY", "lastName": "Abbate", "middle": "J", "country": "US", "emailAddress": "[email protected]", "number": "DLEY-ACLH6Y", "city": "Syracuse", "firstName": "Jessica" }
  13. #engageug URLs and Resources GET POST PUT / PATCH DELETE

    /contacts/ List
 Contacts Create
 Contact N/A N/A /contacts/id Retrieve
 Contact N/A Update / Replace
 Contact Delete
 Contact Source: https://en.wikipedia.org/wiki/Representational_state_transfer
  14. #engageug Example: Collabora<on Today API • CollaboraGon Today API •

    AlternaGve front-end • Integrate to different consumers • Keep the data in NSF • Reuse current implementaGon • Three SecGons: • PresentaGon (Reading News) • CuraGon (Adding/Publishing News) • RSS Feeding (DOTS FeedMonster)
  15. #engageug Designing RESTful API • Contract Design • URLs (Resources

    + Verbs) • Data formats • Rules, Success and Error Responses • Tip: • Start with the end product. • Write a documentaGon first, before coding. • No “correct way”, only “best pracGces” • Follow ConvenGons * Icons made by Flat Icons from www.flaticon.com
  16. #engageug Designing RESTful API • Security Design • AuthenGcaGon /

    AuthorizaGon • EncrypGon • ApplicaGon Security • Tips • The scope of the implementaGon is the key. • Managing security is o`en overlooked. • Security starts with the design phase. * Icons made by Flat Icons from www.flaticon.com
  17. #engageug Designing RESTful API • Versioning • EnumeraGon, Lifecycle •

    DocumentaGon • OpenAPI Specs • Generate Server stubs, client libraries • InteracGve documentaGon • TesGng • Automated tesGng * Icons made by Flat Icons from www.flaticon.com /v1/api/xyz /v2/api/xyz /api1/xyz /api2/xyz
  18. #engageug 6 Polite Ways of RESTful Domino 1. Domino Access

    Services 2. Using Extension Library Components 3. Hardcoding Services (Web Agents, XAgents, Servlets) 4. OSGi Plugins to implement JAX-RS 5. Node.js ApplicaGons with App.Dev.Pack 6. UnconvenGonal implementaGons • SmartNSF, JAX-RS inside NSF, JAX-RS outside Domino • Hybrid Methods
  19. #engageug Domino Access Services HCL Supported Fully Func<onal REST API

    Access to Data / Calendar / Mail* / Freebusy* / Directory** * Mail and Freebusy access is part of the core product since 9.0.1FP10 / ** Directory access is enabled with the ExtLib version of OpenNTF Caching / Na<ve Security (ACL, Reader/Author fields, etc.) Server-level / Database-level / Design-level control Enable on Server Enable For Database Enable For Views
  20. #engageug Domino Access Services • OpenAPI definiGons (Swagger) are hosted

    on OpenNTF! • hWps://github.com/OpenNTF/das-api-specs
  21. #engageug Domino Access Services • Drawbacks: • Weak control over

    the data! • No checkpoints on CRUD, No coding involved… • No place for business logic! • Exposes internals • Not opGmized for some use cases • You should trust consumers and the environment…
  22. #engageug REST Components (ExtLib) • Provided and Supported by HCL

    • Customizable component version of the DAS • Computed/Filtered columns, Custom search, etc. • Event model helps building business logic on top of REST model • Custom REST Service • Write your own SSJS or Java bean • Write your CSJS rouGnes for async access • Remote Service / JSON-RPC • Dojo support for single page model
  23. #engageug REST Components (ExtLib) • Setup REST component(s) on your

    page. • Minimal coding, no administrator needed. Add to your XPage Add a Service Configure Options
  24. #engageug REST Components (ExtLib) • Drawbacks: • Easy to slip

    into a spages code! • Not opGmized for performance and scalability • Difficult to follow RESTful URL ConvenGon https://someserver.domain.com/database.nsf/somepage.xsp/service/…
  25. #engageug Hardcoding (Web agents, XAgents, Servlets…) • Old school way;

    sGll quite useful for some cases. • Great if you have pre-exisGng code (e.g. Lotusscript libraries, etc.) • Also, useful to prototype quick ideas. • Customizable, flexible and simple way to create any service.
  26. #engageug Hardcoding (Web agents, XAgents, Servlets…) • Drawbacks: • Hardcode

    everything… • e.g. Header/parameter extracGon • Very easy to slip into a spages code! • Error handling / proper tesGng needed • Difficult to follow RESTful URL ConvenGon https://someserver.domain.com/database.nsf/xagent.xsp?… https://someserver.domain.com/database.nsf/someagent?OpenAgent&…
  27. #engageug Java (JAX-RS) with OSGi Plugins • JAX-RS: ‘Java-ish’ way

    to define RESTful services • Create JAX-RS based REST services on top of OSGi plugins. • Complete Java soluGon, extensible with custom providers • OpGons • Apache Wink 1.1.2 (DAS uses!) • Jakarta EE (Official Java) • RestEasy, Apache CXF, Jersey, etc. • Code reusability outside HCL Domino world.
  28. #engageug JAX-RS Architecture JAX-RS Runtime Application Code Servlet (Customizable) HTTP/HTTPS

    Client Datastore(s) Resource Resource Resource Resource Controllers Data Accessors Tools/Utilities Request/Response Wrappers Context Helpers /BaseURI/* /BaseURI/Path-Patterns
  29. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } { "zip": "13202", "state": "NY", "lastName": "Abbate", "middle": "J", "country": "US", "emailAddress": "[email protected]", "number": "DLEY-ACLH6Y", "city": "Syracuse", "firstName": "Jessica" } Contact Resource Class Contact Resource Short JSON Representation
  30. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } The base URI for the resource In the demo, the root path of the plugin is “/twink”. So this class is enabled for requests made to: /twink/contacts/*
  31. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } This method responds to GET requests. No path defined, so this is the default responder.
  32. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } This method also responds to GET requests. But it the request path will be elected based on this format.
  33. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } Parameters will be injected into methods. /contacts?start=X&count=Y /contacts/someId JAX-RS servlet will handle type conversion. It supports ordinary java objects, enums, primitives, etc.
  34. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } There are lots of options of returning response. ResponseBuilders and some other helpers make it quite easy.
  35. #engageug @Path("/contacts") public class ContactResource { private DominoAccessor accessor =

    new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } Error handling is handled by the JAX-RS engine as well. You can inject your own errors.
  36. #engageug @Path("/contacts") public class ContactResource { ………… @POST() @Consumes(MediaType.APPLICATION_JSON) public

    Response postContactJson(String body) { Contact contact = ModelUtils.buildContactfromJson(body); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @POST() @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postContactForm(BufferedInMultiPart formData) { Contact contact = ModelUtils.buildContactfromMultipart(formData); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } This methods respond to POST requests. This time the selection depends on the incoming data type. Client marks the request with Content-Type header and Request processor will select the appropriate method here.
  37. #engageug @Path("/contacts") public class ContactResource { ………… @POST() @Consumes(MediaType.APPLICATION_JSON) public

    Response postContactJson(String body) { Contact contact = ModelUtils.buildContactfromJson(body); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @POST() @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postContactForm(BufferedInMultiPart formData) { Contact contact = ModelUtils.buildContactfromMultipart(formData); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } Wink injects the incoming data into the method automatically. JAX-RS libraries provide their own implementation to process different data formats (Multipart, Atom, XML, JSON, etc.)
  38. #engageug JAX-RS Methods • Drawbacks: • Plugin only • Difficult

    if you are not familiar, Takes Gme to learn • Overkill? • Not suitable for small projects and simple needs • Tool selecGon is criGcal. • Apache Wink is old school • IntegraGng alternaGves might be difficult
  39. #engageug Node.js • DQL + Domino AppDev Pack: • Domino

    Query Language • AppDev Pack: Proton Task / domino-db.js • Countless opportuniGes in the most popular framework • Different possibiliGes for the architecture • Wide range of opGons for tooling https://insights.stackoverflow.com/survey/2019
  40. #engageug Node.js Support for Domino Apps HCL Domino Server Proton

    Node.js Layer domino-db.js GRPC Application ……… Application REST New Add-on Domino v10 Routers for the RESTful service Might be on the same box or not MyRoutes.js NSF
  41. #engageug DQL: Domino Query Language • Available since Domino V10

    • OpGmized query access to NSF data • Domino server extracts and opGmizes data for DQL access John Curtis, “Factory Tour DQL Performance Explanation”, https://jdcurtis.blog/2019/07/17/factory-tour-dql-performance-explanation/
  42. #engageug IAM: Iden<ty and Access Management * IAM Setup documentation

    - https://doc.cwpcollaboration.com/appdevpack/docs/en/iam_setup.html
  43. #engageug Other Implementa<ons • UnconvenGonal implementaGons • Why? • Specific

    needs • Matching skills • Prefer soluGons with less effort • Why not? • No support • Version lock-in • ExperGse needed
  44. #engageug SmartNSF • OpenNTF Plugin • Beta-7 version. • Developers:

    • ChrisGan Güdemann • MarGn Jinoch • Define your REST Services from DDE! • Uses Groovy • Auto-generate OpenAPI definiGons Looking for Contributors!
  45. #engageug JAX-RS inside NSF • IniGal work: MarGn Pradny •

    Included in Jakarta EE by Jesse Gallagher • JAX-RS 2.1 support (Apache Wink: JAX RS 1.1.1) • No need to deal with OSGi • Directly create annotated classes inside NSF • OpenAPI SpecificaGon annotaGons supported https://www.pradny.com/2019/05/using-jax-rs-inside-nsf-follow-up.html
  46. #engageug Integrate Domino into Open Liberty • ImplementaGon: Jesse Gallagher

    • Inspired by Sven Hasselbach • Run Java EE server • e.g. Open Liberty, open source variant of Websphere Liberty • Run Domino HTTP Stack side by side • Deploy Java EE projects connecGng to Domino data naGvely. • HTTP/2, Servlet 4, Websockets, etc. • High performance, scalability https://frostillic.us/blog/posts/2019/1/3/be9501a07279b40d85258377007b0dfd
  47. #engageug Hybrid Approaches • Wrap services using a suitable front-end

    • e.g. Express, Node-RED, etc. • Add some magic • Extra processing, computaGon, security, etc. • Wrap missing parts from exisGng assets • Combine best of all worlds • Consistency across the soluGon • OpGmized ImplementaGon Internal Domino Server Node.js JAX-RS Service ExtLib REST Service DAS - Data API Proton domino-db.js JavaScript Services Wrapped Services Data / Application Layer Non-Domino Services API Clients API Clients API Clients
  48. #engageug Tooling - OpenAPI • OpenAPI SpecificaGons / DocumentaGon •

    Swagger Tools - swagger.io • Stoplight Studio - stoplight.io • Apicurio - apicur.io
  49. #engageug Tooling - Tes<ng • TesGng • Postman - postman.com

    • Paw - paw.cloud [Mac only] • SoapUI - soapui.org
  50. #engageug Resources • Serdar Başeğmez: Node.js demo for this session


    hWps://github.com/sbasegmez/Engage19Demo • Tom Van Aken: Rest calls and JSON Parsing in LotusScript
 hWps://vanakentom.wordpress.com/2019/01/15/rest-calls-and-json-parsing-in-lotusscript/ • Serdar Başeğmez: Demo for IBM Connect 2017 session
 hWps://github.com/sbasegmez/IC17RestDemo • Serdar Başeğmez: Apache Wink Template and Demo for Icon UK 2016
 hWps://github.com/sbasegmez/RestAssuredDemo • Graham Acres / Serdar Başeğmez: The Journey to Becoming a Social ApplicaGon Developer (IBM Connect 2014)
 hWps://speakerdeck.com/sbasegmez/bp308-the-journey-to-becoming-a-social-applicaGon-developer • Stephan H. Wissel: Custom REST service in XPages using a service bean
 hWps://wissel.net/blog/d6plinks/SHWL-9Q55QL
  51. #engageug Resources (cont.) • Eric McCormick: Series on JSON Data

    with Java in XPages
 hWps://edm00se.io/json-with-java-in-xpages • Thomas Ladehoff: REST Services with the XPages Extension Library
 hWps://www.assono.de/blog/d6plinks/REST-Services-with-the-XPages-Extension-Library • Paul Withers: XPages OSGi Plugins series
 hWp://www.intec.co.uk/xpages-osgi-plugins-1-an-introducGon/ • John Cooper: Domino OSGI (Part 1) - Configuring Eclipse for XPages OSGI Plugins
 hWp://developmentblog.johnmcooper.co.uk/2014/05/configuring-eclipse-for-xpages-osgi-plugins-part1.html • Toby Samples: JAX-RS or THE way to do REST in Domino series
 hWps://tobysamples.wordpress.com/2015/04/28/jax-rs-or-the-way-to-do-rest-in-domino-part-1/ • Jesse Gallagher: Eclipse Tutorial for Domino Developers
 hWps://github.com/jesse-gallagher/eclipse-tutorial-oct2015/wiki/Java
  52. #engageug Resources (cont.) • Ulrich Krause: NotesJsonNavigator, NotesJsonElement, NotesJsonArray, NotesJsonObject

    example
 hWps://www.eknori.de/2019-01-01/notesjsonnavigator-notesjsonelement-notesjsonarray-notesjsonobject-example/ • John Dalsgaard: REST Services in IBM Domino/XWork
 hWps://www.dalsgaard-data.eu/blog/rest-services-in-ibm-dominoxwork • Oliver Busse: First dive into Domino and Node.js
 hWp://oliverbusse.notesx.net/hp.nsf/blogpost.xsp?documentId=2E52 • Oliver Busse: Domino, Proton, IAM, OAuth (Series)
 hWp://oliverbusse.notesx.net/hp.nsf/blogpost.xsp?documentId=2FEA • Sven Hasselbach: node.js, domino-db & Docker
 hWp://hasselba.ch/blog/?p=2670 • Jesse Gallagher: That Java Thing, Part 1: The Java Problem in the Community
 hWps://frosGllic.us/blog/thread.xsp?thread=That+Java+Thing
  53. #engageug Resources (cont.) • Graham Acres / Heiko Voigt: Engage

    2019 Demo of Domino v10, Proton and Node.js session 
 hWps://github.com/c3ug/engage2019democode • Troy Reimer: OpenNTF Project: LotusScript implementaGon of a reader and writer for JSON
 hWps://opennx.org/main.nsf/project.xsp?r=project/JSON%20LotusScript%20Classes • Bansilal Haudakari: EffecGve API Design 
 hWps://www.slideshare.net/BansilalHaudakari/effecGve-api-design • Heiko Voigt: How to set up the Domino 10 OAUTH2 Provider - IAM Basics 
 hWps://www.harbour-light.org/2019/02/18/how-to-set-up-the-domino-10-oauth2-provider-iam-basics/ • Mar<n Pradny: Using JAX-RS inside NSF follow-up - Swagger UI 
 hWps://www.pradny.com/2019/05/using-jax-rs-inside-nsf-follow-up.html • Cross Canada Collabora<on User Group: John CurGs - Domino Query Language with John CurGs Part 1
 hWps://www.youtube.com/watch?v=qm_LJ9-6OTE