Very useful article, a lot of use-cases and examples! Thank you @Ashok Kumar T !
- Log in to post comments
Very useful article, a lot of use-cases and examples! Thank you @Ashok Kumar T !
Exciting initiative!
I think this line in Update is "orphaned":
set myobj = ##class(%Library.DynamicObject).%FromJSON(%request.Content) @Justin Millette, there are more settings to enable in the system to allow delegated access, e.g. a system-wide setting, mentioned in this great @Pravin Barton's article , also there is another one from @Yuri Marx
Also, I don't recommend using the WebApplication tag as it is not working properly yet; there is at least one important bug not solved: for example , CSPApplication works quite well.
Also, I see %all - I hope this is for development/demo usage only, as it is quite a generous role to use.
Great stuff!
Made an example in ObjectScript Docker template:

@Thomas Wuppermann, you can add an env variable in docker-compose.yml to make merge cpf be implied, and the merge.cpf with the name you prefer.
On a side note, have you tried ObjectScript Quality for the same linting purpose? It is very naturally connected to GitHub Actions - you can add this file to any open-source GitHub repository with objectscript to see it in action, with examples.
I think it's worth pinging CodeTidy's author @Timothy Leavitt
I agree. I changed it to "not read-only" as I wasn't to manage unit-tests without it :)
Don't know how to mark an object "modified" - otherwise, %Save() doesn't trigger the calculated property to update.
But I agree - read only is much better.
Thank you, Kai! Do you have any examples where the case-sensitive principle can be used, e.g., with C++ or C# and InterSystems ObjectScript class properties?
Right. So, what is the reason, or if you may, what are the benefits of properties to be case sensitive?
Exactly. And as I mentioned above, you cannot compile such a class with properties that differ only in case. What are the benefits of properties being case sensitive? Don't see any.
How do you mean "use case"? You just shouldn't bother about the case of property names in IRIS while sending a JSON from your frontend to the IRIS REST-API. Like you don't care while sending it vs any SQL engine backend.
Thank you, @Enrico Parisi ! But are you sure about 1.?
I've just created a class:
Class dc.sample.ObjectScript
{
property Name As %String;
property name as %String;
}}
And have a compilation error:
ERROR! In class 'dc.sample.ObjectScript' Property 'Name' from 'dc.sample.ObjectScript' and 'name' from 'dc.sample.ObjectScript' have the same name but differ in case.
Thank you, @Ashok Kumar T ! I went to submit an idea to fix this, and found your's one :) Voted.
Yes, thank you, AI. But I want it not to care about the case completely.
A yet another workaround for non-obvious caveat of using %JSON.Adapter and JSON transport around persistent data is a necessity to add an ID related property to your persistent class, like PersonId in this case:
Property PersonId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];This will allow to export ID of a record in DB automatically.
Same when you do updates with records, you need to remove this PersonId JSON from an update request, .e.g like this:
set personDynObj= {}.%FromJSON(person.Read())
do personDynObj.%Remove("PersonId") // Remove PersonId if it exists, as it is calculated
set sc = personObj.%JSONImport(personDynObj)
set sc = personObj.%Save()The simplest way to programmatically create a database in IRIS is:
do $SYSTEM.SQL.Execute("CREATE DATABASE MYDB")And 3 parameters for the spec class that help: HandleCorsRequest - it is most likely CORS will be needed, applicaton/json to support JSON content and ExposeServerExceptions for debug reasons:
Class shvarov.person.spec Extends %REST.Spec [ ProcedureBlock ]
{
Parameter HandleCorsRequest = 1;
Parameter CONTENTTYPE = "application/json";
Parameter ExposeServerExceptions = 1;
...also I always add the same _spec GET endpoint to let swagger have a working specification in docker - the default one doesn't work in docker as IRIS default spec one overrides the host variable to one, that doesn't work.
ClassMethod GetSpec() As %DynamicObject
{
set specname=$p(..%ClassName(1),".impl")
Set spec = {}.%FromJSON(##class(%Dictionary.CompiledXData).%OpenId(specname_".spec||OpenAPI").Data)
Set url = $Select(%request.Secure:"https",1:"http") _ "://"_$Get(%request.CgiEnvs("SERVER_NAME")) _ ":" _ $Get(%request.CgiEnvs("SERVER_PORT")) _ %request.Application
Set spec.servers = [{"url" : (url)}]
Quit spec
}Thanks @Daniel.Pasco. This is what I used in the example class.
Yes, it's great that we have it now in IRIS.
thanks @Robert Barbiaux !
like LastUser property too - seems helpful
Great idea on DtCreated DtUpdated naming
@Ben Spead it's great you mentioned %OnAddToSaveSet() triggers - how do you manage cases when records were changed by CREATE/UPDATE SQL query? These triggers not fire in this case, right?
Congrats, @Dmitry Maslennikov ! Well deserved!
Just've published the shvarov-persistent package once installed by
USER>zpm "install shvarov-persistent"
will add shvarov.persistent.base class which can be used as an ancestor after %Persistent, like:
Class yourclass Extends (%Persistent, shvarov.persistent.base), which will add two properties:
Property CreatedAt As %TimeStamp [ InitialExpression = {$ZDT($H, 3)} ];
Property LastUpdate As %TimeStamp [ SqlComputeCode = {set {*}=$ZDATETIME($HOROLOG,3)}, SqlComputed, SqlComputeOnChange = (%%INSERT, %%UPDATE) ];Hi @Luis Petkovicz!
Consider to try csvgen also?
it will be:
USER>zpm "install csvgen"
to install the package, and here is the usage:
USER>do ##class(community.csvgen).Generate("/folder/filename.csv")
to generate class and import the data into it from an arbitrary csv.
Also this could be a typical copy-paste for a REST API app created in a module.xml in a <module> section:
<CSPApplication
Url="/travel/api"
PasswordAuthEnabled="0"
UnauthenticatedEnabled="1"
DispatchClass="shvarov.travel.disp"
MatchRoles=":{$dbrole}"
Recurse="1"
UseCookies="2"
CookiePath="/travel/api"
/>where /travel/api is the app name, and shvarov.travel.disp - a generated class vs the swagger spec file and shvarov.travel.spec class.
Another useful comment: while generating swagger spec ask GPT to provide meaningful OperationId for every endpoint, otherwise the generated impl class will come with generated names for implementation methods like that:

I think it is a good use-case for embedded python. I asked gpt and it suggested the following python code:
from datetime import datetime
from zoneinfo import ZoneInfo
# Original string
input_str = "Thu Jul 03 08:20:00 CEST 2025"
# Strip the abbreviation since it's not useful for parsing
# Replace it with an IANA timezone
# Let's assume "CEST" corresponds to Europe/Paris
input_str_cleaned = input_str.replace("CEST", "").strip()
# Parse the datetime
dt_naive = datetime.strptime(input_str_cleaned, "%a %b %d %H:%M:%S %Y")
# Localize to the correct zone
dt_aware = dt_naive.replace(tzinfo=ZoneInfo("Europe/Paris"))
# Format in ISO format with UTC offset
print(dt_aware.isoformat()) # e.g., '2025-07-03T08:20:00+02:00'
Also it suggested to make a mapping for CEST/CET:
tz_abbreviation_map = {
"CEST": "Europe/Paris",
"CET": "Europe/Paris",
"EDT": "America/New_York",
"EST": "America/New_York",
"PST": "America/Los_Angeles",
# Add more mappings as needed
}
Thank you, @Andrew Sklyarov ! No %OnSave callback is needed for the CreatedAt property in this case