Step 7 - Todos from FoxPro

Everything we've done so far in this tutorial has been done on the client side - the Todo list is managed as an array in memory so every time you refresh the page the list goes back to its initial state. While that works great for the initial demo it's not very useful if the values are not saved.

In this step we'll look at hooking up a couple of very simple REST API endpoints to serve the list and add and delete Todo items to mimic the functionality we have created.

Creating the Server API Methods

So we'll need to create 3 operations in the todoProcess.prg methods on the server to match the client API we've set up so far:

  • Todos - List Todos( - GET)
  • Todo - Add a new Todo ( - PUT)
  • Todo - Delete a Todo ( - DELETE)

Here's what this code looks like (added to TodoProcess.prg):

*  ToDos

IF !FILE(".\todos.dbf")
	CREATE TABLE TODOS (title c(100), descript M, entered T,completed L)

	INSERT INTO TODOS VALUES ("Load up sailing gear","Load up the car, stock up on food.",DATETIME(),.f.)
	INSERT INTO TODOS VALUES ("Get on the road out East","Get in the car and drive until you find wind",DATETIME(),.f.)
	INSERT INTO TODOS VALUES ("Wait for wind","Arrive on the scene only to find no wind",DATETIME(),.f.)
	INSERT INTO TODOS VALUES ("Pray for wind","Still waiting!",DATETIME(),.F.)
	INSERT INTO TODOS VALUES ("Sail!","Score by hitting surprise frontal band and hit it big!",DATETIME(),.F.)

SELECT title, descript as Description, entered, completed FROM TODOS ;
	 ORDER BY entered ;

RETURN "cursor:TQuery"
*   ToDos

*  ToDo

*ERROR "We're not ready to accept your ToDo's just yet..."

IF !USED("ToDos")
   USE ToDos IN 0

lcVerb = Request.GetHttpverb()

IF lcVerb = "PUT" OR lcVerb = "POST"
	IF VARTYPE(loTodo) # "O"
	   ERROR "Invalid operation: No To Do Item passed."

    LOCATE FOR TRIM(title) == loToDo.Title
    IF !FOUND()
	*** Fix for differing field name
	REPLACE descript WITH loTodo.description

IF lcVerb = "DELETE"
   lcTitle = Request.QueryString("title")
   LOCATE FOR TRIM(title) == lcTitle
   IF !FOUND()
      Response.Status = "404 Not found"
      ERROR "Invalid Todo - can't delete."      
   DELETE FOR TRIM(Title) == lcTitle
   RETURN .t.

*   ToDo

The code is pretty straight forward. The Todos method simply retrieves a cursor and returns that as JSON optionally creating the table if it doesn't exist. The ToDo method is overloaded to handle adding, updating and deleting from a single URL based on the HTTP verb. While you can have separate methods for each of those, it's common practice in REST APIs to overload the nouns (Todo) with multiple actions that can be performed on them (POST,PUT,DELETE).

Let's make sure the server is running and working and then navigate to:


to check for the JSON response of customers.

Likewise you can test an update request - here I use West Wind Web Surge to fire the request:

Adding CORS

Our server works but it needs to work with cross-domain access, in order to receive requests from the WebPack dev server that we run the development Web site on. Although it runs locally at http://localhost:3000/ effectively this is a different domain. Browsers prohibit cross domain requests unless the server provides CORS headers that allow it to be accessed by clients for authorized domains.

This is easy to add - if you create a REST service in Web Connection as we did earlier, there's a commented section that can be uncommented in todoProcess.prg in the OnProcessInit() method:

*** Add CORS header to allow cross-site access from other domains/mobile devices on Ajax calls
Response.AppendHeader("Access-Control-Allow-Methods","POST, GET, DELETE, PUT, OPTIONS")
Response.AppendHeader("Access-Control-Allow-Headers","Content-Type, *")

*** Allow cookies and auth headers
*** CORS headers are requested with OPTION by XHR clients. OPTIONS returns no content
lcVerb = Request.GetHttpVerb()
IF (lcVerb == "OPTIONS")
   *** Just exit with CORS headers set
   *** Required to make CORS work from Mobile devices

This effectively allows any browser client to access the site and is what's required in order to get the WebPack server to connect to our site. Note that eventually you'll likely run the entire site via IIS in which case the above headers are not needed, but if you want external access to the service then these sets of headers are required.

Now we're ready to call the methods from our Angular development environment.

© West Wind Technologies, 1996-2021 • Updated: 09/19/16
Comment or report problem with topic