deno-api-starter-oak
deno-api-starter-oak copied to clipboard
Deno RESTful API project starter for oak framework
Deno RESTful API using OAK

This is a starter project to create Deno RESTful API using oak. oak is a middleware framework and router middleware for Deno, inspired by popular Node.js framework Koa and @koa/router.
This project covers
- Swagger Open API doc
- Docker container environment
- JWT authentication
- User authorization
- Request validation
- .env config management
- Coding architecture with
Router
,Service
&Repository
layers - Application Error Handling
- Request timing logging
- Generic request logging
Important links
- Setup
- Migrations
- Modules
- Project Layout
- How to add a new route
- How to validate request body
- How to use JWT authorization
- How to add auth guards
- Error handling
- Contributing
- Contributors
- Roadmap
Setup
We can run the project with/ without Docker.
-
Pre-Requisite
- For dockerized environment we need
- docker,
- docker-compose installed.
- To run API server without Docker we need
- MySQL server running &
- Deno run time installed
- For dockerized environment we need
-
Configuration
- In application root, rename example env file
env.example
to.env
. - An example env file contains MySQL credentials for the dockerized environment. For non-docker setup, update MySQL credentials here.
- In application root, rename example env file
-
Run API
- For Docker: Up docker-compose, this will create a docker container with the database with the given name in env.
$ docker-compose up --build
- For non-docker run API server with Deno run time
$ deno run --allow-read --allow-net app.ts
-
API
- Browse
API
at http://localhost:8000 - Browse (for Docker only) DB
Adminer
at http://localhost:8080 - Browse
Swagger Open API
Doc at http://localhost:8105
- Browse
Migrations
We use nessie to manage database migration.
- In the application root, we have
nessie.config.ts
. Make sure to update this with the DB credentials. - Run the following command to run the migration. Migration should create necessary tables and dump the data.
$ deno run --allow-net --allow-read --allow-write https://deno.land/x/[email protected]/cli.ts migrate
With this, the user table would be created and the table would be seeded with fake data
- Further, to add new migration, for example, to create new product table run
deno run --allow-net --allow-read --allow-write https://deno.land/x/[email protected]/cli.ts make create_product
Modules
Package | Purpose |
---|---|
[email protected] | Deno middleware framework |
[email protected] | Read env variables |
[email protected] | MySQL driver for Deno |
[email protected] | DB migration tool for Deno |
[email protected] | validation library |
[email protected] | JWT token encoding |
[email protected] | bcrypt encription lib |
Project Layout
.
├── .env (Make sure to create this file from given .env.example)
├── config/
| |── config.ts (configuration object)
├── db/
| |── migrations/
| |── seeds/
| ├── db.ts (DB connection object)
├── middlewares/
├── migrations/
├── services/
├── repositories/
├── helpers/
├── routes/
|── types/
|── types.ts (all types exported here)
├── app.ts (application server)
├── openapi.yml (Swagger open api definition)
└── nessie.config.ts (DB configuration for nessie migration)
How to add a new route
- Router hanlders are defined in
routes
folder. For each entity there should be separate routes file. For example user related CRUD router handlers are defined inuser.routes.ts
file. - All routes are bind with router handlers in
routes.ts
file. - To create CRUD for
cat
- Create file
cat.routes.ts
- Write router handler methods,
//cat.routes.ts import * as catService from "./../services/cat.service.ts"; /** * get list of cats */ const getCats = [ async (ctx: Context) => { const cats = await catService.getCats(); ctx.response.body = cats; } ]; //export route handler methods exports { getCats };
- Then bind
getCats
route handler with router inroutes.ts
file -
//routes.ts import * as catRoutes from "./cat.routes.ts"; // ... router initialization codes router .get("/cats", ...catRoutes.getCats);
- Create file
How to validate request body
- Here we used [email protected] module for validating forms or request body. List of available rules can be found here
- requestValidator middleware added to validate the request body.
//auth.routes.ts
import {
required,
isEmail,
} from "https://deno.land/x/[email protected]/src/rules.ts";
import { requestValidator } from "./../middlewares/request-validator.middleware.ts";
/**
* request body schema
* for cat create/update
* */
const catSchema = {
name: [required],
email: [required, isEmail],
};
/**
* create cat
*/
const createCat = [
/** request validation middleware */
requestValidator({ bodyRules: catSchema }),
/** router handler */
async (ctx: Context) => {
// ... router handler code to create cat
},
];
How to use JWT authorization
- Here, We used JWT based authentication
- Necessary JWT constants should be configured in
.env
(copy from.env.example
).
# Access token validity in ms
JWT_ACCESS_TOKEN_EXP=600000
# Refresh token validity in ms
JWT_REFRESH_TOKEN_EXP=3600000
# Secret secuirity string
JWT_TOKEN_SECRET=HEGbulKGDblAFYskBLml
- Request header should contain JWT bearer token as
Authorization
key. - Middleware JWTAuthMiddleware used to parse the
Authorization
header and decode the payload asctx.user
.
How to add auth guards
- Auth guards are dependent on the
ctx.user
provided by JWTAuthMiddleware middleware. - To define different levels of authentication guard in different route handlers, middleware userGuard defined.
-
userGuard
middleware optionally takes allowed user's roles as parameter. Otherwise, it will check only for the signed user. - Here is the example usage:-
//user.routes.ts
/**
* get list of users
* user with ADMIN role only can access
*/
const getUsers = [
userGuard(UserRole.ADMIN),
async (ctx: Context) => {
// ... route handlers code
},
];
/**
* get signed user detail
* any authenticated user can access
*/
const getMe = [
userGuard(),
async (ctx: Context) => {
// ... route handlers code
},
];
Error handling
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/asad-mlbd/deno-api-starter-oak.
Contributors
Roadmap
- [x] Open API integration
- [x] API Doc
- [x] Validation
- [x] JWT Auth
- [ ] Unit Testing
- [ ] Logger