<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
               xmlns:atom="http://www.w3.org/2005/Atom"
               xmlns:dc="http://purl.org/dc/elements/1.1/"
               xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
               xmlns:admin="http://webns.net/mvcb/"
               xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
               xmlns:content="http://purl.org/rss/1.0/modules/content/">
            <channel>
                <title>
                	                    	BOA - Build Online Applications Forum - Recent Topics                                    </title>
                <link>https://boa-platform.com/community/</link>
                <description>BOA - Build Online Applications Discussion Board. Create Web Applications now!</description>
                <language>en-US</language>
                <lastBuildDate>Sat, 18 Apr 2026 05:45:47 +0000</lastBuildDate>
                <generator>wpForo</generator>
                <ttl>60</ttl>
                					                    <item>
                        <title>Licence</title>
                        <link>https://boa-platform.com/community/xbase/licence/</link>
                        <pubDate>Tue, 06 Dec 2022 20:12:47 +0000</pubDate>
                        <description><![CDATA[Hello Guys
I don&#039;t know How to describe my error; At the login page, some times, call the iAPI ( made with xb2net ) and some times show me a Licence Invalid and 
the URL show like this : l...]]></description>
                        <content:encoded><![CDATA[<p>Hello Guys</p>
<p>I don't know How to describe my error; At the login page, some times, call the iAPI ( made with xb2net ) and some times show me a Licence Invalid and </p>
<p>the URL show like this : localhost:82/auth/not-licence.</p>
<p>Then I close the FireFox, restart the webserve, restart the iAPI server, without success.</p>
<p>What Can I do ?</p>
<p>Best Regards</p>
<p>Osvaldo Ramirez</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>osvaldo</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/xbase/licence/</guid>
                    </item>
                                        <item>
                        <title>Automatic logout</title>
                        <link>https://boa-platform.com/community/boa-for-developers/automatic-logout/</link>
                        <pubDate>Fri, 08 Jul 2022 11:42:01 +0000</pubDate>
                        <description><![CDATA[Question from a user:
Regarding logout: As long as no logout is selected on the PC, the front end can be restarted even days later without a login being requested again.
I would like to pr...]]></description>
                        <content:encoded><![CDATA[<p>Question from a user:</p>
<p>Regarding logout: As long as no logout is selected on the PC, the front end can be restarted even days later without a login being requested again.</p>
<p>I would like to prevent this and determine from the server when a user has to register again. For example: If user A simply closes the browser without selecting logout, user B can continue userA's session without logging in. I'm looking for a way to determine from the server that a user has to log in again. For example, all tokens should expire at 3:00 a.m. and force users to log in again.</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/boa-for-developers/automatic-logout/</guid>
                    </item>
                                        <item>
                        <title>Licence system of BOA</title>
                        <link>https://boa-platform.com/community/boa-for-developers/licence-system-of-boa/</link>
                        <pubDate>Thu, 19 May 2022 14:48:59 +0000</pubDate>
                        <description><![CDATA[The licence system of BOA works as follows:

At login time there is one request to our licence server. This will check if you are working with a demo version, or if you have a personal lic...]]></description>
                        <content:encoded><![CDATA[<p>The licence system of BOA works as follows:</p>
<ul>
<li>At login time there is one request to our licence server. This will check if you are working with a demo version, or if you have a personal licence key.</li>
<li>The licence server will return the days left in case of a demo version.</li>
</ul>
<p>There could arise a problem in case a version was used on an intranet without external internet acces, or in case our licence server didn't respond to the request. The use at the client side could not login, and the browser was waiting for a response.</p>
<p>With the new version BOA will proceed in the above cases. This way you will never be blocked because of technical issues of that kind.</p>
<p>Regards,</p>
<p>Chris</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/boa-for-developers/licence-system-of-boa/</guid>
                    </item>
                                        <item>
                        <title>BOA and Harbour</title>
                        <link>https://boa-platform.com/community/any-language/boa-and-harbour/</link>
                        <pubDate>Fri, 13 May 2022 11:41:10 +0000</pubDate>
                        <description><![CDATA[Hi,
There was a question if BOA would work with Harbour.
The answer is yes, why not. As soon as you can setup a rest-API, you can use BOA. Since there is the hbhttpd for Harbour, to create...]]></description>
                        <content:encoded><![CDATA[<p>Hi,</p>
<p>There was a question if BOA would work with Harbour.</p>
<p>The answer is yes, why not. As soon as you can setup a rest-API, you can use BOA. Since there is the <span>hbhttpd for Harbour, to create your own web server, you have everything you need to start with BOA.</span></p>
<p>regards,</p>
<p>Chris.</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/any-language/boa-and-harbour/</guid>
                    </item>
                                        <item>
                        <title>New documentation for BOA</title>
                        <link>https://boa-platform.com/community/announcements/new-documentation-for-boa/</link>
                        <pubDate>Thu, 12 May 2022 11:37:59 +0000</pubDate>
                        <description><![CDATA[The new documentation is online. Check it out!
 
Best regards,
Chris]]></description>
                        <content:encoded><![CDATA[<p>The new documentation is online. Check it out!</p>
<p> </p>
<p>Best regards,</p>
<p>Chris</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/announcements/new-documentation-for-boa/</guid>
                    </item>
                                        <item>
                        <title>API Server sample for BOA</title>
                        <link>https://boa-platform.com/community/xbase/api-server-sample-for-boa/</link>
                        <pubDate>Thu, 12 May 2022 11:30:13 +0000</pubDate>
                        <description><![CDATA[Hi,
Hereby a sample to setup a REST-server. I&#039;m using xb2bnet, but I suppose it won&#039;t be difficult to modify it to the httpEndpoint of Xbase++. In this code you find three functions to add/...]]></description>
                        <content:encoded><![CDATA[<p>Hi,</p>
<p><span>Hereby a sample to setup a REST-server. I'm using xb2bnet, but I suppose it won't be difficult to modify it to the httpEndpoint of Xbase++. </span><span>In this code you find three functions to add/implement in the webserve.prg of xb2net. For the function httpserver() you only need to add this line:</span></p>
<pre contenteditable="false">soServer:FilterRequest   := {|o| FilterRequest(o)}</pre>
<p><span>This is the code, I added some comments to it:</span></p>
<pre contenteditable="false">FUNCTION HTTPServer(cPort)
**************************
   Local i, nPort

   if (i := At("/PORT:", scParamLine)) &gt; 0
      nPort := Val(SubStr(scParamLine, i+6))
   endif

   if Empty(nPort)
      nPort := Val(cPort)
   endif

   soServer := xbHTTPServer():new( INADDR_ANY, nPort )

   if soServer:ErrorCode &gt; 0
      ErrorLog("Unable to start HTTP server!" + CRLF +;
               "Error code: " + NTrim(soServer:ErrorCode) + " (" + soServer:ErrorText(soServer:ErrorCode) + ")" )
      Return NIL
   endif

   soServer:MaxConnections := 100                         // max # of concurrent client threads (note: one client may spawn more than one concurrent connection)
   soServer:RootDir        := dc_curpath()   //".\www_root"               // this is the root directory where our html files are located
   soServer:IndexFile      := nil   // "index.htm"                // default file sent to client
   soServer:onGET          := {|o| HTTPHandler(o,.f.)}       // this codeblock will be fired whenever an HTTP GET request is received
   soServer:onPOST         := {|o| HTTPHandler(o,.f.)}
   soServer:onPUT          := {|o| HTTPHandler(o,.f.)}
   soServer:onDELETE       := {|o| HTTPHandler(o,.f.)}
   soServer:onSOAP         := {|o| SOAPHandler(o)}
   soServer:onMaxConnect   := {|o| OnMaxConnect(o)}      // this is the response that will be sent when the max # of concurrent connections is reached
   soServer:onNotFound     := {|o| OnNotFound(o)}        // log access errors
   soServer:FilterRequest   := {|o| FilterRequest(o)}     // pre-process all requests before they get to the standard HTTP handlers 

   soServer:start()

   Return soServer


//-----------------------------------------------------------------------------

FUNCTION FilterRequest( oClient )
	Local cUserAgent, cFileExt
	Local cPath := oClient:HTTPRequest:Path()
	Local cRef  := oClient:HTTPRequest:Referrer()

    cPath := if(empty(cpath),"",Lower(cPath))
	if "v1.0" $ cPath		// I'm using a version in my API. Only if that version is in the request, it will be processed. All the other requests are ignored.
							// This is a first level of security.
		RestHandler(oClient)
		return .F.
	endif
	// this is an example of how to block some spam and hacker probes
	// this can be expanded and customized by for example loading data from a config or database file

	if ".php" $ cPath .or. ".cgi" $ cPath .or. "cgi-bin" $ cPath .or. ".asp" $ cPath
		oClient:NoLog := .t. // don't want to record this in the log file
		oClient:close()      // don't bother to respond, just close the connection
		Return .F.

	elseif !empty(cRef)     // limit referrer spam
		cRef := lower(cRef)
		if "best-seo" $ cRef .or. "r-e-f-e-r-e-r" $ cRef
			oClient:NoLog := .t.
			oClient:close()
			Return .F.
		endif
	endif

Return .T.

STATIC PROCEDURE RESTHandler( oThread )
*************************************************
   Local cWebFunction , rec , aVerder := {} , cTaal := "" , cUser:="" , cCodeVert:=""
   Local cAction := oThread:HTTPRequest:Path() , bFunction , nCode := 0
   Local cCommand := oThread:httpRequest:command , cEndpoint := "" , aUrl := {}
   Local cOrigin := oThread:HTTPRequest:origin() , cDossier := ""

	cOrigin := "*"
	// always add your headers to avoid CORS problems.
	oThread:HTTPResponse:setheader('Access-Control-Allow-Origin', cOrigin)
	oThread:HTTPResponse:setheader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS' )
	oThread:HTTPResponse:setheader('Access-Control-Allow-Headers', 'Origin, Content-Type, Content-Disposition, Content-Transfer-Encoding, X-Auth-Token, Authorization, BOA-Header')
	oThread:HTTPResponse:setheader('Access-Control-Expose-Headers', 'Content-Disposition')
	// The OPTIONS commnand is a pre-evalute of the browser (See https://boa-platform.com/manual/options_command.htm)
    if upper(cCommand) == "OPTIONS"	// don't validate, we don't want to loose time. 
		rec:=json():new()
		rec:error := "OPTIONS, browser pre test."
		sendjson(rec)
		return 
    endif

	aUrl := dc_tokenarray(substr(cAction,2),"/")
	// examples of URL's.
	// /v1.0/customers/123
	// /v1.0/customers/key=ABC
	// /v1.0/customers/123?labels=1
	
	if len(aUrl) &lt; 2		// url start always with version, so an URL has at least /version/command. If not stop.
		return 
	endif
	if !upper(aUrl) $ "LOGIN LICENCE"	// validate the token before proceding. In case the request is LOGIN or LICENCE, the token has to be created.
		averder := jwt_token("check")	// The token you generated at login is send with each request. If gives the possibility to verify the access rights of the user.
		if !averder					// jwt_token() is your own function to check that token, in this case it returns an array with info I need.
			rec:=json():new()
			rec:error := "Token is expired."
			sendjson(rec)
			return
		endif
		cDossier := aVerder			// These vars are from the token that is send. They are send as a parameter to each rest_ function. 
		cTaal := aVerder
		cUser := aVerder
		cCodeVert := aVerder
		nCode := aVerder
		amain(3,1,cDossier)
	endif
	cEndpoint := "REST_"+aUrl
	cWebFunction := "{|cCommand,aUrl,cDossier,cTaal,cUser,cCodeVert,nCode| " + cEndpoint+"(cCommand,aUrl,cDossier,cTaal,cUser,cCodeVert,nCode) }"
    if IsFunction(cEndpoint)    
        // macro compile and execute the requested function
		bFunction := &amp;(cWebFunction)
	    eval(bFunction,cCommand,aUrl,cDossier,cTaal,cUser,cCodeVert,nCode)	
			// The functions gets the following as parameters.
			// rest_xxx(cCommand,aPara,cDossier,cTaal,cUser,cCodeVert,nCode)
			// cCommand: GET, PUT, POST, DELETE
			// aPara: array made from the URL
			// cDossier specific for my case, since we have a multi-company solution.
			// cTaal: language of the user
			// cUser: Username which defines the rights in our Application. Same business logic as our Windows application
			// cCodeVert: is used to set a filter when opening files, same system as our Windows application. This way an external salesman only see his customers and his sales.
			// nCode: defines which menu items are active, same logis as our Windows application.
			
    else
        oThread:NotFound()
    endif
	ABOCLoseAll()		// close all my tables.
Return
</pre>
<p><span>As you can see above, it is quite similar as setting up a soapserver.</span></p>
<p> </p>
<p>Best regards,</p>
<p>Chris.</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/xbase/api-server-sample-for-boa/</guid>
                    </item>
                                        <item>
                        <title>API Endpoint code sample for BOA</title>
                        <link>https://boa-platform.com/community/xbase/api-endpoint-code-sample-for-boa/</link>
                        <pubDate>Thu, 12 May 2022 11:26:52 +0000</pubDate>
                        <description><![CDATA[Hi,Hereby a sample of an endpoint as you can use it for BOA.Below are some functions that can be used to start a customer multi tab form.BOA will call the customers function with the followi...]]></description>
                        <content:encoded><![CDATA[<p><span>Hi,</span><br /><br /><span>Hereby a sample of an endpoint as you can use it for BOA.</span><br /><br /><span>Below are some functions that can be used to start a customer multi tab form.</span><br /><span>BOA will call the customers function with the following parameters:</span><br /><br /><span>To build an empty grid:</span><br /><span>/customers/grid</span><br /><span>To fill the grid after entering a search key</span><br /><span>/customers?key=abc</span><br /><span>Open a grid and fill with data and headers</span><br /><span>/customers?key=abc&amp;headers=1</span><br /><span>To get all the tabpages with the titles and endpoints</span><br /><span>/customers/form</span><br /><span>To get all the info for an id, number of the tabpage, and all the labels to display</span><br /><span>/customers/id?tab=1&amp;labels=1</span><br /><br /><span>For this first test I added the DBF files you will need:</span><br /><span>klant with 5000 customers</span><br /><span>klantnum.ntx with index on nummer</span><br /><span>klantnaa.ntx with index on naam</span><br /><span>postnrs with the zipcodes, and with the indexes</span><br /><span>postnrs.ntx index on newpost</span><br /><span>poststad index on upper(plaats+postnr)</span><br /><span>landcode with the countrycodes and indexes.</span><br /><span>landcode.ntx with index on code</span><br /><span>landcod1.ntx with index on landcode</span><br /><br /><span>In the customers function, there is a call to:</span><br /><span>APIHEADERS: this will create the headers for the grid</span><br /><span>fCustpages(cTabpage,lLabels,@aLabels) which will create the content for the tabpage.</span><br /><span>APIblock('klant',cFields) which return the data for the row. cFields is created as a codeblock, and is evaluated.</span><br /><br /><span>I also added the following:</span><br /><span>webfileopen() to open the files. I don't know how to specify the path.</span><br /><span>allfileopen() to check if all files are correctly openend.</span><br /><span>allfileclose(cFileopen) closes all files</span><br /><br /><span>I also added the source code for sendjson(), so you can see what this does. This also uses convertJSONtoCHAR to get the json object in a string.</span></p>
<pre contenteditable="false">function rest_Customers(cCommand,aPara,cDossier,cTaal,cUser,cCodeVert,nCode)
****************************************************************************
Local aVarList, aVars:={}, cZoek:="" , nNummer := 0
local aJSON:={} , x:=0, rec, nLenZoek:=0 , index:=0 
Local cFileopen:=webfileopen("klant") + webfileopen("postnrs")+ webfileopen("landcode")

Local oClient:=ThreadObject(), bFilter:={|| .T. }
Local oRec , cVar , nPos , oSubRec 
Local cContent := "", cContentType := "" , oJson
Local nFields := 0 , lCorrectie := .F.
Local cField := "" , uValue := nil , nFieldpos , cType
Local aUnknown := {} ,  cIndexNr , over_arr := {} , uHeaders
Local lHeaders := .F. , aHeaders := {} , aOptions := {}
Local  uVar :="" , lOnlyHeaders := .F. , lLabels := .F.
Local amaandArray , nShowmaand := 1 , oOnclick , ctabPage := ""

Static cFields := "" , alabels := {} , cTab := "x" 

select klant
aVarList:=oClient:getVar(nil,VAR_QUERY)
IF Valtype(aVarList) == 'A'
	FOR x := 1 TO Len(aVarList)
		AAdd(aVars,aVarList)
	NEXT
ENDIF

if aPara == "v1.0"
	x := 0
	if !empty(aVars)
		cFields := DC_HtmlGetVar(aVars, 'fields', .T.)
		cVar := DC_HtmlGetVar(aVars, 'key', .T.)
		lHeaders := if(var2char(DC_HtmlGetVar(aVars, 'headers', .T.))="1",.T.,.F.)
		lLabels  := if(var2char(DC_HtmlGetVar(aVars, 'labels', .T.))="1",.T.,.F.)
		cIndexNr := DC_HtmlGetVar(aVars, 'index', .T.)			// you can create endpoints which defines the ctive index.
		cTabPage := DC_HtmlGetVar(aVars, 'tab', .T.)			// which tab-page is needed.
	endif
	if cTab &lt;&gt; cTabpage .or. valtype(cFields) = "U"
		cFields := ""
		asize(aLabels,0)
		cTab := cTabpage
	endif
	if len(aPara)&gt;2 .And. !empty(aPara)
		uVar := aPara
		if upper(uVar) == "GRID"			// start for the grid. BOA will add this to the request. So you know you don't need to return data.
			lOnlyHeaders := .t.
		elseif upper(uVar) == "FORM"		// start a multi tab form. BOA will the form structure, so you can send the captions and number of pages.
											// each tab is a form, and each form has his own endpoint.
			cTabpage := "0"
		else
			nNummer := val(uVar)
		endif
	else
		if !empty(cVar)
			cZoek:=ConvUtfToCp(upper(cVar))
		endif
	endif
	
	if cTabpage == "0"		// send the info for all the tab-pages of the multi tab form.
		rec:=json():new()
		rec:title := "Address"
		rec:tab := '1'
		rec:endpoint := '/customers/${id}?tab=1'
		rec:pagetype := "form"
		aadd(aheaders,rec)
		
		rec:=json():new()
		rec:title := "Administration"
		rec:tab := '2'
		rec:endpoint := '/customers/${id}?tab=2'
		rec:pagetype := "form"
		aadd(aheaders,rec)
		
		rec:=json():new()
		rec:title := "Extra"
		rec:tab := '3'
		rec:endpoint := '/customers/${id}?tab=3'
		rec:pagetype := "form"
		aadd(aheaders,rec)
		
		rec:=json():new()
		rec:title := "History"
		rec:tab := '4'
		rec:endpoint := '/customers/${id}?tab=4'
		rec:pagetype := "form"
		aadd(aheaders,rec)

		rec:=json():new()
		rec:title := "Bill"
		rec:tab := '5' 
		rec:endpoint := '/customers/${id}?tab=5'
		rec:pagetype := "form"
		aadd(aheaders,rec)	
	
		rec:=json():new()
		rec:title := "Documents"
		rec:tab := '6'
		rec:endpoint := '/customers/${id}?tab=6'
		rec:pagetype := "form"
		aadd(aheaders,rec)		
		
		rec:=json():new()
		rec:title := "Turnover"
		rec:tab := '7'
		rec:endpoint := '/customers/${id}?tab=7'
		rec:pagetype := "form"
		aadd(aheaders,rec)		
		
		rec:=json():new()
		rec:title := "Data"
		rec:tab := '8'
		rec:endpoint := '/customers/${id}?tab=8'
		rec:pagetype := "form"
		aadd(aheaders,rec)	
		
		rec:=json():new()
		rec:file := "klant"
		rec:titlefield := 'naam'
		rec:tabpages := aHeaders
		allfileclose(cFileopen)
		return sendjson(rec)	
		
	endif

	IF lOnlyHeaders		// Send only headers, so this is done.
		APIheaders('klant',@aHeaders,cTaal)
		rec:=json():new()
		rec:file := "klant"
		rec:headers := aHeaders
		allfileclose(cFileopen)
		return sendjson(rec)		
	endif
	do while .T.
		Do CASE 
		case cCommand ="DELETE"
			if !empty(nNummer)    
				klant-&gt;(dbsetorder(1))	
				klant-&gt;(dbseek(nNummer))
				if !klant-&gt;(eof()) .and. klant-&gt;(locked())
					// Here you can delete a record.
					klant-&gt;(dbrunlock())
				endif
			endif
		case cCommand == "GET"
			if empty(nNummer) .and. empty(cFields) .and. lHeaders
				APIheaders('klant',@aHeaders,cTaal)			// with API headers you can create the headers for different requests. 
			endif		
			if !empty(nNummer) 
				if empty(cFields) .and. cTabPage &lt;&gt; "99"			// if tabpage == 99 , only return the fields for the grid.
					if lLabels
						rec:=json():new()
						rec:display := ""
						rec:fieldname := "id"
						rec:length := 6
						rec:inputtype := 'hidden'
						aadd(aLabels,rec)
					endif
					cFields := fCustpages(cTabpage,lLabels,@aLabels)
				endif
				if nNummer &gt; 0
					klant-&gt;(dbsetorder(1))
					klant-&gt;(dbseek(nNummer))
					if !klant-&gt;(eof())
						oRec := APIblock('klant',cFields)
						aadd(aJson,oRec)
					else
						rec:=json():new()
						rec:type := "ID does not exist"
						rec:id := nNummer
						orec:=json():new()
						oRec:file := "klant"
						oRec:error := rec
						allfileclose(cFileopen)
						return sendjson(oRec)
					endif
				endif
			elseif !empty(cZoek)
				x:=0
				nNummer := 0
				cZoek:=upper(alltrim(strip(cZoek)))
				if left(cZoek,1)=="="
						nNummer := val(substr(cZoek,2))
						klant-&gt;(dbsetorder(1))
						klant-&gt;(dbseek(nNummer))
						oRec := APIblock('klant',cFields)
						aadd(aJson,oRec)		
				else
					nLenZoek:=len(cZoek)
					klant-&gt;(dbsetorder(2))
					klant-&gt;(dbseek(cZoek,.T.))
					Do while upper(left(strip(klant-&gt;naam),nLenZoek)) = cZoek
						x++
						oRec := APIblock('klant',cFields)
						aadd(aJson,oRec)
						klant-&gt;(dbskip(1))
					enddo
				endif
				if lHeaders .and. empty(aHeaders) .and. x &gt; 0
					APIheaders('klant',@aHeaders,cTaal)
				endif
			endif
		case cCommand == "POST"			// create the record, then change command to PUT for update
			klant-&gt;(dbsetorder(1))
			set deleted off
			klant-&gt;(dbgobottom())
			nNummer := klant-&gt;nummer+1			// get next number for the customer
			if klant-&gt;(append())
				klant-&gt;nummer := nNummer
				klant-&gt;(dbrunlock())
			endif
			cCommand := "PUT"
			loop
		case cCommand == "PUT"			// update after creation
			cContent := oClient:HTTPRequest:content
			cContentType := oClient:HTTPRequest:contenttype
			if lower(cContentType) = "application/json"
				oJson := json():new(cContent)
				if empty(nNummer) .and. oJson:hasvar("id")
					nNummer := var2num(oJson:id)
				endif
				nFields := len(oJson:_aJson)		// oJson:_aJson returns an array with the fieldnames and the data
				klant-&gt;(dbsetorder(1))
				klant-&gt;(dbseek(nNummer))
				if klant-&gt;(eof())
					rec:=json():new()
					rec:type := "ID does not exist"
					rec:id := nNummer
					orec:=json():new()
					oRec:file := "klant"
					oRec:error := rec
					allfileclose(cFileopen)
					return sendjson(oRec)				
				endif				
				if klant-&gt;(locked())				
					for x = 1 to nFields
						cField := oJson:_aJson
						uValue := oJson:_aJson
						if (nFieldpos := klant-&gt;(fieldpos(cField))) &gt; 0
							cType := upper( valtype(klant-&gt;(fieldget(nFieldpos))))
							if cType $ "CM" 		// Character or memo
								if valtype(uValue) = "L"
									uValue:=if(uValue,"J","N")
								elseif empty(uValue)
									uValue := ""
								endif							
								klant-&gt;(fieldput(nFieldpos, uValue))
							elseif cType == "N" // Numeric
								if empty(uValue)
									uValue := 0
								endif
								klant-&gt;(fieldput(nFieldpos, var2num(uValue)))
							elseif cType == "D" .and. !empty(uValue)	// Date
								if empty(uValue)
									uvalue := "  /  /  "
								endif
								klant-&gt;(fieldput(nFieldpos, ctod(uValue)))

							elseif cType == "L"		// Logical
								if empty(uValue)
									uValue := .f.
								endif
								if valtype(uValue) == "L"
									klant-&gt;(fieldput(nFieldpos, uValue))
								else
									klant-&gt;(fieldput(nFieldpos, if(upper(uValue)$"0NFALSE",.F.,.T.)))
								endif
							endif
						elseif upper(left(cField,2)) &lt;&gt; "ID"
							aadd(aUnknown,cField)
							// cUnknown += if(empty(cUnknown),""," | ") + cField 
						endif
					next
					klant-&gt;(dbrunlock())
				endif
				oRec := APIblock('klant',cFields)
				aadd(aJson,oRec)
			endif
		endcase
		exit
	enddo
endif  // end Version 1.0	
rec:=json():new()
rec:file := "klant"
if lHeaders .and. !empty(aheaders)	
	rec:headers := aHeaders
endif
if lLabels .and. !empty(aLabels)
	rec:labels := aLabels
endif
if nNummer &gt;= 0
	rec:data := aJson
endif

allfileclose(cFileopen)

return sendjson(rec)


function fCustpages(cTabPage,lLabels,aLabels)
******************************************
Local rec , cFields, oSubRec , aOptions := {}
do case					
	case cTabpage = "1"
		if lLabels
			rec:=json():new()
			rec:display := "Name / Company:"
			rec:tooltip := "Name of the customer."
			rec:fieldname := 'naam'
			rec:length := 40
			rec:inputtype := 'edit'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 4
			aadd(aLabels,rec)

			rec:=json():new()
			rec:display := "Contact:"
			rec:tooltip := "Name of contact person."
			rec:fieldname := 'naam2'
			rec:length := 40
			rec:inputtype := 'edit'
			rec:newline := .F.
			rec:labelwidth := 2
			rec:fieldwidth := 4

			
			aadd(aLabels,rec)	
			
			rec:=json():new()
			rec:display := "Address:"
			rec:tooltip := "Address:"
			rec:fieldname := 'adres'
			rec:length := 40
			rec:inputtype := 'edit'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 4
			
			aadd(aLabels,rec)
		endif
	
		cFields := "naam,naam2,adres"
		if lLabels
			rec:=json():new()
			rec:display := "Address 2:"
			rec:tooltip := "Address 2:"
			rec:fieldname := 'adres2'
			rec:length := 40
			rec:inputtype := 'edit'
			rec:newline := .F.
			rec:labelwidth := 2
			rec:fieldwidth := 4
			
			aadd(aLabels,rec)							

			rec:=json():new()
			rec:display := ''
			rec:fieldname := 'idpostnr'
			rec:length := 10
			rec:inputtype := 'hidden'
			oSubRec := json():new()
			oSubrec:endpoint := "/files/postnrs"
			oSubrec:file := "postnrs"
			oSubrec:fieldname := "id"
			rec:data := oSubRec					
			aadd(aLabels,rec)							
		
			rec:=json():new()
			rec:display := ''
			rec:fieldname := 'newpost'
			rec:length := 10
			rec:inputtype := 'hidden'
			oSubRec := json():new()
			oSubrec:endpoint := "/postnrs"
			oSubrec:file := "postnrs"
			oSubrec:fieldname := "newpost"
			rec:data := oSubRec					
			aadd(aLabels,rec)
			
			rec:=json():new()
			rec:display := "Zipcode:"
			rec:tooltip := "Postal code."
			rec:fieldname := 'postnr'
			rec:length := 8
			rec:inputtype := 'search'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 2
			rec:grid := "postnrs"
			
			oSubRec := json():new()
			oSubrec:endpoint := "/postnrs"
			oSubrec:file := "postnrs"
			oSubrec:fieldname := "postnr"
			oSubrec:buttons := "select,add,edit,exit"
			rec:data := oSubRec
			aadd(aLabels,rec)
			
			rec:=json():new()
			rec:display := ''
			rec:fieldname := 'idlandcode'
			rec:length := 10
			rec:inputtype := 'hidden'
			oSubRec := json():new()
			oSubrec:endpoint := "/landcodes"
			oSubrec:file := "/landcodes"
			oSubrec:fieldname := "id"
			rec:data := oSubRec					
			aadd(aLabels,rec)
			
			rec:=json():new()
			rec:display := ""   //fWebMessage(cTaal,60)
			rec:tooltip := "Country codes"
			rec:fieldname := 'land'
			rec:length := 3
			rec:inputtype := 'dropdown'
			rec:newline := .F.
			rec:labelwidth := 0
			rec:fieldwidth := 2							
			rec:grid := "landcode"
			oSubRec := json():new()
			oSubrec:endpoint := "/landcodes"
			oSubrec:file := "landcodes"
			oSubrec:fieldname := "code"
			rec:data := oSubRec							
			aadd(aLabels,rec)
		
			rec:=json():new()
			rec:display :=  "Hometown:"
			rec:tooltip := ""
			rec:fieldname := 'postnrs_plaats'
			rec:length := 30
			rec:inputtype := 'noedit'
			rec:newline := .F.
			rec:labelwidth := 2
			rec:fieldwidth := 4
			oSubRec := json():new()
			oSubrec:endpoint := "/postnrs"
			oSubrec:file := "postnrs"
			oSubrec:fieldname := "plaats"
			rec:data := oSubRec							
			aadd(aLabels,rec)
		
			rec:=json():new()
			rec:display := "Phone:"
			rec:tooltip := "Telephone"
			rec:fieldname := 'telefoon'
			rec:length := 16
			rec:inputtype := 'phone'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 4							
			aadd(aLabels,rec)
		
			rec:=json():new()
			rec:display := "Fax."
			rec:tooltip := "Fax."
			rec:fieldname := 'telefoon2'
			rec:length := 16
			rec:inputtype := 'phone'
			rec:newline := .F.
			rec:labelwidth := 2
			rec:fieldwidth := 4							
			aadd(aLabels,rec)
		
			rec:=json():new()
			rec:display := "Mobile:"
			rec:tooltip := "Mobile number"
			rec:fieldname := 'gsm'
			rec:length := 16
			rec:inputtype := 'phone'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 4						
			aadd(aLabels,rec)	
		

			rec:=json():new()
			rec:display := "E-Mail:"
			rec:tooltip :=  "E-Mail address."
			rec:fieldname := 'email'
			rec:length := 40
			rec:inputtype := 'email'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 10							
			aadd(aLabels,rec)						
		
			rec:=json():new()
			rec:display := "VAT:"
			rec:tooltip := ""
			rec:fieldname := 'btw_kode'
			rec:length := 3
			rec:inputtype := 'combobox'
			rec:newline := .T.
			rec:labelwidth := 2
			rec:fieldwidth := 2							
			
			oSubRec := json():new()
			oSubrec:option := "Individual"
			oSubrec:value := "P"
			aadd(aOptions,oSubrec)

			oSubRec := json():new()
			oSubrec:option := "Company"
			oSubrec:value := "B"
			aadd(aOptions,oSubrec)

			oSubRec := json():new()
			oSubrec:option := "School"
			oSubrec:value := "V"
			aadd(aOptions,oSubrec)

			oSubRec := json():new()
			oSubrec:option := "No VAT"
			oSubrec:value := "N"
			aadd(aOptions,oSubrec)
			rec:options := aOptions						 
			aadd(aLabels,rec)						
		
			rec:=json():new()
			rec:display := ""
			rec:fieldname := 'landcode'
			rec:length := 3
			rec:inputtype := 'noedit'
			rec:newline := .F.
			rec:labelwidth := 0
			rec:fieldwidth := 2
			oSubRec := json():new()
			oSubrec:endpoint := "/landcodes"
			oSubrec:file := "landcodes"
			oSubrec:fieldname := "landcode"
			rec:data := oSubRec
			aadd(aLabels,rec)						

			rec:=json():new()
			rec:display := "VAT Nr."
			rec:tooltip := "VAT number of the customer."
			rec:fieldname := 'btw_nr'
			rec:length := 16
			rec:inputtype := 'edit'
			rec:newline := .F.
			rec:labelwidth := 2
			rec:fieldwidth := 4
			aadd(aLabels,rec)
			
			
		endif
		
		cFields += ",adres2,idpostnrs,postnr,newpost,idlandcode,land,postnrs-&gt;plaats,telefoon,telefoon2,gsm"
		cFields += ",email,btw_kode,landcode,btw_nr"						
	/* tabpage 2 to 9 not converted yet
	case cTabPage = "2"
		....

endcase
return cFields

function APIblock(cAlias,cFields,lLevel)		// creates the json for the data.
****************************************
Local oRecord := json():new() , aFields := {} , aSubFields := {}
Local cBlock := '' , x , nField := 2 , cFieldname , y , cField  // , bBlock := ""
Local nRecord := 0 , cZoek := "" , nParent := 0 , cType := "" , nFldPos := 0

Local bBlock := ""

default lLevel := .F.
cAlias := upper(cAlias)

// according to the opened file, actions can be done. Some of them can be avoided by using set relation while opening the files

	do case
		case "newpost" $ cFields .and. !cAlias== "POSTNRS"
			postnrs-&gt;(dbseek( (cAlias)-&gt;newpost))
		case "land" $ cFields .and. !cAlias== "LANDCODE"
			landcode-&gt;(dbseek( (cAlias)-&gt;land))
	endcase
	// a codeblock with all the data has to be created. This is evaluated and returns the result.
	cBlock := 	'{|rec| rec:id := alltrim(str(recno())),'
	if empty(cFields)
		cFields := (cAlias)-&gt;(fieldname(1))
		do while !empty( cFieldname := (cAlias)-&gt;(fieldname(nField)) )
			cFields += ','+cFieldname
			nField ++
		enddo
	endif

	aFields := dc_tokenarray(cFields,",")

	for x = 1 to len(aFields)
		cField := aFields
		do case
			// In some cases the has to be an json object in the data. This is the system to have relations.
			// Sample in customers there is a link to the zipcodes.
			// Sample in a stock product there is a relation to productcategories
			// Below some samples to check and to create the data
			case "newpost" == cField .and. !cAlias == "POSTNRS"
				postnrs-&gt;(dbseek( (cAlias)-&gt;newpost))
				cBlock += 'rec:postnrs:= apiblock("postnrs","postnr,plaats,newpost",.t.),'
			case "land" == cField .and. !cAlias == "LANDCODE"
				landcode-&gt;(dbseek( (cAlias)-&gt;land))
				cBlock += 'rec:landcodes:= apiblock("landcode","code,land,landcode",.t.),'
		endcase
		endif
		if (nFldPos:=(cAlias)-&gt;(fieldpos( cField ))) &gt; 0 .or. "-&gt;"$cField .or. "+"$cField
			cField := strtran(cField,"-&gt;","_")
			cField := strtran(cField,"+","_")
			cField := strtran(cField,"-","_")
			cField := strtran(cField,"(recno())","id")
			if nFldPos &gt; 0
				cType := (cAlias)-&gt;(fieldinfo(nFldPos , FLD_TYPE))
				if cType == "M"
					cBlock += 'rec:'+cField+':=memoeon('+aFields+'),'
				elseif cType == "D"
					cBlock += 'rec:'+cField+':=dtoc('+aFields+'),'
				else
					cBlock += 'rec:'+cField+':='+aFields+','
				endif
			else
				cBlock += 'rec:'+cField+':='+aFields+','			
			endif
		endif
	next

	cBlock += 'rec}' 

	bBlock := &amp;(cBlock)
endif

return (cAlias)-&gt;(eval(bBlock,oRecord ))

function APIHeaders(cAlias,aHeaders,cTaal)			// create headers for a grid. 
******************************************
Local oRecord := json():new()  
Local cBlock := '' , x , bBlock , nField := 2 , cFieldname

cAlias := upper(cAlias)
if cAlias == "KLANT"
	// you can create headers as below, or you could read some klant.headers.language.txt with the json in it.
	oRecord:title := 'Customer name:'
	oRecord:data := 'naam'
	oRecord:type := 'string'
	oRecord:width := '25%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'Address:'
	oRecord:data := 'adres'
	oRecord:type := 'string'
	oRecord:width := '20%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'City'
	oRecord:data := 'postnrs-&gt;plaats'
	oRecord:type := 'string'
	oRecord:width := '20%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'Cell phone:'
	oRecord:data := 'gsm'
	oRecord:type := 'string'
	oRecord:width := '15%'	
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'Email'
	oRecord:data := 'email'
	oRecord:type := 'string'
	oRecord:width := '20%'	
	aadd(aHeaders , oRecord)

elseif cAlias == "LEVER"
	oRecord:title := 'Supplier:'
	oRecord:data := 'naam'
	oRecord:type := 'string'
	oRecord:width := '25%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'Address:'
	oRecord:data := 'adres'
	oRecord:type := 'string'
	oRecord:width := '20%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'City:'
	oRecord:data := 'postnrs-&gt;plaats'
	oRecord:type := 'string'
	oRecord:width := '15%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'Cell phone:'
	oRecord:data := 'gsm'
	oRecord:type := 'string'
	oRecord:width := '15%'
	aadd(aHeaders , oRecord)
	oRecord := json():new() 
	oRecord:title := 'Email:'
	oRecord:data := 'email'
	oRecord:type := 'string'
	oRecord:width := '25%'
	aadd(aHeaders , oRecord)
	
elseif cAlias == "STOCK"
....
else

endif

return .t.

Function  webfileopen(cFile)
***************************************
local nDelta := 0
cFile := upper(cFile)

// sample for the opening of the files and corresponding indexes.
if cFile == "KLANT" 
  do while nDelta &lt; 10
     use klant alias klant new
     if .not. neterr()
        set index to klantnum,klantnaa
         return cFile+"/"
       else
         nDelta ++
     endif
     sleep(nDelta*3)
  enddo
  return 'error/'
endif

if cFile == "POSTNRS" 
  do while nDelta &lt; 10
     use postnrs alias postnrs new
     if .not. neterr()
        set index to postnrs, poststad
         return cFile+"/"
       else
         nDelta ++
     endif
     sleep(nDelta*3)
  enddo
  return 'error/'
endif

if cFile == "LANDCODE" 
  do while nDelta &lt; 10
     use landcode alias landcode new
     if .not. neterr()
        set index to landcode, landcod1
         return cFile+"/"
       else
         nDelta ++
     endif
     sleep(nDelta*3)
  enddo
  return 'error/'
endif
return ""


function allfileopen(cFiles)
****************************
if !"error" $ lower(cFiles)
    return .T.
  else
    allfileclose(cFiles)
endif
return .F.

function allfileclose(cFiles)
****************************
Local nPos
do while (nPos:=at("/",cFiles)) &gt; 0
	close(substr(cFiles,1,nPos-1))
	cFiles := substr(cFiles,nPos+1)
enddo
return .F.

function SENDJSON(ocJSON,lFlat)  // 
*****************************
    local nErr     := 0
    local o        := threadobject()
    local cContent := if(valtype(ocJson)=="C",ocJson,convertJSONtoCHAR(ocJSON))
	default lFlat := .F.

    o:httpResponse:contenttype := 'application/json'
 
	if len(cContent) &lt; 10000 .or. lFlat
		o:httpResponse:ContentEncoding("identity") 
		o:httpresponse:CompressLevel = 0  
		o:httpResponse:content :=  cContent   
	else
		o:httpResponse:ContentEncoding( "deflate" )  
		o:httpResponse:content :=  xbZCompress( cContent, 4, @nErr, .t. )  
	endif
return .t.

</pre>
<p><span>The above is not a copy/paste solution, since it is dependant on the json parser you uses. It should give a enough information to understand how BOA works, to get an idea about the work you have to do, and if further interested to start with a test which you can do for free. You can test BOA without buying anything.</span><br /><br /><span>In attachment the files the above sample is refering to.</span><br /><br /><span>If any question, you can ask them.</span></p>
<p> </p>
<p>Best regards,</p>
<p>Chris</p>
<div id="wpfa-1331" class="wpforo-attached-file"><a class="wpforo-default-attachment" href="//boa-platform.com/wp-content/uploads/wpforo/default_attachments/1652354812-Data.zip" target="_blank" title="Data.zip"><i class="fas fa-paperclip"></i>&nbsp;Data.zip</a></div>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/xbase/api-endpoint-code-sample-for-boa/</guid>
                    </item>
                                        <item>
                        <title>Modal form + grid for listing porpouses</title>
                        <link>https://boa-platform.com/community/grids-forms/modal-form-grid-for-listing-porpouses/</link>
                        <pubDate>Wed, 20 Oct 2021 21:22:39 +0000</pubDate>
                        <description><![CDATA[I’m trying to create a list with a previous selection form.The idea is to create a modalform to give the user the chance of selecting some options and, after the user submit, show a grid wit...]]></description>
                        <content:encoded><![CDATA[<p>I’m trying to create a list with a previous selection form.</p><p>The idea is to create a modalform to give the user the chance of selecting some options and, after the user submit, show a grid with the list information requested.</p><p>I was reading the documentation and I supose that the best way to do it is to create a button element inside the modalform and call the grid when the user clicks on it. Is it the right way? If it is correct, could you give me a json definition example for this kind of buttons?</p><p>Thanks</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>David Alonso</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/grids-forms/modal-form-grid-for-listing-porpouses/</guid>
                    </item>
                                        <item>
                        <title>Date format in forms</title>
                        <link>https://boa-platform.com/community/grids-forms/date-format-in-forms/</link>
                        <pubDate>Thu, 30 Sep 2021 14:32:35 +0000</pubDate>
                        <description><![CDATA[How can I set the date format for date fields in forms?]]></description>
                        <content:encoded><![CDATA[<p>How can I set the date format for date fields in forms?</p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>David Alonso</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/grids-forms/date-format-in-forms/</guid>
                    </item>
                                        <item>
                        <title>Dashboard implementation</title>
                        <link>https://boa-platform.com/community/announcements/dashboard-implementation/</link>
                        <pubDate>Wed, 08 Jan 2020 09:28:15 +0000</pubDate>
                        <description><![CDATA[Great news, we have worked hard to implement a system for creation of a dashboard.Most online application have the ability to display information in a dashboard. Now you can accomplish this ...]]></description>
                        <content:encoded><![CDATA[<p>Great news, we have worked hard to implement a system for creation of a dashboard.</p><p>Most online application have the ability to display information in a dashboard. Now you can accomplish this with BOA. You can define different blocks, background colors and position and size of different widgets.</p><p>These widgets can contain different pagetypes as:</p><ul><li>Grids, Treegrids both with editable forms.</li><li>Kanban project.</li><li>Forms with data, static or editable.</li><li>Charts.</li><li>Pivot table.</li></ul><p>For each widget you can define a refresh rate, so it is updated automatically.</p><p>And yes, this dashboard is responsive. Now you can build everything you want, with the 'old school' language you are an expert in. </p><p> </p><p> </p>]]></content:encoded>
                        <category domain="https://boa-platform.com/community/"></category>                        <dc:creator>admin</dc:creator>
                        <guid isPermaLink="true">https://boa-platform.com/community/announcements/dashboard-implementation/</guid>
                    </item>
                                                </channel>
        </rss>
        