"Stop writing endpoints"
Typescript RESTful Server Architecture
Created and maintained by Stateless Studio
PointyAPI is a library for quickly creating robust API servers.
- ORM (TypeORM) Create models which automatically create and maintain your database
- Validation (Class Validator) Use Typescript decorators to automatically validate fields
- Authentication (JWT) JWT and
request.usermake authorization a breeze
- Authorization Use Guards,
CanWrite()fields to ensure the user can read/write specific fields
Models are created as Typescript classes. Here's an example class for a basic user:
You can check out more sample models in
examples/, or read more about them in the
Routes are Express routes which chain together PointyAPI middleware functions. Here's an example User router:
;/*** Load model into PointyAPI*/// POST - Load model, filter members, and saverouter.post'/', loader, postFilter, postEndpoint;// GET - Load model, filter members, and sendrouter.get'/', loader, getFilter, getEndpoint;// PATCH - Load model, check if user owns, filter members, and saverouter.patch`/:id`, loader, onlySelf, patchFilter, patchEndpoint;// DELETE - Load model, check if user owns, and deleterouter.delete`/:id`, loader, onlySelf, deleteEndpoint;;
- Database (Postgres Recommended)
- HTTP Testing Client (Postman, cUrl, etc)
npm i -g ts-node
Create a folder for your project, and run these commands:
cd my-project npm init -y npm i pointyapi
Create a source directory,
src/, create a file
index.ts// src/index.ts;;;// Routes// TODO: We will import routes here// Setuppointy.before =;// Start the server!pointy.start;
Create a user route
By default, PointyAPI will use
ExampleUseras the user model. Let's create a route for this model, so that we can access this model through our API:
// src/routes/user.ts;;;;;;;// Set the route model to ExampleUser// Route endpointsrouter.get'/', loader, getFilter, getEndpoint;router.post'/', loader, postFilter, postEndpoint;router.patch`/:id`, loader, onlySelf, patchFilter, patchEndpoint;router.delete`/:id`, loader, onlySelf, deleteEndpoint;;
- Create a folder for routes,
- Create a new router file,
- Copy & paste router code:
- Create a folder for routes,
Import user route into server
src/index.tsup again, and let's import our new User route.; // Add import to our user model...// Routes// TODO: We will import routes here; // Add import to our user route...// Routes// TODO: We will set our routes hereapp.use'/api/v1/user', userRouter; // Add our user route to the app// Databaseawait pointy.db.setEntities...
Add a start script to
Create a database, create a
local.config.jsonfile in the root folder of your app, and replace the values:
Start & Test
Our server is ready to run:
Open Postman, and send a GET request for all users. You'll see the result as an empty array, as there are no users yet:
Create a user:
We'll send a POST request to
/api/v1/user, with the JSON body of our new user
Let's get all users again:
You can see that now a get request for all users produces an array of our one user.
So we can get and post users, but what if we try to delete or update a user? Let's try it:
So the server responded with
401 Unauthorized, and a body of
"not self". What gives?
Open at our user router (
/src/routes/user.ts). Look at our DELETE route:router.delete`/:id`, loader, onlySelf, deleteEndpoint;
onlySelf- that means only the user can access this route. We're not logged in yet, so we're certainly not "self"
Create another router file,
src/routes/auth.ts. This will be our authentication route so that we can log-in.// src/routes/auth.ts;;;;;// Set our route model & activate auth route// Router endpointsrouter.post'/', loader, loginEndpoint;router.delete'/', loader, logoutEndpoint;;
And import this into our
index.ts:...// Routes// TODO: We will import routes here;; // Add import to our auth route...// Routes// TODO: We will set our routes hereapp.use'/api/v1/user', userRouter;app.use'/api/v1/auth', authRouter; // Add our user route to the app// Databaseawait pointy.db.setEntities...
Restart the server (ctrl+c to stop the server)
We can login with Postman:
We received a "token" back (yours will be different):"token": "ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBaQ0k2TVN3aWFXRjBJam94TlRVeE1qa3dOREkxTENKbGVIQWlPakUxTlRFek1EUTRNalY5Lk40UWNJV1hPYWVzWW9KOWh4VGx1X182dnhNVndpbkFXNGhRY2JWNkRKSUE="
We can now use that token to delete our user:
Notice that now we get a
204 No Content(which means deleted successfully!).
What about the refreshToken? You may notice you got two tokens back:
tokenis short-lived - it only lasts 15 minutes or so. The
refreshTokenis long-living - it lasts about a week.
You can use the
refreshTokento issue a new
accessTokenwhen it expires.
To launch in production mode, please make sure the following variables are set (environment variables/.env)
- SITE_TITLE - Set the site title
- ALLOW_ORIGIN - Set your client URL to add the client to the CORS policy
- JWT_KEY - Set your token key to make JWT cryptographically secure
- JWT_TTL - Set your token time-to-live (seconds). Default is 15 minutes
- JWT_REFRESH_TTL - Set your refresh token time-to-live (seconds). Default is 7 days.
UUID vs Auto-Incremented IDs
It is a security risk to use auto-incremented IDs, and you should instead use UUID for all ID columns.
To switch to using UUIDs:
- Install the
- Tell TypeORM to use pgcrypto by placing the line
- Change all
- Change all
public id: numbermembers to
public id: string
- Make sure you remove
IsInt()from all ID fields, if it exists
- Ensure your front-end and anywhere you may access the ID is expecting a string
// User ID// Primary column// Authentication - User must match this to be considered "self"// Authorization - Anyone is allowed to read this fieldpublic id: string = undefined;
Read more about PointyAPI by cloning the repository and building the docs:
git clone https://github.com/StatelessStudio/pointyapi cd pointyapi npm install --ignore-scripts npm i -g typedoc npm run docs
docs/index.html in your web browser.
You can also check out the examples in
test/examples and even check out the test specs in
Read more about TypeORM: https://github.com/typeorm/typeorm
Read more about Class Validator: https://github.com/typestack/class-validator
Read more about Express: https://www.express.com/
Securing your Server
Read the security checklist: https://github.com/shieldfy/API-Security-Checklist