Basic Todo App
In this tutorial, we'll create a todo application with user authentication. A user can have many todos, and they can only access their own todos, and anyone can create a user account.
Initialize Project
To start, initialize a new Pragma project by running:
After answering Pragma's questions, there will be a directory with the name of your project containing a Pragmafile
, where you'll be writing all your Pragma code.
User
Model
Define Now we can define our User
model in the Pragmafile
:
Notice the @1
, @2
, on the User
model and it's fields, these are called indices and they are important for Pragma to be able to perform database migrations automatically.
Notice also the @user
syntax. This is a directive that tells Pragma that this is a user model, so Pragma would set up auth workflows for this model.
Todo
Model
Define Now we define the Todo
model:
enum
s are definitions of all the possible string values that a field can hold.
Define Permissions
Ok, now we need to define permissions. Our requirements dictate that a User
can only edit, read, write, and delete their own Todo
s, and that anyone can create a user account.
The first line is a definition of an access rule that applies to anonymous users; it says anonymous users of your API can create new User
records. After that comes a role definition. This block contains one section for the User
role, which contains three rules:
- When a
User
is logged in, they can push new todos and remove existing todos from theirtodos
array field - When a
User
is logged in, they canREAD
,UPDATE
, andDELETE
their own data
But still, we need to tell Pragma that a user can UPDATE
a Todo
only if it's in their list of todos
. We're going to write a function that checks whether a todo is in the current User
's list of todos.
We'll create a file called functions.js
in the same directory as the Pragmafile
containing:
Now that we've defined selfOwnsTodo
, let's use it in the User
role:
A Note On Authorization Predicates
In the example above we're returning a JSON object of the shape { result: boolean }
, not a boolean
value directly, this is because all imported functions are run as OpenWhisk serverless functions and OpenWhisk requires that all functions must return a JSON object for some reason. This will be solved in the future. This is only a problem for functions that are used by authorization rules.
For more information about how authorization rules work with functions, see the Permissions section.
Run Development Server
Alright, now that we've done all the "hard work," we can start our server by running the following command in the root of our project:
Congratulations! Now if you follow the URL printed out in your terminal, you'll find a GraphQL Playground where you can run queries/mutations such as:
User
Creating a new john
Login as and we'll get a JWT token from the server:
john
Adding todos to We need to add an authorization header containing the JWT token that was returned from the loginByUsername
mutation
john
's todos
List We need to put the JWT token in the Authorization
header here too